Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .openpublishing.publish.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"build_output_subfolder": "agent-framework",
"locale": "en-us",
"monikers": [],
"open_to_public_contributors": false,
"open_to_public_contributors": true,
"type_mapping": {
"Conceptual": "Content"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ To see tool calls and results in real-time, extend the client's streaming loop t

```csharp
// Inside the streaming loop from getting-started.md
await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(messages, thread))
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread))
{
ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate();

Expand Down
4 changes: 2 additions & 2 deletions agent-framework/integrations/ag-ui/frontend-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ AIAgent inspectableAgent = baseAgent
.Use(runFunc: null, runStreamingFunc: InspectToolsMiddleware)
.Build();

static async IAsyncEnumerable<AgentRunResponseUpdate> InspectToolsMiddleware(
static async IAsyncEnumerable<AgentResponseUpdate> InspectToolsMiddleware(
IEnumerable<ChatMessage> messages,
AgentThread? thread,
AgentRunOptions? options,
Expand All @@ -109,7 +109,7 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> InspectToolsMiddleware(
}
}

await foreach (AgentRunResponseUpdate update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken))
await foreach (AgentResponseUpdate update in innerAgent.RunStreamingAsync(messages, thread, options, cancellationToken))
{
yield return update;
}
Expand Down
6 changes: 3 additions & 3 deletions agent-framework/integrations/ag-ui/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ try
bool isFirstUpdate = true;
string? threadId = null;

await foreach (AgentRunResponseUpdate update in agent.RunStreamingAsync(messages, thread))
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, thread))
{
ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate();

Expand Down Expand Up @@ -257,7 +257,7 @@ catch (Exception ex)
- **Server-Sent Events (SSE)**: The protocol uses SSE for streaming responses
- **AGUIChatClient**: Client class that connects to AG-UI servers and implements `IChatClient`
- **CreateAIAgent**: Extension method on `AGUIChatClient` to create an agent from the client
- **RunStreamingAsync**: Streams responses as `AgentRunResponseUpdate` objects
- **RunStreamingAsync**: Streams responses as `AgentResponseUpdate` objects
- **AsChatResponseUpdate**: Extension method to access chat-specific properties like `ConversationId` and `ResponseId`
- **Thread Management**: The `AgentThread` maintains conversation context across requests
- **Content Types**: Responses include `TextContent` for messages and `ErrorContent` for errors
Expand Down Expand Up @@ -327,7 +327,7 @@ The client displays different content types with distinct colors:

1. `AGUIChatClient` sends HTTP POST request to server endpoint
2. Server responds with SSE stream
3. Client parses incoming events into `AgentRunResponseUpdate` objects
3. Client parses incoming events into `AgentResponseUpdate` objects
4. Each update is displayed based on its content type
5. `ConversationId` is captured for conversation continuity
6. Stream completes when run finishes
Expand Down
20 changes: 10 additions & 10 deletions agent-framework/integrations/ag-ui/human-in-the-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ var agent = baseAgent
cancellationToken))
.Build();

static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsMiddleware(
static async IAsyncEnumerable<AgentResponseUpdate> HandleApprovalRequestsMiddleware(
IEnumerable<ChatMessage> messages,
AgentThread? thread,
AgentRunOptions? options,
Expand Down Expand Up @@ -250,8 +250,8 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsMidd
}

// Local function: Convert FunctionApprovalRequestContent to client tool calls
static async IAsyncEnumerable<AgentRunResponseUpdate> ConvertFunctionApprovalsToToolCalls(
AgentRunResponseUpdate update,
static async IAsyncEnumerable<AgentResponseUpdate> ConvertFunctionApprovalsToToolCalls(
AgentResponseUpdate update,
JsonSerializerOptions jsonSerializerOptions)
{
// Check if this update contains a FunctionApprovalRequestContent
Expand Down Expand Up @@ -292,7 +292,7 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsMidd
var approvalJson = JsonSerializer.Serialize(approvalData, jsonSerializerOptions.GetTypeInfo(typeof(ApprovalRequest)));

// Yield a tool call update that represents the approval request
yield return new AgentRunResponseUpdate(ChatRole.Assistant, [
yield return new AgentResponseUpdate(ChatRole.Assistant, [
new FunctionCallContent(
callId: approvalId,
name: "request_approval",
Expand Down Expand Up @@ -337,7 +337,7 @@ var wrappedAgent = agent
cancellationToken))
.Build();

static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsClientMiddleware(
static async IAsyncEnumerable<AgentResponseUpdate> HandleApprovalRequestsClientMiddleware(
IEnumerable<ChatMessage> messages,
AgentThread? thread,
AgentRunOptions? options,
Expand Down Expand Up @@ -414,8 +414,8 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsClie
}

// Local function: Convert request_approval tool calls to FunctionApprovalRequestContent
static async IAsyncEnumerable<AgentRunResponseUpdate> ConvertToolCallsToApprovalRequests(
AgentRunResponseUpdate update,
static async IAsyncEnumerable<AgentResponseUpdate> ConvertToolCallsToApprovalRequests(
AgentResponseUpdate update,
JsonSerializerOptions jsonSerializerOptions)
{
FunctionCallContent? approvalToolCall = null;
Expand Down Expand Up @@ -452,7 +452,7 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsClie
arguments: functionArguments);

// Yield the original tool call first (for message history)
yield return new AgentRunResponseUpdate(ChatRole.Assistant, [approvalToolCall]);
yield return new AgentResponseUpdate(ChatRole.Assistant, [approvalToolCall]);

// Create approval request with CallId stored in AdditionalProperties
var approvalRequestContent = new FunctionApprovalRequestContent(
Expand All @@ -463,7 +463,7 @@ static async IAsyncEnumerable<AgentRunResponseUpdate> HandleApprovalRequestsClie
approvalRequestContent.AdditionalProperties ??= new Dictionary<string, object?>();
approvalRequestContent.AdditionalProperties["request_approval_call_id"] = approvalToolCall.CallId;

yield return new AgentRunResponseUpdate(ChatRole.Assistant, [approvalRequestContent]);
yield return new AgentResponseUpdate(ChatRole.Assistant, [approvalRequestContent]);
}
}
#pragma warning restore MEAI001
Expand All @@ -490,7 +490,7 @@ do
approvalResponses.Clear();
approvalToolCalls.Clear();

await foreach (AgentRunResponseUpdate update in wrappedAgent.RunStreamingAsync(
await foreach (AgentResponseUpdate update in wrappedAgent.RunStreamingAsync(
messages, thread, cancellationToken: cancellationToken))
{
foreach (AIContent content in update.Contents)
Expand Down
2 changes: 1 addition & 1 deletion agent-framework/integrations/ag-ui/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Understanding how Agent Framework concepts map to AG-UI helps you build effectiv
| `AIAgent` | Agent Endpoint | Each agent becomes an HTTP endpoint |
| `agent.Run()` | HTTP POST Request | Client sends messages via HTTP |
| `agent.RunStreamingAsync()` | Server-Sent Events | Streaming responses via SSE |
| `AgentRunResponseUpdate` | AG-UI Events | Converted to protocol events automatically |
| `AgentResponseUpdate` | AG-UI Events | Converted to protocol events automatically |
| `AIFunctionFactory.Create()` | Backend Tools | Executed on server, results streamed |
| `ApprovalRequiredAIFunction` | Human-in-the-Loop | Middleware converts to approval protocol |
| `AgentThread` | Thread Management | `ConversationId` maintains context |
Expand Down
12 changes: 6 additions & 6 deletions agent-framework/integrations/ag-ui/state-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,17 @@ internal sealed class SharedStateAgent : DelegatingAIAgent
this._jsonSerializerOptions = jsonSerializerOptions;
}

public override Task<AgentRunResponse> RunAsync(
public override Task<AgentResponse> RunAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
return this.RunStreamingAsync(messages, thread, options, cancellationToken)
.ToAgentRunResponseAsync(cancellationToken);
.ToAgentResponseAsync(cancellationToken);
}

public override async IAsyncEnumerable<AgentRunResponseUpdate> RunStreamingAsync(
public override async IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentThread? thread = null,
AgentRunOptions? options = null,
Expand Down Expand Up @@ -187,7 +187,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent
var firstRunMessages = messages.Append(stateUpdateMessage);

// Collect all updates from first run
var allUpdates = new List<AgentRunResponseUpdate>();
var allUpdates = new List<AgentResponseUpdate>();
await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, thread, firstRunOptions, cancellationToken).ConfigureAwait(false))
{
allUpdates.Add(update);
Expand All @@ -200,7 +200,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent
}
}

var response = allUpdates.ToAgentRunResponse();
var response = allUpdates.ToAgentResponse();

// Try to deserialize the structured state response
if (response.TryDeserialize(this._jsonSerializerOptions, out JsonElement stateSnapshot))
Expand All @@ -209,7 +209,7 @@ internal sealed class SharedStateAgent : DelegatingAIAgent
byte[] stateBytes = JsonSerializer.SerializeToUtf8Bytes(
stateSnapshot,
this._jsonSerializerOptions.GetTypeInfo(typeof(JsonElement)));
yield return new AgentRunResponseUpdate
yield return new AgentResponseUpdate
{
Contents = [new DataContent(stateBytes, "application/json")]
};
Expand Down
59 changes: 34 additions & 25 deletions agent-framework/migration-guide/from-autogen/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,33 +221,39 @@ def get_time() -> str:
client = OpenAIChatClient(model_id="gpt-5")

async def example():
# Direct creation
# Direct creation with default options
agent = ChatAgent(
name="assistant",
chat_client=client,
instructions="You are a helpful assistant.",
tools=[get_weather] # Multi-turn by default
tools=[get_weather], # Multi-turn by default
default_options={
"temperature": 0.7,
"max_tokens": 1000,
}
)

# Factory method (more convenient)
agent = client.create_agent(
name="assistant",
instructions="You are a helpful assistant.",
tools=[get_weather]
tools=[get_weather],
default_options={"temperature": 0.7}
)

# Execution with runtime tool configuration
# Execution with runtime tool and options configuration
result = await agent.run(
"What's the weather?",
tools=[get_time], # Can add tools at runtime
tool_choice="auto"
tools=[get_time], # Can add tools at runtime (keyword arg)
options={"tool_choice": "auto"} # Other options go in options dict
)
```

**Key Differences:**

- **Default behavior**: `ChatAgent` automatically iterates through tool calls, while `AssistantAgent` requires explicit `max_tool_iterations` setting
- **Runtime configuration**: `ChatAgent.run()` accepts `tools` and `tool_choice` parameters for per-invocation customization
- **Runtime configuration**: `ChatAgent.run()` accepts `tools` as a keyword argument and other options via the `options` dict parameter for per-invocation customization
- **Options system**: Agent Framework uses TypedDict-based options (e.g., `OpenAIChatOptions`) for type safety and IDE autocomplete. Options are passed via `default_options` at construction and `options` at runtime
- **Factory methods**: Agent Framework provides convenient factory methods directly from chat clients
- **State management**: `ChatAgent` is stateless and doesn't maintain conversation history between invocations, unlike `AssistantAgent` which maintains conversation history as part of its state

Expand Down Expand Up @@ -340,18 +346,21 @@ async for event in agent.run_stream(task="Hello"):
```python
# Assume we have client, agent, and tools from previous examples
async def streaming_example():
# Chat client streaming
async for chunk in client.get_streaming_response("Hello", tools=tools):
# Chat client streaming - tools go in options dict
async for chunk in client.get_streaming_response(
"Hello",
options={"tools": tools}
):
if chunk.text:
print(chunk.text, end="")

# Agent streaming
async for chunk in agent.run_stream("Hello"):
# Agent streaming - tools can be keyword arg on agents
async for chunk in agent.run_stream("Hello", tools=tools):
if chunk.text:
print(chunk.text, end="", flush=True)
```

Tip: In Agent Framework, both clients and agents yield the same update shape; you can read `chunk.text` in either case.
Tip: In Agent Framework, both clients and agents yield the same update shape; you can read `chunk.text` in either case. Note that for chat clients, `tools` goes in the `options` dict, while for agents, `tools` remains a direct keyword argument.

### Message Types and Creation

Expand Down Expand Up @@ -688,8 +697,8 @@ Notes:
from collections.abc import AsyncIterable
from typing import Any
from agent_framework import (
AgentRunResponse,
AgentRunResponseUpdate,
AgentResponse,
AgentResponseUpdate,
AgentThread,
BaseAgent,
ChatMessage,
Expand All @@ -704,7 +713,7 @@ class StaticAgent(BaseAgent):
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AgentRunResponse:
) -> AgentResponse:
# Build a static reply
reply = ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text="Hello from AF custom agent")])

