Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@
import json
import re
import sys
from collections.abc import AsyncIterable, Mapping, MutableMapping, MutableSequence, Sequence
from collections.abc import AsyncIterable, Callable, Mapping, MutableMapping, MutableSequence, Sequence
from typing import Any, ClassVar, Generic, TypedDict

from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
BaseChatClient,
ChatAgent,
ChatMessage,
ChatMessageStoreProtocol,
ChatOptions,
ChatResponse,
ChatResponseUpdate,
CitationAnnotation,
Contents,
ContextProvider,
DataContent,
FunctionApprovalRequestContent,
FunctionApprovalResponseContent,
FunctionCallContent,
FunctionResultContent,
HostedFileContent,
HostedMCPTool,
Middleware,
Role,
TextContent,
TextSpanRegion,
Expand Down Expand Up @@ -1162,3 +1166,59 @@ def service_url(self) -> str:
The service URL for the chat client, or None if not set.
"""
return self.agents_client._config.endpoint # type: ignore

@override
def as_agent(
self,
*,
id: str | None = None,
name: str | None = None,
description: str | None = None,
instructions: str | None = None,
tools: ToolProtocol
| Callable[..., Any]
| MutableMapping[str, Any]
| Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]
| None = None,
default_options: TAzureAIAgentOptions | None = None,
chat_message_store_factory: Callable[[], ChatMessageStoreProtocol] | None = None,
context_provider: ContextProvider | None = None,
middleware: Sequence[Middleware] | None = None,
**kwargs: Any,
) -> ChatAgent[TAzureAIAgentOptions]:
"""Convert this chat client to a ChatAgent.

This method creates a ChatAgent instance with this client pre-configured.
It does NOT create an agent on the Azure AI service - the actual agent
will be created on the server during the first invocation (run).

For creating and managing persistent agents on the server, use
:class:`~agent_framework_azure_ai.AzureAIAgentsProvider` instead.

Keyword Args:
id: The unique identifier for the agent. Will be created automatically if not provided.
name: The name of the agent.
description: A brief description of the agent's purpose.
instructions: Optional instructions for the agent.
tools: The tools to use for the request.
default_options: A TypedDict containing chat options.
chat_message_store_factory: Factory function to create an instance of ChatMessageStoreProtocol.
context_provider: Context providers to include during agent invocation.
middleware: List of middleware to intercept agent and function invocations.
kwargs: Any additional keyword arguments.

Returns:
A ChatAgent instance configured with this chat client.
"""
return super().as_agent(
id=id,
name=name,
description=description,
instructions=instructions,
tools=tools,
default_options=default_options,
chat_message_store_factory=chat_message_store_factory,
context_provider=context_provider,
middleware=middleware,
**kwargs,
)
63 changes: 62 additions & 1 deletion python/packages/azure-ai/agent_framework_azure_ai/_client.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# Copyright (c) Microsoft. All rights reserved.

import sys
from collections.abc import Mapping, MutableSequence
from collections.abc import Callable, Mapping, MutableMapping, MutableSequence, Sequence
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypedDict, TypeVar, cast

from agent_framework import (
AGENT_FRAMEWORK_USER_AGENT,
ChatAgent,
ChatMessage,
ChatMessageStoreProtocol,
ContextProvider,
HostedMCPTool,
Middleware,
TextContent,
ToolProtocol,
get_logger,
use_chat_middleware,
use_function_invocation,
Expand Down Expand Up @@ -511,3 +516,59 @@ def _prepare_mcp_tool(tool: HostedMCPTool) -> MCPTool: # type: ignore[override]
mcp["require_approval"] = {"never": {"tool_names": list(never_require_approvals)}}

return mcp

@override
def as_agent(
self,
*,
id: str | None = None,
name: str | None = None,
description: str | None = None,
instructions: str | None = None,
tools: ToolProtocol
| Callable[..., Any]
| MutableMapping[str, Any]
| Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]
| None = None,
default_options: TAzureAIClientOptions | None = None,
chat_message_store_factory: Callable[[], ChatMessageStoreProtocol] | None = None,
context_provider: ContextProvider | None = None,
middleware: Sequence[Middleware] | None = None,
**kwargs: Any,
) -> ChatAgent[TAzureAIClientOptions]:
"""Convert this chat client to a ChatAgent.

This method creates a ChatAgent instance with this client pre-configured.
It does NOT create an agent on the Azure AI service - the actual agent
will be created on the server during the first invocation (run).

