Python: [Breaking] removed pydantic from types and workflows (#917)

* removed pydantic from types

* fix test

* fix test

* fix tests

* fix assistants client

* Remove Pydantic usage from workflow code.

* updated pydantic removal

* updated lock and test fixes

* fix mypy

* updated build system

* updated chat client parsing

* fix broken test

---------

Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
Eduard van Valkenburg
2025-09-29 23:19:58 +02:00
committed by GitHub
Unverified
parent 647db9635a
commit b4ebafa9b1
56 changed files with 3881 additions and 1735 deletions
@@ -28,12 +28,12 @@ from .._types import (
ChatOptions,
ChatResponse,
ChatResponseUpdate,
ChatToolMode,
Contents,
FunctionCallContent,
FunctionResultContent,
Role,
TextContent,
ToolMode,
UriContent,
UsageContent,
UsageDetails,
@@ -115,7 +115,7 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
if not openai_settings.chat_model_id:
raise ServiceInitializationError(
"OpenAI model ID is required. "
"Set via 'ai_model_id' parameter or 'OPENAI_CHAT_MODEL_ID' environment variable."
"Set via 'model_id' parameter or 'OPENAI_CHAT_MODEL_ID' environment variable."
)
super().__init__(
@@ -361,7 +361,7 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
if chat_options is not None:
run_options["max_completion_tokens"] = chat_options.max_tokens
run_options["model"] = chat_options.ai_model_id
run_options["model"] = chat_options.model_id
run_options["top_p"] = chat_options.top_p
run_options["temperature"] = chat_options.temperature
@@ -392,7 +392,7 @@ class OpenAIAssistantsClient(OpenAIConfigMixin, BaseChatClient):
if chat_options.tool_choice == "none" or chat_options.tool_choice == "auto":
run_options["tool_choice"] = chat_options.tool_choice
elif (
isinstance(chat_options.tool_choice, ChatToolMode)
isinstance(chat_options.tool_choice, ToolMode)
and chat_options.tool_choice == "required"
and chat_options.tool_choice.required_function_name is not None
):
@@ -217,7 +217,7 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient):
return ChatResponseUpdate(
role=Role.ASSISTANT,
contents=[UsageContent(details=self._usage_details_from_openai(chunk.usage), raw_representation=chunk)],
ai_model_id=chunk.model,
model_id=chunk.model,
additional_properties=chunk_metadata,
response_id=chunk.id,
message_id=chunk.id,
@@ -236,7 +236,7 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient):
created_at=datetime.fromtimestamp(chunk.created).strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
contents=contents,
role=Role.ASSISTANT,
ai_model_id=chunk.model,
model_id=chunk.model,
additional_properties=chunk_metadata,
finish_reason=finish_reason,
raw_representation=chunk,
@@ -402,8 +402,8 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient):
elif content.media_type and "mp3" in content.media_type:
audio_format = "mp3"
else:
# Fallback to default model_dump for unsupported audio formats
return content.model_dump(exclude_none=True)
# Fallback to default to_dict for unsupported audio formats
return content.to_dict(exclude_none=True)
# Extract base64 data from data URI
audio_data = content.uri
@@ -435,11 +435,11 @@ class OpenAIBaseChatClient(OpenAIBase, BaseChatClient):
},
}
return content.model_dump(exclude_none=True)
return content.to_dict(exclude_none=True)
return content.model_dump(exclude_none=True)
return content.to_dict(exclude_none=True)
case _:
return content.model_dump(exclude_none=True)
return content.to_dict(exclude_none=True)
@override
def service_url(self) -> str:
@@ -905,7 +905,7 @@ class OpenAIBaseResponsesClient(OpenAIBase, BaseChatClient):
contents=contents,
conversation_id=conversation_id,
role=Role.ASSISTANT,
ai_model_id=model,
model_id=model,
additional_properties=metadata,
raw_representation=event,
)
@@ -17,13 +17,13 @@ from openai.types.chat import ChatCompletion, ChatCompletionChunk
from openai.types.images_response import ImagesResponse
from openai.types.responses.response import Response
from openai.types.responses.response_stream_event import ResponseStreamEvent
from pydantic import BaseModel, ConfigDict, Field, SecretStr, validate_call
from pydantic import ConfigDict, Field, SecretStr, validate_call
from pydantic.types import StringConstraints
from .._logging import get_logger
from .._pydantic import AFBaseModel, AFBaseSettings
from .._telemetry import APP_INFO, USER_AGENT_KEY, prepend_agent_framework_to_user_agent
from .._types import ChatOptions, Contents, SpeechToTextOptions, TextToSpeechOptions
from .._types import ChatOptions, Contents
from ..exceptions import ServiceInitializationError
logger: logging.Logger = get_logger("agent_framework.openai")
@@ -42,7 +42,7 @@ RESPONSE_TYPE = Union[
_legacy_response.HttpxBinaryResponseContent,
]
OPTION_TYPE = Union[ChatOptions, SpeechToTextOptions, TextToSpeechOptions, dict[str, Any]]
OPTION_TYPE = Union[ChatOptions, dict[str, Any]]
__all__ = [
@@ -52,20 +52,20 @@ __all__ = [
def _prepare_function_call_results_as_dumpable(content: Contents | Any | list[Contents | Any]) -> Any:
if isinstance(content, list):
# Particularly deal with lists of BaseModel
# Particularly deal with lists of Content
return [_prepare_function_call_results_as_dumpable(item) for item in content]
if isinstance(content, dict):
return {k: _prepare_function_call_results_as_dumpable(v) for k, v in content.items()}
if isinstance(content, BaseModel):
return content.model_dump(exclude={"raw_representation", "additional_properties"})
if hasattr(content, "to_dict"):
return content.to_dict(exclude={"raw_representation", "additional_properties"})
return content
def prepare_function_call_results(content: Contents | Any | list[Contents | Any]) -> str | list[str]:
"""Prepare the values of the function call results."""
if isinstance(content, BaseModel):
# BaseModel is already dumpable, shortcut for performance
return content.model_dump_json(exclude={"raw_representation", "additional_properties"})
if isinstance(content, Contents):
# For BaseContent objects, use to_dict and serialize to JSON
return json.dumps(content.to_dict(exclude={"raw_representation", "additional_properties"}))
dumpable = _prepare_function_call_results_as_dumpable(content)
if isinstance(dumpable, str):