Expand All @@ -713,17 +722,17 @@ class StaticAgent(BaseAgent):
normalized = self._normalize_messages(messages)
await self._notify_thread_of_new_messages(thread, normalized, reply)

return AgentRunResponse(messages=[reply])
return AgentResponse(messages=[reply])

async def run_stream(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AsyncIterable[AgentRunResponseUpdate]:
) -> AsyncIterable[AgentResponseUpdate]:
# Stream the same static response in a single chunk for simplicity
yield AgentRunResponseUpdate(contents=[TextContent(text="Hello from AF custom agent")], role=Role.ASSISTANT)
yield AgentResponseUpdate(contents=[TextContent(text="Hello from AF custom agent")], role=Role.ASSISTANT)

# Notify thread of input and the complete response once streaming ends
if thread is not None:
Expand Down Expand Up @@ -1199,7 +1208,7 @@ from typing import cast
from agent_framework import (
MAGENTIC_EVENT_TYPE_AGENT_DELTA,
MAGENTIC_EVENT_TYPE_ORCHESTRATOR,
AgentRunUpdateEvent,
AgentResponseUpdateEvent,
ChatAgent,
ChatMessage,
MagenticBuilder,
Expand Down Expand Up @@ -1231,7 +1240,7 @@ workflow = (
async def magentic_example():
output: str | None = None
async for event in workflow.run_stream("Complex research task"):
if isinstance(event, AgentRunUpdateEvent):
if isinstance(event, AgentResponseUpdateEvent):
props = event.data.additional_properties if event.data else None
event_type = props.get("magentic_event_type") if props else None

Expand All @@ -1255,7 +1264,7 @@ The Magentic workflow provides extensive customization options:

- **Manager configuration**: Use a ChatAgent with custom instructions and model settings
- **Round limits**: `max_round_count`, `max_stall_count`, `max_reset_count`
- **Event streaming**: Use `AgentRunUpdateEvent` with `magentic_event_type` metadata
- **Event streaming**: Use `AgentResponseUpdateEvent` with `magentic_event_type` metadata
- **Agent specialization**: Custom instructions and tools per agent
- **Human-in-the-loop**: Plan review, tool approval, and stall intervention

Expand All @@ -1265,7 +1274,7 @@ from typing import cast
from agent_framework import (
MAGENTIC_EVENT_TYPE_AGENT_DELTA,
MAGENTIC_EVENT_TYPE_ORCHESTRATOR,
AgentRunUpdateEvent,
AgentResponseUpdateEvent,
ChatAgent,
MagenticBuilder,
MagenticHumanInterventionDecision,
Expand Down Expand Up @@ -1345,7 +1354,7 @@ Agent Framework provides built-in request-response capabilities where any execut

```python
from agent_framework import (
RequestInfoEvent, WorkflowBuilder, WorkflowContext,
RequestInfoEvent, WorkflowBuilder, WorkflowContext,
Executor, handler, response_handler
)
from dataclasses import dataclass
Expand All @@ -1361,7 +1370,7 @@ class ApprovalRequest:

# Workflow executor that requests human approval
class ReviewerExecutor(Executor):

@handler
async def review_content(
self,
Expand All @@ -1374,7 +1383,7 @@ class ReviewerExecutor(Executor):
agent_name="writer_agent"
)
await ctx.request_info(request_data=approval_request, response_type=str)

@response_handler
async def handle_approval_response(
self,
Expand Down
Loading