mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Azure chat client (#185)
* updated openai, fcc works, with sample * reduced files in openai * Add azure chat client * fix tests * Update python/packages/main/tests/unit/test_openai_chat_completion_base.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update python/packages/azure/agent_framework/azure/__init__.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update python/packages/azure/agent_framework/azure/_azure_openai_settings.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * PR comments * fix bad merge * disable tests for now * actually disable tests for azure * fix tests, align test files with merge changes * update code for new project structure * PR comments * add streaming integration tests. Fix flakiness --------- Co-authored-by: eavanvalkenburg <github@vanvalkenburg.eu> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
fa96f74ee9
commit
f0dc661c3e
@@ -10,7 +10,6 @@ except importlib.metadata.PackageNotFoundError:
|
||||
__version__ = "0.0.0" # Fallback for development mode
|
||||
|
||||
_IMPORTS = {
|
||||
"get_logger": "._logging",
|
||||
"AFBaseModel": "._pydantic",
|
||||
"AFBaseSettings": "._pydantic",
|
||||
"Agent": "._agents",
|
||||
@@ -18,39 +17,41 @@ _IMPORTS = {
|
||||
"AgentRunResponseUpdate": "._types",
|
||||
"AgentThread": "._agents",
|
||||
"AITool": "._tools",
|
||||
"ai_function": "._tools",
|
||||
"AIFunction": "._tools",
|
||||
"AIContent": "._types",
|
||||
"AIContents": "._types",
|
||||
"ChatClient": "._clients",
|
||||
"ChatClientAgent": "._agents",
|
||||
"ChatClientAgentThread": "._agents",
|
||||
"ChatClientAgentThreadType": "._agents",
|
||||
"ChatClientBase": "._clients",
|
||||
"ChatFinishReason": "._types",
|
||||
"ChatMessage": "._types",
|
||||
"ChatOptions": "._types",
|
||||
"ChatResponse": "._types",
|
||||
"ChatResponseUpdate": "._types",
|
||||
"ChatRole": "._types",
|
||||
"ChatToolMode": "._types",
|
||||
"DataContent": "._types",
|
||||
"EmbeddingGenerator": "._clients",
|
||||
"ErrorContent": "._types",
|
||||
"FunctionCallContent": "._types",
|
||||
"FunctionResultContent": "._types",
|
||||
"GeneratedEmbeddings": "._types",
|
||||
"HttpsUrl": "._pydantic",
|
||||
"InputGuardrail": ".guard_rails",
|
||||
"OutputGuardrail": ".guard_rails",
|
||||
"SpeechToTextOptions": "._types",
|
||||
"StructuredResponse": "._types",
|
||||
"TextContent": "._types",
|
||||
"TextReasoningContent": "._types",
|
||||
"DataContent": "._types",
|
||||
"TextToSpeechOptions": "._types",
|
||||
"UriContent": "._types",
|
||||
"UsageContent": "._types",
|
||||
"UsageDetails": "._types",
|
||||
"FunctionCallContent": "._types",
|
||||
"FunctionResultContent": "._types",
|
||||
"ChatFinishReason": "._types",
|
||||
"ChatMessage": "._types",
|
||||
"ChatResponse": "._types",
|
||||
"StructuredResponse": "._types",
|
||||
"ChatResponseUpdate": "._types",
|
||||
"ChatRole": "._types",
|
||||
"ErrorContent": "._types",
|
||||
"GeneratedEmbeddings": "._types",
|
||||
"ChatOptions": "._types",
|
||||
"ChatToolMode": "._types",
|
||||
"ChatClient": "._clients",
|
||||
"ChatClientBase": "._clients",
|
||||
"ai_function": "._tools",
|
||||
"get_logger": "._logging",
|
||||
"use_tool_calling": "._clients",
|
||||
"EmbeddingGenerator": "._clients",
|
||||
"InputGuardrail": ".guard_rails",
|
||||
"OutputGuardrail": ".guard_rails",
|
||||
"TextToSpeechOptions": "._types",
|
||||
"SpeechToTextOptions": "._types",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from . import __version__ # type: ignore[attr-defined]
|
||||
from ._agents import Agent, AgentThread, ChatClientAgent, ChatClientAgentThread, ChatClientAgentThreadType
|
||||
from ._clients import ChatClient, ChatClientBase, EmbeddingGenerator, use_tool_calling
|
||||
from ._logging import get_logger
|
||||
from ._pydantic import AFBaseModel, AFBaseSettings
|
||||
from ._pydantic import AFBaseModel, AFBaseSettings, HttpsUrl
|
||||
from ._tools import AIFunction, AITool, ai_function
|
||||
from ._types import (
|
||||
AgentRunResponse,
|
||||
@@ -63,6 +63,7 @@ __all__ = [
|
||||
"FunctionCallContent",
|
||||
"FunctionResultContent",
|
||||
"GeneratedEmbeddings",
|
||||
"HttpsUrl",
|
||||
"InputGuardrail",
|
||||
"OutputGuardrail",
|
||||
"SpeechToTextOptions",
|
||||
|
||||
@@ -112,7 +112,14 @@ def _tool_call_non_streaming(func: TInnerGetResponse) -> TInnerGetResponse:
|
||||
for attempt_idx in range(getattr(self, "__maximum_iterations_per_request", 10)):
|
||||
response = await func(self, messages=messages, chat_options=chat_options)
|
||||
# if there are function calls, we will handle them first
|
||||
function_calls = [it for it in response.messages[0].contents if isinstance(it, FunctionCallContent)]
|
||||
function_results = {
|
||||
it.call_id for it in response.messages[0].contents if isinstance(it, FunctionResultContent)
|
||||
}
|
||||
function_calls = [
|
||||
it
|
||||
for it in response.messages[0].contents
|
||||
if isinstance(it, FunctionCallContent) and it.call_id not in function_results
|
||||
]
|
||||
if function_calls:
|
||||
# Run all function calls concurrently
|
||||
results = await asyncio.gather(*[
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
|
||||
from typing import Any, ClassVar, TypeVar
|
||||
from typing import Annotated, Any, ClassVar, TypeVar
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, UrlConstraints
|
||||
from pydantic.networks import AnyUrl
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
HttpsUrl = Annotated[AnyUrl, UrlConstraints(max_length=2083, allowed_schemes=["https"])]
|
||||
|
||||
|
||||
class AFBaseModel(BaseModel):
|
||||
"""Base class for all pydantic models in the Agent Framework."""
|
||||
|
||||
@@ -16,7 +16,16 @@ from collections.abc import (
|
||||
)
|
||||
from typing import Annotated, Any, ClassVar, Generic, Literal, TypeVar, overload
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, ValidationError, field_validator, model_validator
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
ConfigDict,
|
||||
Field,
|
||||
PrivateAttr,
|
||||
ValidationError,
|
||||
field_validator,
|
||||
model_serializer,
|
||||
model_validator,
|
||||
)
|
||||
|
||||
from ._pydantic import AFBaseModel
|
||||
from ._tools import AITool, ai_function
|
||||
@@ -1398,6 +1407,11 @@ class ChatToolMode(AFBaseModel):
|
||||
return self.mode == other.mode and self.required_function_name == other.required_function_name
|
||||
return False
|
||||
|
||||
@model_serializer
|
||||
def serialize_model(self) -> str:
|
||||
"""Serializes the ChatToolMode to just the mode string."""
|
||||
return self.mode
|
||||
|
||||
|
||||
ChatToolMode.AUTO = ChatToolMode(mode="auto") # type: ignore[assignment]
|
||||
ChatToolMode.REQUIRED_ANY = ChatToolMode(mode="required") # type: ignore[assignment]
|
||||
@@ -1496,10 +1510,13 @@ class ChatOptions(AFBaseModel):
|
||||
Dictionary of settings for provider.
|
||||
"""
|
||||
default_exclude = {"additional_properties"}
|
||||
# No tool choice if no tools are defined
|
||||
if self.tools is None or len(self.tools) == 0:
|
||||
default_exclude.add("tool_choice")
|
||||
merged_exclude = default_exclude if exclude is None else default_exclude | set(exclude)
|
||||
|
||||
settings = self.model_dump(exclude_none=True, by_alias=by_alias, exclude=merged_exclude)
|
||||
settings = {k: v for k, v in settings.items() if v and not isinstance(v, dict)}
|
||||
settings = {k: v for k, v in settings.items() if v}
|
||||
settings.update(self.additional_properties)
|
||||
for key in merged_exclude:
|
||||
settings.pop(key, None)
|
||||
|
||||
@@ -7,6 +7,8 @@ PACKAGE_EXTRA = "azure"
|
||||
|
||||
_IMPORTS = {
|
||||
"__version__": "agent_framework_azure",
|
||||
"AzureChatClient": "agent_framework_azure",
|
||||
"get_entra_auth_token": "agent_framework_azure",
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,12 @@ class ServiceContentFilterException(ServiceResponseException):
|
||||
pass
|
||||
|
||||
|
||||
class ServiceInvalidAuthError(ServiceException):
|
||||
"""An error occurred while authenticating the service."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ServiceInvalidExecutionSettingsError(ServiceResponseException):
|
||||
"""An error occurred while validating the execution settings of the service."""
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
|
||||
from ._chat_client import OpenAIChatClient
|
||||
from ._shared import OpenAISettings
|
||||
from ._shared import OpenAIHandler, OpenAIModelTypes, OpenAISettings
|
||||
|
||||
__all__ = [
|
||||
"OpenAIChatClient",
|
||||
"OpenAIHandler",
|
||||
"OpenAIModelTypes",
|
||||
"OpenAISettings",
|
||||
]
|
||||
|
||||
@@ -239,11 +239,11 @@ class OpenAIChatClientBase(OpenAIHandler, ChatClientBase):
|
||||
function_call = self._openai_content_parser(content)
|
||||
if "tool_calls" not in args:
|
||||
args["tool_calls"] = []
|
||||
args["tool_calls"].append(function_call)
|
||||
args["tool_calls"].append(function_call) # type: ignore
|
||||
case _:
|
||||
if "content" not in args:
|
||||
args["content"] = []
|
||||
args["content"].append(self._openai_content_parser(content))
|
||||
args["content"].append(self._openai_content_parser(content)) # type: ignore
|
||||
if "content" in args or "tool_calls" in args:
|
||||
all_messages.append(args)
|
||||
return all_messages
|
||||
|
||||
Reference in New Issue
Block a user