diff --git a/python/packages/ag-ui/tests/ag_ui/test_tooling.py b/python/packages/ag-ui/tests/ag_ui/test_tooling.py index f62c55c7df9..92462663a82 100644 --- a/python/packages/ag-ui/tests/ag_ui/test_tooling.py +++ b/python/packages/ag-ui/tests/ag_ui/test_tooling.py @@ -34,7 +34,7 @@ def regular_tool() -> str: def _create_chat_agent_with_tool(tool_name: str = "regular_tool") -> Agent: - """Create a Agent with a mocked chat client and a simple tool. + """Create an Agent with a mocked chat client and a simple tool. Note: tool_name parameter is kept for API compatibility but the tool will always be named 'regular_tool' since tool uses the function name. diff --git a/python/packages/core/agent_framework/_agents.py b/python/packages/core/agent_framework/_agents.py index 5215c14880a..2c7db9327c1 100644 --- a/python/packages/core/agent_framework/_agents.py +++ b/python/packages/core/agent_framework/_agents.py @@ -995,7 +995,7 @@ def __init__( tokenizer: TokenizerProtocol | None = None, additional_properties: MutableMapping[str, Any] | None = None, ) -> None: - """Initialize a Agent instance. + """Initialize an Agent instance. Args: client: The chat client to use for the agent. @@ -2104,7 +2104,7 @@ def __init__( tokenizer: TokenizerProtocol | None = None, additional_properties: MutableMapping[str, Any] | None = None, ) -> None: - """Initialize a Agent instance.""" + """Initialize an Agent instance.""" super().__init__( client=client, instructions=instructions, diff --git a/python/packages/core/agent_framework/_workflows/_typing_utils.py b/python/packages/core/agent_framework/_workflows/_typing_utils.py index 6496716f1ec..e4ee10224de 100644 --- a/python/packages/core/agent_framework/_workflows/_typing_utils.py +++ b/python/packages/core/agent_framework/_workflows/_typing_utils.py @@ -71,13 +71,13 @@ def _resolve_function_annotations( # pyright: ignore[reportUnusedFunction] def is_chat_agent(agent: Any) -> TypeGuard[Agent]: - """Check if the given agent is a Agent. + """Check if the given agent is an Agent. Args: agent (Any): The agent to check. Returns: - TypeGuard[Agent]: True if the agent is a Agent, False otherwise. + TypeGuard[Agent]: True if the agent is an Agent, False otherwise. """ return isinstance(agent, Agent) diff --git a/python/packages/declarative/agent_framework_declarative/_loader.py b/python/packages/declarative/agent_framework_declarative/_loader.py index 5e7e0b8cf27..85ea77c474c 100644 --- a/python/packages/declarative/agent_framework_declarative/_loader.py +++ b/python/packages/declarative/agent_framework_declarative/_loader.py @@ -290,7 +290,7 @@ def __init__( load_dotenv(dotenv_path=env_file_path, encoding=env_file_encoding) def create_agent_from_yaml_path(self, yaml_path: str | Path) -> Agent: - """Create a Agent from a YAML file path. + """Create an Agent from a YAML file path. This method does the following things: @@ -298,7 +298,7 @@ def create_agent_from_yaml_path(self, yaml_path: str | Path) -> Agent: 2. Validates that the loaded object is a PromptAgent. 3. Creates the appropriate ChatClient based on the model provider and apiType. 4. Parses the tools, options, and response format from the PromptAgent. - 5. Creates and returns a Agent instance with the configured properties. + 5. Creates and returns an Agent instance with the configured properties. Args: yaml_path: Path to the YAML file representation of a PromptAgent. @@ -344,7 +344,7 @@ def create_agent_from_yaml_path(self, yaml_path: str | Path) -> Agent: return self.create_agent_from_yaml(yaml_str) def create_agent_from_yaml(self, yaml_str: str) -> Agent: - """Create a Agent from a YAML string. + """Create an Agent from a YAML string. This method does the following things: @@ -352,7 +352,7 @@ def create_agent_from_yaml(self, yaml_str: str) -> Agent: 2. Validates that the loaded object is a PromptAgent. 3. Creates the appropriate ChatClient based on the model provider and apiType. 4. Parses the tools, options, and response format from the PromptAgent. - 5. Creates and returns a Agent instance with the configured properties. + 5. Creates and returns an Agent instance with the configured properties. Args: yaml_str: YAML string representation of a PromptAgent. @@ -417,7 +417,7 @@ def create_agent_from_yaml(self, yaml_str: str) -> Agent: return self.create_agent_from_dict(yaml.safe_load(yaml_str)) def create_agent_from_dict(self, agent_def: dict[str, Any]) -> Agent: - """Create a Agent from a dictionary definition. + """Create an Agent from a dictionary definition. This method does the following things: @@ -425,7 +425,7 @@ def create_agent_from_dict(self, agent_def: dict[str, Any]) -> Agent: 2. Validates that the loaded object is a PromptAgent. 3. Creates the appropriate ChatClient based on the model provider and apiType. 4. Parses the tools, options, and response format from the PromptAgent. - 5. Creates and returns a Agent instance with the configured properties. + 5. Creates and returns an Agent instance with the configured properties. Args: agent_def: Dictionary representation of a PromptAgent. @@ -485,7 +485,7 @@ def create_agent_from_dict(self, agent_def: dict[str, Any]) -> Agent: return agent async def create_agent_from_yaml_path_async(self, yaml_path: str | Path) -> Agent: - """Async version: Create a Agent from a YAML file path. + """Async version: Create an Agent from a YAML file path. This is the async counterpart to ``create_agent_from_dict`` and is useful when the rest of your setup is already async. @@ -516,7 +516,7 @@ async def create_agent_from_yaml_path_async(self, yaml_path: str | Path) -> Agen return await self.create_agent_from_yaml_async(yaml_str) async def create_agent_from_yaml_async(self, yaml_str: str) -> Agent: - """Async version: Create a Agent from a YAML string. + """Async version: Create an Agent from a YAML string. Use this method when the surrounding call site is already async and you want to build an agent directly from YAML text. @@ -547,7 +547,7 @@ async def create_agent_from_yaml_async(self, yaml_str: str) -> Agent: return await self.create_agent_from_dict_async(yaml.safe_load(yaml_str)) async def create_agent_from_dict_async(self, agent_def: dict[str, Any]) -> Agent: - """Async version: Create a Agent from a dictionary definition. + """Async version: Create an Agent from a dictionary definition. This is the async counterpart to ``create_agent_from_dict`` and is useful when the rest of your setup is already async. diff --git a/python/packages/declarative/agent_framework_declarative/_workflows/_factory.py b/python/packages/declarative/agent_framework_declarative/_workflows/_factory.py index 0b3be5de94f..a8329a59734 100644 --- a/python/packages/declarative/agent_framework_declarative/_workflows/_factory.py +++ b/python/packages/declarative/agent_framework_declarative/_workflows/_factory.py @@ -596,7 +596,7 @@ def register_agent(self, name: str, agent: SupportsAgentRun | AgentExecutor) -> Args: name: The name to register the agent under. Must match the agent name referenced in InvokeAzureAgent actions. - agent: The agent instance (typically a Agent or similar). + agent: The agent instance (typically an Agent or similar). Returns: Self for method chaining. diff --git a/python/packages/orchestrations/agent_framework_orchestrations/_magentic.py b/python/packages/orchestrations/agent_framework_orchestrations/_magentic.py index 6206022f043..d409c081efb 100644 --- a/python/packages/orchestrations/agent_framework_orchestrations/_magentic.py +++ b/python/packages/orchestrations/agent_framework_orchestrations/_magentic.py @@ -517,7 +517,7 @@ def on_checkpoint_restore(self, state: dict[str, Any]) -> None: class StandardMagenticManager(MagenticManagerBase): - """Standard Magentic manager that performs real LLM calls via a Agent. + """Standard Magentic manager that performs real LLM calls via an Agent. The manager constructs prompts that mirror the original Magentic One orchestration: - Facts gathering diff --git a/python/samples/02-agents/observability/workflow_observability.py b/python/samples/02-agents/observability/workflow_observability.py index f7893b2c986..43a38025663 100644 --- a/python/samples/02-agents/observability/workflow_observability.py +++ b/python/samples/02-agents/observability/workflow_observability.py @@ -14,7 +14,7 @@ from typing_extensions import Never """ -This sample shows the telemetry collected when running a Agent Framework workflow. +This sample shows the telemetry collected when running an Agent Framework workflow. This simple workflow consists of two executors arranged sequentially: 1. An executor that converts input text to uppercase. diff --git a/python/samples/02-agents/providers/anthropic/anthropic_skills.py b/python/samples/02-agents/providers/anthropic/anthropic_skills.py index 5724955c2be..653762c0db0 100644 --- a/python/samples/02-agents/providers/anthropic/anthropic_skills.py +++ b/python/samples/02-agents/providers/anthropic/anthropic_skills.py @@ -38,7 +38,7 @@ async def main() -> None: for skill in skills.data: print(f"{skill.source}: {skill.id} (version: {skill.latest_version_id})") - # Create a agent with the pptx skill enabled + # Create an agent with the pptx skill enabled # Skills also need the code interpreter tool to function agent = Agent( client=client, diff --git a/python/samples/02-agents/response_stream.py b/python/samples/02-agents/response_stream.py index 917a4490233..41eed06a7c6 100644 --- a/python/samples/02-agents/response_stream.py +++ b/python/samples/02-agents/response_stream.py @@ -98,20 +98,24 @@ === Two Consumption Patterns === **Pattern 1: Async Iteration** + ```python async for update in response_stream: print(update.text) # Process each update # Stream is now consumed; updates are stored internally ``` + - Transform hooks are called for each yielded item - Cleanup hooks are called after the last item - The stream collects all updates internally for later finalization - The stream finalizes automatically when iteration reaches the end **Pattern 2: Direct Finalization** + ```python final = await response_stream.get_final_response() ``` + - If the stream hasn't been iterated, it auto-iterates (consuming all updates) - The finalizer converts collected updates to a final response - Update and result pipelines run normally @@ -119,21 +123,21 @@ ** Pattern 3: Combined Usage ** -When you first iterate the stream and then call `get_final_response()`, the following occurs: -- Iteration yields updates with transform hooks applied -- Cleanup hooks run after iteration completes -- Calling `get_final_response()` uses the already collected updates to produce the final response -- Note that it does not re-iterate the stream since it's already been consumed - ```python async for update in response_stream: print(update.text) # See each update final = await response_stream.get_final_response() # Get the aggregated result ``` +When you first iterate the stream and then call `get_final_response()`, the following occurs: +- Iteration yields updates with transform hooks applied +- Cleanup hooks run after iteration completes +- Calling `get_final_response()` uses the already collected updates to produce the final response +- Note that it does not re-iterate the stream since it's already been consumed + === Chaining with .map(), .flat_map(), and .with_finalizer() === -When building a Agent on top of a ChatClient, we face a challenge: +When building an Agent on top of a ChatClient, we face a challenge: - The ChatClient returns a ResponseStream[ChatResponseUpdate, ChatResponse] - The Agent needs to return a ResponseStream[AgentResponseUpdate, AgentResponse] - We can't iterate the ChatClient's stream twice! @@ -275,8 +279,8 @@ async def cleanup_hook() -> None: ) print("Starting iteration (cleanup happens after):") - async for _update in stream4: - pass # Just consume the stream + async for update in stream4: + print(f" Received: '{update.text}'") print(f"Cleanup was performed: {cleanup_performed['value']}") # ========================================================================= @@ -488,6 +492,7 @@ def add_stats_to_response(response: ChatResponse) -> ChatResponse: # Expected output includes: # === Example 6: Gates Around Transforms === +# Released updates: # [Before gate] Saw: 'Public content. ' # -> 'Public content. ' # [Before gate] Saw: 'Internal secret.' @@ -495,6 +500,7 @@ def add_stats_to_response(response: ChatResponse) -> ChatResponse: # Final result: 'Public content. [redacted].' # # === Example 7: Buffered Final Replacement === +# The source is fully consumed and the replacement is approved before the first update is released: # [Before result gate] Original: 'Public content. Internal secret.' # -> 'Approved replacement response.' # Final replacement: 'Approved replacement response.' diff --git a/python/samples/03-workflows/agents/custom_agent_executors.py b/python/samples/03-workflows/agents/custom_agent_executors.py index 56943b558f2..e832bbb7d68 100644 --- a/python/samples/03-workflows/agents/custom_agent_executors.py +++ b/python/samples/03-workflows/agents/custom_agent_executors.py @@ -43,7 +43,7 @@ class Writer(Executor): """Custom executor that owns a domain specific agent responsible for generating content. This class demonstrates: - - Attaching a Agent to an Executor so it participates as a node in a workflow. + - Attaching an Agent to an Executor so it participates as a node in a workflow. - Using a @handler method to accept a typed input and forward a typed output via ctx.send_message. """ diff --git a/python/samples/03-workflows/agents/group_chat_workflow_as_agent.py b/python/samples/03-workflows/agents/group_chat_workflow_as_agent.py index a93155917db..68ba852dee4 100644 --- a/python/samples/03-workflows/agents/group_chat_workflow_as_agent.py +++ b/python/samples/03-workflows/agents/group_chat_workflow_as_agent.py @@ -16,7 +16,7 @@ Sample: Group Chat Orchestration What it does: -- Demonstrates the generic GroupChatBuilder with a agent orchestrator directing two agents. +- Demonstrates the generic GroupChatBuilder with an agent orchestrator directing two agents. - The orchestrator coordinates a researcher (chat completions) and a writer (responses API) to solve a task. Prerequisites: diff --git a/python/samples/04-hosting/foundry-hosted-agents/README.md b/python/samples/04-hosting/foundry-hosted-agents/README.md index 381b0064d56..1c91b59bfea 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/README.md @@ -55,11 +55,11 @@ Agent Framework `AgentSession`, but they are not interchangeable. During a hosted-agent conversation, one `AgentSession` can therefore contain both remote values: ```python -session.service_session_id # Response or conversation continuation handle +session.service_session_id -session.state[FOUNDRY_HOSTED_AGENT_SESSION_ID_KEY] # Foundry hosted-agent session ID +session.state[FOUNDRY_HOSTED_AGENT_SESSION_ID_KEY] ``` Keep the same `AgentSession` across turns so Agent Framework can forward both values correctly. When cleaning up, @@ -191,11 +191,11 @@ Or in PowerShell: #### Running the Agent Host with Python -Clone the repository containing the sample code: +Clone the repository containing the sample code and change directory to the selected sample (here we use `/responses/basic`): ```bash git clone https://github.com/microsoft/agent-framework.git -cd agent-framework/python/samples/04-hosting/foundry-hosted-agents/responses +cd agent-framework/python/samples/04-hosting/foundry-hosted-agents/responses/basic ``` #### Environment setup @@ -225,7 +225,7 @@ cd agent-framework/python/samples/04-hosting/foundry-hosted-agents/responses uv pip install -r requirements.txt ``` -3. Create a `.env` file with your Foundry configuration following the `env.example` file in the sample. +3. Create a `.env` file with your Foundry configuration following the `.env.example` file in the sample. 4. Make sure you are logged in with the Azure CLI: @@ -246,6 +246,10 @@ Right now, the agent host should be running on `http://localhost:8088` On another terminal, run the following command to invoke the agent: ```bash +# Windows Command Prompt +curl.exe -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d "{\"input\":\"Hello!\"}" + +# macOS/Linux curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Hello!"}' ```