Description
Describe the bug
I encountered an issue while using openai-python version 1.52.0. The error started occurring today, whereas the same code was working fine before. The error occurs in the following line in file src/openai/_models.py:
if field_info.annotation and is_literal_type(field_info.annotation):
Error:
AttributeError: 'ModelField' object has no attribute 'annotation'
To resolve this issue, I modified the code as follows:
if hasattr(field_info, 'annotation') and is_literal_type(field_info.annotation):
After this change, my service started working again as expected.
Key Concerns:
- Possible Regression: The issue was not present in earlier versions but started occurring after openai-python 1.65.0 was released.
- Backward Compatibility: Can you confirm if recent changes have introduced an incompatibility with older versions?
- Priority Investigation: Since this issue affects previously stable versions, it could impact multiple users relying on openai-python.
To Reproduce
Using Python 3.9 and pydantic 1.9.0, run the following:
run = await self.client.beta.threads.runs.create_and_poll(
thread_id=thread_id,
assistant_id=assistant_id,
)
messages = await self.client.beta.threads.messages.list(
thread_id=thread_id, run_id=run.id
)
Expected Behavior:
The code should execute successfully and return messages from the assistant's thread.
Actual Behavior:
The error AttributeError: 'ModelField' object has no attribute 'annotation' is raised.
Code snippets
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
if isinstance(union, CachedDiscriminatorType):
return union.__discriminator__
discriminator_field_name: str | None = None
for annotation in meta_annotations:
if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None:
discriminator_field_name = annotation.discriminator
break
if not discriminator_field_name:
return None
mapping: dict[str, type] = {}
discriminator_alias: str | None = None
for variant in get_args(union):
variant = strip_annotated_type(variant)
if is_basemodel_type(variant):
if PYDANTIC_V2:
field = _extract_field_schema_pv2(variant, discriminator_field_name)
if not field:
continue
# Note: if one variant defines an alias then they all should
discriminator_alias = field.get("serialization_alias")
field_schema = field["schema"]
if field_schema["type"] == "literal":
for entry in cast("LiteralSchema", field_schema)["expected"]:
if isinstance(entry, str):
mapping[entry] = variant
else:
field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
if not field_info:
continue
# Note: if one variant defines an alias then they all should
discriminator_alias = field_info.alias
if hasattr(field_info, 'annotation') and is_literal_type(field_info.annotation):
for entry in get_args(field_info.annotation):
if isinstance(entry, str):
mapping[entry] = variant
if not mapping:
return None
details = DiscriminatorDetails(
mapping=mapping,
discriminator_field=discriminator_field_name,
discriminator_alias=discriminator_alias,
)
cast(CachedDiscriminatorType, union).__discriminator__ = details
return details
OS
mac 15.3.1
Python version
3.9.0
Library version
1.52.0