For creating and managing persistent agents on the server, use
:class:`~agent_framework_azure_ai.AzureAIProjectAgentProvider` instead.

Keyword Args:
id: The unique identifier for the agent. Will be created automatically if not provided.
name: The name of the agent.
description: A brief description of the agent's purpose.
instructions: Optional instructions for the agent.
tools: The tools to use for the request.
default_options: A TypedDict containing chat options.
chat_message_store_factory: Factory function to create an instance of ChatMessageStoreProtocol.
context_provider: Context providers to include during agent invocation.
middleware: List of middleware to intercept agent and function invocations.
kwargs: Any additional keyword arguments.

Returns:
A ChatAgent instance configured with this chat client.
"""
return super().as_agent(
id=id,
name=name,
description=description,
instructions=instructions,
tools=tools,
default_options=default_options,
chat_message_store_factory=chat_message_store_factory,
context_provider=context_provider,
middleware=middleware,
**kwargs,
)
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ class AgentFunctionApp(DFAppBase):
from agent_framework.azure import AgentFunctionApp, AzureOpenAIChatClient
# Create agents with unique names
weather_agent = AzureOpenAIChatClient(...).create_agent(
weather_agent = AzureOpenAIChatClient(...).as_agent(
name="WeatherAgent",
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
math_agent = AzureOpenAIChatClient(...).create_agent(
math_agent = AzureOpenAIChatClient(...).as_agent(
name="MathAgent",
instructions="You are a helpful math assistant.",
tools=[calculate],
Expand Down
4 changes: 2 additions & 2 deletions python/packages/core/agent_framework/_clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ def service_url(self) -> str:
"""
return "Unknown"

