mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [Breaking] Upgrade to azure-ai-projects 2.0+ (#4536)
* Prepare azure-ai-projects 2.0 GA compatibility Add allow_preview support for internal AIProjectClient creation, keep backward compatibility for renamed SDK model classes, and align Azure AI/core paths and tests for GA validation workflows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * upgrade to ai-project==2.0.0 * Python: remove azure-ai-projects keyword-guard paths Assume azure-ai-projects 2.0+ in Azure AI client/provider/responses code paths by removing _supports_keyword_argument gating and related fallback branching. Also fix pyright typing in FoundryMemoryProvider memory store calls by using ResponseInputItemParam-typed items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * check fixes * Python: remove unsupported foundry_features option Drop foundry_features from Azure AI client and provider surfaces because azure-ai-projects 2.0.0 does not expose that create_version parameter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: add allow_preview to Foundry memory provider Propagate allow_preview when FoundryMemoryProvider constructs an AIProjectClient and update tests accordingly. Also finish wiring allow_preview through AzureAIClient-facing surfaces and related docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * aligning docstrings * udpated lock --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
d5e240b375
commit
23d6d91c8f
@@ -37,9 +37,8 @@ from agent_framework.openai._responses_client import RawOpenAIResponsesClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
ApproximateLocation,
|
||||
CodeInterpreterContainerAuto,
|
||||
AutoCodeInterpreterToolParam,
|
||||
CodeInterpreterTool,
|
||||
FoundryFeaturesOptInKeys,
|
||||
ImageGenTool,
|
||||
MCPTool,
|
||||
PromptAgentDefinition,
|
||||
@@ -66,7 +65,6 @@ if sys.version_info >= (3, 11):
|
||||
else:
|
||||
from typing_extensions import Self, TypedDict # type: ignore # pragma: no cover
|
||||
|
||||
|
||||
logger = logging.getLogger("agent_framework.azure")
|
||||
|
||||
|
||||
@@ -79,9 +77,6 @@ class AzureAIProjectAgentOptions(OpenAIResponsesOptions, total=False):
|
||||
reasoning: Reasoning # type: ignore[misc]
|
||||
"""Configuration for enabling reasoning capabilities (requires azure.ai.projects.models.Reasoning)."""
|
||||
|
||||
foundry_features: FoundryFeaturesOptInKeys | str
|
||||
"""Optional Foundry preview feature opt-in for agent version creation."""
|
||||
|
||||
|
||||
AzureAIClientOptionsT = TypeVar(
|
||||
"AzureAIClientOptionsT",
|
||||
@@ -123,6 +118,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
model_deployment_name: str | None = None,
|
||||
credential: AzureCredentialTypes | None = None,
|
||||
use_latest_version: bool | None = None,
|
||||
allow_preview: bool | None = None,
|
||||
env_file_path: str | None = None,
|
||||
env_file_encoding: str | None = None,
|
||||
**kwargs: Any,
|
||||
@@ -148,6 +144,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
AsyncTokenCredential, or a callable token provider.
|
||||
use_latest_version: Boolean flag that indicates whether to use latest agent version
|
||||
if it exists in the service.
|
||||
allow_preview: Enables preview opt-in on internally-created ``AIProjectClient``.
|
||||
env_file_path: Path to environment file for loading settings.
|
||||
env_file_encoding: Encoding of the environment file.
|
||||
kwargs: Additional keyword arguments passed to the parent class.
|
||||
@@ -208,11 +205,14 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
# Use provided credential
|
||||
if not credential:
|
||||
raise ValueError("Azure credential is required when project_client is not provided.")
|
||||
project_client = AIProjectClient(
|
||||
endpoint=resolved_endpoint,
|
||||
credential=credential, # type: ignore[arg-type]
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
project_client_kwargs: dict[str, Any] = {
|
||||
"endpoint": resolved_endpoint,
|
||||
"credential": credential, # type: ignore[arg-type]
|
||||
"user_agent": AGENT_FRAMEWORK_USER_AGENT,
|
||||
}
|
||||
if allow_preview is not None:
|
||||
project_client_kwargs["allow_preview"] = allow_preview
|
||||
project_client = AIProjectClient(**project_client_kwargs)
|
||||
should_close_client = True
|
||||
|
||||
# Initialize parent
|
||||
@@ -413,8 +413,6 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
"definition": PromptAgentDefinition(**args),
|
||||
"description": self.agent_description,
|
||||
}
|
||||
if foundry_features := run_options.get("foundry_features"):
|
||||
create_version_kwargs["foundry_features"] = foundry_features
|
||||
|
||||
created_agent = await self.project_client.agents.create_version(**create_version_kwargs)
|
||||
|
||||
@@ -513,7 +511,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
"temperature": ("temperature",),
|
||||
"top_p": ("top_p",),
|
||||
"reasoning": ("reasoning",),
|
||||
"foundry_features": ("foundry_features",),
|
||||
"allow_preview": ("allow_preview",),
|
||||
}
|
||||
|
||||
for run_keys in agent_level_option_to_run_keys.values():
|
||||
@@ -939,7 +937,7 @@ class RawAzureAIClient(RawOpenAIResponsesClient[AzureAIClientOptionsT], Generic[
|
||||
if file_ids is None and isinstance(container, dict):
|
||||
file_ids = container.get("file_ids")
|
||||
resolved = resolve_file_ids(file_ids)
|
||||
tool_container = CodeInterpreterContainerAuto(file_ids=resolved)
|
||||
tool_container = AutoCodeInterpreterToolParam(file_ids=resolved)
|
||||
return CodeInterpreterTool(container=tool_container, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
@@ -1244,6 +1242,7 @@ class AzureAIClient(
|
||||
model_deployment_name: str | None = None,
|
||||
credential: AzureCredentialTypes | None = None,
|
||||
use_latest_version: bool | None = None,
|
||||
allow_preview: bool | None = None,
|
||||
middleware: Sequence[ChatAndFunctionMiddlewareTypes] | None = None,
|
||||
function_invocation_configuration: FunctionInvocationConfiguration | None = None,
|
||||
env_file_path: str | None = None,
|
||||
@@ -1268,6 +1267,7 @@ class AzureAIClient(
|
||||
or AsyncTokenCredential.
|
||||
use_latest_version: Boolean flag that indicates whether to use latest agent version
|
||||
if it exists in the service.
|
||||
allow_preview: Enables preview opt-in on internally-created ``AIProjectClient``
|
||||
middleware: Optional sequence of chat middlewares to include.
|
||||
function_invocation_configuration: Optional function invocation configuration.
|
||||
env_file_path: Path to environment file for loading settings.
|
||||
@@ -1318,6 +1318,7 @@ class AzureAIClient(
|
||||
model_deployment_name=model_deployment_name,
|
||||
credential=credential,
|
||||
use_latest_version=use_latest_version,
|
||||
allow_preview=allow_preview,
|
||||
middleware=middleware,
|
||||
function_invocation_configuration=function_invocation_configuration,
|
||||
env_file_path=env_file_path,
|
||||
|
||||
@@ -18,6 +18,7 @@ from agent_framework._sessions import AgentSession, BaseContextProvider, Session
|
||||
from agent_framework._settings import load_settings
|
||||
from agent_framework.azure._entra_id_authentication import AzureCredentialTypes
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from openai.types.responses import ResponseInputItemParam
|
||||
|
||||
from ._shared import AzureAISettings
|
||||
|
||||
@@ -58,6 +59,7 @@ class FoundryMemoryProvider(BaseContextProvider):
|
||||
project_client: AIProjectClient | None = None,
|
||||
project_endpoint: str | None = None,
|
||||
credential: AzureCredentialTypes | None = None,
|
||||
allow_preview: bool | None = None,
|
||||
memory_store_name: str,
|
||||
scope: str | None = None,
|
||||
context_prompt: str | None = None,
|
||||
@@ -74,6 +76,7 @@ class FoundryMemoryProvider(BaseContextProvider):
|
||||
credential: Azure credential for authentication. Accepts a TokenCredential,
|
||||
AsyncTokenCredential, or a callable token provider.
|
||||
Required when project_client is not provided.
|
||||
allow_preview: Enables preview opt-in on internally-created ``AIProjectClient``.
|
||||
memory_store_name: The name of the memory store to use.
|
||||
scope: The namespace that logically groups and isolates memories (e.g., user ID).
|
||||
If None, `session_id` will be used.
|
||||
@@ -100,11 +103,14 @@ class FoundryMemoryProvider(BaseContextProvider):
|
||||
)
|
||||
if not credential:
|
||||
raise ValueError("Azure credential is required when project_client is not provided.")
|
||||
project_client = AIProjectClient(
|
||||
endpoint=resolved_endpoint,
|
||||
credential=credential, # type: ignore[arg-type]
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
project_client_kwargs: dict[str, Any] = {
|
||||
"endpoint": resolved_endpoint,
|
||||
"credential": credential, # type: ignore[arg-type]
|
||||
"user_agent": AGENT_FRAMEWORK_USER_AGENT,
|
||||
}
|
||||
if allow_preview is not None:
|
||||
project_client_kwargs["allow_preview"] = allow_preview
|
||||
project_client = AIProjectClient(**project_client_kwargs)
|
||||
|
||||
if not memory_store_name:
|
||||
raise ValueError("memory_store_name is required")
|
||||
@@ -169,8 +175,8 @@ class FoundryMemoryProvider(BaseContextProvider):
|
||||
return
|
||||
|
||||
# Convert input messages to memory search item format
|
||||
items = [
|
||||
{"type": "text", "text": msg.text}
|
||||
items: list[ResponseInputItemParam] = [
|
||||
{"type": "message", "role": "user", "content": msg.text}
|
||||
for msg in context.input_messages
|
||||
if msg and msg.text and msg.text.strip()
|
||||
]
|
||||
@@ -224,7 +230,7 @@ class FoundryMemoryProvider(BaseContextProvider):
|
||||
messages_to_store.extend(context.response.messages)
|
||||
|
||||
# Filter and convert messages to memory update item format
|
||||
items: list[dict[str, str]] = []
|
||||
items: list[ResponseInputItemParam] = []
|
||||
for message in messages_to_store:
|
||||
if message.role in {"user", "assistant", "system"} and message.text and message.text.strip():
|
||||
if message.role == "user":
|
||||
|
||||
@@ -102,6 +102,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
|
||||
project_endpoint: str | None = None,
|
||||
model: str | None = None,
|
||||
credential: AzureCredentialTypes | None = None,
|
||||
allow_preview: bool | None = None,
|
||||
env_file_path: str | None = None,
|
||||
env_file_encoding: str | None = None,
|
||||
) -> None:
|
||||
@@ -117,6 +118,7 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
|
||||
credential: Azure credential for authentication. Accepts a TokenCredential,
|
||||
AsyncTokenCredential, or a callable token provider.
|
||||
Required when project_client is not provided.
|
||||
allow_preview: Enables preview opt-in on internally-created ``AIProjectClient``.
|
||||
env_file_path: Path to environment file for loading settings.
|
||||
env_file_encoding: Encoding of the environment file.
|
||||
|
||||
@@ -146,11 +148,14 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
|
||||
if not credential:
|
||||
raise ValueError("Azure credential is required when project_client is not provided.")
|
||||
|
||||
project_client = AIProjectClient(
|
||||
endpoint=resolved_endpoint,
|
||||
credential=credential, # type: ignore[arg-type]
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
project_client_kwargs: dict[str, Any] = {
|
||||
"endpoint": resolved_endpoint,
|
||||
"credential": credential, # type: ignore[arg-type]
|
||||
"user_agent": AGENT_FRAMEWORK_USER_AGENT,
|
||||
}
|
||||
if allow_preview is not None:
|
||||
project_client_kwargs["allow_preview"] = allow_preview
|
||||
project_client = AIProjectClient(**project_client_kwargs)
|
||||
self._should_close_client = True
|
||||
|
||||
self._project_client = project_client
|
||||
@@ -199,7 +204,6 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
|
||||
response_format = opts.get("response_format")
|
||||
rai_config = opts.get("rai_config")
|
||||
reasoning = opts.get("reasoning")
|
||||
foundry_features = opts.get("foundry_features")
|
||||
|
||||
args: dict[str, Any] = {"model": resolved_model}
|
||||
|
||||
@@ -246,8 +250,6 @@ class AzureAIProjectAgentProvider(Generic[OptionsCoT]):
|
||||
"definition": PromptAgentDefinition(**args),
|
||||
"description": description,
|
||||
}
|
||||
if foundry_features:
|
||||
create_version_kwargs["foundry_features"] = foundry_features
|
||||
|
||||
created_agent = await self._project_client.agents.create_version(**create_version_kwargs)
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ from azure.ai.agents.models import (
|
||||
from azure.ai.projects.models import (
|
||||
CodeInterpreterTool,
|
||||
MCPTool,
|
||||
TextResponseFormatConfigurationResponseFormatJsonObject,
|
||||
TextResponseFormatConfigurationResponseFormatText,
|
||||
TextResponseFormatJsonObject,
|
||||
TextResponseFormatJsonSchema,
|
||||
TextResponseFormatText,
|
||||
Tool,
|
||||
WebSearchPreviewTool,
|
||||
)
|
||||
@@ -479,11 +479,7 @@ def _prepare_mcp_tool_dict_for_azure_ai(tool_dict: dict[str, Any]) -> MCPTool:
|
||||
|
||||
def create_text_format_config(
|
||||
response_format: type[BaseModel] | Mapping[str, Any],
|
||||
) -> (
|
||||
TextResponseFormatJsonSchema
|
||||
| TextResponseFormatConfigurationResponseFormatJsonObject
|
||||
| TextResponseFormatConfigurationResponseFormatText
|
||||
):
|
||||
) -> TextResponseFormatJsonSchema | TextResponseFormatJsonObject | TextResponseFormatText:
|
||||
"""Convert response_format into Azure text format configuration."""
|
||||
if isinstance(response_format, type) and issubclass(response_format, BaseModel):
|
||||
schema = response_format.model_json_schema()
|
||||
@@ -513,9 +509,9 @@ def create_text_format_config(
|
||||
config_kwargs["description"] = format_config["description"]
|
||||
return TextResponseFormatJsonSchema(**config_kwargs)
|
||||
if format_type == "json_object":
|
||||
return TextResponseFormatConfigurationResponseFormatJsonObject()
|
||||
return TextResponseFormatJsonObject()
|
||||
if format_type == "text":
|
||||
return TextResponseFormatConfigurationResponseFormatText()
|
||||
return TextResponseFormatText()
|
||||
|
||||
raise IntegrationInvalidRequestException("response_format must be a Pydantic model or mapping.")
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from agent_framework.openai._responses_client import RawOpenAIResponsesClient
|
||||
from azure.ai.projects.aio import AIProjectClient
|
||||
from azure.ai.projects.models import (
|
||||
ApproximateLocation,
|
||||
CodeInterpreterContainerAuto,
|
||||
AutoCodeInterpreterToolParam,
|
||||
CodeInterpreterTool,
|
||||
FileSearchTool,
|
||||
ImageGenTool,
|
||||
@@ -1296,7 +1296,7 @@ def test_from_azure_ai_tools_mcp() -> None:
|
||||
|
||||
def test_from_azure_ai_tools_code_interpreter() -> None:
|
||||
"""Test from_azure_ai_tools with Code Interpreter tool."""
|
||||
ci_tool = CodeInterpreterTool(container=CodeInterpreterContainerAuto(file_ids=["file-1"]))
|
||||
ci_tool = CodeInterpreterTool(container=AutoCodeInterpreterToolParam(file_ids=["file-1"]))
|
||||
parsed_tools = from_azure_ai_tools([ci_tool])
|
||||
assert len(parsed_tools) == 1
|
||||
assert parsed_tools[0]["type"] == "code_interpreter"
|
||||
|
||||
@@ -86,6 +86,7 @@ class TestInit:
|
||||
provider = FoundryMemoryProvider(
|
||||
project_endpoint="https://test.project.endpoint",
|
||||
credential=mock_credential, # type: ignore[arg-type]
|
||||
allow_preview=True,
|
||||
memory_store_name="test_store",
|
||||
scope="user_123",
|
||||
)
|
||||
@@ -93,6 +94,7 @@ class TestInit:
|
||||
mock_ai_project_client.assert_called_once_with(
|
||||
endpoint="https://test.project.endpoint",
|
||||
credential=mock_credential,
|
||||
allow_preview=True,
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
|
||||
|
||||
@@ -112,9 +112,7 @@ class SkillResource:
|
||||
self._accepts_kwargs: bool = False
|
||||
if function is not None:
|
||||
sig = inspect.signature(function)
|
||||
self._accepts_kwargs = any(
|
||||
p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
|
||||
)
|
||||
self._accepts_kwargs = any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values())
|
||||
|
||||
|
||||
class Skill:
|
||||
|
||||
@@ -73,6 +73,7 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
async_client: AsyncOpenAI | None = None,
|
||||
project_client: Any | None = None,
|
||||
project_endpoint: str | None = None,
|
||||
allow_preview: bool | None = None,
|
||||
env_file_path: str | None = None,
|
||||
env_file_encoding: str | None = None,
|
||||
instruction_role: str | None = None,
|
||||
@@ -120,6 +121,7 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
project_endpoint: The Azure AI Foundry project endpoint URL.
|
||||
When provided with ``credential``, an ``AIProjectClient`` will be created
|
||||
and used to obtain the OpenAI client. Requires the ``azure-ai-projects`` package.
|
||||
allow_preview: Enables preview opt-in on internally-created ``AIProjectClient``.
|
||||
env_file_path: Use the environment settings file as a fallback to using env vars.
|
||||
env_file_encoding: The encoding of the environment settings file, defaults to 'utf-8'.
|
||||
instruction_role: The role to use for 'instruction' messages, for example, summarization
|
||||
@@ -189,6 +191,7 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
project_client=project_client,
|
||||
project_endpoint=project_endpoint,
|
||||
credential=credential,
|
||||
allow_preview=allow_preview,
|
||||
)
|
||||
|
||||
azure_openai_settings = load_settings(
|
||||
@@ -246,21 +249,9 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
project_client: AIProjectClient | None,
|
||||
project_endpoint: str | None,
|
||||
credential: AzureCredentialTypes | AzureTokenProvider | None,
|
||||
allow_preview: bool | None = None,
|
||||
) -> AsyncOpenAI:
|
||||
"""Create an AsyncOpenAI client from an Azure AI Foundry project.
|
||||
|
||||
Args:
|
||||
project_client: An existing AIProjectClient to use.
|
||||
project_endpoint: The Azure AI Foundry project endpoint URL.
|
||||
credential: Azure credential for authentication.
|
||||
|
||||
Returns:
|
||||
An AsyncAzureOpenAI client obtained from the project client.
|
||||
|
||||
Raises:
|
||||
ValueError: If required parameters are missing or
|
||||
the azure-ai-projects package is not installed.
|
||||
"""
|
||||
"""Create an AsyncOpenAI client from an Azure AI Foundry project."""
|
||||
if project_client is not None:
|
||||
return project_client.get_openai_client()
|
||||
|
||||
@@ -268,11 +259,14 @@ class AzureOpenAIResponsesClient( # type: ignore[misc]
|
||||
raise ValueError("Azure AI project endpoint is required when project_client is not provided.")
|
||||
if not credential:
|
||||
raise ValueError("Azure credential is required when using project_endpoint without a project_client.")
|
||||
project_client = AIProjectClient(
|
||||
endpoint=project_endpoint,
|
||||
credential=credential, # type: ignore[arg-type]
|
||||
user_agent=AGENT_FRAMEWORK_USER_AGENT,
|
||||
)
|
||||
project_client_kwargs: dict[str, Any] = {
|
||||
"endpoint": project_endpoint,
|
||||
"credential": credential, # type: ignore[arg-type]
|
||||
"user_agent": AGENT_FRAMEWORK_USER_AGENT,
|
||||
}
|
||||
if allow_preview is not None:
|
||||
project_client_kwargs["allow_preview"] = allow_preview
|
||||
project_client = AIProjectClient(**project_client_kwargs)
|
||||
return project_client.get_openai_client()
|
||||
|
||||
@override
|
||||
|
||||
@@ -34,7 +34,7 @@ dependencies = [
|
||||
# connectors and functions
|
||||
"openai>=1.99.0",
|
||||
"azure-identity>=1,<2",
|
||||
"azure-ai-projects == 2.0.0b4",
|
||||
"azure-ai-projects>=2.0.0,<3.0",
|
||||
"mcp[ws]>=1.24.0,<2",
|
||||
"packaging>=24.1",
|
||||
]
|
||||
|
||||
@@ -544,19 +544,19 @@ class TestFunctionExecutor:
|
||||
static_wrapped = staticmethod(my_async_func)
|
||||
|
||||
# Direct check on descriptor object fails (this is the bug)
|
||||
assert not asyncio.iscoroutinefunction(static_wrapped)
|
||||
assert not asyncio.iscoroutinefunction(static_wrapped) # type: ignore[reportDeprecated]
|
||||
assert isinstance(static_wrapped, staticmethod)
|
||||
|
||||
# But unwrapping __func__ reveals the async function
|
||||
unwrapped = static_wrapped.__func__
|
||||
assert asyncio.iscoroutinefunction(unwrapped)
|
||||
assert asyncio.iscoroutinefunction(unwrapped) # type: ignore[reportDeprecated]
|
||||
|
||||
# When accessed via class attribute, Python's descriptor protocol
|
||||
# automatically unwraps it, so it works:
|
||||
class C:
|
||||
async_static = static_wrapped
|
||||
|
||||
assert asyncio.iscoroutinefunction(C.async_static) # Works via descriptor protocol
|
||||
assert asyncio.iscoroutinefunction(C.async_static) # type: ignore[reportDeprecated] # Works via descriptor protocol
|
||||
|
||||
|
||||
class TestExecutorExplicitTypes:
|
||||
|
||||
+1
-2
@@ -15,11 +15,10 @@ import json
|
||||
import logging
|
||||
import uuid
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Callable, Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from inspect import isawaitable
|
||||
from typing import Any, cast
|
||||
from collections.abc import Callable
|
||||
|
||||
from agent_framework import (
|
||||
Content,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any, cast
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
Reference in New Issue
Block a user