From 46b4e823733df46606f6fce30e20be5a2dd0fe43 Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 11:44:16 +0530 Subject: [PATCH 1/8] Refactor code structure for improved readability and maintainability --- infra/vscode_web/endpoint-requirements.txt | 2 +- infra/vscode_web/requirements.txt | 2 +- src/backend-api/pyproject.toml | 2 +- src/backend-api/uv.lock | 8 +- src/processor/pyproject.toml | 6 +- .../src/libs/agent_framework/agent_builder.py | 120 +++---- .../agent_framework/agent_framework_helper.py | 48 ++- .../src/libs/agent_framework/agent_info.py | 10 +- .../agent_framework/agent_speaking_capture.py | 8 +- .../azure_openai_response_retry.py | 9 +- .../coordinator_selection_response.py | 11 + .../agent_framework/groupchat_orchestrator.py | 85 +++-- .../src/libs/agent_framework/middlewares.py | 48 ++- .../shared_memory_context_provider.py | 31 +- .../src/libs/base/orchestrator_base.py | 38 ++- .../src/libs/mcp_server/MCPBlobIOTool.py | 14 +- .../src/libs/mcp_server/MCPDatetimeTool.py | 12 +- .../src/libs/mcp_server/MCPMicrosoftDocs.py | 8 +- .../orchestration/analysis_orchestrator.py | 9 +- .../yaml_convert_orchestrator.py | 17 +- .../orchestration/design_orchestrator.py | 17 +- .../documentation_orchestrator.py | 17 +- .../src/steps/migration_processor.py | 63 ++-- .../agent_framework/test_agent_builder.py | 8 +- .../test_agent_framework_helper.py | 48 ++- .../test_groupchat_orchestrator_internals.py | 58 ++-- .../test_input_observer_middleware.py | 18 +- .../test_middlewares_extras.py | 21 +- .../test_shared_memory_context_provider.py | 2 +- .../steps/test_migration_processor_run.py | 87 +++-- src/processor/uv.lock | 322 +++++++++++++++--- 31 files changed, 746 insertions(+), 403 deletions(-) create mode 100644 src/processor/src/libs/agent_framework/coordinator_selection_response.py diff --git a/infra/vscode_web/endpoint-requirements.txt b/infra/vscode_web/endpoint-requirements.txt index 18d6803e..d7ff98e4 100644 --- a/infra/vscode_web/endpoint-requirements.txt +++ b/infra/vscode_web/endpoint-requirements.txt @@ -1,3 +1,3 @@ -azure-ai-projects==1.0.0b12 +azure-ai-projects==2.1.0 azure-identity==1.20.0 ansible-core~=2.17.0 \ No newline at end of file diff --git a/infra/vscode_web/requirements.txt b/infra/vscode_web/requirements.txt index 18d6803e..d7ff98e4 100644 --- a/infra/vscode_web/requirements.txt +++ b/infra/vscode_web/requirements.txt @@ -1,3 +1,3 @@ -azure-ai-projects==1.0.0b12 +azure-ai-projects==2.1.0 azure-identity==1.20.0 ansible-core~=2.17.0 \ No newline at end of file diff --git a/src/backend-api/pyproject.toml b/src/backend-api/pyproject.toml index 81c5c5b5..f65b4b9f 100644 --- a/src/backend-api/pyproject.toml +++ b/src/backend-api/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "aiofiles==24.1.0", - "azure-ai-agents==1.2.0b3", + "azure-ai-agents==1.2.0b6", "azure-appconfiguration==1.7.1", "azure-identity==1.25.0", "azure-monitor-opentelemetry==1.7.0", diff --git a/src/backend-api/uv.lock b/src/backend-api/uv.lock index 86f6b53f..e4e0c988 100644 --- a/src/backend-api/uv.lock +++ b/src/backend-api/uv.lock @@ -200,7 +200,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "aiofiles", specifier = "==24.1.0" }, - { name = "azure-ai-agents", specifier = "==1.2.0b3" }, + { name = "azure-ai-agents", specifier = "==1.2.0b6" }, { name = "azure-appconfiguration", specifier = "==1.7.1" }, { name = "azure-identity", specifier = "==1.25.0" }, { name = "azure-monitor-opentelemetry", specifier = "==1.7.0" }, @@ -311,16 +311,16 @@ wheels = [ [[package]] name = "azure-ai-agents" -version = "1.2.0b3" +version = "1.2.0b6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/52/3c1af9ed86582f09343f135d527ca26f0bf9659c01ccbddb650bbb952963/azure_ai_agents-1.2.0b3.tar.gz", hash = "sha256:440d7fca98c0b13654a57dcd159cdf64d1024f9baacd1a4354ce91a290d3741e", size = 362563, upload-time = "2025-08-22T22:41:58.609Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/32/f4e534dc05dfb714705df56a190d690c5452cd4dd7e936612cb1adddc44f/azure_ai_agents-1.2.0b6.tar.gz", hash = "sha256:d3c10848c3b19dec98a292f8c10cee4ba4aac1050d4faabf9c2e2456b727f528", size = 396865, upload-time = "2025-10-24T18:04:47.877Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/a4/c916745e150b5e157688da9a7965d62efb82ad940f2991260d1d2b79fcf1/azure_ai_agents-1.2.0b3-py3-none-any.whl", hash = "sha256:fec3e92fac5de2c18dee2d4def734825c2a4880bee39b3c237a7ad8079bfa8a7", size = 208129, upload-time = "2025-08-22T22:42:00.249Z" }, + { url = "https://files.pythonhosted.org/packages/96/d0/930c522f5fa9da163de057e57f8b44539424e13f46618c52624ebc712293/azure_ai_agents-1.2.0b6-py3-none-any.whl", hash = "sha256:ce23ad8fb9791118905be1ec8eae5c907cca2e536a455f1d3b830062c72cf2a7", size = 217950, upload-time = "2025-10-24T18:04:49.72Z" }, ] [[package]] diff --git a/src/processor/pyproject.toml b/src/processor/pyproject.toml index 1f36bdf2..2d622ad5 100644 --- a/src/processor/pyproject.toml +++ b/src/processor/pyproject.toml @@ -5,12 +5,12 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [ - "agent-framework==1.0.0b260107", + "agent-framework==1.3.0", "aiohttp==3.13.4", "art==6.5", - "azure-ai-agents==1.2.0b5", + "azure-ai-agents==1.2.0b6", "azure-ai-inference==1.0.0b9", - "azure-ai-projects==2.0.0b3", + "azure-ai-projects==2.1.0", "azure-appconfiguration==1.7.2", "azure-core==1.38.0", "azure-cosmos==4.15.0", diff --git a/src/processor/src/libs/agent_framework/agent_builder.py b/src/processor/src/libs/agent_framework/agent_builder.py index 8b9c629e..6cae1a67 100644 --- a/src/processor/src/libs/agent_framework/agent_builder.py +++ b/src/processor/src/libs/agent_framework/agent_builder.py @@ -1,21 +1,31 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -"""Fluent builder for constructing ChatAgent instances with chainable configuration.""" +"""Fluent builder for constructing Agent instances with chainable configuration.""" from collections.abc import Callable, MutableMapping, Sequence from typing import Any, Literal -from agent_framework import ( - AggregateContextProvider, - ChatAgent, - ChatClientProtocol, - ChatMessageStoreProtocol, - ContextProvider, - Middleware, - ToolMode, - ToolProtocol, -) +try: + from agent_framework import ( + Agent, + AgentMiddleware, + BaseChatClient, + ChatMiddleware, + ContextProvider, + FunctionTool, + ToolMode, + ) +except ImportError: + from agent_framework import ( + AgentMiddleware, + BaseChatClient, + ChatAgent as Agent, + ChatMiddleware, + ContextProvider, + ToolMode, + ToolProtocol as FunctionTool, + ) from pydantic import BaseModel from libs.agent_framework.agent_info import AgentInfo @@ -23,7 +33,7 @@ class AgentBuilder: - """Fluent builder for creating ChatAgent instances with a chainable API. + """Fluent builder for creating Agent instances with a chainable API. This class provides two ways to create agents: 1. Fluent API with method chaining (recommended for readability) @@ -59,7 +69,7 @@ class AgentBuilder: ) """ - def __init__(self, chat_client: ChatClientProtocol): + def __init__(self, chat_client: BaseChatClient): """Initialize the builder with a chat client. Args: @@ -70,14 +80,15 @@ def __init__(self, chat_client: ChatClientProtocol): self._id: str | None = None self._name: str | None = None self._description: str | None = None - self._chat_message_store_factory: ( - Callable[[], ChatMessageStoreProtocol] | None - ) = None + self._chat_message_store_factory: Callable[[], Any] | None = None self._conversation_id: str | None = None - self._context_providers: ( - ContextProvider | list[ContextProvider] | AggregateContextProvider | None + self._context_providers: ContextProvider | list[ContextProvider] | None = None + self._middleware: ( + AgentMiddleware + | ChatMiddleware + | list[AgentMiddleware | ChatMiddleware] + | None ) = None - self._middleware: Middleware | list[Middleware] | None = None self._frequency_penalty: float | None = None self._logit_bias: dict[str | int, float] | None = None self._max_tokens: int | None = None @@ -93,10 +104,10 @@ def __init__(self, chat_client: ChatClientProtocol): ToolMode | Literal["auto", "required", "none"] | dict[str, Any] | None ) = "auto" self._tools: ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] | None ) = None self._top_p: float | None = None @@ -178,10 +189,10 @@ def with_max_tokens(self, max_tokens: int) -> "AgentBuilder": def with_tools( self, - tools: ToolProtocol + tools: FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]], + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]], ) -> "AgentBuilder": """Set the tools available to the agent. @@ -210,7 +221,8 @@ def with_tool_choice( return self def with_middleware( - self, middleware: Middleware | list[Middleware] + self, + middleware: AgentMiddleware | ChatMiddleware | list[AgentMiddleware | ChatMiddleware], ) -> "AgentBuilder": """Set middleware for request/response processing. @@ -225,9 +237,7 @@ def with_middleware( def with_context_providers( self, - context_providers: ContextProvider - | list[ContextProvider] - | AggregateContextProvider, + context_providers: ContextProvider | list[ContextProvider], ) -> "AgentBuilder": """Set context providers for additional conversation context. @@ -385,7 +395,7 @@ def with_store(self, store: bool) -> "AgentBuilder": return self def with_message_store_factory( - self, factory: Callable[[], ChatMessageStoreProtocol] + self, factory: Callable[[], Any] ) -> "AgentBuilder": """Set the message store factory. @@ -422,11 +432,11 @@ def with_kwargs(self, **kwargs: Any) -> "AgentBuilder": self._kwargs.update(kwargs) return self - def build(self) -> ChatAgent: - """Build and return the configured ChatAgent. + def build(self) -> Agent: + """Build and return the configured Agent. Returns: - ChatAgent: Configured agent instance ready for use + Agent: Configured agent instance ready for use Example: .. code-block:: python @@ -442,7 +452,7 @@ def build(self) -> ChatAgent: async with agent: response = await agent.run("Hello!") """ - return ChatAgent( + return Agent( chat_client=self._chat_client, instructions=self._instructions, id=self._id, @@ -477,14 +487,10 @@ def create_agent_by_agentinfo( agent_info: AgentInfo, *, id: str | None = None, - chat_message_store_factory: Callable[[], ChatMessageStoreProtocol] - | None = None, + chat_message_store_factory: Callable[[], Any] | None = None, conversation_id: str | None = None, - context_providers: ContextProvider - | list[ContextProvider] - | AggregateContextProvider - | None = None, - middleware: Middleware | list[Middleware] | None = None, + context_providers: ContextProvider | list[ContextProvider] | None = None, + middleware: AgentMiddleware | ChatMiddleware | list[AgentMiddleware | ChatMiddleware] | None = None, frequency_penalty: float | None = None, logit_bias: dict[str | int, float] | None = None, max_tokens: int | None = None, @@ -500,20 +506,20 @@ def create_agent_by_agentinfo( | Literal["auto", "required", "none"] | dict[str, Any] | None = "auto", - tools: ToolProtocol + tools: FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] | None = None, top_p: float | None = None, user: str | None = None, additional_chat_options: dict[str, Any] | None = None, **kwargs: Any, - ) -> ChatAgent: + ) -> Agent: """Create an agent using AgentInfo configuration with full parameter support. This method creates a chat client from the service configuration and then - creates a ChatAgent with the specified parameters. Agent name, description, + creates a Agent with the specified parameters. Agent name, description, and instructions are taken from AgentInfo but can be overridden via kwargs. Args: @@ -543,7 +549,7 @@ def create_agent_by_agentinfo( **kwargs: Additional keyword arguments Returns: - ChatAgent: Configured agent instance ready for use + Agent: Configured agent instance ready for use Example: .. code-block:: python @@ -611,20 +617,16 @@ def create_agent_by_agentinfo( @staticmethod def create_agent( - chat_client: ChatClientProtocol, + chat_client: BaseChatClient, instructions: str | None = None, *, id: str | None = None, name: str | None = None, description: str | None = None, - chat_message_store_factory: Callable[[], ChatMessageStoreProtocol] - | None = None, + chat_message_store_factory: Callable[[], Any] | None = None, conversation_id: str | None = None, - context_providers: ContextProvider - | list[ContextProvider] - | AggregateContextProvider - | None = None, - middleware: Middleware | list[Middleware] | None = None, + context_providers: ContextProvider | list[ContextProvider] | None = None, + middleware: AgentMiddleware | ChatMiddleware | list[AgentMiddleware | ChatMiddleware] | None = None, frequency_penalty: float | None = None, logit_bias: dict[str | int, float] | None = None, max_tokens: int | None = None, @@ -640,19 +642,19 @@ def create_agent( | Literal["auto", "required", "none"] | dict[str, Any] | None = "auto", - tools: ToolProtocol + tools: FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] | None = None, top_p: float | None = None, user: str | None = None, additional_chat_options: dict[str, Any] | None = None, **kwargs: Any, - ) -> ChatAgent: + ) -> Agent: """Create a Chat Client Agent. - Factory method that creates a ChatAgent instance with the specified configuration. + Factory method that creates a Agent instance with the specified configuration. The agent uses a chat client to interact with language models and supports tools (MCP tools, callable functions), context providers, middleware, and both streaming and non-streaming responses. @@ -686,7 +688,7 @@ def create_agent( **kwargs: Additional keyword arguments Returns: - ChatAgent: Configured chat agent instance that can be used directly or with async context manager + Agent: Configured chat agent instance that can be used directly or with async context manager Examples: Non-streaming example (from azure_response_client_basic.py): @@ -761,10 +763,10 @@ def create_agent( Note: When the agent has MCP tools or needs proper resource cleanup, use it with - ``async with`` to ensure proper initialization and cleanup via the ChatAgent's + ``async with`` to ensure proper initialization and cleanup via the Agent's async context manager protocol. """ - return ChatAgent( + return Agent( chat_client=chat_client, instructions=instructions, id=id, diff --git a/src/processor/src/libs/agent_framework/agent_framework_helper.py b/src/processor/src/libs/agent_framework/agent_framework_helper.py index 61da842a..e2609e04 100644 --- a/src/processor/src/libs/agent_framework/agent_framework_helper.py +++ b/src/processor/src/libs/agent_framework/agent_framework_helper.py @@ -27,12 +27,12 @@ ) if TYPE_CHECKING: - from agent_framework.azure import ( - AzureAIAgentClient, - AzureOpenAIAssistantsClient, - AzureOpenAIChatClient, - AzureOpenAIResponsesClient, - ) + from agent_framework.azure import DurableAIAgentClient + + # TODO: agent-framework 1.3.0 removed these azure clients with no replacement. + # from agent_framework.azure import AzureOpenAIAssistantsClient + # from agent_framework.azure import AzureOpenAIChatClient + # from agent_framework.azure import AzureOpenAIResponsesClient class ClientType(Enum): @@ -147,7 +147,7 @@ def create_client( env_file_path: str | None = None, env_file_encoding: str | None = None, instruction_role: str | None = None, - ) -> "AzureOpenAIChatClient": + ) -> Any: pass @overload @@ -171,7 +171,7 @@ def create_client( async_client: object | None = None, env_file_path: str | None = None, env_file_encoding: str | None = None, - ) -> "AzureOpenAIAssistantsClient": + ) -> Any: pass @overload @@ -193,7 +193,7 @@ def create_client( env_file_path: str | None = None, env_file_encoding: str | None = None, instruction_role: str | None = None, - ) -> "AzureOpenAIResponsesClient": + ) -> Any: pass @overload @@ -233,7 +233,7 @@ def create_client( async_credential: object | None = None, env_file_path: str | None = None, env_file_encoding: str | None = None, - ) -> "AzureAIAgentClient": + ) -> "DurableAIAgentClient": pass @staticmethod @@ -366,7 +366,12 @@ def create_client( "OpenAIResponsesClient is not implemented in this context." ) elif client_type == ClientType.AzureOpenAIChatCompletion: - from agent_framework.azure import AzureOpenAIChatClient + try: + from agent_framework.azure import AzureOpenAIChatClient + except ImportError as exc: + raise NotImplementedError( + "ClientType.AzureOpenAIChatCompletion is not supported in agent-framework 1.3.0; AzureOpenAIChatClient was removed." + ) from exc return AzureOpenAIChatClient( api_key=api_key, @@ -385,7 +390,12 @@ def create_client( instruction_role=instruction_role, ) elif client_type == ClientType.AzureOpenAIAssistant: - from agent_framework.azure import AzureOpenAIAssistantsClient + try: + from agent_framework.azure import AzureOpenAIAssistantsClient + except ImportError as exc: + raise NotImplementedError( + "ClientType.AzureOpenAIAssistant is not supported in agent-framework 1.3.0; AzureOpenAIAssistantsClient was removed." + ) from exc return AzureOpenAIAssistantsClient( deployment_name=deployment_name, @@ -406,7 +416,12 @@ def create_client( env_file_encoding=env_file_encoding, ) elif client_type == ClientType.AzureOpenAIResponse: - from agent_framework.azure import AzureOpenAIResponsesClient + try: + from agent_framework.azure import AzureOpenAIResponsesClient + except ImportError as exc: + raise NotImplementedError( + "ClientType.AzureOpenAIResponse is not supported in agent-framework 1.3.0; AzureOpenAIResponsesClient was removed." + ) from exc return AzureOpenAIResponsesClient( api_key=api_key, @@ -443,9 +458,12 @@ def create_client( retry_config=retry_config, ) elif client_type == ClientType.AzureOpenAIAgent: - from agent_framework.azure import AzureAIAgentClient + try: + from agent_framework.azure import DurableAIAgentClient + except ImportError: + from agent_framework.azure import AzureAIAgentClient as DurableAIAgentClient - return AzureAIAgentClient( + return DurableAIAgentClient( project_client=project_client, agent_id=agent_id, agent_name=agent_name, diff --git a/src/processor/src/libs/agent_framework/agent_info.py b/src/processor/src/libs/agent_framework/agent_info.py index 8eb18de3..6e3dfac0 100644 --- a/src/processor/src/libs/agent_framework/agent_info.py +++ b/src/processor/src/libs/agent_framework/agent_info.py @@ -4,7 +4,11 @@ """Pydantic model describing an agent participant with Jinja2 template rendering.""" from typing import Any, Callable, MutableMapping, Sequence -from agent_framework import ToolProtocol + +try: + from agent_framework import FunctionTool +except ImportError: + from agent_framework import ToolProtocol as FunctionTool from jinja2 import Template from openai import BaseModel from pydantic import Field @@ -20,10 +24,10 @@ class AgentInfo(BaseModel): agent_instruction: str | None = Field(default=None) agent_framework_helper: AgentFrameworkHelper | None = Field(default=None) tools: ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] | None ) = Field(default=None) diff --git a/src/processor/src/libs/agent_framework/agent_speaking_capture.py b/src/processor/src/libs/agent_framework/agent_speaking_capture.py index 8243d755..5dac0cba 100644 --- a/src/processor/src/libs/agent_framework/agent_speaking_capture.py +++ b/src/processor/src/libs/agent_framework/agent_speaking_capture.py @@ -5,7 +5,11 @@ from datetime import datetime from typing import Any, Callable, Optional -from agent_framework import AgentRunContext, AgentMiddleware + +try: + from agent_framework import AgentContext, AgentMiddleware +except ImportError: + from agent_framework import AgentMiddleware, AgentRunContext as AgentContext class AgentSpeakingCaptureMiddleware(AgentMiddleware): @@ -72,7 +76,7 @@ def __init__( str, list[str] ] = {} # Buffer for streaming responses - async def process(self, context: AgentRunContext, next): + async def process(self, context: AgentContext, next): """Process the agent invocation and capture the response. Args: diff --git a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py index 48b829b3..b51e324a 100644 --- a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py +++ b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py @@ -12,7 +12,14 @@ from dataclasses import dataclass from typing import Any, AsyncIterable, MutableSequence -from agent_framework.azure import AzureOpenAIResponsesClient +try: + from agent_framework.azure import AzureOpenAIResponsesClient +except ImportError: + class AzureOpenAIResponsesClient: + def __init__(self, *args: Any, **kwargs: Any): + raise NotImplementedError( + "AzureOpenAIResponsesClient was removed from agent_framework.azure in 1.3.0." + ) from tenacity import ( AsyncRetrying, retry_if_exception, diff --git a/src/processor/src/libs/agent_framework/coordinator_selection_response.py b/src/processor/src/libs/agent_framework/coordinator_selection_response.py new file mode 100644 index 00000000..5a4f4d25 --- /dev/null +++ b/src/processor/src/libs/agent_framework/coordinator_selection_response.py @@ -0,0 +1,11 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +from pydantic import BaseModel, Field + + +class CoordinatorSelectionResponse(BaseModel): + selected_participant: str | None = Field(default=None) + instruction: str | None = Field(default=None) + finish: bool = Field(default=False) + final_message: str | None = Field(default=None) diff --git a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py index 5cb63938..ab9975c1 100644 --- a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py +++ b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py @@ -21,23 +21,38 @@ from datetime import datetime from typing import Any, Awaitable, Callable, Generic, Mapping, Sequence, TypeVar -from agent_framework import ( - AgentProtocol, - AgentRunUpdateEvent, - ChatAgent, - ChatMessage, - Executor, - GroupChatBuilder, - ManagerSelectionResponse, - Role, - Workflow, - WorkflowOutputEvent, -) +try: + from agent_framework import ( + Agent, + AgentResponseUpdate, + Executor, + Message, + Role, + SupportsAgentRun, + Workflow, + WorkflowBuilder as GroupChatBuilder, + WorkflowEvent as WorkflowOutputEvent, + ) +except ImportError: + from agent_framework import ( + AgentProtocol as SupportsAgentRun, + AgentRunUpdateEvent as AgentResponseUpdate, + ChatAgent as Agent, + ChatMessage as Message, + Executor, + GroupChatBuilder, + Role, + Workflow, + WorkflowOutputEvent, + ) from mem0 import AsyncMemory from pydantic import BaseModel, ValidationError +from .coordinator_selection_response import CoordinatorSelectionResponse + logger = logging.getLogger(__name__) +ROLE_ASSISTANT = getattr(Role, "ASSISTANT", "assistant") # Generic type variables TInput = TypeVar("TInput") # Input type (str, dict, BaseModel, etc.) @@ -87,7 +102,7 @@ class OrchestrationResult(Generic[TOutput]): """Final workflow execution result with generic output type""" success: bool - conversation: list[ChatMessage] + conversation: list[Message] agent_responses: list[AgentResponse] tool_usage: dict[str, list[dict[str, Any]]] result: TOutput | None = None @@ -180,7 +195,7 @@ class GroupChatOrchestrator(ABC, Generic[TInput, TOutput]): Note: This orchestrator expects agents to be pre-created and passed in via - `participants`. Creation of `ChatAgent` instances (and wiring tools) + `participants`. Creation of `Agent` instances (and wiring tools) is handled elsewhere in the app. """ @@ -188,8 +203,8 @@ def __init__( self, name: str, process_id: str, - participants: Mapping[str, AgentProtocol | Executor] - | Sequence[AgentProtocol | Executor], + participants: Mapping[str, SupportsAgentRun | Executor] + | Sequence[SupportsAgentRun | Executor], memory_client: AsyncMemory, coordinator_name: str = "Coordinator", max_rounds: int = 100, @@ -225,7 +240,7 @@ def __init__( self.result_format = result_output_format # Runtime state - self.agents: dict[str, ChatAgent] = participants + self.agents: dict[str, Agent] = participants self.agent_tool_usage: dict[str, list[dict[str, Any]]] = {} self.agent_responses: list[AgentResponse] = [] self._initialized: bool = False @@ -338,7 +353,7 @@ def get_result_generator_name(self) -> str: """ return "ResultGenerator" - def _validate_sign_offs(self, conversation: list[ChatMessage]) -> tuple[bool, str]: + def _validate_sign_offs(self, conversation: list[Message]) -> tuple[bool, str]: """ Validate that all required reviewers have SIGN-OFF: PASS. @@ -475,7 +490,7 @@ async def run_stream( self._tool_call_emitted.clear() self._tool_call_recorded.clear() self._tool_call_index.clear() - self._conversation: list[ChatMessage] = [] # Track conversation during workflow + self._conversation: list[Message] = [] # Track conversation during workflow try: # Ensure initialized @@ -489,7 +504,7 @@ async def run_stream( group_chat_workflow = await self._build_groupchat() # Execute with streaming - conversation: list[ChatMessage] = [] + conversation: list[Message] = [] async for event in group_chat_workflow.run_stream(task_prompt): # Enforce wall-clock timeout if configured. @@ -503,7 +518,7 @@ async def run_stream( termination_type="hard_timeout", ) - if isinstance(event, AgentRunUpdateEvent): + if isinstance(event, AgentResponseUpdate): await self._handle_agent_update( event, stream_callback=on_agent_response_stream, @@ -542,8 +557,8 @@ async def run_stream( self._conversation = conversation # Update instance variable # Backfill tool usage from the final conversation (more reliable than streaming updates) - # AgentRunUpdateEvent may stream text only; tool calls are represented as FunctionCallContent - # items inside ChatMessage.contents. + # AgentResponseUpdate may stream text only; tool calls are represented as FunctionCallContent + # items inside Message.contents. self._backfill_tool_usage_from_conversation(conversation) # Post-workflow analysis (optional) @@ -642,7 +657,7 @@ async def run_stream( async def _handle_agent_update( self, - event: AgentRunUpdateEvent, + event: AgentResponseUpdate, stream_callback: AgentResponseStreamCallback | None = None, callback: AgentResponseCallback | None = None, ) -> None: @@ -705,7 +720,7 @@ async def _start_agent_if_needed( logger.info(f"\n[AGENT] {agent_name}:", extra={"agent_name": agent_name}) - def _append_text_chunk(self, event: AgentRunUpdateEvent) -> None: + def _append_text_chunk(self, event: AgentResponseUpdate) -> None: """Append streamed text chunks to the current agent buffer.""" if not hasattr(event.data, "text") or not event.data.text: return @@ -717,7 +732,7 @@ def _append_text_chunk(self, event: AgentRunUpdateEvent) -> None: async def _process_tool_calls( self, - event: AgentRunUpdateEvent, + event: AgentResponseUpdate, agent_name: str, stream_callback: AgentResponseStreamCallback | None, ) -> None: @@ -884,7 +899,7 @@ def _extract_function_calls(self, contents: Any) -> list[dict[str, Any]]: return calls def _backfill_tool_usage_from_conversation( - self, conversation: list[ChatMessage] + self, conversation: list[Message] ) -> None: """Populate `agent_tool_usage` from final conversation messages. @@ -894,7 +909,7 @@ def _backfill_tool_usage_from_conversation( for msg in conversation: try: role = getattr(msg, "role", None) - if role != Role.ASSISTANT: + if role != ROLE_ASSISTANT: continue agent_name = getattr(msg, "author_name", None) or "assistant" @@ -989,13 +1004,13 @@ async def _complete_agent_response( self._progress_counter += 1 # Detect manager termination signal (finish=true) from Coordinator. - # NOTE: The underlying GroupChatBuilder does not automatically stop on finish, + # NOTE: The underlying WorkflowBuilder does not automatically stop on finish, # so we enforce it here. if agent_name == self.coordinator_name: try: json_payload = self._extract_first_json_payload(complete_message) response_dict = json.loads(json_payload) - manager_response = ManagerSelectionResponse.model_validate( + manager_response = CoordinatorSelectionResponse.model_validate( response_dict ) manager_instruction = getattr(manager_response, "instruction", None) @@ -1122,7 +1137,7 @@ async def _build_groupchat(self) -> Workflow: async def _generate_final_result( self, - conversation: list[ChatMessage], + conversation: list[Message], result_format: type[TOutput], result_generator_name: str, ) -> TOutput: @@ -1220,7 +1235,7 @@ def _truncate_text( def _build_result_generator_conversation( self, - conversation: Iterable[ChatMessage], + conversation: Iterable[Message], *, exclude_authors: set[str] | None, max_messages: int, @@ -1228,7 +1243,7 @@ def _build_result_generator_conversation( max_chars_per_message: int, keep_head_chars: int, keep_tail_chars: int, - ) -> list[ChatMessage]: + ) -> list[Message]: """Build a size-bounded conversation slice for the ResultGenerator. The raw conversation can contain extremely large tool outputs or repeated @@ -1241,7 +1256,7 @@ def _build_result_generator_conversation( """ exclude = {a.lower() for a in (exclude_authors or set())} - selected: list[ChatMessage] = [] + selected: list[Message] = [] seen_fingerprints: set[tuple[str | None, str, str]] = set() total_chars = 0 @@ -1296,7 +1311,7 @@ def _build_result_generator_conversation( # Preserve role + author_name so downstream can attribute sign-offs. selected.append( - ChatMessage( + Message( role=role, text=truncated, author_name=author, diff --git a/src/processor/src/libs/agent_framework/middlewares.py b/src/processor/src/libs/agent_framework/middlewares.py index a24f5b00..0319a6a0 100644 --- a/src/processor/src/libs/agent_framework/middlewares.py +++ b/src/processor/src/libs/agent_framework/middlewares.py @@ -6,16 +6,31 @@ import time from collections.abc import Awaitable, Callable -from agent_framework import ( - AgentMiddleware, - AgentRunContext, - ChatContext, - ChatMessage, - ChatMiddleware, - FunctionInvocationContext, - FunctionMiddleware, - Role, -) +try: + from agent_framework import ( + AgentContext, + AgentMiddleware, + ChatContext, + ChatMiddleware, + FunctionInvocationContext, + FunctionMiddleware, + Message, + Role, + ) +except ImportError: + from agent_framework import ( + AgentMiddleware, + AgentRunContext as AgentContext, + ChatContext, + ChatMessage as Message, + ChatMiddleware, + FunctionInvocationContext, + FunctionMiddleware, + Role, + ) + + +ROLE_USER = getattr(Role, "USER", "user") class DebuggingMiddleware(AgentMiddleware): @@ -23,8 +38,8 @@ class DebuggingMiddleware(AgentMiddleware): async def process( self, - context: AgentRunContext, - next: Callable[[AgentRunContext], Awaitable[None]], + context: AgentContext, + next: Callable[[AgentContext], Awaitable[None]], ) -> None: """Run-level debugging middleware for troubleshooting specific runs.""" print("[Debug] Debug mode enabled for this run") @@ -136,16 +151,17 @@ async def process( for i, message in enumerate(context.messages): content = message.text if message.text else str(message.contents) - print(f" Message {i + 1} ({message.role.value}): {content}") + role_value = getattr(message.role, "value", message.role) + print(f" Message {i + 1} ({role_value}): {content}") print(f"[InputObserverMiddleware] Total messages: {len(context.messages)}") # Modify user messages by creating new messages with enhanced text - modified_messages: list[ChatMessage] = [] + modified_messages: list[Message] = [] modified_count = 0 for message in context.messages: - if message.role == Role.USER and message.text: + if message.role == ROLE_USER and message.text: original_text = message.text updated_text = original_text @@ -155,7 +171,7 @@ async def process( f"[InputObserverMiddleware] Updated: '{original_text}' -> '{updated_text}'" ) - modified_message = ChatMessage(role=message.role, text=updated_text) + modified_message = Message(role=message.role, text=updated_text) modified_messages.append(modified_message) modified_count += 1 else: diff --git a/src/processor/src/libs/agent_framework/shared_memory_context_provider.py b/src/processor/src/libs/agent_framework/shared_memory_context_provider.py index a143a88e..16b64e00 100644 --- a/src/processor/src/libs/agent_framework/shared_memory_context_provider.py +++ b/src/processor/src/libs/agent_framework/shared_memory_context_provider.py @@ -18,7 +18,17 @@ from collections.abc import MutableSequence, Sequence from typing import TYPE_CHECKING -from agent_framework import ChatMessage, Context, ContextProvider +try: + from agent_framework import Context, ContextProvider, Message +except ImportError: + try: + from agent_framework import ContextProvider, Message + except ImportError: + from agent_framework import ChatMessage as Message, ContextProvider + + class Context: + def __init__(self, instructions: str | None = None, **kwargs): + self.instructions = instructions if TYPE_CHECKING: from libs.agent_framework.qdrant_memory_store import QdrantMemoryStore @@ -49,6 +59,11 @@ class SharedMemoryContextProvider(ContextProvider): redundant embedding calls for intermediate turns) """ + DEFAULT_CONTEXT_PROMPT = ( + "The following are relevant memories from previous migration steps. " + "Use them as context to inform your current task:" + ) + def __init__( self, memory_store: QdrantMemoryStore, @@ -87,7 +102,7 @@ def __init__( async def invoking( self, - messages: ChatMessage | MutableSequence[ChatMessage], + messages: Message | MutableSequence[Message], **kwargs, ) -> Context: """Called before the agent's LLM call. Injects relevant shared memories. @@ -140,8 +155,8 @@ async def invoking( async def invoked( self, - request_messages: ChatMessage | Sequence[ChatMessage], - response_messages: ChatMessage | Sequence[ChatMessage] | None = None, + request_messages: Message | Sequence[Message], + response_messages: Message | Sequence[Message] | None = None, invoke_exception: Exception | None = None, **kwargs, ) -> None: @@ -249,7 +264,7 @@ async def _flush_memory(self) -> None: ) def _extract_query( - self, messages: ChatMessage | MutableSequence[ChatMessage] + self, messages: Message | MutableSequence[Message] ) -> str: """Extract a search query from the input messages. @@ -292,8 +307,8 @@ def _format_memories(self, memories: list) -> str: return "\n".join(lines) @staticmethod - def _get_text(message: ChatMessage) -> str: - """Extract text content from a ChatMessage.""" + def _get_text(message: Message) -> str: + """Extract text content from a Message.""" if hasattr(message, "text") and message.text: return message.text if hasattr(message, "content"): @@ -302,7 +317,7 @@ def _get_text(message: ChatMessage) -> str: @staticmethod def _extract_text( - messages: ChatMessage | Sequence[ChatMessage], + messages: Message | Sequence[Message], ) -> str: """Extract text content from response message(s).""" if not isinstance(messages, (list, Sequence)) or isinstance(messages, str): diff --git a/src/processor/src/libs/base/orchestrator_base.py b/src/processor/src/libs/base/orchestrator_base.py index 46dce8c6..2d6616e9 100644 --- a/src/processor/src/libs/base/orchestrator_base.py +++ b/src/processor/src/libs/base/orchestrator_base.py @@ -9,12 +9,23 @@ from abc import abstractmethod from typing import Any, Callable, Generic, MutableMapping, Sequence, TypeVar -from agent_framework import ChatAgent, ManagerSelectionResponse, ToolProtocol +try: + from agent_framework import Agent, FunctionTool, ToolResultCompactionStrategy +except ImportError: + from agent_framework import ChatAgent as Agent, ToolProtocol as FunctionTool + + try: + from agent_framework import ToolResultCompactionStrategy + except ImportError: + ToolResultCompactionStrategy = None # type: ignore[assignment,misc] from libs.agent_framework.agent_builder import AgentBuilder from libs.agent_framework.agent_framework_helper import ClientType from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.azure_openai_response_retry import RateLimitRetryConfig +from libs.agent_framework.coordinator_selection_response import ( + CoordinatorSelectionResponse, +) from libs.agent_framework.groupchat_orchestrator import ( AgentResponse, AgentResponseStream, @@ -60,10 +71,10 @@ def is_console_summarization_enabled(self) -> bool: async def initialize(self, process_id: str): self.mcp_tools: ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ) = await self.prepare_mcp_tools() self.agentinfos = await self.prepare_agent_infos() @@ -90,7 +101,7 @@ async def flush_agent_memories(self) -> None: is stored in the shared memory before the next step begins. """ for agent in (self.agents or {}).values(): - # ChatAgent stores providers in agent.context_provider (AggregateContextProvider) + # Agent stores providers in agent.context_provider (ContextProvider) # which has a .providers list of individual ContextProvider instances agg_provider = getattr(agent, "context_provider", None) if agg_provider is None: @@ -130,10 +141,10 @@ async def execute( async def prepare_mcp_tools( self, ) -> ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ): pass @@ -144,8 +155,8 @@ async def prepare_agent_infos(self) -> list[AgentInfo]: async def create_agents( self, agent_infos: list[AgentInfo], process_id: str - ) -> list[ChatAgent]: - agents = dict[str, ChatAgent]() + ) -> list[Agent]: + agents = dict[str, Agent]() agent_client = await self.get_client(thread_id=process_id) # Workspace context — injected into every agent's system instructions @@ -176,13 +187,20 @@ async def create_agents( .with_temperature(0.0) .with_max_tokens(20_000) ) + # Prevent context window overflow by summarizing older tool results. + if ToolResultCompactionStrategy is not None: + builder = builder.with_kwargs( + compaction_strategy=ToolResultCompactionStrategy( + keep_last_tool_call_groups=2 + ) + ) if agent_info.agent_name == "Coordinator": # Routing-only: keep deterministic. Needs enough tokens for long instructions. builder = ( builder .with_temperature(0.0) - .with_response_format(ManagerSelectionResponse) + .with_response_format(CoordinatorSelectionResponse) .with_max_tokens(4_000) .with_tools(agent_info.tools) # for checking file existence ) @@ -292,7 +310,7 @@ async def on_agent_response(self, response: AgentResponse): # print different information. from Coordinator's response structure try: response_dict = json.loads(response.message) - coordinator_response = ManagerSelectionResponse.model_validate( + coordinator_response = CoordinatorSelectionResponse.model_validate( response_dict ) diff --git a/src/processor/src/libs/mcp_server/MCPBlobIOTool.py b/src/processor/src/libs/mcp_server/MCPBlobIOTool.py index 40a68fe2..f821c925 100644 --- a/src/processor/src/libs/mcp_server/MCPBlobIOTool.py +++ b/src/processor/src/libs/mcp_server/MCPBlobIOTool.py @@ -22,14 +22,14 @@ from libs.mcp_server.MCPBlobIOTool import get_blob_file_mcp from libs.agent_framework.mcp_context import MCPContext - from agent_framework import ChatAgent + from agent_framework import Agent # Get the Blob Storage MCP tool blob_tool = get_blob_file_mcp() # Use with MCPContext for TaskGroup-safe management async with MCPContext(tools=[blob_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run( "Upload the file 'data.csv' to my Azure storage container 'datasets'" ) @@ -76,7 +76,7 @@ def get_blob_file_mcp() -> MCPStdioTool: blob_tool = get_blob_file_mcp() async with blob_tool: - async with ChatAgent(client, tools=[blob_tool]) as agent: + async with Agent(client, tools=[blob_tool]) as agent: result = await agent.run( "Upload 'report.pdf' to container 'documents'" ) @@ -91,7 +91,7 @@ def get_blob_file_mcp() -> MCPStdioTool: blob_tool = get_blob_file_mcp() async with MCPContext(tools=[blob_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: # List all containers containers = await agent.run("List all my blob containers") print(containers) @@ -111,13 +111,13 @@ def get_blob_file_mcp() -> MCPStdioTool: async with MCPContext(tools=[blob_tool, datetime_tool]) as mcp_ctx: # Data processing agent - async with ChatAgent(client1, tools=mcp_ctx.tools) as processor: + async with Agent(client1, tools=mcp_ctx.tools) as processor: data = await processor.run( "Download 'raw_data.csv' from 'input-container'" ) # Analysis agent - async with ChatAgent(client2, tools=mcp_ctx.tools) as analyst: + async with Agent(client2, tools=mcp_ctx.tools) as analyst: result = await analyst.run( f"Analyze the data and upload results to 'output-container'" ) @@ -137,7 +137,7 @@ def get_blob_file_mcp() -> MCPStdioTool: blob_tool = get_blob_file_mcp() async with MCPContext(tools=[blob_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run("Upload 'image.png' to 'media-container'") Note: diff --git a/src/processor/src/libs/mcp_server/MCPDatetimeTool.py b/src/processor/src/libs/mcp_server/MCPDatetimeTool.py index 83aca397..157d07a2 100644 --- a/src/processor/src/libs/mcp_server/MCPDatetimeTool.py +++ b/src/processor/src/libs/mcp_server/MCPDatetimeTool.py @@ -15,14 +15,14 @@ from libs.mcp_server.MCPDatetimeTool import get_datetime_mcp from libs.agent_framework.mcp_context import MCPContext - from agent_framework import ChatAgent + from agent_framework import Agent # Get the datetime MCP tool datetime_tool = get_datetime_mcp() # Use with MCPContext for TaskGroup-safe management async with MCPContext(tools=[datetime_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run("What time is it right now?") print(response) """ @@ -60,7 +60,7 @@ def get_datetime_mcp() -> MCPStdioTool: datetime_tool = get_datetime_mcp() async with datetime_tool: - async with ChatAgent(client, tools=[datetime_tool]) as agent: + async with Agent(client, tools=[datetime_tool]) as agent: result = await agent.run("What's today's date?") print(result) @@ -74,7 +74,7 @@ def get_datetime_mcp() -> MCPStdioTool: weather_tool = get_weather_mcp() async with MCPContext(tools=[datetime_tool, weather_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run( "What's the current time and what's the weather like?" ) @@ -88,10 +88,10 @@ def get_datetime_mcp() -> MCPStdioTool: async with MCPContext(tools=[datetime_tool]) as mcp_ctx: # Share tool across multiple agents - async with ChatAgent(client1, tools=mcp_ctx.tools) as agent1: + async with Agent(client1, tools=mcp_ctx.tools) as agent1: time_info = await agent1.run("Get the current time") - async with ChatAgent(client2, tools=mcp_ctx.tools) as agent2: + async with Agent(client2, tools=mcp_ctx.tools) as agent2: schedule = await agent2.run( f"Based on the time {time_info}, suggest a meeting slot" ) diff --git a/src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py b/src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py index d9a2ca0e..989f7d75 100644 --- a/src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py +++ b/src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py @@ -12,14 +12,14 @@ from libs.mcp_server.MCPMicrosoftDocs import get_microsoft_docs_mcp from libs.agent_framework.mcp_context import MCPContext - from agent_framework import ChatAgent + from agent_framework import Agent # Get the Microsoft Docs MCP tool docs_tool = get_microsoft_docs_mcp() # Use with MCPContext for TaskGroup-safe management async with MCPContext(tools=[docs_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run("Search Microsoft Learn for Azure Functions best practices") print(response) """ @@ -47,7 +47,7 @@ def get_microsoft_docs_mcp() -> MCPStreamableHTTPTool: docs_tool = get_microsoft_docs_mcp() async with docs_tool: - async with ChatAgent(client, tools=[docs_tool]) as agent: + async with Agent(client, tools=[docs_tool]) as agent: result = await agent.run("Find documentation about Azure App Service") Advanced usage with multiple tools: @@ -60,7 +60,7 @@ def get_microsoft_docs_mcp() -> MCPStreamableHTTPTool: datetime_tool = MCPStdioTool(name="datetime", command="npx", args=["-y", "@modelcontextprotocol/server-datetime"]) async with MCPContext(tools=[docs_tool, datetime_tool]) as mcp_ctx: - async with ChatAgent(client, tools=mcp_ctx.tools) as agent: + async with Agent(client, tools=mcp_ctx.tools) as agent: response = await agent.run("What's the latest Azure Functions documentation?") Note: diff --git a/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py b/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py index 93f8f2f0..461005a6 100644 --- a/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py +++ b/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py @@ -12,7 +12,10 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -from agent_framework import MCPStdioTool, MCPStreamableHTTPTool, ToolProtocol +try: + from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool +except ImportError: + from agent_framework import MCPStdioTool, MCPStreamableHTTPTool, ToolProtocol as FunctionTool from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( @@ -98,10 +101,10 @@ async def execute( async def prepare_mcp_tools( self, ) -> ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ): """Create and return the MCP tools used by analysis agents. diff --git a/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py b/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py index f1fe8b4d..580e56b8 100644 --- a/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py +++ b/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py @@ -13,11 +13,14 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol, -) +try: + from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool +except ImportError: + from agent_framework import ( + MCPStdioTool, + MCPStreamableHTTPTool, + ToolProtocol as FunctionTool, + ) from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( @@ -107,10 +110,10 @@ async def execute( async def prepare_mcp_tools( self, ) -> ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ): """Create and return the MCP tools used by conversion agents.""" ms_doc_mcp_tool = MCPStreamableHTTPTool( diff --git a/src/processor/src/steps/design/orchestration/design_orchestrator.py b/src/processor/src/steps/design/orchestration/design_orchestrator.py index d2dd47f0..2e49c850 100644 --- a/src/processor/src/steps/design/orchestration/design_orchestrator.py +++ b/src/processor/src/steps/design/orchestration/design_orchestrator.py @@ -11,11 +11,14 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol, -) +try: + from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool +except ImportError: + from agent_framework import ( + MCPStdioTool, + MCPStreamableHTTPTool, + ToolProtocol as FunctionTool, + ) from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( @@ -98,10 +101,10 @@ async def execute( async def prepare_mcp_tools( self, ) -> ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ): """Create and return the MCP tools used by design agents.""" # Create MCP tools (not connected yet) diff --git a/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py b/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py index 0aa6c443..b623b9dd 100644 --- a/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py +++ b/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py @@ -15,11 +15,14 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol, -) +try: + from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool +except ImportError: + from agent_framework import ( + MCPStdioTool, + MCPStreamableHTTPTool, + ToolProtocol as FunctionTool, + ) from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( @@ -112,10 +115,10 @@ async def execute( async def prepare_mcp_tools( self, ) -> ( - ToolProtocol + FunctionTool | Callable[..., Any] | MutableMapping[str, Any] - | Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]] + | Sequence[FunctionTool | Callable[..., Any] | MutableMapping[str, Any]] ): """Create and return the MCP tools used by documentation agents.""" ms_doc_mcp_tool = MCPStreamableHTTPTool( diff --git a/src/processor/src/steps/migration_processor.py b/src/processor/src/steps/migration_processor.py index 73b2954a..b1451ef3 100644 --- a/src/processor/src/steps/migration_processor.py +++ b/src/processor/src/steps/migration_processor.py @@ -32,16 +32,7 @@ from datetime import datetime from typing import Any -from agent_framework import ( - ExecutorCompletedEvent, - ExecutorFailedEvent, - ExecutorInvokedEvent, - Workflow, - WorkflowBuilder, - WorkflowFailedEvent, - WorkflowOutputEvent, - WorkflowStartedEvent, -) +from agent_framework import Workflow, WorkflowBuilder, WorkflowEvent from openai import AsyncAzureOpenAI @@ -368,7 +359,7 @@ async def _generate_report_summary( } async for event in self.workflow.run_stream(input_data): - if isinstance(event, WorkflowStartedEvent): + if event.type == "started": logger.info("Workflow started (%s)", event.origin.value) report_collector.set_current_step("analysis", step_phase="start") @@ -377,16 +368,16 @@ async def _generate_report_summary( await telemetry.init_process( process_id=input_data.process_id, step="analysis", phase="start" ) - elif isinstance(event, WorkflowOutputEvent): - # WorkflowOutputEvent carries the step output (success or hard-termination). + elif event.type == "output": + # WorkflowEvent carries the step output (success or hard-termination). # Note: a None payload is an error that must be surfaced clearly. if event.data is None: report_collector.set_current_step( - event.source_executor_id or "unknown" + event.executor_id or "unknown" ) # Build a meaningful error message instead of generic "Workflow output is None" - executor_id = event.source_executor_id or "unknown" + executor_id = event.executor_id or "unknown" error_msg = f"Step '{executor_id}' completed without producing output. This may be caused by context length overflow, agent timeout, or an internal orchestration error. Check processor logs for '[AOAI_CTX_TRIM_STREAM]' or exception details." report_collector.record_failure( @@ -407,13 +398,13 @@ async def _generate_report_summary( await telemetry.record_failure_outcome( process_id=input_data.process_id, - failed_step=event.source_executor_id or "unknown", + failed_step=event.executor_id or "unknown", error_message=error_msg, failure_details=failure_details, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.source_executor_id] - if event.source_executor_id in step_start_perf + - step_start_perf[event.executor_id] + if event.executor_id in step_start_perf else None ), ) @@ -423,7 +414,7 @@ async def _generate_report_summary( # Raise a rich exception so the queue worker reports a meaningful reason. raise WorkflowExecutorFailedException({ - "executor_id": event.source_executor_id or "unknown", + "executor_id": event.executor_id or "unknown", "error_type": "WorkflowOutputMissing", "message": error_msg, "traceback": None, @@ -477,15 +468,15 @@ async def _generate_report_summary( } report_collector.set_current_step( - event.source_executor_id or "unknown" + event.executor_id or "unknown" ) report_collector.record_failure( exception=ValueError( getattr(event.data, "reason", None) - or f"Hard terminated in {event.source_executor_id} step" + or f"Hard terminated in {event.executor_id} step" ), custom_message=getattr(event.data, "reason", None) - or f"Hard terminated in {event.source_executor_id} step", + or f"Hard terminated in {event.executor_id} step", ) failure_details: Any = ( @@ -510,14 +501,14 @@ async def _generate_report_summary( await telemetry.record_failure_outcome( process_id=input_data.process_id, - failed_step=event.source_executor_id or "unknown", + failed_step=event.executor_id or "unknown", error_message=getattr(event.data, "reason", None) - or f"Hard terminated in {event.source_executor_id} step", + or f"Hard terminated in {event.executor_id} step", failure_details=failure_details, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.source_executor_id] - if event.source_executor_id in step_start_perf + - step_start_perf[event.executor_id] + if event.executor_id in step_start_perf else None ), ) @@ -533,21 +524,21 @@ async def _generate_report_summary( logger.info("Workflow output (%s): %s", event.origin.value, event.data) await telemetry.record_step_result( process_id=input_data.process_id, - step_name=event.source_executor_id, + step_name=event.executor_id, step_result=event.data, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.source_executor_id] - if event.source_executor_id in step_start_perf + - step_start_perf[event.executor_id] + if event.executor_id in step_start_perf else None ), ) - if event.source_executor_id in step_start_perf: + if event.executor_id in step_start_perf: report_collector.mark_step_completed( - event.source_executor_id, + event.executor_id, execution_time=time.perf_counter() - - step_start_perf[event.source_executor_id], + - step_start_perf[event.executor_id], ) try: @@ -572,10 +563,10 @@ async def _generate_report_summary( ) return event.data - elif isinstance(event, ExecutorFailedEvent): + elif event.type == "executor_failed": pass # will handle in WorkflowFailedEvent - elif isinstance(event, WorkflowFailedEvent): + elif event.type == "failed": logger.error( "Executor failed (%s): %s [%s]: %s (traceback: %s)", event.origin.value, @@ -644,7 +635,7 @@ async def _generate_report_summary( # Raise a rich exception containing the full WorkflowErrorDetails payload. raise WorkflowExecutorFailedException(event.details) - elif isinstance(event, ExecutorInvokedEvent): + elif event.type == "executor_invoked": # The bug. the first executor's event fired after completing execution. if event.executor_id != "analysis": telemetry: TelemetryManager = ( @@ -675,7 +666,7 @@ async def _generate_report_summary( # near-zero and incorrect. if event.executor_id not in step_start_perf: step_start_perf[event.executor_id] = time.perf_counter() - elif isinstance(event, ExecutorCompletedEvent): + elif event.type == "executor_completed": # print(f"Executor completed ({event.executor_id}): {event.data}") # Log shared memory stats after each step diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_agent_builder.py b/src/processor/src/tests/unit/libs/agent_framework/test_agent_builder.py index 26fcbfe5..cbfede63 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_agent_builder.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_agent_builder.py @@ -144,7 +144,7 @@ def test_chaining_returns_self_each_step(self): class TestBuild: def test_build_passes_all_state_to_chat_agent(self): chat_client = MagicMock() - with patch("libs.agent_framework.agent_builder.ChatAgent") as mock_chat: + with patch("libs.agent_framework.agent_builder.Agent") as mock_chat: agent = ( AgentBuilder(chat_client) .with_instructions("inst") @@ -172,7 +172,7 @@ def test_build_passes_all_state_to_chat_agent(self): class TestStaticFactories: def test_create_agent_invokes_chat_agent(self): chat_client = MagicMock() - with patch("libs.agent_framework.agent_builder.ChatAgent") as mock_chat: + with patch("libs.agent_framework.agent_builder.Agent") as mock_chat: agent = AgentBuilder.create_agent( chat_client=chat_client, instructions="i", @@ -206,7 +206,7 @@ def test_create_agent_by_agentinfo_uses_helper_and_creates_client(self): with patch( "libs.agent_framework.agent_builder.get_bearer_token_provider", return_value="token-provider", - ), patch("libs.agent_framework.agent_builder.ChatAgent") as mock_chat: + ), patch("libs.agent_framework.agent_builder.Agent") as mock_chat: agent = AgentBuilder.create_agent_by_agentinfo( service_id="default", agent_info=agent_info, @@ -241,7 +241,7 @@ def test_create_agent_by_agentinfo_falls_back_to_system_prompt(self): with patch( "libs.agent_framework.agent_builder.get_bearer_token_provider", return_value="tp", - ), patch("libs.agent_framework.agent_builder.ChatAgent") as mock_chat: + ), patch("libs.agent_framework.agent_builder.Agent") as mock_chat: AgentBuilder.create_agent_by_agentinfo( service_id="default", agent_info=agent_info ) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py b/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py index 64a8d415..767d5359 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py @@ -110,45 +110,41 @@ def test_default_token_provider_when_no_credential(self): assert mock_cls.call_args.kwargs["ad_token_provider"] == "default-token" def test_azure_openai_chat_completion(self): - # Patch the lazily imported module fake_module = types.ModuleType("agent_framework.azure") - fake_module.AzureOpenAIChatClient = MagicMock(return_value="chat_client") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - client = AgentFrameworkHelper.create_client( - ClientType.AzureOpenAIChatCompletion, - endpoint="https://x", - deployment_name="gpt-4", - ad_token_provider="t", - ) - assert client == "chat_client" + with pytest.raises(NotImplementedError, match="AzureOpenAIChatClient was removed"): + AgentFrameworkHelper.create_client( + ClientType.AzureOpenAIChatCompletion, + endpoint="https://x", + deployment_name="gpt-4", + ad_token_provider="t", + ) def test_azure_openai_assistant(self): fake_module = types.ModuleType("agent_framework.azure") - fake_module.AzureOpenAIAssistantsClient = MagicMock(return_value="asst_client") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - client = AgentFrameworkHelper.create_client( - ClientType.AzureOpenAIAssistant, - endpoint="https://x", - deployment_name="gpt-4", - ad_token_provider="t", - ) - assert client == "asst_client" + with pytest.raises(NotImplementedError, match="AzureOpenAIAssistantsClient was removed"): + AgentFrameworkHelper.create_client( + ClientType.AzureOpenAIAssistant, + endpoint="https://x", + deployment_name="gpt-4", + ad_token_provider="t", + ) def test_azure_openai_response(self): fake_module = types.ModuleType("agent_framework.azure") - fake_module.AzureOpenAIResponsesClient = MagicMock(return_value="resp_client") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - client = AgentFrameworkHelper.create_client( - ClientType.AzureOpenAIResponse, - endpoint="https://x", - deployment_name="gpt-4", - ad_token_provider="t", - ) - assert client == "resp_client" + with pytest.raises(NotImplementedError, match="AzureOpenAIResponsesClient was removed"): + AgentFrameworkHelper.create_client( + ClientType.AzureOpenAIResponse, + endpoint="https://x", + deployment_name="gpt-4", + ad_token_provider="t", + ) def test_azure_openai_agent(self): fake_module = types.ModuleType("agent_framework.azure") - fake_module.AzureAIAgentClient = MagicMock(return_value="agent_client") + fake_module.DurableAIAgentClient = MagicMock(return_value="agent_client") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): client = AgentFrameworkHelper.create_client( ClientType.AzureOpenAIAgent, diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py b/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py index a95d9623..263b157e 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py @@ -17,6 +17,21 @@ import pytest +import libs.agent_framework.groupchat_orchestrator as groupchat_module + +ROLE_USER = "user" +ROLE_ASSISTANT = "assistant" + + +class Message: + def __init__(self, *, role, text=None, contents=None, author_name=None): + self.role = role + self.text = text + self.contents = contents + self.author_name = author_name + + +groupchat_module.Message = Message from libs.agent_framework.groupchat_orchestrator import ( AgentResponse, AgentResponseStream, @@ -31,7 +46,7 @@ def _run(coro): @dataclass class _Msg: - """Lightweight stand-in for a ChatMessage.""" + """Lightweight stand-in for a Message.""" source: str = "" content: str = "" @@ -602,30 +617,27 @@ def test_skips_unrelated(self): class TestBackfillToolUsage: def test_skips_non_assistant(self): - from agent_framework import Role orch = _make_orch() - msg = SimpleNamespace(role=Role.USER, contents=[]) + msg = SimpleNamespace(role=ROLE_USER, contents=[]) orch._backfill_tool_usage_from_conversation([msg]) assert orch.agent_tool_usage == {} def test_records_calls_from_assistant(self): - from agent_framework import Role orch = _make_orch() item = SimpleNamespace(name="t", call_id="c", arguments={"x": 1}) msg = SimpleNamespace( - role=Role.ASSISTANT, author_name="A", contents=[item] + role=ROLE_ASSISTANT, author_name="A", contents=[item] ) orch._backfill_tool_usage_from_conversation([msg]) assert orch.agent_tool_usage["A"][0]["tool_name"] == "t" def test_dedup_already_recorded(self): - from agent_framework import Role orch = _make_orch() # Pre-mark this call as already recorded orch._tool_call_recorded.add(("A", "c")) item = SimpleNamespace(name="t", call_id="c", arguments={}) msg = SimpleNamespace( - role=Role.ASSISTANT, author_name="A", contents=[item] + role=ROLE_ASSISTANT, author_name="A", contents=[item] ) orch._backfill_tool_usage_from_conversation([msg]) assert "A" in orch.agent_tool_usage @@ -777,13 +789,11 @@ def test_tail_zero_returns_head(self): class TestBuildResultGeneratorConversation: def test_excludes_named_authors(self): - from agent_framework import Role - from agent_framework import ChatMessage orch = _make_orch() msgs = [ - ChatMessage(role=Role.ASSISTANT, text="from coord", author_name="Coordinator"), - ChatMessage(role=Role.ASSISTANT, text="from architect", author_name="Architect"), + Message(role=ROLE_ASSISTANT, text="from coord", author_name="Coordinator"), + Message(role=ROLE_ASSISTANT, text="from architect", author_name="Architect"), ] out = orch._build_result_generator_conversation( msgs, @@ -798,14 +808,12 @@ def test_excludes_named_authors(self): assert all("Coordinator" != m.author_name for m in out) def test_dedupes_identical_payloads(self): - from agent_framework import Role - from agent_framework import ChatMessage orch = _make_orch() big = "X" * 1000 msgs = [ - ChatMessage(role=Role.ASSISTANT, text=big, author_name="A"), - ChatMessage(role=Role.ASSISTANT, text=big, author_name="A"), + Message(role=ROLE_ASSISTANT, text=big, author_name="A"), + Message(role=ROLE_ASSISTANT, text=big, author_name="A"), ] out = orch._build_result_generator_conversation( msgs, @@ -819,12 +827,10 @@ def test_dedupes_identical_payloads(self): assert len(out) == 1 def test_truncates_messages_to_per_message_budget(self): - from agent_framework import Role - from agent_framework import ChatMessage orch = _make_orch() msgs = [ - ChatMessage(role=Role.ASSISTANT, text="A" * 500, author_name="X"), + Message(role=ROLE_ASSISTANT, text="A" * 500, author_name="X"), ] out = orch._build_result_generator_conversation( msgs, @@ -838,12 +844,10 @@ def test_truncates_messages_to_per_message_budget(self): assert len(out[-1].text) <= 100 def test_total_budget_enforced(self): - from agent_framework import Role - from agent_framework import ChatMessage orch = _make_orch() msgs = [ - ChatMessage(role=Role.ASSISTANT, text="A" * 100, author_name=str(i)) + Message(role=ROLE_ASSISTANT, text="A" * 100, author_name=str(i)) for i in range(20) ] out = orch._build_result_generator_conversation( @@ -859,12 +863,10 @@ def test_total_budget_enforced(self): assert total <= 200 def test_max_messages_caps_count(self): - from agent_framework import Role - from agent_framework import ChatMessage orch = _make_orch() msgs = [ - ChatMessage(role=Role.ASSISTANT, text=f"m{i}", author_name=str(i)) + Message(role=ROLE_ASSISTANT, text=f"m{i}", author_name=str(i)) for i in range(20) ] out = orch._build_result_generator_conversation( @@ -916,8 +918,6 @@ def test_unknown_tool_name(self): class TestGenerateFinalResult: def test_parses_valid_json(self): from pydantic import BaseModel - from agent_framework import Role - from agent_framework import ChatMessage class Model(BaseModel): x: int @@ -928,7 +928,7 @@ class Model(BaseModel): orch = _make_orch(participants={"Coordinator": object(), "ResultGenerator": rg}, result_format=Model) out = _run( orch._generate_final_result( - conversation=[ChatMessage(role=Role.ASSISTANT, text="x", author_name="A")], + conversation=[Message(role=ROLE_ASSISTANT, text="x", author_name="A")], result_format=Model, result_generator_name="ResultGenerator", ) @@ -937,8 +937,6 @@ class Model(BaseModel): def test_retry_on_validation_error(self): from pydantic import BaseModel - from agent_framework import Role - from agent_framework import ChatMessage class Model(BaseModel): x: int @@ -951,7 +949,7 @@ class Model(BaseModel): orch = _make_orch(participants={"Coordinator": object(), "ResultGenerator": rg}, result_format=Model) out = _run( orch._generate_final_result( - conversation=[ChatMessage(role=Role.ASSISTANT, text="x", author_name="A")], + conversation=[Message(role=ROLE_ASSISTANT, text="x", author_name="A")], result_format=Model, result_generator_name="ResultGenerator", ) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py b/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py index 7556b989..fca26fa8 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py @@ -4,8 +4,20 @@ import asyncio from types import SimpleNamespace -from agent_framework import ChatMessage, Role +import libs.agent_framework.middlewares as middlewares_module +ROLE_USER = "user" + + +class Message: + def __init__(self, *, role, text=None, contents=None, author_name=None): + self.role = role + self.text = text + self.contents = contents + self.author_name = author_name + + +middlewares_module.Message = Message from libs.agent_framework.middlewares import InputObserverMiddleware @@ -13,7 +25,7 @@ def test_input_observer_middleware_replaces_user_text_when_configured() -> None: async def _run() -> None: ctx = SimpleNamespace( messages=[ - ChatMessage(role=Role.USER, text="original"), + Message(role=ROLE_USER, text="original"), ] ) @@ -24,7 +36,7 @@ async def _next(_context): await mw.process(ctx, _next) - assert ctx.messages[0].role == Role.USER + assert ctx.messages[0].role == ROLE_USER assert ctx.messages[0].text == "replacement" asyncio.run(_run()) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py b/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py index c4c32f5a..1b2e1e2f 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py @@ -7,8 +7,21 @@ from types import SimpleNamespace from unittest.mock import AsyncMock, MagicMock -from agent_framework import ChatMessage, Role +import libs.agent_framework.middlewares as middlewares_module +ROLE_USER = "user" +ROLE_ASSISTANT = "assistant" + + +class Message: + def __init__(self, *, role, text=None, contents=None, author_name=None): + self.role = role + self.text = text + self.contents = contents + self.author_name = author_name + + +middlewares_module.Message = Message from libs.agent_framework.middlewares import ( DebuggingMiddleware, LoggingFunctionMiddleware, @@ -86,8 +99,8 @@ class TestInputObserverMiddleware: def test_replaces_user_messages_when_replacement_set(self): from libs.agent_framework.middlewares import InputObserverMiddleware - msg_user = ChatMessage(role=Role.USER, text="orig user") - msg_assistant = ChatMessage(role=Role.ASSISTANT, text="hi") + msg_user = Message(role=ROLE_USER, text="orig user") + msg_assistant = Message(role=ROLE_ASSISTANT, text="hi") ctx = MagicMock() ctx.messages = [msg_user, msg_assistant] next_fn = AsyncMock() @@ -101,7 +114,7 @@ def test_replaces_user_messages_when_replacement_set(self): def test_no_replacement_keeps_text(self): from libs.agent_framework.middlewares import InputObserverMiddleware - msg = ChatMessage(role=Role.USER, text="keep me") + msg = Message(role=ROLE_USER, text="keep me") ctx = MagicMock() ctx.messages = [msg] mw = InputObserverMiddleware(replacement=None) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_shared_memory_context_provider.py b/src/processor/src/tests/unit/libs/agent_framework/test_shared_memory_context_provider.py index 1d75ee7a..ab2bc8b2 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_shared_memory_context_provider.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_shared_memory_context_provider.py @@ -90,7 +90,7 @@ async def _run(): provider, _ = _make_provider() context = await provider.invoking([]) assert context.instructions is None - assert context.messages == [] + assert getattr(context, "messages", []) == [] asyncio.run(_run()) diff --git a/src/processor/src/tests/unit/steps/test_migration_processor_run.py b/src/processor/src/tests/unit/steps/test_migration_processor_run.py index acd4ee40..683fcc5d 100644 --- a/src/processor/src/tests/unit/steps/test_migration_processor_run.py +++ b/src/processor/src/tests/unit/steps/test_migration_processor_run.py @@ -11,14 +11,7 @@ import pytest -from agent_framework import ( - ExecutorCompletedEvent, - ExecutorFailedEvent, - ExecutorInvokedEvent, - WorkflowFailedEvent, - WorkflowOutputEvent, - WorkflowStartedEvent, -) +from agent_framework import WorkflowEvent from agent_framework._workflows._events import WorkflowErrorDetails from steps.analysis.models.step_param import Analysis_TaskParam @@ -79,11 +72,11 @@ class TestRunSuccessFlow: def test_workflow_started_then_normal_output_returns_data(self): data = SimpleNamespace(is_hard_terminated=False, value="ok") events = [ - WorkflowStartedEvent(), - ExecutorInvokedEvent(executor_id="analysis", data=_make_input()), - ExecutorCompletedEvent(executor_id="analysis", data={"r": 1}), - ExecutorInvokedEvent(executor_id="design", data=_make_input()), - WorkflowOutputEvent(data=data, source_executor_id="design"), + WorkflowEvent.started(), + WorkflowEvent.executor_invoked(executor_id="analysis", data=_make_input()), + WorkflowEvent.executor_completed(executor_id="analysis", data={"r": 1}), + WorkflowEvent.executor_invoked(executor_id="design", data=_make_input()), + WorkflowEvent.output(executor_id="design", data=data), ] proc = _make_processor(events) result = _run(proc.run(_make_input())) @@ -96,10 +89,10 @@ def test_workflow_started_then_normal_output_returns_data(self): def test_invoked_event_for_non_analysis_triggers_transition_phase(self): data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), + WorkflowEvent.started(), # Documentation invocation should map to "Documentation" display - ExecutorInvokedEvent(executor_id="documentation", data=_make_input()), - WorkflowOutputEvent(data=data, source_executor_id="documentation"), + WorkflowEvent.executor_invoked(executor_id="documentation", data=_make_input()), + WorkflowEvent.output(executor_id="documentation", data=data), ] proc = _make_processor(events) _run(proc.run(_make_input())) @@ -112,9 +105,9 @@ def test_invoked_event_for_non_analysis_triggers_transition_phase(self): def test_invoked_event_unknown_executor_uses_capitalize(self): data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), - ExecutorInvokedEvent(executor_id="custom", data=_make_input()), - WorkflowOutputEvent(data=data, source_executor_id="custom"), + WorkflowEvent.started(), + WorkflowEvent.executor_invoked(executor_id="custom", data=_make_input()), + WorkflowEvent.output(executor_id="custom", data=data), ] proc = _make_processor(events) _run(proc.run(_make_input())) @@ -132,8 +125,8 @@ def test_hard_terminated_returns_data_and_records_failure(self): blocking_issues=["NEED_HUMAN_REVIEW"], ) events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id="analysis", data=data), ] proc = _make_processor(events) result = _run(proc.run(_make_input())) @@ -150,8 +143,8 @@ def test_hard_terminated_security_policy_collects_evidence(self): blocking_issues=["SECURITY_POLICY_VIOLATION"], ) events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id="analysis", data=data), ] proc = _make_processor(events) @@ -181,8 +174,8 @@ def test_hard_terminated_security_policy_handles_collector_error(self): blocking_issues=["SECURITY_POLICY_VIOLATION"], ) events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id="analysis", data=data), ] proc = _make_processor(events) with patch( @@ -198,8 +191,8 @@ def test_hard_terminated_security_policy_handles_collector_error(self): class TestRunOutputMissingFlow: def test_missing_output_raises_workflow_executor_failed_exception(self): events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=None, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id="analysis", data=None), ] proc = _make_processor(events) with pytest.raises(WorkflowExecutorFailedException) as excinfo: @@ -209,8 +202,8 @@ def test_missing_output_raises_workflow_executor_failed_exception(self): def test_missing_output_with_none_source_uses_unknown(self): events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=None, source_executor_id=None), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id=None, data=None), ] proc = _make_processor(events) with pytest.raises(WorkflowExecutorFailedException): @@ -226,9 +219,9 @@ def test_workflow_failed_event_raises_with_details(self): executor_id="yaml", ) events = [ - WorkflowStartedEvent(), - ExecutorInvokedEvent(executor_id="yaml", data=_make_input()), - WorkflowFailedEvent(details=details), + WorkflowEvent.started(), + WorkflowEvent.executor_invoked(executor_id="yaml", data=_make_input()), + WorkflowEvent.failed(details=details), ] proc = _make_processor(events) with pytest.raises(WorkflowExecutorFailedException) as excinfo: @@ -246,8 +239,8 @@ def test_workflow_failed_classifies_context_size_message(self): executor_id="design", ) events = [ - WorkflowStartedEvent(), - WorkflowFailedEvent(details=details), + WorkflowEvent.started(), + WorkflowEvent.failed(details=details), ] proc = _make_processor(events) with pytest.raises(WorkflowExecutorFailedException): @@ -261,8 +254,8 @@ def test_workflow_failed_classifies_context_error_type(self): executor_id="analysis", ) events = [ - WorkflowStartedEvent(), - WorkflowFailedEvent(details=details), + WorkflowEvent.started(), + WorkflowEvent.failed(details=details), ] proc = _make_processor(events) with pytest.raises(WorkflowExecutorFailedException): @@ -275,9 +268,9 @@ def test_executor_failed_event_is_silently_ignored(self): ) data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), - ExecutorFailedEvent(executor_id="analysis", details=details), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.executor_failed(executor_id="analysis", details=details), + WorkflowEvent.output(executor_id="analysis", data=data), ] proc = _make_processor(events) result = _run(proc.run(_make_input())) @@ -288,9 +281,9 @@ class TestRunMemoryStoreLifecycle: def test_memory_store_is_registered_and_closed(self): data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), - ExecutorCompletedEvent(executor_id="analysis", data=None), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.executor_completed(executor_id="analysis", data=None), + WorkflowEvent.output(executor_id="analysis", data=data), ] memory_store = MagicMock() memory_store.get_count = AsyncMock(return_value=3) @@ -304,8 +297,8 @@ def test_memory_store_is_registered_and_closed(self): def test_memory_store_close_error_is_swallowed(self): data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), - WorkflowOutputEvent(data=data, source_executor_id="analysis"), + WorkflowEvent.started(), + WorkflowEvent.output(executor_id="analysis", data=data), ] memory_store = MagicMock() memory_store.get_count = AsyncMock(side_effect=RuntimeError("x")) @@ -318,11 +311,11 @@ def test_memory_store_close_error_is_swallowed(self): def test_executor_completed_with_memory_store_logs_count(self): data = SimpleNamespace(is_hard_terminated=False) events = [ - WorkflowStartedEvent(), - ExecutorCompletedEvent( + WorkflowEvent.started(), + WorkflowEvent.executor_completed( executor_id="analysis", data={"some": "result"} ), - WorkflowOutputEvent(data=data, source_executor_id="design"), + WorkflowEvent.output(executor_id="design", data=data), ] memory_store = MagicMock() memory_store.get_count = AsyncMock(return_value=7) diff --git a/src/processor/uv.lock b/src/processor/uv.lock index 2727133a..376ecbd9 100644 --- a/src/processor/uv.lock +++ b/src/processor/uv.lock @@ -50,14 +50,14 @@ wheels = [ [[package]] name = "agent-framework" -version = "1.0.0b260107" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core", extra = ["all"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/e7/5ad52075da4e586ca94fb8806b3085ac5dea8059413e413bff88c0452e88/agent_framework-1.0.0b260107.tar.gz", hash = "sha256:a2f6508a0ca1df3b7ca4e3a64e45bac8e33cdfe02cf69e9056e37e881a58aad7", size = 2898189, upload-time = "2026-01-07T23:57:48.213Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/e8/c2ee1c4dae4a86b95091969426d11361232a0c554124ba321852a6b6b9bd/agent_framework-1.3.0.tar.gz", hash = "sha256:a13423aceaf587cf28180138151d445bd2d4ce82908cef4a6fbb85fa1771bac1", size = 5509571, upload-time = "2026-05-08T00:09:16.022Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/55/ffef27526cc26bf163ccf9d58ba87bf4e677bba343a542e7b666846f744d/agent_framework-1.0.0b260107-py3-none-any.whl", hash = "sha256:080deb32bff4ef07227a4ba709798c67079ff8a2997fe7a0aed0010adc0c18cf", size = 5554, upload-time = "2026-01-07T23:57:08.433Z" }, + { url = "https://files.pythonhosted.org/packages/a0/81/050f8f8bce8c629a88197837b4beb35cb287f880789fc01923fd5938f142/agent_framework-1.3.0-py3-none-any.whl", hash = "sha256:baaaa932639c87be99d43333f612c3b4112d6d976f0e1e72238e42a4bd572438", size = 5684, upload-time = "2026-05-08T00:09:54.064Z" }, ] [[package]] @@ -102,31 +102,29 @@ wheels = [ ] [[package]] -name = "agent-framework-azure-ai" +name = "agent-framework-azure-ai-search" version = "1.0.0b260130" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core" }, - { name = "aiohttp" }, - { name = "azure-ai-agents" }, - { name = "azure-ai-projects" }, + { name = "azure-search-documents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ac/ef/69ead4fcd2c21608ce35353a507df23df51872552747f803c43d1d81f612/agent_framework_azure_ai-1.0.0b260130.tar.gz", hash = "sha256:c571275089a801f961370ba824568c8b02143b1a6bb5b1d78b97c6debdf4906f", size = 32723, upload-time = "2026-01-30T18:56:41.07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/63/81c7853aa526f3c3667871cea14667af73323c6c53d31c34be34926a9de4/agent_framework_azure_ai_search-1.0.0b260130.tar.gz", hash = "sha256:0a622fdddd7dc0287de693f2aa6f770ec52ea8d1eaca817c4276daa08001c10b", size = 13312, upload-time = "2026-01-30T19:01:08.046Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/8f/a1467c352fed5eb6ebb9567109251cc39b5b3ebb5137a2d14c71fea51bc8/agent_framework_azure_ai-1.0.0b260130-py3-none-any.whl", hash = "sha256:87f0248fe6d4f2f4146f0a56a53527af6365d4a377dc2e3d56c37cbb9deae098", size = 38542, upload-time = "2026-01-30T19:01:12.102Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ec/ac8143dbb1af2ec510f7772d712803193a6a0ad5f36b06e7ec7121df5c80/agent_framework_azure_ai_search-1.0.0b260130-py3-none-any.whl", hash = "sha256:0278c948696d7a00193a0271074c6057b57589ff98eda5544f2eafeac051d6e9", size = 13449, upload-time = "2026-01-30T19:01:23.262Z" }, ] [[package]] -name = "agent-framework-azure-ai-search" -version = "1.0.0b260130" +name = "agent-framework-azure-cosmos" +version = "1.0.0b260507" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "agent-framework-core" }, - { name = "azure-search-documents" }, + { name = "azure-cosmos" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/63/81c7853aa526f3c3667871cea14667af73323c6c53d31c34be34926a9de4/agent_framework_azure_ai_search-1.0.0b260130.tar.gz", hash = "sha256:0a622fdddd7dc0287de693f2aa6f770ec52ea8d1eaca817c4276daa08001c10b", size = 13312, upload-time = "2026-01-30T19:01:08.046Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/97/fd8b045fc4eb1d213d7a91eff6e48e030fdb67da30505f46f1ed20a7aa48/agent_framework_azure_cosmos-1.0.0b260507.tar.gz", hash = "sha256:2c8ec2d5eae52b9e92fd14b4adecd5a52a900a7897589549c32852d9488112c7", size = 10984, upload-time = "2026-05-08T00:09:22.016Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/ec/ac8143dbb1af2ec510f7772d712803193a6a0ad5f36b06e7ec7121df5c80/agent_framework_azure_ai_search-1.0.0b260130-py3-none-any.whl", hash = "sha256:0278c948696d7a00193a0271074c6057b57589ff98eda5544f2eafeac051d6e9", size = 13449, upload-time = "2026-01-30T19:01:23.262Z" }, + { url = "https://files.pythonhosted.org/packages/84/b9/6ac1960dae49ecde8ea906b302abe79b66d09d4cf74f8ed3f7dd9fc6230f/agent_framework_azure_cosmos-1.0.0b260507-py3-none-any.whl", hash = "sha256:c1d7ae4a560b592d2bff9c1ec75a7910101baf8c1778443644cc8cb81c82c1a1", size = 11989, upload-time = "2026-05-08T00:09:02.858Z" }, ] [[package]] @@ -145,6 +143,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0d/fa/200b40db670f79f561ff1e69e9626729ceb6486af970e3489f6c3a295d76/agent_framework_azurefunctions-1.0.0b260130-py3-none-any.whl", hash = "sha256:7d529a0bad67caa38d8823462c439e97de5e1cf364c0e9a0895df5fb44996f64", size = 17788, upload-time = "2026-01-30T18:56:45.741Z" }, ] +[[package]] +name = "agent-framework-bedrock" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "boto3" }, + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/86/0b7dd9d1c043b251ff8bd0e037a20495c82c798914db0372040625cae889/agent_framework_bedrock-1.0.0b260507.tar.gz", hash = "sha256:38953ab30f7aff651a9c85c1ceeefd2ad85fa094b3316858930f1c18dcaff2c6", size = 17467, upload-time = "2026-05-08T00:09:24.852Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/b4/fc4277a50b7a0a7cd038e4511a0215fb98ab5e394f719506e30c31854335/agent_framework_bedrock-1.0.0b260507-py3-none-any.whl", hash = "sha256:28ce485c639e467ca4fae4d5b747cd7f9438b8145ca096c658ab5c694611edcc", size = 13907, upload-time = "2026-05-08T00:09:18.84Z" }, +] + [[package]] name = "agent-framework-chatkit" version = "1.0.0b260130" @@ -158,6 +170,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9f/f1/68496e52aa36e66cf2962b8a8c6937053e2e57ad5f135b6983d705172554/agent_framework_chatkit-1.0.0b260130-py3-none-any.whl", hash = "sha256:a7814a5b222de7a0ac57fb89f4a6e534521c7e58bdc86a6465885fb9d57e63f1", size = 11712, upload-time = "2026-01-30T18:56:49.14Z" }, ] +[[package]] +name = "agent-framework-claude" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "claude-agent-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/17/1a/1a1c810e7c74075a4766ac0de66e3e510e0267533baa41a089ab1eb5bf01/agent_framework_claude-1.0.0b260507.tar.gz", hash = "sha256:0daccfef8141470fd206bb8b30925a44ba42ec6fb8946934dbcefe50cfeae14c", size = 11618, upload-time = "2026-05-08T00:08:57.253Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/f8/4977b7d7f1f2ea82c396de07b04f999c58475476722836f3ed0337722495/agent_framework_claude-1.0.0b260507-py3-none-any.whl", hash = "sha256:3ebd1d391b4413512970da62eb5377099ecd66305048594ec5b65cbdf141623f", size = 11588, upload-time = "2026-05-08T00:09:00.32Z" }, +] + [[package]] name = "agent-framework-copilotstudio" version = "1.0.0b260130" @@ -173,23 +198,17 @@ wheels = [ [[package]] name = "agent-framework-core" -version = "1.0.0b260107" +version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "azure-identity" }, - { name = "mcp", extra = ["ws"] }, - { name = "openai" }, { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "opentelemetry-semantic-conventions-ai" }, - { name = "packaging" }, { name = "pydantic" }, - { name = "pydantic-settings" }, + { name = "python-dotenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9d/44/06f5d2c99dd7bdb82c2cb5cbc354b5bc6af72d1886d20eff1dff83508fae/agent_framework_core-1.0.0b260107.tar.gz", hash = "sha256:12636fb64664c6153546f0d85dafccdbe57226767c14b3f38985867389f980bb", size = 3574757, upload-time = "2026-01-07T23:57:16.113Z" } +sdist = { url = "https://files.pythonhosted.org/packages/90/59/4c212abdb93074677d643e31a3c21e33ff26a3ccc351145475cd1ffffad7/agent_framework_core-1.3.0.tar.gz", hash = "sha256:91c3659718b733f70dde6fb3626edb044733e0f7aa5f9726c9774e17fae328ef", size = 365395, upload-time = "2026-05-08T00:09:09.36Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/5a/8c6315a2ca119ad48340344616d4b8e77fd68e2892f82c402069a52ad647/agent_framework_core-1.0.0b260107-py3-none-any.whl", hash = "sha256:5bd119b8d30dc2d5bee1c4a5c3597d7afc808a52e4de148725c4f2d9bcc7632b", size = 5687298, upload-time = "2026-01-07T23:57:26.286Z" }, + { url = "https://files.pythonhosted.org/packages/56/f2/c4258333f2691ee10869bf72f51d423808962ccf0c195b1f893c06c348ad/agent_framework_core-1.3.0-py3-none-any.whl", hash = "sha256:b7a5baf2beb383e9042af057df79dae4fda0b836cbc8530b3b2a57a3c12bb7ac", size = 407978, upload-time = "2026-05-08T00:09:32.752Z" }, ] [package.optional-dependencies] @@ -197,18 +216,28 @@ all = [ { name = "agent-framework-a2a" }, { name = "agent-framework-ag-ui" }, { name = "agent-framework-anthropic" }, - { name = "agent-framework-azure-ai" }, { name = "agent-framework-azure-ai-search" }, + { name = "agent-framework-azure-cosmos" }, { name = "agent-framework-azurefunctions" }, + { name = "agent-framework-bedrock" }, { name = "agent-framework-chatkit" }, + { name = "agent-framework-claude" }, { name = "agent-framework-copilotstudio" }, { name = "agent-framework-declarative" }, { name = "agent-framework-devui" }, + { name = "agent-framework-durabletask" }, + { name = "agent-framework-foundry" }, + { name = "agent-framework-foundry-local" }, + { name = "agent-framework-github-copilot" }, + { name = "agent-framework-hyperlight", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, { name = "agent-framework-lab" }, { name = "agent-framework-mem0" }, { name = "agent-framework-ollama" }, + { name = "agent-framework-openai" }, + { name = "agent-framework-orchestrations" }, { name = "agent-framework-purview" }, { name = "agent-framework-redis" }, + { name = "mcp" }, ] [[package]] @@ -255,6 +284,63 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ba/22/122ed515935926137cc3c6ca795ef01b30feb82160cfc0f29a34f9d603de/agent_framework_durabletask-1.0.0b260130-py3-none-any.whl", hash = "sha256:a46e292800d10a62ce0923efe753594ddbf0bd6d1bb6e1258380f0dbf7d0302f", size = 36357, upload-time = "2026-01-30T19:01:24.057Z" }, ] +[[package]] +name = "agent-framework-foundry" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "agent-framework-openai" }, + { name = "azure-ai-inference" }, + { name = "azure-ai-projects" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/f6/8700acd779cbffd933dcb5dc878abce3e0a2f536962567665ccc49965715/agent_framework_foundry-1.3.0.tar.gz", hash = "sha256:8a4b137efa0a7000e60fb396ad90e01c271d14a52f1325f1f0a32177d944bcff", size = 32620, upload-time = "2026-05-08T00:09:04.274Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a5/53/9acf5831263d4fcd1d5b8d39af99ee430ec2710d2f9adeab5a1fe7559da0/agent_framework_foundry-1.3.0-py3-none-any.whl", hash = "sha256:49987bc01b077f6c60af33c475f9770a02b4ff6d6822aede18fc5471b46ffd41", size = 37052, upload-time = "2026-05-08T00:09:13.139Z" }, +] + +[[package]] +name = "agent-framework-foundry-local" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "agent-framework-openai" }, + { name = "foundry-local-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/03/8f0b8a2209fd091903bbb068c4458f19c74e48d37f4fa08748d76c3f3091/agent_framework_foundry_local-1.0.0b260507.tar.gz", hash = "sha256:fc2d98ff1f98d0481544c3ad8453f2d56096203fd368d0b68f52ef6ae4c7b0a6", size = 6719, upload-time = "2026-05-08T00:09:35.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/07/1120c862714d89f40d4575a052a495f86bda0fdb4132d5c4597c7a735875/agent_framework_foundry_local-1.0.0b260507-py3-none-any.whl", hash = "sha256:515346ca7716d86c9a4110db9f5586a65c4970ac442aaa00725d27341c5825df", size = 7176, upload-time = "2026-05-08T00:09:28.74Z" }, +] + +[[package]] +name = "agent-framework-github-copilot" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "github-copilot-sdk" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/0f/0cab3d20c84ff309f820d02e810c1fa17f1a6fc432775605e34f651955ae/agent_framework_github_copilot-1.0.0b260507.tar.gz", hash = "sha256:f8640d4a18beca67a83b833b5d23f873aa5e1d4e91423ee1923d650b7b97d06d", size = 12546, upload-time = "2026-05-08T00:08:59.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/75/c8747c30acf236daa97063763fd16e443a2734e80c5678c42e103d1b50d6/agent_framework_github_copilot-1.0.0b260507-py3-none-any.whl", hash = "sha256:53a5daae86824fce017f30637edd5e50675e4630da5be09bb259383713198f40", size = 12510, upload-time = "2026-05-08T00:09:42.889Z" }, +] + +[[package]] +name = "agent-framework-hyperlight" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox", marker = "python_full_version < '3.14'" }, + { name = "hyperlight-sandbox-backend-wasm", marker = "(python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux') or (python_full_version < '3.14' and platform_machine == 'AMD64' and sys_platform == 'win32')" }, + { name = "hyperlight-sandbox-python-guest", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/1f/52a2541d4a0bc5657ca9c2ef4f85885fb323682052da3fc1451eabafb73d/agent_framework_hyperlight-1.0.0b260507.tar.gz", hash = "sha256:845baab7439ac7b94ee53805cf3d32d0eea3b77a040d0f1b367f0a395fd8c08b", size = 19057, upload-time = "2026-05-08T00:09:56.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/d8/c2e0d3f63ea53f9897bd6c31a3d07c41c48a7b30fd7a1c2b5182fffe32ca/agent_framework_hyperlight-1.0.0b260507-py3-none-any.whl", hash = "sha256:121b464edf32f3db0e5b2891525d8937f0854bc19102a7c50b1905ff29063da7", size = 19589, upload-time = "2026-05-08T00:09:52.71Z" }, +] + [[package]] name = "agent-framework-lab" version = "1.0.0b251024" @@ -293,6 +379,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b8/27/23e23a1919592dcf2aaf25aa9950a7dbda77c4ba03cba8843491b9f12024/agent_framework_ollama-1.0.0b260130-py3-none-any.whl", hash = "sha256:55e4e17f226ad61e8a9dcbbcc24ab006a3480043ecb4d32c12d2444f628054d6", size = 9167, upload-time = "2026-01-30T19:01:05.647Z" }, ] +[[package]] +name = "agent-framework-openai" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, + { name = "openai" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/54/26595b5fa394dd91a5bd434f87b1e7d781545efbf0bd8053de193f89ec63/agent_framework_openai-1.3.0.tar.gz", hash = "sha256:770828447875ee169dde8cd2f2a0343f427d856af7c83895ca12d59f8c24a7f2", size = 49146, upload-time = "2026-05-08T00:09:44.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/d8/a0e0af08123d3c2ff3f42b6976eed155536c73be4d61b898bc15cf31a38c/agent_framework_openai-1.3.0-py3-none-any.whl", hash = "sha256:1953dcb9f3e852362be84b4316ee69639313a7f119eab6ce8c88949e1f24aa4b", size = 54041, upload-time = "2026-05-08T00:09:17.744Z" }, +] + +[[package]] +name = "agent-framework-orchestrations" +version = "1.0.0b260507" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "agent-framework-core" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ca/84/1a26978d91c40f62ef472fd36d1502545bb7425b94b03765c41b322e3398/agent_framework_orchestrations-1.0.0b260507.tar.gz", hash = "sha256:3f17281a2603240e3eed26174cab6b3dca153cb18cec8380f4719e598a55013f", size = 55971, upload-time = "2026-05-08T00:09:37.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/dd/f2df27ba789130470311e7487d19815483f837094672408a22655b33784a/agent_framework_orchestrations-1.0.0b260507-py3-none-any.whl", hash = "sha256:396a5ed962c2a3b1f09d8fc777933397df486bdae0a5f81cf63595c4c6f102de", size = 62074, upload-time = "2026-05-08T00:09:31.24Z" }, +] + [[package]] name = "agent-framework-purview" version = "1.0.0b260130" @@ -567,16 +678,16 @@ wheels = [ [[package]] name = "azure-ai-agents" -version = "1.2.0b5" +version = "1.2.0b6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, { name = "isodate" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ed/57/8adeed578fa8984856c67b4229e93a58e3f6024417d448d0037aafa4ee9b/azure_ai_agents-1.2.0b5.tar.gz", hash = "sha256:1a16ef3f305898aac552269f01536c34a00473dedee0bca731a21fdb739ff9d5", size = 394876, upload-time = "2025-09-30T01:55:02.328Z" } +sdist = { url = "https://files.pythonhosted.org/packages/68/32/f4e534dc05dfb714705df56a190d690c5452cd4dd7e936612cb1adddc44f/azure_ai_agents-1.2.0b6.tar.gz", hash = "sha256:d3c10848c3b19dec98a292f8c10cee4ba4aac1050d4faabf9c2e2456b727f528", size = 396865, upload-time = "2025-10-24T18:04:47.877Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/6d/15070d23d7a94833a210da09d5d7ed3c24838bb84f0463895e5d159f1695/azure_ai_agents-1.2.0b5-py3-none-any.whl", hash = "sha256:257d0d24a6bf13eed4819cfa5c12fb222e5908deafb3cbfd5711d3a511cc4e88", size = 217948, upload-time = "2025-09-30T01:55:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/96/d0/930c522f5fa9da163de057e57f8b44539424e13f46618c52624ebc712293/azure_ai_agents-1.2.0b6-py3-none-any.whl", hash = "sha256:ce23ad8fb9791118905be1ec8eae5c907cca2e536a455f1d3b830062c72cf2a7", size = 217950, upload-time = "2025-10-24T18:04:49.72Z" }, ] [[package]] @@ -595,7 +706,7 @@ wheels = [ [[package]] name = "azure-ai-projects" -version = "2.0.0b3" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "azure-core" }, @@ -603,10 +714,11 @@ dependencies = [ { name = "azure-storage-blob" }, { name = "isodate" }, { name = "openai" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/24/e0/3512d3f07e9dd2eb4af684387c31598c435bd87833b6a81850972963cb9c/azure_ai_projects-2.0.0b3.tar.gz", hash = "sha256:6d09ad110086e450a47b991ee8a3644f1be97fa3085d5981d543f900d78f4505", size = 431749, upload-time = "2026-01-06T05:31:25.849Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/76/3fdede8eddfe5927a571898a15f0288ba30fee78e5ba099f88df3ded70af/azure_ai_projects-2.1.0.tar.gz", hash = "sha256:f0749fa9a174255aa1a5550fb6078208521518472907a4c6dd552767d9b39caa", size = 543343, upload-time = "2026-04-20T17:06:48.751Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/b6/8fbd4786bb5c0dd19eaff86ddce0fbfb53a6f90d712038272161067a076a/azure_ai_projects-2.0.0b3-py3-none-any.whl", hash = "sha256:3b3048a3ba3904d556ba392b7bd20b6e84c93bb39df6d43a6470cdb0ad08af8c", size = 240717, upload-time = "2026-01-06T05:31:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f6/4984e7772a97c7a9e6505a3de8e55a5070fa2b02cd7e980da91e0d9b9b97/azure_ai_projects-2.1.0-py3-none-any.whl", hash = "sha256:6f259d8eb9167d2dfd372006d0221a8118faeaeb05829fa898b595bc6f19c699", size = 274309, upload-time = "2026-04-20T17:06:50.542Z" }, ] [[package]] @@ -834,6 +946,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/0d/52d98722666d6fc6c3dd4c76df339501d6efd40e0ff95e6186a7b7f0befd/black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b", size = 207542, upload-time = "2026-03-12T03:36:01.668Z" }, ] +[[package]] +name = "boto3" +version = "1.43.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/27/ae1a71e945ce7bde39b0677b252fe7d8a0ad7fa3d6b724d78b81469c08fe/boto3-1.43.10.tar.gz", hash = "sha256:27342e5d5f6170fcc8d1e21cdd939af2448d58ac56b08d494250eaad998e30c7", size = 113159, upload-time = "2026-05-18T20:42:34.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/1b/439234598449f846b17333e67ec63c3dd8f8880c13de9089383b4bab58c3/boto3-1.43.10-py3-none-any.whl", hash = "sha256:83918184d95967e4c6e9ed1e9a2f58250b291e6ea2cb847ab0825d52596b39e5", size = 140534, upload-time = "2026-05-18T20:42:32.009Z" }, +] + +[[package]] +name = "botocore" +version = "1.43.10" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/4e/c127dd0628c551f10cb890e279a9c0e367523b880c4cd3e81a1e76886174/botocore-1.43.10.tar.gz", hash = "sha256:2f4af585b41dbccdfc9f49677d7bd72d713a12ef89a1dc9c8538a927649498bf", size = 15365344, upload-time = "2026-05-18T20:42:21.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a6/0e/41f64d6c267edf03f4fe8f461edc4c644243e77c8d5a1fef1e0166ac4ed0/botocore-1.43.10-py3-none-any.whl", hash = "sha256:8a0176d8c2f8bebe95d4f923a824a1ace04b02f360e220681c388e097f32c3b6", size = 15043571, upload-time = "2026-05-18T20:42:16.664Z" }, +] + [[package]] name = "cachetools" version = "7.1.1" @@ -1012,6 +1152,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] +[[package]] +name = "claude-agent-sdk" +version = "0.1.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "mcp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/dd/2818538efd18ed4ef72d4775efa75bb36cbea0fa418eda51df85ee9c2424/claude_agent_sdk-0.1.48.tar.gz", hash = "sha256:ee294d3f02936c0b826119ffbefcf88c67731cf8c2d2cb7111ccc97f76344272", size = 87375, upload-time = "2026-03-07T00:21:37.087Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c3/cf/bbbdee52ee0c63c8709b0ac03ce3c1da5bdc37def5da0eca63363448744f/claude_agent_sdk-0.1.48-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5761ff1d362e0f17c2b1bfd890d1c897f0aa81091e37bbd15b7d06f05ced552d", size = 57559306, upload-time = "2026-03-07T00:21:20.011Z" }, + { url = "https://files.pythonhosted.org/packages/57/d1/2179154b88d4cf6ba1cf6a15066ee8e96257aaeb1330e625e809ba2f28eb/claude_agent_sdk-0.1.48-py3-none-manylinux_2_17_aarch64.whl", hash = "sha256:39c1307daa17e42fa8a71180bb20af8a789d72d3891fc93519ff15540badcb83", size = 73980309, upload-time = "2026-03-07T00:21:24.592Z" }, + { url = "https://files.pythonhosted.org/packages/dc/99/55b0cd3bf54a7449e744d23cf50be104e9445cf623e1ed75722112aa6264/claude_agent_sdk-0.1.48-py3-none-manylinux_2_17_x86_64.whl", hash = "sha256:543d70acba468eccfff836965a14b8ac88cf90809aeeb88431dfcea3ee9a2fa9", size = 74583686, upload-time = "2026-03-07T00:21:28.969Z" }, + { url = "https://files.pythonhosted.org/packages/c8/f6/4851bd9a238b7aadba7639eb906aca7da32a51f01563fa4488469c608b3a/claude_agent_sdk-0.1.48-py3-none-win_amd64.whl", hash = "sha256:0d37e60bd2b17efc3f927dccef080f14897ab62cd1d0d67a4abc8a0e2d4f1006", size = 74956045, upload-time = "2026-03-07T00:21:33.475Z" }, +] + [[package]] name = "click" version = "8.3.3" @@ -1280,6 +1436,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, ] +[[package]] +name = "foundry-local-sdk" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "pydantic" }, + { name = "tqdm" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/6b/76a7fe8f9f4c52cc84eaa1cd1b66acddf993496d55d6ea587bf0d0854d1c/foundry_local_sdk-0.5.1-py3-none-any.whl", hash = "sha256:f3639a3666bc3a94410004a91671338910ac2e1b8094b1587cc4db0f4a7df07e", size = 14003, upload-time = "2025-11-21T05:39:58.099Z" }, +] + [[package]] name = "frozenlist" version = "1.8.0" @@ -1382,6 +1551,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/61/8c/dce3b1b7593858eba995b2dfdb833f872c7f863e3da92aab7128a6b11af4/furl-2.1.4-py2.py3-none-any.whl", hash = "sha256:da34d0b34e53ffe2d2e6851a7085a05d96922b5b578620a37377ff1dbeeb11c8", size = 27550, upload-time = "2025-03-09T05:36:19.928Z" }, ] +[[package]] +name = "github-copilot-sdk" +version = "1.0.0b2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dateutil" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/fe/2cb98d4b9f57f8062ea72775bde72aed1958305016753f7296398e0ceb45/github_copilot_sdk-1.0.0b2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:1b5941d8b6e3d94d42a5bec6607a26f562e6535d5c981089d23d3d224b94601c", size = 67061619, upload-time = "2026-05-06T20:02:08.636Z" }, + { url = "https://files.pythonhosted.org/packages/57/45/76567821b2d36f81e6bca78c98d265e2762733f765fa51d69602b7f81867/github_copilot_sdk-1.0.0b2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b8f6a087a0cf02bb0d33976e8f8c009578d84d701a0b28d52051304791ac70", size = 63790955, upload-time = "2026-05-06T20:02:12.354Z" }, + { url = "https://files.pythonhosted.org/packages/15/67/684b0da0b1207a2bdf025c22ee075d34a1736d61a4973651035d4fd4d8dc/github_copilot_sdk-1.0.0b2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f403638c11b82bddb81c94675fc4e8014a1bb2e86a679a39fa167dcc3ad5416a", size = 69538664, upload-time = "2026-05-06T20:02:16.363Z" }, + { url = "https://files.pythonhosted.org/packages/57/1d/80d88ecf83683535d1a16d4817f1683db3b125f52a924ebdfe9764f5e4c3/github_copilot_sdk-1.0.0b2-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:433d16bb31171fee8d3a5b70259c527f63b297e83a8f8761ae1f16f14d641f32", size = 68163648, upload-time = "2026-05-06T20:02:21.139Z" }, + { url = "https://files.pythonhosted.org/packages/32/d3/b72aa2fbb3194b50b53e8cb1484f5606a1f8eedcdb0bfb5747da52079553/github_copilot_sdk-1.0.0b2-py3-none-win_amd64.whl", hash = "sha256:a6e9782dae4c3c2ab3527b45bb5de0f61998104c10e9ff64698280eaf37ab5dd", size = 62649144, upload-time = "2026-05-06T20:02:24.953Z" }, + { url = "https://files.pythonhosted.org/packages/b6/e2/be95b8ea0ac11d1ca474e28a59284f4e395c2710734eadfb657f5de8ace2/github_copilot_sdk-1.0.0b2-py3-none-win_arm64.whl", hash = "sha256:2e97d0ce4bad67dc5929091cb429e7bbae7d4643e4908a6af256a41439000740", size = 60374365, upload-time = "2026-05-06T20:02:29.02Z" }, +] + [[package]] name = "google-api-core" version = "2.30.3" @@ -1587,6 +1773,35 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/30/47d0bf6072f7252e6521f3447ccfa40b421b6824517f82854703d0f5a98b/hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5", size = 13007, upload-time = "2025-01-22T21:41:47.295Z" }, ] +[[package]] +name = "hyperlight-sandbox" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/5e/14c69eac7e1c74fbd556c6f890729a3d232d32d65cd9f8cfde72c0534e61/hyperlight_sandbox-0.4.0.tar.gz", hash = "sha256:90d7b91d4d8e17054e282b0daed55c261392a748dafc57e6416d3184cdac910b", size = 9262, upload-time = "2026-05-02T00:00:02.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/e3/b8c106a274c08a30261105afa5511e0ec55960e86b2f6c51e3095e96647c/hyperlight_sandbox-0.4.0-py3-none-any.whl", hash = "sha256:7ae44d2448ed6ecadb368373c7e45eb395521e7774c86a1cbc1ef9cdfc25cd2a", size = 5723, upload-time = "2026-05-02T00:00:03.811Z" }, +] + +[[package]] +name = "hyperlight-sandbox-backend-wasm" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/e5/3cdf21594eb28de7ca1a5a1ade27e137c8f3d7ab48d65fed87a3b74c4039/hyperlight_sandbox_backend_wasm-0.4.0-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:ff4627950708909202ee24c6175dc41e9c05479f89393575e3de0f14e6f5a193", size = 3918189, upload-time = "2026-05-01T23:59:16.666Z" }, + { url = "https://files.pythonhosted.org/packages/5b/97/b1bb9893bbeb979d133dc542520125dcbf8394d1a2537e753118b37c7cab/hyperlight_sandbox_backend_wasm-0.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:cce7dc28b9ded034a11a9a8cf7b9ffb838e29006be8d2e01646dd131ba501b73", size = 3383520, upload-time = "2026-05-01T23:59:27.261Z" }, + { url = "https://files.pythonhosted.org/packages/8c/29/deee4e31086628750f0ce1f67da1e28c613fd2df68465de130cbfe51e72d/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:88e194515e4784f68676b6906c98a4000f913c93172cf07981d8a977e756bbd6", size = 3917939, upload-time = "2026-05-01T23:59:14.805Z" }, + { url = "https://files.pythonhosted.org/packages/15/2a/6822aec3c04c46893406d0d6ed576dbdb4b5c1d76a0124dc220bb45b0d34/hyperlight_sandbox_backend_wasm-0.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d1cd2269a5651ea9be1f94a3e3388f6af69e41dbc2b808c3b806481fe17ce163", size = 3383110, upload-time = "2026-05-01T23:59:23.736Z" }, +] + +[[package]] +name = "hyperlight-sandbox-python-guest" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/fd/816d1f3f277ff149a45da5381967aa04c22bc7702b5c14f0acfd9db2cee7/hyperlight_sandbox_python_guest-0.4.0.tar.gz", hash = "sha256:64c3c6c13fe550bf5b680fa0b965cf62bc4668084cc275c3467e3c015e6ead36", size = 21657381, upload-time = "2026-05-01T23:59:46.589Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/ba/efb9aacf993f0ac142da5beb9177b221e49dc860c6ea398de236015a52a0/hyperlight_sandbox_python_guest-0.4.0-py3-none-any.whl", hash = "sha256:0789eb794b99606288402ed3921b5e2630800a69d24117ecd9b82e816568202d", size = 21822062, upload-time = "2026-05-01T23:59:50.99Z" }, +] + [[package]] name = "id" version = "1.6.1" @@ -1773,6 +1988,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/e9/1f9ada30cef7b05e74bb06f52127e7a724976c225f46adb65c37b1dadfb6/jiter-0.14.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67f00d94b281174144d6532a04b66a12cb866cbdc47c3af3bfe2973677f9861a", size = 349613, upload-time = "2026-04-10T14:28:40.066Z" }, ] +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + [[package]] name = "joserfc" version = "1.6.4" @@ -1979,11 +2203,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e2/fc/6dc7659c2ae5ddf280477011f4213a74f806862856b796ef08f028e664bf/mcp-1.25.0-py3-none-any.whl", hash = "sha256:b37c38144a666add0862614cc79ec276e97d72aa8ca26d622818d4e278b9721a", size = 233076, upload-time = "2025-12-19T10:19:55.416Z" }, ] -[package.optional-dependencies] -ws = [ - { name = "websockets" }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -2454,19 +2673,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/a6/83dc2ab6fa397ee66fba04fe2e74bdf7be3b3870005359ceb7689103c058/opentelemetry_semantic_conventions-0.62b1-py3-none-any.whl", hash = "sha256:cf506938103d331fbb78eded0d9788095f7fd59016f2bda813c3324e5a74a93c", size = 231620, upload-time = "2026-04-24T13:15:35.454Z" }, ] -[[package]] -name = "opentelemetry-semantic-conventions-ai" -version = "0.5.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "opentelemetry-sdk" }, - { name = "opentelemetry-semantic-conventions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/24/02/10aeacc37a38a3a8fa16ff67bec1ae3bf882539f6f9efb0f70acf802ca2d/opentelemetry_semantic_conventions_ai-0.5.1.tar.gz", hash = "sha256:153906200d8c1d2f8e09bd78dbef526916023de85ac3dab35912bfafb69ff04c", size = 26533, upload-time = "2026-03-26T14:20:38.73Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/55/22/41fb05f1dc5fda2c468e05a41814c20859016c85117b66c8a257cae814f6/opentelemetry_semantic_conventions_ai-0.5.1-py3-none-any.whl", hash = "sha256:25aeb22bd261543b4898a73824026d96770e5351209c7d07a0b1314762b1f6e4", size = 11250, upload-time = "2026-03-26T14:20:37.108Z" }, -] - [[package]] name = "orderedmultidict" version = "1.0.2" @@ -2627,12 +2833,12 @@ dev = [ [package.metadata] requires-dist = [ - { name = "agent-framework", specifier = "==1.0.0b260107" }, + { name = "agent-framework", specifier = "==1.3.0" }, { name = "aiohttp", specifier = "==3.13.4" }, { name = "art", specifier = "==6.5" }, - { name = "azure-ai-agents", specifier = "==1.2.0b5" }, + { name = "azure-ai-agents", specifier = "==1.2.0b6" }, { name = "azure-ai-inference", specifier = "==1.0.0b9" }, - { name = "azure-ai-projects", specifier = "==2.0.0b3" }, + { name = "azure-ai-projects", specifier = "==2.1.0" }, { name = "azure-appconfiguration", specifier = "==1.7.2" }, { name = "azure-core", specifier = "==1.38.0" }, { name = "azure-cosmos", specifier = "==4.15.0" }, @@ -3437,6 +3643,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, ] +[[package]] +name = "s3transfer" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/ec/7c692cde9125b77e84b307354d4fb705f98b8ccad59a036d5957ca75bfc3/s3transfer-0.17.0.tar.gz", hash = "sha256:9edeb6d1c3c2f89d6050348548834ad8289610d886e5bf7b7207728bd43ce33a", size = 155337, upload-time = "2026-04-29T22:07:36.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/72/c6c32d2b657fa3dad1de340254e14390b1e334ce38268b7ad51abda3c8c2/s3transfer-0.17.0-py3-none-any.whl", hash = "sha256:ce3801712acf4ad3e89fb9990df97b4972e93f4b3b0004d214be5bce12814c20", size = 86811, upload-time = "2026-04-29T22:07:34.966Z" }, +] + [[package]] name = "sas-cosmosdb" version = "0.1.4" From 4e0a59aef22b8340fdae5c48572e802abd64222a Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 12:32:10 +0530 Subject: [PATCH 2/8] refactor: remove ImportError handling for agent_framework imports --- .../src/libs/agent_framework/agent_builder.py | 29 +++++---------- .../src/libs/agent_framework/agent_info.py | 5 +-- .../agent_framework/agent_speaking_capture.py | 5 +-- .../azure_openai_response_retry.py | 15 ++++---- .../agent_framework/groupchat_orchestrator.py | 35 ++++++------------- .../src/libs/agent_framework/middlewares.py | 32 ++++++----------- .../shared_memory_context_provider.py | 18 ++++------ .../src/libs/base/orchestrator_base.py | 10 +----- .../orchestration/analysis_orchestrator.py | 5 +-- .../yaml_convert_orchestrator.py | 9 +---- .../orchestration/design_orchestrator.py | 9 +---- .../documentation_orchestrator.py | 9 +---- 12 files changed, 51 insertions(+), 130 deletions(-) diff --git a/src/processor/src/libs/agent_framework/agent_builder.py b/src/processor/src/libs/agent_framework/agent_builder.py index 6cae1a67..6a7e4409 100644 --- a/src/processor/src/libs/agent_framework/agent_builder.py +++ b/src/processor/src/libs/agent_framework/agent_builder.py @@ -6,26 +6,15 @@ from collections.abc import Callable, MutableMapping, Sequence from typing import Any, Literal -try: - from agent_framework import ( - Agent, - AgentMiddleware, - BaseChatClient, - ChatMiddleware, - ContextProvider, - FunctionTool, - ToolMode, - ) -except ImportError: - from agent_framework import ( - AgentMiddleware, - BaseChatClient, - ChatAgent as Agent, - ChatMiddleware, - ContextProvider, - ToolMode, - ToolProtocol as FunctionTool, - ) +from agent_framework import ( + Agent, + AgentMiddleware, + BaseChatClient, + ChatMiddleware, + ContextProvider, + FunctionTool, + ToolMode, +) from pydantic import BaseModel from libs.agent_framework.agent_info import AgentInfo diff --git a/src/processor/src/libs/agent_framework/agent_info.py b/src/processor/src/libs/agent_framework/agent_info.py index 6e3dfac0..82f657b6 100644 --- a/src/processor/src/libs/agent_framework/agent_info.py +++ b/src/processor/src/libs/agent_framework/agent_info.py @@ -5,10 +5,7 @@ from typing import Any, Callable, MutableMapping, Sequence -try: - from agent_framework import FunctionTool -except ImportError: - from agent_framework import ToolProtocol as FunctionTool +from agent_framework import FunctionTool from jinja2 import Template from openai import BaseModel from pydantic import Field diff --git a/src/processor/src/libs/agent_framework/agent_speaking_capture.py b/src/processor/src/libs/agent_framework/agent_speaking_capture.py index 5dac0cba..63608969 100644 --- a/src/processor/src/libs/agent_framework/agent_speaking_capture.py +++ b/src/processor/src/libs/agent_framework/agent_speaking_capture.py @@ -6,10 +6,7 @@ from datetime import datetime from typing import Any, Callable, Optional -try: - from agent_framework import AgentContext, AgentMiddleware -except ImportError: - from agent_framework import AgentMiddleware, AgentRunContext as AgentContext +from agent_framework import AgentContext, AgentMiddleware class AgentSpeakingCaptureMiddleware(AgentMiddleware): diff --git a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py index b51e324a..91cdf4c9 100644 --- a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py +++ b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py @@ -12,14 +12,13 @@ from dataclasses import dataclass from typing import Any, AsyncIterable, MutableSequence -try: - from agent_framework.azure import AzureOpenAIResponsesClient -except ImportError: - class AzureOpenAIResponsesClient: - def __init__(self, *args: Any, **kwargs: Any): - raise NotImplementedError( - "AzureOpenAIResponsesClient was removed from agent_framework.azure in 1.3.0." - ) +# agent_framework 1.3.0 removed AzureOpenAIResponsesClient from agent_framework.azure. +# Keep this stub so legacy code paths fail explicitly if invoked. +class AzureOpenAIResponsesClient: + def __init__(self, *args: Any, **kwargs: Any): + raise NotImplementedError( + "AzureOpenAIResponsesClient was removed from agent_framework.azure in 1.3.0." + ) from tenacity import ( AsyncRetrying, retry_if_exception, diff --git a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py index ab9975c1..02be619e 100644 --- a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py +++ b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py @@ -21,30 +21,17 @@ from datetime import datetime from typing import Any, Awaitable, Callable, Generic, Mapping, Sequence, TypeVar -try: - from agent_framework import ( - Agent, - AgentResponseUpdate, - Executor, - Message, - Role, - SupportsAgentRun, - Workflow, - WorkflowBuilder as GroupChatBuilder, - WorkflowEvent as WorkflowOutputEvent, - ) -except ImportError: - from agent_framework import ( - AgentProtocol as SupportsAgentRun, - AgentRunUpdateEvent as AgentResponseUpdate, - ChatAgent as Agent, - ChatMessage as Message, - Executor, - GroupChatBuilder, - Role, - Workflow, - WorkflowOutputEvent, - ) +from agent_framework import ( + Agent, + AgentResponseUpdate, + Executor, + Message, + Role, + SupportsAgentRun, + Workflow, + WorkflowBuilder as GroupChatBuilder, + WorkflowEvent as WorkflowOutputEvent, +) from mem0 import AsyncMemory from pydantic import BaseModel, ValidationError diff --git a/src/processor/src/libs/agent_framework/middlewares.py b/src/processor/src/libs/agent_framework/middlewares.py index 0319a6a0..1f26d547 100644 --- a/src/processor/src/libs/agent_framework/middlewares.py +++ b/src/processor/src/libs/agent_framework/middlewares.py @@ -6,28 +6,16 @@ import time from collections.abc import Awaitable, Callable -try: - from agent_framework import ( - AgentContext, - AgentMiddleware, - ChatContext, - ChatMiddleware, - FunctionInvocationContext, - FunctionMiddleware, - Message, - Role, - ) -except ImportError: - from agent_framework import ( - AgentMiddleware, - AgentRunContext as AgentContext, - ChatContext, - ChatMessage as Message, - ChatMiddleware, - FunctionInvocationContext, - FunctionMiddleware, - Role, - ) +from agent_framework import ( + AgentContext, + AgentMiddleware, + ChatContext, + ChatMiddleware, + FunctionInvocationContext, + FunctionMiddleware, + Message, + Role, +) ROLE_USER = getattr(Role, "USER", "user") diff --git a/src/processor/src/libs/agent_framework/shared_memory_context_provider.py b/src/processor/src/libs/agent_framework/shared_memory_context_provider.py index 16b64e00..fd95a5de 100644 --- a/src/processor/src/libs/agent_framework/shared_memory_context_provider.py +++ b/src/processor/src/libs/agent_framework/shared_memory_context_provider.py @@ -18,17 +18,13 @@ from collections.abc import MutableSequence, Sequence from typing import TYPE_CHECKING -try: - from agent_framework import Context, ContextProvider, Message -except ImportError: - try: - from agent_framework import ContextProvider, Message - except ImportError: - from agent_framework import ChatMessage as Message, ContextProvider - - class Context: - def __init__(self, instructions: str | None = None, **kwargs): - self.instructions = instructions +from agent_framework import ContextProvider, Message + + +class Context: + def __init__(self, instructions: str | None = None, **kwargs): + self.instructions = instructions + if TYPE_CHECKING: from libs.agent_framework.qdrant_memory_store import QdrantMemoryStore diff --git a/src/processor/src/libs/base/orchestrator_base.py b/src/processor/src/libs/base/orchestrator_base.py index 2d6616e9..90b564a8 100644 --- a/src/processor/src/libs/base/orchestrator_base.py +++ b/src/processor/src/libs/base/orchestrator_base.py @@ -9,15 +9,7 @@ from abc import abstractmethod from typing import Any, Callable, Generic, MutableMapping, Sequence, TypeVar -try: - from agent_framework import Agent, FunctionTool, ToolResultCompactionStrategy -except ImportError: - from agent_framework import ChatAgent as Agent, ToolProtocol as FunctionTool - - try: - from agent_framework import ToolResultCompactionStrategy - except ImportError: - ToolResultCompactionStrategy = None # type: ignore[assignment,misc] +from agent_framework import Agent, FunctionTool, ToolResultCompactionStrategy from libs.agent_framework.agent_builder import AgentBuilder from libs.agent_framework.agent_framework_helper import ClientType diff --git a/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py b/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py index 461005a6..1ce73a68 100644 --- a/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py +++ b/src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py @@ -12,10 +12,7 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -try: - from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool -except ImportError: - from agent_framework import MCPStdioTool, MCPStreamableHTTPTool, ToolProtocol as FunctionTool +from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( diff --git a/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py b/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py index 580e56b8..e3d6937b 100644 --- a/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py +++ b/src/processor/src/steps/convert/orchestration/yaml_convert_orchestrator.py @@ -13,14 +13,7 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -try: - from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool -except ImportError: - from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol as FunctionTool, - ) +from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( diff --git a/src/processor/src/steps/design/orchestration/design_orchestrator.py b/src/processor/src/steps/design/orchestration/design_orchestrator.py index 2e49c850..b6d96efe 100644 --- a/src/processor/src/steps/design/orchestration/design_orchestrator.py +++ b/src/processor/src/steps/design/orchestration/design_orchestrator.py @@ -11,14 +11,7 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -try: - from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool -except ImportError: - from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol as FunctionTool, - ) +from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( diff --git a/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py b/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py index b623b9dd..a16e6b4d 100644 --- a/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py +++ b/src/processor/src/steps/documentation/orchestration/documentation_orchestrator.py @@ -15,14 +15,7 @@ from pathlib import Path from typing import Any, Callable, MutableMapping, Sequence -try: - from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool -except ImportError: - from agent_framework import ( - MCPStdioTool, - MCPStreamableHTTPTool, - ToolProtocol as FunctionTool, - ) +from agent_framework import FunctionTool, MCPStdioTool, MCPStreamableHTTPTool from libs.agent_framework.agent_info import AgentInfo from libs.agent_framework.groupchat_orchestrator import ( From b6001e8e479055f2b52ff45cce5d6b62e53a9013 Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 13:21:04 +0530 Subject: [PATCH 3/8] refactor: update Message stubs for consistency and clarity in tests --- .../azure_openai_response_retry.py | 15 +++++++++------ .../test_groupchat_orchestrator_internals.py | 2 +- .../test_input_observer_middleware.py | 6 +++++- .../agent_framework/test_middlewares_extras.py | 6 +++++- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py index 91cdf4c9..d254bf72 100644 --- a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py +++ b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py @@ -12,6 +12,14 @@ from dataclasses import dataclass from typing import Any, AsyncIterable, MutableSequence +from tenacity import ( + AsyncRetrying, + retry_if_exception, + stop_after_attempt, +) +from tenacity.wait import wait_base + + # agent_framework 1.3.0 removed AzureOpenAIResponsesClient from agent_framework.azure. # Keep this stub so legacy code paths fail explicitly if invoked. class AzureOpenAIResponsesClient: @@ -19,12 +27,7 @@ def __init__(self, *args: Any, **kwargs: Any): raise NotImplementedError( "AzureOpenAIResponsesClient was removed from agent_framework.azure in 1.3.0." ) -from tenacity import ( - AsyncRetrying, - retry_if_exception, - stop_after_attempt, -) -from tenacity.wait import wait_base + logger = logging.getLogger(__name__) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py b/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py index 263b157e..2e0068cf 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_groupchat_orchestrator_internals.py @@ -32,7 +32,7 @@ def __init__(self, *, role, text=None, contents=None, author_name=None): groupchat_module.Message = Message -from libs.agent_framework.groupchat_orchestrator import ( +from libs.agent_framework.groupchat_orchestrator import ( # noqa: E402 AgentResponse, AgentResponseStream, GroupChatOrchestrator, diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py b/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py index fca26fa8..2db61a41 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_input_observer_middleware.py @@ -10,6 +10,8 @@ class Message: + """Test stub for Message - the real Message in 1.3.0 uses contents= instead of text=.""" + def __init__(self, *, role, text=None, contents=None, author_name=None): self.role = role self.text = text @@ -17,8 +19,10 @@ def __init__(self, *, role, text=None, contents=None, author_name=None): self.author_name = author_name +# Patch at module level: middleware code references Message at runtime for isinstance +# checks and construction. This is scoped to test execution only. middlewares_module.Message = Message -from libs.agent_framework.middlewares import InputObserverMiddleware +from libs.agent_framework.middlewares import InputObserverMiddleware # noqa: E402 def test_input_observer_middleware_replaces_user_text_when_configured() -> None: diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py b/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py index 1b2e1e2f..9648f581 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_middlewares_extras.py @@ -14,6 +14,8 @@ class Message: + """Test stub for Message - the real Message in 1.3.0 uses contents= instead of text=.""" + def __init__(self, *, role, text=None, contents=None, author_name=None): self.role = role self.text = text @@ -21,8 +23,10 @@ def __init__(self, *, role, text=None, contents=None, author_name=None): self.author_name = author_name +# Patch at module level: middleware code references Message at runtime for isinstance +# checks and construction. This is scoped to test execution only. middlewares_module.Message = Message -from libs.agent_framework.middlewares import ( +from libs.agent_framework.middlewares import ( # noqa: E402 DebuggingMiddleware, LoggingFunctionMiddleware, ) From 64c78f49d5091860fa5baa0dd0d9553d452282ae Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 14:38:02 +0530 Subject: [PATCH 4/8] refactor: simplify client import handling in AgentFrameworkHelper --- .../agent_framework/agent_framework_helper.py | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/src/processor/src/libs/agent_framework/agent_framework_helper.py b/src/processor/src/libs/agent_framework/agent_framework_helper.py index e2609e04..4741b44c 100644 --- a/src/processor/src/libs/agent_framework/agent_framework_helper.py +++ b/src/processor/src/libs/agent_framework/agent_framework_helper.py @@ -366,12 +366,7 @@ def create_client( "OpenAIResponsesClient is not implemented in this context." ) elif client_type == ClientType.AzureOpenAIChatCompletion: - try: - from agent_framework.azure import AzureOpenAIChatClient - except ImportError as exc: - raise NotImplementedError( - "ClientType.AzureOpenAIChatCompletion is not supported in agent-framework 1.3.0; AzureOpenAIChatClient was removed." - ) from exc + from agent_framework.azure import AzureOpenAIChatClient return AzureOpenAIChatClient( api_key=api_key, @@ -390,12 +385,7 @@ def create_client( instruction_role=instruction_role, ) elif client_type == ClientType.AzureOpenAIAssistant: - try: - from agent_framework.azure import AzureOpenAIAssistantsClient - except ImportError as exc: - raise NotImplementedError( - "ClientType.AzureOpenAIAssistant is not supported in agent-framework 1.3.0; AzureOpenAIAssistantsClient was removed." - ) from exc + from agent_framework.azure import AzureOpenAIAssistantsClient return AzureOpenAIAssistantsClient( deployment_name=deployment_name, @@ -416,12 +406,7 @@ def create_client( env_file_encoding=env_file_encoding, ) elif client_type == ClientType.AzureOpenAIResponse: - try: - from agent_framework.azure import AzureOpenAIResponsesClient - except ImportError as exc: - raise NotImplementedError( - "ClientType.AzureOpenAIResponse is not supported in agent-framework 1.3.0; AzureOpenAIResponsesClient was removed." - ) from exc + from agent_framework.azure import AzureOpenAIResponsesClient return AzureOpenAIResponsesClient( api_key=api_key, From 7e514c5098537a363156cf8e6ec87d17d5eeb417 Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 14:46:52 +0530 Subject: [PATCH 5/8] refactor: update exception handling in client creation tests to use ImportError --- .../libs/agent_framework/test_agent_framework_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py b/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py index 767d5359..9392baaa 100644 --- a/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py +++ b/src/processor/src/tests/unit/libs/agent_framework/test_agent_framework_helper.py @@ -112,7 +112,7 @@ def test_default_token_provider_when_no_credential(self): def test_azure_openai_chat_completion(self): fake_module = types.ModuleType("agent_framework.azure") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - with pytest.raises(NotImplementedError, match="AzureOpenAIChatClient was removed"): + with pytest.raises(ImportError): AgentFrameworkHelper.create_client( ClientType.AzureOpenAIChatCompletion, endpoint="https://x", @@ -123,7 +123,7 @@ def test_azure_openai_chat_completion(self): def test_azure_openai_assistant(self): fake_module = types.ModuleType("agent_framework.azure") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - with pytest.raises(NotImplementedError, match="AzureOpenAIAssistantsClient was removed"): + with pytest.raises(ImportError): AgentFrameworkHelper.create_client( ClientType.AzureOpenAIAssistant, endpoint="https://x", @@ -134,7 +134,7 @@ def test_azure_openai_assistant(self): def test_azure_openai_response(self): fake_module = types.ModuleType("agent_framework.azure") with patch.dict(sys.modules, {"agent_framework.azure": fake_module}): - with pytest.raises(NotImplementedError, match="AzureOpenAIResponsesClient was removed"): + with pytest.raises(ImportError): AgentFrameworkHelper.create_client( ClientType.AzureOpenAIResponse, endpoint="https://x", From aab94b902cca832e1d017f834fc8442746a32aa6 Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 15:23:32 +0530 Subject: [PATCH 6/8] refactor: normalize executor_id handling in MigrationProcessor for improved telemetry and error reporting --- .../src/steps/migration_processor.py | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/processor/src/steps/migration_processor.py b/src/processor/src/steps/migration_processor.py index b1451ef3..adeb031f 100644 --- a/src/processor/src/steps/migration_processor.py +++ b/src/processor/src/steps/migration_processor.py @@ -370,14 +370,13 @@ async def _generate_report_summary( ) elif event.type == "output": # WorkflowEvent carries the step output (success or hard-termination). + # Normalize executor_id once to avoid None in telemetry/reporting. + executor_id = event.executor_id or "unknown" # Note: a None payload is an error that must be surfaced clearly. if event.data is None: - report_collector.set_current_step( - event.executor_id or "unknown" - ) + report_collector.set_current_step(executor_id) # Build a meaningful error message instead of generic "Workflow output is None" - executor_id = event.executor_id or "unknown" error_msg = f"Step '{executor_id}' completed without producing output. This may be caused by context length overflow, agent timeout, or an internal orchestration error. Check processor logs for '[AOAI_CTX_TRIM_STREAM]' or exception details." report_collector.record_failure( @@ -398,13 +397,13 @@ async def _generate_report_summary( await telemetry.record_failure_outcome( process_id=input_data.process_id, - failed_step=event.executor_id or "unknown", + failed_step=executor_id, error_message=error_msg, failure_details=failure_details, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.executor_id] - if event.executor_id in step_start_perf + - step_start_perf[executor_id] + if executor_id in step_start_perf else None ), ) @@ -414,7 +413,7 @@ async def _generate_report_summary( # Raise a rich exception so the queue worker reports a meaningful reason. raise WorkflowExecutorFailedException({ - "executor_id": event.executor_id or "unknown", + "executor_id": executor_id, "error_type": "WorkflowOutputMissing", "message": error_msg, "traceback": None, @@ -467,16 +466,14 @@ async def _generate_report_summary( "error": f"security evidence scan failed: {type(e).__name__}: {e}", } - report_collector.set_current_step( - event.executor_id or "unknown" - ) + report_collector.set_current_step(executor_id) report_collector.record_failure( exception=ValueError( getattr(event.data, "reason", None) - or f"Hard terminated in {event.executor_id} step" + or f"Hard terminated in {executor_id} step" ), custom_message=getattr(event.data, "reason", None) - or f"Hard terminated in {event.executor_id} step", + or f"Hard terminated in {executor_id} step", ) failure_details: Any = ( @@ -501,14 +498,14 @@ async def _generate_report_summary( await telemetry.record_failure_outcome( process_id=input_data.process_id, - failed_step=event.executor_id or "unknown", + failed_step=executor_id, error_message=getattr(event.data, "reason", None) - or f"Hard terminated in {event.executor_id} step", + or f"Hard terminated in {executor_id} step", failure_details=failure_details, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.executor_id] - if event.executor_id in step_start_perf + - step_start_perf[executor_id] + if executor_id in step_start_perf else None ), ) @@ -524,21 +521,21 @@ async def _generate_report_summary( logger.info("Workflow output (%s): %s", event.origin.value, event.data) await telemetry.record_step_result( process_id=input_data.process_id, - step_name=event.executor_id, + step_name=executor_id, step_result=event.data, execution_time_seconds=( time.perf_counter() - - step_start_perf[event.executor_id] - if event.executor_id in step_start_perf + - step_start_perf[executor_id] + if executor_id in step_start_perf else None ), ) - if event.executor_id in step_start_perf: + if executor_id in step_start_perf: report_collector.mark_step_completed( - event.executor_id, + executor_id, execution_time=time.perf_counter() - - step_start_perf[event.executor_id], + - step_start_perf[executor_id], ) try: From 913bffa9f89352fe6ac9a068a708a6a101946aa9 Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 15:53:02 +0530 Subject: [PATCH 7/8] refactor: streamline WorkflowEvent handling in GroupChatOrchestrator and OrchestratorBase --- .../src/libs/agent_framework/groupchat_orchestrator.py | 4 ++-- src/processor/src/libs/base/orchestrator_base.py | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py index 02be619e..78fb899e 100644 --- a/src/processor/src/libs/agent_framework/groupchat_orchestrator.py +++ b/src/processor/src/libs/agent_framework/groupchat_orchestrator.py @@ -30,7 +30,7 @@ SupportsAgentRun, Workflow, WorkflowBuilder as GroupChatBuilder, - WorkflowEvent as WorkflowOutputEvent, + WorkflowEvent, ) from mem0 import AsyncMemory from pydantic import BaseModel, ValidationError @@ -527,7 +527,7 @@ async def run_stream( # If the Coordinator requested finish=true, stop immediately. if self._termination_requested: break - elif isinstance(event, WorkflowOutputEvent): + elif isinstance(event, WorkflowEvent) and getattr(event, "type", None) == "output": # Complete last agent's response before finishing if self._last_executor_id and self._current_agent_response: await self._complete_agent_response( diff --git a/src/processor/src/libs/base/orchestrator_base.py b/src/processor/src/libs/base/orchestrator_base.py index 90b564a8..81e489a2 100644 --- a/src/processor/src/libs/base/orchestrator_base.py +++ b/src/processor/src/libs/base/orchestrator_base.py @@ -180,12 +180,11 @@ async def create_agents( .with_max_tokens(20_000) ) # Prevent context window overflow by summarizing older tool results. - if ToolResultCompactionStrategy is not None: - builder = builder.with_kwargs( - compaction_strategy=ToolResultCompactionStrategy( - keep_last_tool_call_groups=2 - ) + builder = builder.with_kwargs( + compaction_strategy=ToolResultCompactionStrategy( + keep_last_tool_call_groups=2 ) + ) if agent_info.agent_name == "Coordinator": # Routing-only: keep deterministic. Needs enough tokens for long instructions. From f64adcdbf9f02134ab93604bcd2ab61fe76b25eb Mon Sep 17 00:00:00 2001 From: Dhanushree-Microsoft Date: Thu, 21 May 2026 17:52:12 +0530 Subject: [PATCH 8/8] refactor: enhance AzureOpenAIResponseClientWithRetry for legacy parameter support and backward compatibility --- .../azure_openai_response_retry.py | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py index d254bf72..458d32a1 100644 --- a/src/processor/src/libs/agent_framework/azure_openai_response_retry.py +++ b/src/processor/src/libs/agent_framework/azure_openai_response_retry.py @@ -20,13 +20,8 @@ from tenacity.wait import wait_base -# agent_framework 1.3.0 removed AzureOpenAIResponsesClient from agent_framework.azure. -# Keep this stub so legacy code paths fail explicitly if invoked. -class AzureOpenAIResponsesClient: - def __init__(self, *args: Any, **kwargs: Any): - raise NotImplementedError( - "AzureOpenAIResponsesClient was removed from agent_framework.azure in 1.3.0." - ) +# agent_framework 1.3.0 removed AzureOpenAIResponsesClient; alias to its replacement. +from agent_framework.openai import OpenAIChatClient as AzureOpenAIResponsesClient logger = logging.getLogger(__name__) @@ -525,15 +520,31 @@ def __init__( self, *args: Any, retry_config: RateLimitRetryConfig | None = None, + # Legacy parameter names (mapped to OpenAIChatClient equivalents) + deployment_name: str | None = None, + endpoint: str | None = None, + ad_token: str | None = None, + ad_token_provider: object | None = None, + token_endpoint: str | None = None, **kwargs: Any, ): + # Map legacy params to OpenAIChatClient params + if deployment_name and "model" not in kwargs: + kwargs["model"] = deployment_name + if endpoint and "azure_endpoint" not in kwargs: + kwargs["azure_endpoint"] = endpoint + if ad_token_provider and "credential" not in kwargs: + kwargs["credential"] = ad_token_provider + super().__init__(*args, **kwargs) self._retry_config = retry_config or RateLimitRetryConfig.from_env() self._context_trim_config = ContextTrimConfig.from_env() async def _inner_get_response( - self, *, messages: MutableSequence[Any], chat_options: Any, **kwargs: Any + self, *, messages: MutableSequence[Any], chat_options: Any = None, options: Any = None, stream: bool = False, **kwargs: Any ) -> Any: + # Support both old (chat_options) and new (options) parameter names + effective_options = options if options is not None else chat_options parent_inner_get_response = super( AzureOpenAIResponseClientWithRetry, self )._inner_get_response @@ -559,7 +570,7 @@ async def _inner_get_response( try: return await _retry_call( lambda: parent_inner_get_response( - messages=effective_messages, chat_options=chat_options, **kwargs + messages=effective_messages, options=effective_options, stream=stream, **kwargs ), config=self._retry_config, ) @@ -607,16 +618,22 @@ async def _inner_get_response( await asyncio.sleep(trim_delay) return await _retry_call( lambda: parent_inner_get_response( - messages=trimmed, chat_options=chat_options, **kwargs + messages=trimmed, options=effective_options, stream=stream, **kwargs ), config=self._retry_config, ) async def _inner_get_streaming_response( - self, *, messages: MutableSequence[Any], chat_options: Any, **kwargs: Any + self, *, messages: MutableSequence[Any], chat_options: Any = None, options: Any = None, **kwargs: Any ) -> AsyncIterable[Any]: + """Streaming with retry. Delegates to parent._inner_get_response(stream=True). + + This method is kept for backward compatibility in case any internal code path + calls it directly. The new framework uses _inner_get_response(stream=True). + """ # Conservative retry: only retries failures before the first yielded update. attempts = self._retry_config.max_retries + 1 + effective_options = options if options is not None else chat_options effective_messages: MutableSequence[Any] | list[Any] = messages if self._context_trim_config.enabled: @@ -639,8 +656,8 @@ async def _inner_get_streaming_response( for attempt_index in range(attempts): stream = super( AzureOpenAIResponseClientWithRetry, self - )._inner_get_streaming_response( - messages=effective_messages, chat_options=chat_options, **kwargs + )._inner_get_response( + messages=effective_messages, options=effective_options, stream=True, **kwargs ) iterator = stream.__aiter__()