def create_agent(
def as_agent(
self,
*,
id: str | None = None,
Expand Down Expand Up @@ -428,7 +428,7 @@ def create_agent(
client = OpenAIChatClient(model_id="gpt-4")
# Create an agent using the convenience method
agent = client.create_agent(
agent = client.as_agent(
name="assistant",
instructions="You are a helpful assistant.",
default_options={"temperature": 0.7, "max_tokens": 500},
Expand Down
6 changes: 3 additions & 3 deletions python/packages/core/agent_framework/_workflows/_handoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,9 +710,9 @@ def participants(self, participants: Sequence[AgentProtocol]) -> "HandoffBuilder
from agent_framework.openai import OpenAIChatClient
client = OpenAIChatClient()
triage = client.create_agent(instructions="...", name="triage_agent")
refund = client.create_agent(instructions="...", name="refund_agent")
billing = client.create_agent(instructions="...", name="billing_agent")
triage = client.as_agent(instructions="...", name="triage_agent")
refund = client.as_agent(instructions="...", name="refund_agent")
billing = client.as_agent(instructions="...", name="billing_agent")
builder = HandoffBuilder().participants([triage, refund, billing])
builder.with_start_agent(triage)
Expand Down
67 changes: 65 additions & 2 deletions python/packages/core/agent_framework/openai/_assistants_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
Mapping,
MutableMapping,
MutableSequence,
Sequence,
)
from typing import Any, Generic, Literal, TypedDict, cast
from typing import TYPE_CHECKING, Any, Generic, Literal, TypedDict, cast

if TYPE_CHECKING:
from .._agents import ChatAgent

from openai import AsyncOpenAI
from openai.types.beta.threads import (
Expand All @@ -28,11 +32,14 @@
from pydantic import ValidationError

from .._clients import BaseChatClient
from .._middleware import use_chat_middleware
from .._memory import ContextProvider
from .._middleware import Middleware, use_chat_middleware
from .._threads import ChatMessageStoreProtocol
from .._tools import (
AIFunction,
HostedCodeInterpreterTool,
HostedFileSearchTool,
ToolProtocol,
use_function_invocation,
)
from .._types import (
Expand Down Expand Up @@ -761,3 +768,59 @@ def _update_agent_name_and_description(self, agent_name: str | None, description
self.assistant_name = agent_name
if description and not self.assistant_description:
self.assistant_description = description

@override
def as_agent(
self,
*,
id: str | None = None,
name: str | None = None,
description: str | None = None,
instructions: str | None = None,
tools: ToolProtocol
| Callable[..., Any]
| MutableMapping[str, Any]
| Sequence[ToolProtocol | Callable[..., Any] | MutableMapping[str, Any]]
| None = None,
default_options: TOpenAIAssistantsOptions | None = None,
chat_message_store_factory: Callable[[], ChatMessageStoreProtocol] | None = None,
context_provider: ContextProvider | None = None,
middleware: Sequence[Middleware] | None = None,
**kwargs: Any,
) -> "ChatAgent[TOpenAIAssistantsOptions]":
"""Convert this chat client to a ChatAgent.

This method creates a ChatAgent instance with this client pre-configured.
It does NOT create an assistant on the OpenAI service - the actual assistant
will be created on the server during the first invocation (run).

For creating and managing persistent assistants on the server, use
:class:`~agent_framework.openai.OpenAIAssistantProvider` instead.

Keyword Args:
id: The unique identifier for the agent. Will be created automatically if not provided.
name: The name of the agent.
description: A brief description of the agent's purpose.
instructions: Optional instructions for the agent.
tools: The tools to use for the request.
default_options: A TypedDict containing chat options.
chat_message_store_factory: Factory function to create an instance of ChatMessageStoreProtocol.
context_provider: Context providers to include during agent invocation.
middleware: List of middleware to intercept agent and function invocations.
kwargs: Any additional keyword arguments.

Returns:
A ChatAgent instance configured with this chat client.
"""
return super().as_agent(
id=id,
name=name,
description=description,
instructions=instructions,
tools=tools,
default_options=default_options,
chat_message_store_factory=chat_message_store_factory,
context_provider=context_provider,
middleware=middleware,
**kwargs,
)
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class WorkflowFactory:
# Pre-register agents for InvokeAzureAgent actions
chat_client = AzureOpenAIChatClient()
agent = chat_client.create_agent(name="MyAgent", instructions="You are helpful.")
agent = chat_client.as_agent(name="MyAgent", instructions="You are helpful.")
factory = WorkflowFactory(agents={"MyAgent": agent})
workflow = factory.create_workflow_from_yaml_path("workflow.yaml")
Expand Down Expand Up @@ -115,8 +115,8 @@ def __init__(
# With pre-registered agents
client = AzureOpenAIChatClient()
agents = {
"WriterAgent": client.create_agent(name="Writer", instructions="Write content."),
"ReviewerAgent": client.create_agent(name="Reviewer", instructions="Review content."),
"WriterAgent": client.as_agent(name="Writer", instructions="Write content."),
"ReviewerAgent": client.as_agent(name="Reviewer", instructions="Review content."),
}
factory = WorkflowFactory(agents=agents)
Expand Down Expand Up @@ -533,14 +533,14 @@ def register_agent(self, name: str, agent: AgentProtocol | AgentExecutor) -> "Wo
WorkflowFactory()
.register_agent(
"Writer",
client.create_agent(
client.as_agent(
name="Writer",
instructions="Write content.",
),
)
.register_agent(
"Reviewer",
client.create_agent(
client.as_agent(
name="Reviewer",
instructions="Review content.",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def __init__(

client = FoundryLocalClient(model_id="phi-4-mini")

agent = client.create_agent(
agent = client.as_agent(
name="LocalAgent",
instructions="You are a helpful agent.",
tools=get_weather,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def main() -> None:
print(
f"- {model.alias} for {model.task} - id={model.id} - {(model.file_size_mb / 1000):.2f} GB - {model.license}"
)
agent = client.create_agent(
agent = client.as_agent(
name="LocalAgent",
instructions="You are a helpful agent.",
tools=get_weather,
Expand Down
2 changes: 1 addition & 1 deletion python/packages/lab/gaia/samples/azure_ai_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def create_gaia_agent() -> AsyncIterator[ChatAgent]:
"""
async with (
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).create_agent(
AzureAIAgentClient(credential=credential).as_agent(
name="GaiaAgent",
instructions="Solve tasks to your best ability. Use Bing Search to find "
"information and Code Interpreter to perform calculations and data analysis.",
Expand Down
2 changes: 1 addition & 1 deletion python/packages/lab/gaia/samples/openai_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def create_gaia_agent() -> AsyncIterator[ChatAgent]:
"""
chat_client = OpenAIResponsesClient()

async with chat_client.create_agent(
async with chat_client.as_agent(
name="GaiaAgent",
instructions="Solve tasks to your best ability. Use Web Search to find "
"information and Code Interpreter to perform calculations and data analysis.",
Expand Down
Loading
Loading