Skip to content
Merged
2 changes: 1 addition & 1 deletion python/packages/ag-ui/tests/ag_ui/test_tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions python/packages/core/agent_framework/_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,15 +290,15 @@ 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:

1. Loads the YAML file into an AgentSchema object.
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.
Expand Down Expand Up @@ -344,15 +344,15 @@ 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:

1. Loads the YAML string into an AgentSchema object.
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.
Expand Down Expand Up @@ -417,15 +417,15 @@ 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:

1. Converts the dictionary into an AgentSchema object.
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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
24 changes: 15 additions & 9 deletions python/samples/02-agents/response_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,42 +98,46 @@
=== 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
- You get the complete response without ever seeing individual updates

Comment thread
dargilco marked this conversation as resolved.
** 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:
Comment thread
dargilco marked this conversation as resolved.
- 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!
Expand Down Expand Up @@ -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']}")

# =========================================================================
Expand Down Expand Up @@ -488,13 +492,15 @@ 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.'
# -> '[redacted].'
# 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.'
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 9 additions & 5 deletions python/samples/04-hosting/foundry-hosted-agents/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand 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
Expand Down Expand Up @@ -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:

Expand All @@ -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!"}'
```

Expand Down
Loading