From 9647ef239270238c97eb07053df7b23aa5ebc720 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:05:49 -0700 Subject: [PATCH 01/14] Added non-streaming and streaming examples --- .../agents/foundry/foundry_basic.py | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/python/samples/getting_started/agents/foundry/foundry_basic.py b/python/samples/getting_started/agents/foundry/foundry_basic.py index ed698b2a5e1..5df4b8e0891 100644 --- a/python/samples/getting_started/agents/foundry/foundry_basic.py +++ b/python/samples/getting_started/agents/foundry/foundry_basic.py @@ -17,8 +17,9 @@ def get_weather( return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." -async def main() -> None: - print("=== Basic Foundry Chat Client Example ===") +async def non_streaming_example() -> None: + """Example of non-streaming response (get the complete result at once).""" + print("=== Non-streaming Response Example ===") # Since no Agent ID is provided, the agent will be automatically created # and deleted after getting a response @@ -27,9 +28,38 @@ async def main() -> None: instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: - result = await agent.run("What's the weather like in Seattle?") + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) print(f"Result: {result}\n") +async def streaming_example() -> None: + """Example of streaming response (get results as they are generated).""" + print("=== Streaming Response Example ===") + + # Since no Agent ID is provided, the agent will be automatically created + # and deleted after getting a response + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) as agent: + query = "What's the weather like in Portland?" + print(f"User: {query}") + print("Assistant: ", end="", flush=True) + async for chunk in agent.run_stream(query): + if chunk.text: + print(chunk.text, end="", flush=True) + print("\n") + + +async def main() -> None: + print("=== Basic Foundry Chat Client Example ===") + + await non_streaming_example() + await streaming_example() + + if __name__ == "__main__": asyncio.run(main()) From 2d0630eaaa38b89f7ef238d10bbad6d6b71263c0 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:03:00 -0700 Subject: [PATCH 02/14] Updated resource management --- .../agent_framework_foundry/_chat_client.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/foundry/agent_framework_foundry/_chat_client.py index fc6f17e2185..e3631d01766 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/foundry/agent_framework_foundry/_chat_client.py @@ -85,9 +85,12 @@ class FoundrySettings(AFBaseSettings): @use_tool_calling class FoundryChatClient(ChatClientBase): client: AIProjectClient = Field(...) + credential: AsyncTokenCredential | None = Field(...) agent_id: str | None = Field(default=None) thread_id: str | None = Field(default=None) _should_delete_agent: bool = PrivateAttr(default=False) # Track whether we should delete the agent + _should_close_client: bool = PrivateAttr(default=False) # Track whether we should close client connection + _should_close_credential: bool = PrivateAttr(default=False) # Track whether we should close credential _foundry_settings: FoundrySettings = PrivateAttr() def __init__( @@ -133,6 +136,8 @@ def __init__( raise ServiceInitializationError("Failed to create Foundry settings.", ex) from ex # If no client is provided, create one + should_close_client = False + should_close_credential = False if client is None: if not foundry_settings.project_endpoint: raise ServiceInitializationError("Project endpoint is required when client is not provided.") @@ -145,17 +150,22 @@ def __init__( from azure.identity.aio import DefaultAzureCredential credential = DefaultAzureCredential() + should_close_credential = True client = AIProjectClient(endpoint=foundry_settings.project_endpoint, credential=credential) + should_close_client = True super().__init__( client=client, # type: ignore[reportCallIssue] + credential=credential, # type: ignore[reportCallIssue] agent_id=agent_id, # type: ignore[reportCallIssue] thread_id=thread_id, # type: ignore[reportCallIssue] **kwargs, ) self._should_delete_agent = False + self._should_close_client = should_close_client + self._should_close_credential = should_close_credential self._foundry_settings = foundry_settings async def __aenter__(self) -> "FoundryChatClient": @@ -169,6 +179,8 @@ async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseExc async def close(self) -> None: """Close the client and clean up any agents we created.""" await self._cleanup_agent_if_needed() + await self._close_client_if_needed() + await self._close_credential_if_needed() @classmethod def from_dict(cls, settings: dict[str, Any]) -> "FoundryChatClient": @@ -432,6 +444,16 @@ def _create_function_call_contents(self, event_data: ThreadRun, response_id: str return contents + async def _close_credential_if_needed(self) -> None: + """Close credential if we created it.""" + if self._should_close_credential and self.credential is not None: + await self.credential.close() + + async def _close_client_if_needed(self) -> None: + """Close client session if we created it.""" + if self._should_close_client: + await self.client.close() + async def _cleanup_agent_if_needed(self) -> None: """Clean up the agent if we created it.""" if self._should_delete_agent and self.agent_id is not None: From b6bc04a7df464384306c57a4b521608648efd04a Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:33:38 -0700 Subject: [PATCH 03/14] Added examples with thread management --- .../packages/main/agent_framework/_agents.py | 2 + .../agents/foundry/foundry_with_thread.py | 128 ++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 python/samples/getting_started/agents/foundry/foundry_with_thread.py diff --git a/python/packages/main/agent_framework/_agents.py b/python/packages/main/agent_framework/_agents.py index 14c326baa26..e84905a3577 100644 --- a/python/packages/main/agent_framework/_agents.py +++ b/python/packages/main/agent_framework/_agents.py @@ -453,6 +453,7 @@ async def run( chat_options=self.chat_options & ChatOptions( ai_model_id=model, + conversation_id=thread.id, frequency_penalty=frequency_penalty, logit_bias=logit_bias, max_tokens=max_tokens, @@ -561,6 +562,7 @@ async def run_stream( messages=thread_messages, chat_options=self.chat_options & ChatOptions( + conversation_id=thread.id, frequency_penalty=frequency_penalty, logit_bias=logit_bias, max_tokens=max_tokens, diff --git a/python/samples/getting_started/agents/foundry/foundry_with_thread.py b/python/samples/getting_started/agents/foundry/foundry_with_thread.py new file mode 100644 index 00000000000..5a88b1412f0 --- /dev/null +++ b/python/samples/getting_started/agents/foundry/foundry_with_thread.py @@ -0,0 +1,128 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent, ChatClientAgentThread +from agent_framework.foundry import FoundryChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def example_with_automatic_thread_creation() -> None: + """Example showing automatic thread creation (service-managed thread).""" + print("=== Automatic Thread Creation Example ===") + + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) as agent: + # First conversation - no thread provided, will be created automatically + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Assistant: {result1.text}") + + # Second conversation - still no thread provided, will create another new thread + query2 = "What was the last city I asked about?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2) + print(f"Assistant: {result2.text}") + print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n") + + +async def example_with_thread_persistence() -> None: + """Example showing thread persistence across multiple conversations.""" + print("=== Thread Persistence Example ===") + print("Using the same thread across multiple conversations to maintain context.\n") + + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful weather agent. Remember previous cities asked about.", + tools=get_weather, + ) as agent: + # Create a new thread that will be reused + thread = agent.get_new_thread() + + # First conversation + query1 = "What's the weather like in Tokyo?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Assistant: {result1.text}") + + # Second conversation using the same thread - maintains context + query2 = "How about comparing it to London?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2, thread=thread) + print(f"Assistant: {result2.text}") + + # Third conversation - agent should remember both previous cities + query3 = "Which of the cities I asked about has better weather?" + print(f"\nUser: {query3}") + result3 = await agent.run(query3, thread=thread) + print(f"Assistant: {result3.text}") + print("Note: The agent remembers context from previous messages in the same thread.\n") + + +async def example_with_existing_thread_id() -> None: + """Example showing how to work with an existing thread ID from the service.""" + print("=== Existing Thread ID Example ===") + print("Using a specific thread ID to continue an existing conversation.\n") + + # First, create a conversation and capture the thread ID + existing_thread_id = None + + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) as agent: + # Start a conversation and get the thread ID + thread = agent.get_new_thread() + query1 = "What's the weather in Paris?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Assistant: {result1.text}") + + # The thread ID is set after the first response + existing_thread_id = thread.id + print(f"Thread ID: {existing_thread_id}") + + if existing_thread_id: + print("\n--- Continuing with the same thread ID in a new agent instance ---") + + # Create a new agent instance but use the existing thread ID + async with ChatClientAgent( + chat_client=FoundryChatClient(thread_id=existing_thread_id), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) as agent: + # Create a thread with the existing ID + thread = ChatClientAgentThread(id=existing_thread_id) + + query2 = "What was the last city I asked about?" + print(f"User: {query2}") + result2 = await agent.run(query2, thread=thread) + print(f"Assistant: {result2.text}") + print("Note: The agent continues the conversation from the previous thread.\n") + + +async def main() -> None: + print("=== Foundry Chat Client Thread Management Examples ===\n") + + await example_with_automatic_thread_creation() + await example_with_thread_persistence() + await example_with_existing_thread_id() + + +if __name__ == "__main__": + asyncio.run(main()) From e0c8c151658cb846f261be92377f9e897f1cb796 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:58:38 -0700 Subject: [PATCH 04/14] Added function tools examples --- .../agents/foundry/foundry_with_thread.py | 2 +- .../agents/foundry/foundry_with_tools.py | 117 ++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 python/samples/getting_started/agents/foundry/foundry_with_tools.py diff --git a/python/samples/getting_started/agents/foundry/foundry_with_thread.py b/python/samples/getting_started/agents/foundry/foundry_with_thread.py index 5a88b1412f0..8bd0e591f19 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_thread.py +++ b/python/samples/getting_started/agents/foundry/foundry_with_thread.py @@ -117,7 +117,7 @@ async def example_with_existing_thread_id() -> None: async def main() -> None: - print("=== Foundry Chat Client Thread Management Examples ===\n") + print("=== Foundry Chat Client Agent Thread Management Examples ===\n") await example_with_automatic_thread_creation() await example_with_thread_persistence() diff --git a/python/samples/getting_started/agents/foundry/foundry_with_tools.py b/python/samples/getting_started/agents/foundry/foundry_with_tools.py new file mode 100644 index 00000000000..9583750d175 --- /dev/null +++ b/python/samples/getting_started/agents/foundry/foundry_with_tools.py @@ -0,0 +1,117 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from datetime import datetime, timezone +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent +from agent_framework.foundry import FoundryChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +def get_time() -> str: + """Get the current UTC time.""" + current_time = datetime.now(timezone.utc) + return f"The current UTC time is {current_time.strftime('%Y-%m-%d %H:%M:%S')}." + + +async def tools_on_agent_level() -> None: + """Example showing tools defined when creating the agent.""" + print("=== Tools Defined on Agent Level ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful assistant that can provide weather and time information.", + tools=[get_weather, get_time], # Tools defined at agent creation + ) as agent: + # First query - agent can use weather tool + query1 = "What's the weather like in New York?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Assistant: {result1}\n") + + # Second query - agent can use time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2) + print(f"Assistant: {result2}\n") + + # Third query - agent can use both tools if needed + query3 = "What's the weather in London and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3) + print(f"Assistant: {result3}\n") + + +async def tools_on_run_level() -> None: + """Example showing tools passed to the run method.""" + print("=== Tools Passed to Run Method ===") + + # Agent created without tools + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful assistant.", + # No tools defined here + ) as agent: + # First query with weather tool + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1, tools=[get_weather]) # Tool passed to run method + print(f"Assistant: {result1}\n") + + # Second query with time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2, tools=[get_time]) # Different tool for this query + print(f"Assistant: {result2}\n") + + # Third query with multiple tools + query3 = "What's the weather in Chicago and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3, tools=[get_weather, get_time]) # Multiple tools + print(f"Assistant: {result3}\n") + + +async def mixed_tools_example() -> None: + """Example showing both agent-level tools and run-method tools.""" + print("=== Mixed Tools Example (Agent + Run Method) ===") + + # Agent created with some base tools + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a comprehensive assistant that can help with various information requests.", + tools=[get_weather], # Base tool available for all queries + ) as agent: + # Query using both agent tool and additional run-method tools + query = "What's the weather in Denver and what's the current UTC time?" + print(f"User: {query}") + + # Agent has access to get_weather (from creation) + additional tools from run method + result = await agent.run( + query, + tools=[get_time], # Additional tools for this specific query + ) + print(f"Assistant: {result}\n") + + +async def main() -> None: + print("=== Foundry Chat Client Agent with Function Tools Examples ===\n") + + await tools_on_agent_level() + await tools_on_run_level() + await mixed_tools_example() + + +if __name__ == "__main__": + asyncio.run(main()) From 0bfeb0f38e37fc0c51d0cf0280b8c9a3435da646 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:59:19 -0700 Subject: [PATCH 05/14] Small rename --- .../{foundry_with_tools.py => foundry_with_function_tools.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/samples/getting_started/agents/foundry/{foundry_with_tools.py => foundry_with_function_tools.py} (100%) diff --git a/python/samples/getting_started/agents/foundry/foundry_with_tools.py b/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/foundry/foundry_with_tools.py rename to python/samples/getting_started/agents/foundry/foundry_with_function_tools.py From faeb6d5b50c145eeb560af6ef071cdf17668aee5 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:32:02 -0700 Subject: [PATCH 06/14] Added code interpreter example --- .../packages/main/agent_framework/_tools.py | 34 ++++++++++++++++++- .../foundry/foundry_with_code_interpreter.py | 25 ++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py diff --git a/python/packages/main/agent_framework/_tools.py b/python/packages/main/agent_framework/_tools.py index 94012b28b4d..378861c7247 100644 --- a/python/packages/main/agent_framework/_tools.py +++ b/python/packages/main/agent_framework/_tools.py @@ -7,7 +7,7 @@ from pydantic import BaseModel, create_model -__all__ = ["AIFunction", "AITool", "ai_function"] +__all__ = ["AIFunction", "AITool", "HostedCodeInterpreterTool", "ai_function"] @runtime_checkable @@ -159,3 +159,35 @@ def wrapper(f: Callable[..., ReturnT | Awaitable[ReturnT]]) -> AIFunction[Any, R return wrapper(func) return decorator(func) if func else decorator # type: ignore[reportReturnType, return-value] + + +class HostedCodeInterpreterTool(AITool): + """Represents a hosted tool that can be specified to an AI service to enable it to execute generated code. + + This tool does not implement code interpretation itself. It serves as a marker to inform a service + that it is allowed to execute generated code if the service is capable of doing so. + """ + + def __init__( + self, + name: str = "code_interpreter", + description: str | None = None, + additional_properties: dict[str, Any] | None = None, + ): + """Initialize a HostedCodeInterpreterTool. + + Args: + name: The name of the tool. Defaults to "code_interpreter". + description: A description of the tool. + additional_properties: Additional properties associated with the tool. + """ + self.name = name + self.description = description + self.additional_properties = additional_properties + + def __str__(self) -> str: + """Return a string representation of the tool.""" + return f"HostedCodeInterpreterTool(name={self.name})" + + def parameters(self) -> dict[str, Any]: + return {} diff --git a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py new file mode 100644 index 00000000000..abc38483835 --- /dev/null +++ b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py @@ -0,0 +1,25 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio + +from agent_framework import ChatClientAgent, HostedCodeInterpreterTool +from agent_framework.foundry import FoundryChatClient + + +async def main() -> None: + """Example showing how to use the HostedCodeInterpreterTool with Foundry.""" + print("=== Foundry Chat Client with Code Interpreter Example ===") + + async with ChatClientAgent( + chat_client=FoundryChatClient(), + instructions="You are a helpful assistant that can write and execute Python code to solve problems.", + tools=HostedCodeInterpreterTool(), + ) as agent: + query = "Calculate the fibonacci sequence for the first 10 numbers and plot them" + print(f"User: {query}") + result = await agent.run(query) + print(f"Assistant: {result}\n") + + +if __name__ == "__main__": + asyncio.run(main()) From ea5d0d51d9c03a929f43816d6d10e1b5bb1cfa43 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:06:19 -0700 Subject: [PATCH 07/14] Updated example --- .../agent_framework_foundry/_chat_client.py | 9 ++-- .../foundry/foundry_with_code_interpreter.py | 41 +++++++++++++++++-- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/foundry/agent_framework_foundry/_chat_client.py index 66e4e7db192..a3e42dea174 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/foundry/agent_framework_foundry/_chat_client.py @@ -19,6 +19,7 @@ DataContent, FunctionCallContent, FunctionResultContent, + HostedCodeInterpreterTool, TextContent, UriContent, UsageContent, @@ -34,6 +35,7 @@ AgentStreamEvent, AsyncAgentEventHandler, AsyncAgentRunStream, + CodeInterpreterToolDefinition, FunctionName, ListSortOrder, MessageDeltaChunk, @@ -477,14 +479,15 @@ def _create_run_options( run_options["parallel_tool_calls"] = chat_options.allow_multiple_tool_calls if chat_options.tools is not None: - # TODO (eavanvalkenburg): replace with _prepare_tools_and_tool_choice overload tool_definitions: list[MutableMapping[str, Any]] = [] for tool in chat_options.tools: if isinstance(tool, AIFunction): tool_definitions.append(ai_function_to_json_schema_spec(tool)) - else: - tool_definitions.append(tool) # type: ignore + elif isinstance(tool, HostedCodeInterpreterTool): + tool_definitions.append(CodeInterpreterToolDefinition()) + elif isinstance(tool, MutableMapping): + tool_definitions.append(tool) if len(tool_definitions) > 0: run_options["tools"] = tool_definitions diff --git a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py index abc38483835..493990b45d4 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py +++ b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py @@ -2,8 +2,33 @@ import asyncio -from agent_framework import ChatClientAgent, HostedCodeInterpreterTool +from agent_framework import AgentRunResponseUpdate, ChatClientAgent, HostedCodeInterpreterTool from agent_framework.foundry import FoundryChatClient +from azure.ai.agents.models import ( + RunStepDelta, + RunStepDeltaChunk, + RunStepDeltaCodeInterpreterDetailItemObject, + RunStepDeltaCodeInterpreterToolCall, + RunStepDeltaToolCallObject, +) + + +def get_code_interpreter_chunk(chunk: AgentRunResponseUpdate) -> str | None: + """Helper method to access code interpreter data.""" + if ( + isinstance(chunk.raw_representation, RunStepDeltaChunk) + and isinstance(chunk.raw_representation.delta, RunStepDelta) + and isinstance(chunk.raw_representation.delta.step_details, RunStepDeltaToolCallObject) + and chunk.raw_representation.delta.step_details.tool_calls + ): + for tool_call in chunk.raw_representation.delta.step_details.tool_calls: + if ( + isinstance(tool_call, RunStepDeltaCodeInterpreterToolCall) + and isinstance(tool_call.code_interpreter, RunStepDeltaCodeInterpreterDetailItemObject) + and tool_call.code_interpreter.input is not None + ): + return tool_call.code_interpreter.input + return None async def main() -> None: @@ -15,10 +40,18 @@ async def main() -> None: instructions="You are a helpful assistant that can write and execute Python code to solve problems.", tools=HostedCodeInterpreterTool(), ) as agent: - query = "Calculate the fibonacci sequence for the first 10 numbers and plot them" + query = "What is current datetime?" print(f"User: {query}") - result = await agent.run(query) - print(f"Assistant: {result}\n") + print("Assistant: ", end="", flush=True) + generated_code = "" + async for chunk in agent.run_stream(query): + if chunk.text: + print(chunk.text, end="", flush=True) + code_interpreter_chunk = get_code_interpreter_chunk(chunk) + if code_interpreter_chunk is not None: + generated_code += code_interpreter_chunk + + print(f"\nGenerated code:\n{generated_code}") if __name__ == "__main__": From 622758017cdb592fbaeb093105f116d92065a352 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:11:30 -0700 Subject: [PATCH 08/14] Addressed PR feedback --- .../foundry/agent_framework_foundry/_chat_client.py | 8 +++++++- python/packages/main/agent_framework/_tools.py | 9 +-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/python/packages/foundry/agent_framework_foundry/_chat_client.py b/python/packages/foundry/agent_framework_foundry/_chat_client.py index a3e42dea174..cd9b35332ac 100644 --- a/python/packages/foundry/agent_framework_foundry/_chat_client.py +++ b/python/packages/foundry/agent_framework_foundry/_chat_client.py @@ -2,6 +2,7 @@ import contextlib import json +import sys from collections.abc import AsyncIterable, MutableMapping, MutableSequence from typing import Any, ClassVar @@ -58,6 +59,11 @@ from azure.core.credentials_async import AsyncTokenCredential from pydantic import Field, PrivateAttr, ValidationError +if sys.version_info >= (3, 11): + from typing import Self # pragma: no cover +else: + from typing_extensions import Self # pragma: no cover + class FoundrySettings(AFBaseSettings): """Foundry model settings. @@ -170,7 +176,7 @@ def __init__( self._should_close_credential = should_close_credential self._foundry_settings = foundry_settings - async def __aenter__(self) -> "FoundryChatClient": + async def __aenter__(self) -> "Self": """Async context manager entry.""" return self diff --git a/python/packages/main/agent_framework/_tools.py b/python/packages/main/agent_framework/_tools.py index 378861c7247..1a19492ef06 100644 --- a/python/packages/main/agent_framework/_tools.py +++ b/python/packages/main/agent_framework/_tools.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft. All rights reserved. import inspect -from collections.abc import Awaitable, Callable, Mapping +from collections.abc import Awaitable, Callable from functools import wraps from typing import Any, Generic, Protocol, TypeVar, runtime_checkable @@ -34,10 +34,6 @@ def __str__(self) -> str: """Return a string representation of the tool.""" ... - def parameters(self) -> Mapping[str, Any]: - """Return the parameters of the tool as a JSON schema.""" - ... - ArgsT = TypeVar("ArgsT", bound=BaseModel) ReturnT = TypeVar("ReturnT") @@ -188,6 +184,3 @@ def __init__( def __str__(self) -> str: """Return a string representation of the tool.""" return f"HostedCodeInterpreterTool(name={self.name})" - - def parameters(self) -> dict[str, Any]: - return {} From 78f97d916aa5423ba8743426d98391d63f1f7439 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:41:11 -0700 Subject: [PATCH 09/14] Added more OpenAI and Azure chat completion examples --- .../packages/main/agent_framework/_agents.py | 43 ++++-- .../agents/azure/azure_chat_client_agent.py | 27 ---- .../azure_chat_completion/azure_basic.py | 65 ++++++++ .../azure_with_function_tools.py | 120 +++++++++++++++ .../azure_with_thread.py | 139 ++++++++++++++++++ .../agents/foundry/foundry_basic.py | 4 +- .../foundry/foundry_with_code_interpreter.py | 2 +- .../foundry/foundry_with_function_tools.py | 14 +- .../agents/foundry/foundry_with_thread.py | 16 +- .../agents/openai/openai_chat_client_agent.py | 27 ---- .../openai_chat_completion/openai_basic.py | 63 ++++++++ .../openai_with_function_tools.py | 120 +++++++++++++++ .../openai_with_thread.py | 139 ++++++++++++++++++ .../chat_client/azure_chat_client.py | 4 +- .../chat_client/foundry_chat_client.py | 4 +- .../chat_client/openai_chat_client.py | 4 +- 16 files changed, 698 insertions(+), 93 deletions(-) delete mode 100644 python/samples/getting_started/agents/azure/azure_chat_client_agent.py create mode 100644 python/samples/getting_started/agents/azure_chat_completion/azure_basic.py create mode 100644 python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py create mode 100644 python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py delete mode 100644 python/samples/getting_started/agents/openai/openai_chat_client_agent.py create mode 100644 python/samples/getting_started/agents/openai_chat_completion/openai_basic.py create mode 100644 python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py create mode 100644 python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py diff --git a/python/packages/main/agent_framework/_agents.py b/python/packages/main/agent_framework/_agents.py index e84905a3577..30c98fc1e5f 100644 --- a/python/packages/main/agent_framework/_agents.py +++ b/python/packages/main/agent_framework/_agents.py @@ -441,9 +441,11 @@ async def run( kwargs: Additional keyword arguments for the agent. will only be passed to functions that are called. """ + input_messages = self._normalize_messages(messages) + thread, thread_messages = await self._prepare_thread_and_messages( thread=thread, - input_messages=messages, + input_messages=input_messages, construct_thread=lambda: ChatClientAgentThread(), expected_type=ChatClientAgentThread, ) @@ -477,7 +479,7 @@ async def run( # Only notify the thread of new messages if the chatResponse was successful # to avoid inconsistent messages state in the thread. - await self._notify_thread_of_new_messages(thread, thread_messages) + await self._notify_thread_of_new_messages(thread, input_messages) await self._notify_thread_of_new_messages(thread, response.messages) return AgentRunResponse( @@ -549,9 +551,11 @@ async def run_stream( will only be passed to functions that are called. """ + input_messages = self._normalize_messages(messages) + thread, thread_messages = await self._prepare_thread_and_messages( thread=thread, - input_messages=messages, + input_messages=input_messages, construct_thread=lambda: ChatClientAgentThread(), expected_type=ChatClientAgentThread, ) @@ -600,7 +604,7 @@ async def run_stream( # Only notify the thread of new messages if the chatResponse was successful # to avoid inconsistent messages state in the thread. - await self._notify_thread_of_new_messages(thread, thread_messages) + await self._notify_thread_of_new_messages(thread, input_messages) await self._notify_thread_of_new_messages(thread, response.messages) def get_new_thread(self) -> AgentThread: @@ -639,7 +643,7 @@ async def _prepare_thread_and_messages( self, *, thread: AgentThread | None, - input_messages: str | ChatMessage | Sequence[str] | Sequence[ChatMessage] | None = None, + input_messages: list[ChatMessage] | None = None, construct_thread: Callable[[], TThreadType], expected_type: type[TThreadType], ) -> tuple[TThreadType, list[ChatMessage]]: @@ -647,7 +651,7 @@ async def _prepare_thread_and_messages( Args: thread: The conversation thread, or None to create a new one. - input_messages: Messages to process, can be string, ChatMessage, or sequence. + input_messages: Messages to process. construct_thread: Factory function to create a new thread. expected_type: Expected thread type for validation. @@ -673,15 +677,24 @@ async def _prepare_thread_and_messages( if isinstance(thread, MessagesRetrievableThread): async for message in thread.get_messages(): messages.append(message) + if input_messages is None: return thread, messages - if isinstance(input_messages, str): - messages.append(ChatMessage(role=ChatRole.USER, text=input_messages)) - return thread, messages - if isinstance(input_messages, ChatMessage): - messages.append(input_messages) - return thread, messages - messages.extend([ - ChatMessage(role=ChatRole.USER, text=msg) if isinstance(msg, str) else msg for msg in input_messages - ]) + + messages.extend(input_messages) return thread, messages + + def _normalize_messages( + self, + messages: str | ChatMessage | Sequence[str] | Sequence[ChatMessage] | None = None, + ) -> list[ChatMessage]: + if messages is None: + return [] + + if isinstance(messages, str): + return [ChatMessage(role=ChatRole.USER, text=messages)] + + if isinstance(messages, ChatMessage): + return [messages] + + return [ChatMessage(role=ChatRole.USER, text=msg) if isinstance(msg, str) else msg for msg in messages] diff --git a/python/samples/getting_started/agents/azure/azure_chat_client_agent.py b/python/samples/getting_started/agents/azure/azure_chat_client_agent.py deleted file mode 100644 index b35689beb76..00000000000 --- a/python/samples/getting_started/agents/azure/azure_chat_client_agent.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import asyncio -from random import randint -from typing import Annotated - -from agent_framework import ChatClientAgent -from agent_framework.azure import AzureChatClient -from pydantic import Field - - -def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: - """Get the weather for a given location.""" - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." - - -async def main() -> None: - instructions = "You are a helpful assistant, you can help the user with weather information." - agent = ChatClientAgent(AzureChatClient(), instructions=instructions, tools=get_weather) - print(str(await agent.run("What's the weather in Amsterdam?"))) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_basic.py b/python/samples/getting_started/agents/azure_chat_completion/azure_basic.py new file mode 100644 index 00000000000..21a8a25b27f --- /dev/null +++ b/python/samples/getting_started/agents/azure_chat_completion/azure_basic.py @@ -0,0 +1,65 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent +from agent_framework.azure import AzureChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def non_streaming_example() -> None: + """Example of non-streaming response (get the complete result at once).""" + print("=== Non-streaming Response Example ===") + + # Create agent with Azure Chat Client + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Result: {result}\n") + + +async def streaming_example() -> None: + """Example of streaming response (get results as they are generated).""" + print("=== Streaming Response Example ===") + + # Create agent with Azure Chat Client + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + query = "What's the weather like in Portland?" + print(f"User: {query}") + print("Agent: ", end="", flush=True) + async for chunk in agent.run_stream(query): + if chunk.text: + print(chunk.text, end="", flush=True) + print("\n") + + +async def main() -> None: + print("=== Basic Azure Chat Client Agent Example ===") + + await non_streaming_example() + await streaming_example() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py b/python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py new file mode 100644 index 00000000000..7050c75f4e9 --- /dev/null +++ b/python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py @@ -0,0 +1,120 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from datetime import datetime, timezone +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent +from agent_framework.azure import AzureChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +def get_time() -> str: + """Get the current UTC time.""" + current_time = datetime.now(timezone.utc) + return f"The current UTC time is {current_time.strftime('%Y-%m-%d %H:%M:%S')}." + + +async def tools_on_agent_level() -> None: + """Example showing tools defined when creating the agent.""" + print("=== Tools Defined on Agent Level ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful assistant that can provide weather and time information.", + tools=[get_weather, get_time], # Tools defined at agent creation + ) + + # First query - agent can use weather tool + query1 = "What's the weather like in New York?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Agent: {result1}\n") + + # Second query - agent can use time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2) + print(f"Agent: {result2}\n") + + # Third query - agent can use both tools if needed + query3 = "What's the weather in London and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3) + print(f"Agent: {result3}\n") + + +async def tools_on_run_level() -> None: + """Example showing tools passed to the run method.""" + print("=== Tools Passed to Run Method ===") + + # Agent created without tools + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful assistant.", + # No tools defined here + ) + + # First query with weather tool + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1, tools=[get_weather]) # Tool passed to run method + print(f"Agent: {result1}\n") + + # Second query with time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2, tools=[get_time]) # Different tool for this query + print(f"Agent: {result2}\n") + + # Third query with multiple tools + query3 = "What's the weather in Chicago and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3, tools=[get_weather, get_time]) # Multiple tools + print(f"Agent: {result3}\n") + + +async def mixed_tools_example() -> None: + """Example showing both agent-level tools and run-method tools.""" + print("=== Mixed Tools Example (Agent + Run Method) ===") + + # Agent created with some base tools + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a comprehensive assistant that can help with various information requests.", + tools=[get_weather], # Base tool available for all queries + ) + + # Query using both agent tool and additional run-method tools + query = "What's the weather in Denver and what's the current UTC time?" + print(f"User: {query}") + + # Agent has access to get_weather (from creation) + additional tools from run method + result = await agent.run( + query, + tools=[get_time], # Additional tools for this specific query + ) + print(f"Agent: {result}\n") + + +async def main() -> None: + print("=== Azure Chat Client Agent with Function Tools Examples ===\n") + + await tools_on_agent_level() + await tools_on_run_level() + await mixed_tools_example() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py b/python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py new file mode 100644 index 00000000000..85aba7d3ab1 --- /dev/null +++ b/python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py @@ -0,0 +1,139 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent, ChatClientAgentThread +from agent_framework.azure import AzureChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def example_with_automatic_thread_creation() -> None: + """Example showing automatic thread creation (service-managed thread).""" + print("=== Automatic Thread Creation Example ===") + + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # First conversation - no thread provided, will be created automatically + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Agent: {result1.text}") + + # Second conversation - still no thread provided, will create another new thread + query2 = "What was the last city I asked about?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2) + print(f"Agent: {result2.text}") + print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n") + + +async def example_with_thread_persistence() -> None: + """Example showing thread persistence across multiple conversations.""" + print("=== Thread Persistence Example ===") + print("Using the same thread across multiple conversations to maintain context.\n") + + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Create a new thread that will be reused + thread = agent.get_new_thread() + + # First conversation + query1 = "What's the weather like in Tokyo?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Agent: {result1.text}") + + # Second conversation using the same thread - maintains context + query2 = "How about comparing it to London?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2, thread=thread) + print(f"Agent: {result2.text}") + + # Third conversation - agent should remember both previous cities + query3 = "Which of the cities I asked about has better weather?" + print(f"\nUser: {query3}") + result3 = await agent.run(query3, thread=thread) + print(f"Agent: {result3.text}") + print("Note: The agent remembers context from previous messages in the same thread.\n") + + +async def example_with_existing_thread_messages() -> None: + """Example showing how to work with existing thread messages for Azure.""" + print("=== Existing Thread Messages Example ===") + + agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Start a conversation and build up message history + thread = agent.get_new_thread() + assert isinstance(thread, ChatClientAgentThread) # Ensure we have the right type + + query1 = "What's the weather in Paris?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Agent: {result1.text}") + + # The thread now contains the conversation history in memory + message_count = len(thread.chat_messages or []) + print(f"Thread contains {message_count} messages") + + print("\n--- Continuing with the same thread in a new agent instance ---") + + # Create a new agent instance but use the existing thread with its message history + new_agent = ChatClientAgent( + chat_client=AzureChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Use the same thread object which contains the conversation history + query2 = "What was the last city I asked about?" + print(f"User: {query2}") + result2 = await new_agent.run(query2, thread=thread) + print(f"Agent: {result2.text}") + print("Note: The agent continues the conversation using the local message history.\n") + + print("\n--- Alternative: Creating a new thread from existing messages ---") + + # You can also create a new thread from existing messages + existing_messages = thread.chat_messages or [] + new_thread = ChatClientAgentThread(messages=existing_messages) + + query3 = "How does the Paris weather compare to London?" + print(f"User: {query3}") + result3 = await new_agent.run(query3, thread=new_thread) + print(f"Agent: {result3.text}") + print("Note: This creates a new thread with the same conversation history.\n") + + +async def main() -> None: + print("=== Azure Chat Client Agent Thread Management Examples ===\n") + + # await example_with_automatic_thread_creation() + # await example_with_thread_persistence() + await example_with_existing_thread_messages() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/agents/foundry/foundry_basic.py b/python/samples/getting_started/agents/foundry/foundry_basic.py index 5df4b8e0891..bfcbed40ba6 100644 --- a/python/samples/getting_started/agents/foundry/foundry_basic.py +++ b/python/samples/getting_started/agents/foundry/foundry_basic.py @@ -47,7 +47,7 @@ async def streaming_example() -> None: ) as agent: query = "What's the weather like in Portland?" print(f"User: {query}") - print("Assistant: ", end="", flush=True) + print("Agent: ", end="", flush=True) async for chunk in agent.run_stream(query): if chunk.text: print(chunk.text, end="", flush=True) @@ -55,7 +55,7 @@ async def streaming_example() -> None: async def main() -> None: - print("=== Basic Foundry Chat Client Example ===") + print("=== Basic Foundry Chat Client Agent Example ===") await non_streaming_example() await streaming_example() diff --git a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py index 493990b45d4..1c4fedb7750 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py +++ b/python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py @@ -42,7 +42,7 @@ async def main() -> None: ) as agent: query = "What is current datetime?" print(f"User: {query}") - print("Assistant: ", end="", flush=True) + print("Agent: ", end="", flush=True) generated_code = "" async for chunk in agent.run_stream(query): if chunk.text: diff --git a/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py b/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py index 9583750d175..5d3fc42c2ee 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py +++ b/python/samples/getting_started/agents/foundry/foundry_with_function_tools.py @@ -39,19 +39,19 @@ async def tools_on_agent_level() -> None: query1 = "What's the weather like in New York?" print(f"User: {query1}") result1 = await agent.run(query1) - print(f"Assistant: {result1}\n") + print(f"Agent: {result1}\n") # Second query - agent can use time tool query2 = "What's the current UTC time?" print(f"User: {query2}") result2 = await agent.run(query2) - print(f"Assistant: {result2}\n") + print(f"Agent: {result2}\n") # Third query - agent can use both tools if needed query3 = "What's the weather in London and what's the current UTC time?" print(f"User: {query3}") result3 = await agent.run(query3) - print(f"Assistant: {result3}\n") + print(f"Agent: {result3}\n") async def tools_on_run_level() -> None: @@ -68,19 +68,19 @@ async def tools_on_run_level() -> None: query1 = "What's the weather like in Seattle?" print(f"User: {query1}") result1 = await agent.run(query1, tools=[get_weather]) # Tool passed to run method - print(f"Assistant: {result1}\n") + print(f"Agent: {result1}\n") # Second query with time tool query2 = "What's the current UTC time?" print(f"User: {query2}") result2 = await agent.run(query2, tools=[get_time]) # Different tool for this query - print(f"Assistant: {result2}\n") + print(f"Agent: {result2}\n") # Third query with multiple tools query3 = "What's the weather in Chicago and what's the current UTC time?" print(f"User: {query3}") result3 = await agent.run(query3, tools=[get_weather, get_time]) # Multiple tools - print(f"Assistant: {result3}\n") + print(f"Agent: {result3}\n") async def mixed_tools_example() -> None: @@ -102,7 +102,7 @@ async def mixed_tools_example() -> None: query, tools=[get_time], # Additional tools for this specific query ) - print(f"Assistant: {result}\n") + print(f"Agent: {result}\n") async def main() -> None: diff --git a/python/samples/getting_started/agents/foundry/foundry_with_thread.py b/python/samples/getting_started/agents/foundry/foundry_with_thread.py index 8bd0e591f19..a34a32a420e 100644 --- a/python/samples/getting_started/agents/foundry/foundry_with_thread.py +++ b/python/samples/getting_started/agents/foundry/foundry_with_thread.py @@ -30,13 +30,13 @@ async def example_with_automatic_thread_creation() -> None: query1 = "What's the weather like in Seattle?" print(f"User: {query1}") result1 = await agent.run(query1) - print(f"Assistant: {result1.text}") + print(f"Agent: {result1.text}") # Second conversation - still no thread provided, will create another new thread query2 = "What was the last city I asked about?" print(f"\nUser: {query2}") result2 = await agent.run(query2) - print(f"Assistant: {result2.text}") + print(f"Agent: {result2.text}") print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n") @@ -47,7 +47,7 @@ async def example_with_thread_persistence() -> None: async with ChatClientAgent( chat_client=FoundryChatClient(), - instructions="You are a helpful weather agent. Remember previous cities asked about.", + instructions="You are a helpful weather agent.", tools=get_weather, ) as agent: # Create a new thread that will be reused @@ -57,19 +57,19 @@ async def example_with_thread_persistence() -> None: query1 = "What's the weather like in Tokyo?" print(f"User: {query1}") result1 = await agent.run(query1, thread=thread) - print(f"Assistant: {result1.text}") + print(f"Agent: {result1.text}") # Second conversation using the same thread - maintains context query2 = "How about comparing it to London?" print(f"\nUser: {query2}") result2 = await agent.run(query2, thread=thread) - print(f"Assistant: {result2.text}") + print(f"Agent: {result2.text}") # Third conversation - agent should remember both previous cities query3 = "Which of the cities I asked about has better weather?" print(f"\nUser: {query3}") result3 = await agent.run(query3, thread=thread) - print(f"Assistant: {result3.text}") + print(f"Agent: {result3.text}") print("Note: The agent remembers context from previous messages in the same thread.\n") @@ -91,7 +91,7 @@ async def example_with_existing_thread_id() -> None: query1 = "What's the weather in Paris?" print(f"User: {query1}") result1 = await agent.run(query1, thread=thread) - print(f"Assistant: {result1.text}") + print(f"Agent: {result1.text}") # The thread ID is set after the first response existing_thread_id = thread.id @@ -112,7 +112,7 @@ async def example_with_existing_thread_id() -> None: query2 = "What was the last city I asked about?" print(f"User: {query2}") result2 = await agent.run(query2, thread=thread) - print(f"Assistant: {result2.text}") + print(f"Agent: {result2.text}") print("Note: The agent continues the conversation from the previous thread.\n") diff --git a/python/samples/getting_started/agents/openai/openai_chat_client_agent.py b/python/samples/getting_started/agents/openai/openai_chat_client_agent.py deleted file mode 100644 index d5c7512d7cd..00000000000 --- a/python/samples/getting_started/agents/openai/openai_chat_client_agent.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) Microsoft. All rights reserved. - -import asyncio -from random import randint -from typing import Annotated - -from agent_framework import ChatClientAgent -from agent_framework.openai import OpenAIChatClient -from pydantic import Field - - -def get_weather( - location: Annotated[str, Field(description="The location to get the weather for.")], -) -> str: - """Get the weather for a given location.""" - conditions = ["sunny", "cloudy", "rainy", "stormy"] - return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." - - -async def main() -> None: - instructions = "You are a helpful assistant, you can help the user with weather information." - agent = ChatClientAgent(OpenAIChatClient(), instructions=instructions, tools=get_weather) - print(str(await agent.run("What's the weather in Amsterdam?"))) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_basic.py b/python/samples/getting_started/agents/openai_chat_completion/openai_basic.py new file mode 100644 index 00000000000..01425caba24 --- /dev/null +++ b/python/samples/getting_started/agents/openai_chat_completion/openai_basic.py @@ -0,0 +1,63 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent +from agent_framework.openai import OpenAIChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def non_streaming_example() -> None: + """Example of non-streaming response (get the complete result at once).""" + print("=== Non-streaming Response Example ===") + + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + query = "What's the weather like in Seattle?" + print(f"User: {query}") + result = await agent.run(query) + print(f"Result: {result}\n") + + +async def streaming_example() -> None: + """Example of streaming response (get results as they are generated).""" + print("=== Streaming Response Example ===") + + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + query = "What's the weather like in Portland?" + print(f"User: {query}") + print("Agent: ", end="", flush=True) + async for chunk in agent.run_stream(query): + if chunk.text: + print(chunk.text, end="", flush=True) + print("\n") + + +async def main() -> None: + print("=== Basic OpenAI Chat Client Agent Example ===") + + await non_streaming_example() + await streaming_example() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py b/python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py new file mode 100644 index 00000000000..068f505c8e9 --- /dev/null +++ b/python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py @@ -0,0 +1,120 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from datetime import datetime, timezone +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent +from agent_framework.openai import OpenAIChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +def get_time() -> str: + """Get the current UTC time.""" + current_time = datetime.now(timezone.utc) + return f"The current UTC time is {current_time.strftime('%Y-%m-%d %H:%M:%S')}." + + +async def tools_on_agent_level() -> None: + """Example showing tools defined when creating the agent.""" + print("=== Tools Defined on Agent Level ===") + + # Tools are provided when creating the agent + # The agent can use these tools for any query during its lifetime + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful assistant that can provide weather and time information.", + tools=[get_weather, get_time], # Tools defined at agent creation + ) + + # First query - agent can use weather tool + query1 = "What's the weather like in New York?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Agent: {result1}\n") + + # Second query - agent can use time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2) + print(f"Agent: {result2}\n") + + # Third query - agent can use both tools if needed + query3 = "What's the weather in London and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3) + print(f"Agent: {result3}\n") + + +async def tools_on_run_level() -> None: + """Example showing tools passed to the run method.""" + print("=== Tools Passed to Run Method ===") + + # Agent created without tools + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful assistant.", + # No tools defined here + ) + + # First query with weather tool + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1, tools=[get_weather]) # Tool passed to run method + print(f"Agent: {result1}\n") + + # Second query with time tool + query2 = "What's the current UTC time?" + print(f"User: {query2}") + result2 = await agent.run(query2, tools=[get_time]) # Different tool for this query + print(f"Agent: {result2}\n") + + # Third query with multiple tools + query3 = "What's the weather in Chicago and what's the current UTC time?" + print(f"User: {query3}") + result3 = await agent.run(query3, tools=[get_weather, get_time]) # Multiple tools + print(f"Agent: {result3}\n") + + +async def mixed_tools_example() -> None: + """Example showing both agent-level tools and run-method tools.""" + print("=== Mixed Tools Example (Agent + Run Method) ===") + + # Agent created with some base tools + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a comprehensive assistant that can help with various information requests.", + tools=[get_weather], # Base tool available for all queries + ) + + # Query using both agent tool and additional run-method tools + query = "What's the weather in Denver and what's the current UTC time?" + print(f"User: {query}") + + # Agent has access to get_weather (from creation) + additional tools from run method + result = await agent.run( + query, + tools=[get_time], # Additional tools for this specific query + ) + print(f"Agent: {result}\n") + + +async def main() -> None: + print("=== OpenAI Chat Client Agent with Function Tools Examples ===\n") + + await tools_on_agent_level() + await tools_on_run_level() + await mixed_tools_example() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py b/python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py new file mode 100644 index 00000000000..92bd7e113c4 --- /dev/null +++ b/python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py @@ -0,0 +1,139 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +from random import randint +from typing import Annotated + +from agent_framework import ChatClientAgent, ChatClientAgentThread +from agent_framework.openai import OpenAIChatClient +from pydantic import Field + + +def get_weather( + location: Annotated[str, Field(description="The location to get the weather for.")], +) -> str: + """Get the weather for a given location.""" + conditions = ["sunny", "cloudy", "rainy", "stormy"] + return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." + + +async def example_with_automatic_thread_creation() -> None: + """Example showing automatic thread creation (service-managed thread).""" + print("=== Automatic Thread Creation Example ===") + + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # First conversation - no thread provided, will be created automatically + query1 = "What's the weather like in Seattle?" + print(f"User: {query1}") + result1 = await agent.run(query1) + print(f"Agent: {result1.text}") + + # Second conversation - still no thread provided, will create another new thread + query2 = "What was the last city I asked about?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2) + print(f"Agent: {result2.text}") + print("Note: Each call creates a separate thread, so the agent doesn't remember previous context.\n") + + +async def example_with_thread_persistence() -> None: + """Example showing thread persistence across multiple conversations.""" + print("=== Thread Persistence Example ===") + print("Using the same thread across multiple conversations to maintain context.\n") + + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Create a new thread that will be reused + thread = agent.get_new_thread() + + # First conversation + query1 = "What's the weather like in Tokyo?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Agent: {result1.text}") + + # Second conversation using the same thread - maintains context + query2 = "How about comparing it to London?" + print(f"\nUser: {query2}") + result2 = await agent.run(query2, thread=thread) + print(f"Agent: {result2.text}") + + # Third conversation - agent should remember both previous cities + query3 = "Which of the cities I asked about has better weather?" + print(f"\nUser: {query3}") + result3 = await agent.run(query3, thread=thread) + print(f"Agent: {result3.text}") + print("Note: The agent remembers context from previous messages in the same thread.\n") + + +async def example_with_existing_thread_messages() -> None: + """Example showing how to work with existing thread messages for OpenAI.""" + print("=== Existing Thread Messages Example ===") + + agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Start a conversation and build up message history + thread = agent.get_new_thread() + assert isinstance(thread, ChatClientAgentThread) # Ensure we have the right type + + query1 = "What's the weather in Paris?" + print(f"User: {query1}") + result1 = await agent.run(query1, thread=thread) + print(f"Agent: {result1.text}") + + # The thread now contains the conversation history in memory + message_count = len(thread.chat_messages or []) + print(f"Thread contains {message_count} messages") + + print("\n--- Continuing with the same thread in a new agent instance ---") + + # Create a new agent instance but use the existing thread with its message history + new_agent = ChatClientAgent( + chat_client=OpenAIChatClient(), + instructions="You are a helpful weather agent.", + tools=get_weather, + ) + + # Use the same thread object which contains the conversation history + query2 = "What was the last city I asked about?" + print(f"User: {query2}") + result2 = await new_agent.run(query2, thread=thread) + print(f"Agent: {result2.text}") + print("Note: The agent continues the conversation using the local message history.\n") + + print("\n--- Alternative: Creating a new thread from existing messages ---") + + # You can also create a new thread from existing messages + existing_messages = thread.chat_messages or [] + new_thread = ChatClientAgentThread(messages=existing_messages) + + query3 = "How does the Paris weather compare to London?" + print(f"User: {query3}") + result3 = await new_agent.run(query3, thread=new_thread) + print(f"Agent: {result3.text}") + print("Note: This creates a new thread with the same conversation history.\n") + + +async def main() -> None: + print("=== OpenAI Chat Client Agent Thread Management Examples ===\n") + + await example_with_automatic_thread_creation() + await example_with_thread_persistence() + await example_with_existing_thread_messages() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/getting_started/chat_client/azure_chat_client.py b/python/samples/getting_started/chat_client/azure_chat_client.py index dc7ea4069ca..64aa5ae6b61 100644 --- a/python/samples/getting_started/chat_client/azure_chat_client.py +++ b/python/samples/getting_started/chat_client/azure_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Assistant: ", end="") + print("Agent: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Assistant: {response}") + print(f"Agent: {response}") if __name__ == "__main__": diff --git a/python/samples/getting_started/chat_client/foundry_chat_client.py b/python/samples/getting_started/chat_client/foundry_chat_client.py index 3cfb157bcee..6f9c5498002 100644 --- a/python/samples/getting_started/chat_client/foundry_chat_client.py +++ b/python/samples/getting_started/chat_client/foundry_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Assistant: ", end="") + print("Agent: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Assistant: {response}") + print(f"Agent: {response}") if __name__ == "__main__": diff --git a/python/samples/getting_started/chat_client/openai_chat_client.py b/python/samples/getting_started/chat_client/openai_chat_client.py index 183a1f61d4a..ad1c1516c41 100644 --- a/python/samples/getting_started/chat_client/openai_chat_client.py +++ b/python/samples/getting_started/chat_client/openai_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Assistant: ", end="") + print("Agent: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Assistant: {response}") + print(f"Agent: {response}") if __name__ == "__main__": From eb4152b57ebb559f84a41967c0419ea5ef9c934e Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:42:48 -0700 Subject: [PATCH 10/14] File renaming --- .../{azure_basic.py => azure_chat_completion_basic.py} | 0 ...tion_tools.py => azure_chat_completion_with_function_tools.py} | 0 ...{azure_with_thread.py => azure_chat_completion_with_thread.py} | 0 .../{openai_basic.py => openai_chat_completion_basic.py} | 0 ...ion_tools.py => openai_chat_completion_with_function_tools.py} | 0 ...penai_with_thread.py => openai_chat_completion_with_thread.py} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename python/samples/getting_started/agents/azure_chat_completion/{azure_basic.py => azure_chat_completion_basic.py} (100%) rename python/samples/getting_started/agents/azure_chat_completion/{azure_with_function_tools.py => azure_chat_completion_with_function_tools.py} (100%) rename python/samples/getting_started/agents/azure_chat_completion/{azure_with_thread.py => azure_chat_completion_with_thread.py} (100%) rename python/samples/getting_started/agents/openai_chat_completion/{openai_basic.py => openai_chat_completion_basic.py} (100%) rename python/samples/getting_started/agents/openai_chat_completion/{openai_with_function_tools.py => openai_chat_completion_with_function_tools.py} (100%) rename python/samples/getting_started/agents/openai_chat_completion/{openai_with_thread.py => openai_chat_completion_with_thread.py} (100%) diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_basic.py b/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_basic.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_basic.py rename to python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_basic.py diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py b/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_with_function_tools.py rename to python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_function_tools.py diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py b/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_with_thread.py rename to python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_thread.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_basic.py b/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_basic.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_basic.py rename to python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_basic.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py b/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_with_function_tools.py rename to python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_function_tools.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py b/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_with_thread.py rename to python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_thread.py From a6a87c7ecf1595cc4ee9863f00d23ab4082f0e39 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:50:49 -0700 Subject: [PATCH 11/14] Small fix in tests --- python/packages/main/tests/test_agents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/packages/main/tests/test_agents.py b/python/packages/main/tests/test_agents.py index b6023b668f9..8bcb75a35a0 100644 --- a/python/packages/main/tests/test_agents.py +++ b/python/packages/main/tests/test_agents.py @@ -229,12 +229,12 @@ async def test_chat_client_agent_get_new_thread(chat_client: ChatClient) -> None async def test_chat_client_agent_prepare_thread_and_messages(chat_client: ChatClient) -> None: agent = ChatClientAgent(chat_client=chat_client) - message = ChatMessage(role=ChatRole.USER, contents=[TextContent("Hello")]) + message = ChatMessage(role=ChatRole.USER, text="Hello") thread = ChatClientAgentThread(messages=[message]) result_thread, result_messages = await agent._prepare_thread_and_messages( # type: ignore[reportPrivateUsage] thread=thread, - input_messages="Test", + input_messages=[ChatMessage(role=ChatRole.USER, text="Test")], construct_thread=lambda: ChatClientAgentThread(), expected_type=ChatClientAgentThread, ) From 3b0bb1fd447d13b84c2b6e7ee9ef09d70dcafbbe Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:56:37 -0700 Subject: [PATCH 12/14] Small revert --- .../samples/getting_started/chat_client/azure_chat_client.py | 4 ++-- .../getting_started/chat_client/foundry_chat_client.py | 4 ++-- .../samples/getting_started/chat_client/openai_chat_client.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/python/samples/getting_started/chat_client/azure_chat_client.py b/python/samples/getting_started/chat_client/azure_chat_client.py index 64aa5ae6b61..dc7ea4069ca 100644 --- a/python/samples/getting_started/chat_client/azure_chat_client.py +++ b/python/samples/getting_started/chat_client/azure_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Agent: ", end="") + print("Assistant: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Agent: {response}") + print(f"Assistant: {response}") if __name__ == "__main__": diff --git a/python/samples/getting_started/chat_client/foundry_chat_client.py b/python/samples/getting_started/chat_client/foundry_chat_client.py index 6f9c5498002..3cfb157bcee 100644 --- a/python/samples/getting_started/chat_client/foundry_chat_client.py +++ b/python/samples/getting_started/chat_client/foundry_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Agent: ", end="") + print("Assistant: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Agent: {response}") + print(f"Assistant: {response}") if __name__ == "__main__": diff --git a/python/samples/getting_started/chat_client/openai_chat_client.py b/python/samples/getting_started/chat_client/openai_chat_client.py index ad1c1516c41..183a1f61d4a 100644 --- a/python/samples/getting_started/chat_client/openai_chat_client.py +++ b/python/samples/getting_started/chat_client/openai_chat_client.py @@ -22,14 +22,14 @@ async def main() -> None: stream = False print(f"User: {message}") if stream: - print("Agent: ", end="") + print("Assistant: ", end="") async for chunk in client.get_streaming_response(message, tools=get_weather): if str(chunk): print(str(chunk), end="") print("") else: response = await client.get_response(message, tools=get_weather) - print(f"Agent: {response}") + print(f"Assistant: {response}") if __name__ == "__main__": From 84fffb7e5eeb570a24e95a1491f450d817eb9775 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:01:16 -0700 Subject: [PATCH 13/14] More renaming --- .../azure_chat_client_basic.py} | 0 .../azure_chat_client_with_function_tools.py} | 0 .../azure_chat_client_with_thread.py} | 0 .../openai_chat_client_basic.py} | 0 .../openai_chat_client_with_function_tools.py} | 0 .../openai_chat_client_with_thread.py} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename python/samples/getting_started/agents/{azure_chat_completion/azure_chat_completion_basic.py => azure_chat_client/azure_chat_client_basic.py} (100%) rename python/samples/getting_started/agents/{azure_chat_completion/azure_chat_completion_with_function_tools.py => azure_chat_client/azure_chat_client_with_function_tools.py} (100%) rename python/samples/getting_started/agents/{azure_chat_completion/azure_chat_completion_with_thread.py => azure_chat_client/azure_chat_client_with_thread.py} (100%) rename python/samples/getting_started/agents/{openai_chat_completion/openai_chat_completion_basic.py => openai_chat_client/openai_chat_client_basic.py} (100%) rename python/samples/getting_started/agents/{openai_chat_completion/openai_chat_completion_with_function_tools.py => openai_chat_client/openai_chat_client_with_function_tools.py} (100%) rename python/samples/getting_started/agents/{openai_chat_completion/openai_chat_completion_with_thread.py => openai_chat_client/openai_chat_client_with_thread.py} (100%) diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_basic.py b/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_basic.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_basic.py rename to python/samples/getting_started/agents/azure_chat_client/azure_chat_client_basic.py diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_function_tools.py b/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_function_tools.py rename to python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_function_tools.py diff --git a/python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_thread.py b/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/azure_chat_completion/azure_chat_completion_with_thread.py rename to python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_basic.py b/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_basic.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_basic.py rename to python/samples/getting_started/agents/openai_chat_client/openai_chat_client_basic.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_function_tools.py b/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_function_tools.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_function_tools.py rename to python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_function_tools.py diff --git a/python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_thread.py b/python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_thread.py similarity index 100% rename from python/samples/getting_started/agents/openai_chat_completion/openai_chat_completion_with_thread.py rename to python/samples/getting_started/agents/openai_chat_client/openai_chat_client_with_thread.py From fd2a513f6357948af0a845255bce05905b5c44d7 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 23 Jul 2025 16:03:46 -0700 Subject: [PATCH 14/14] Small fix --- .../agents/azure_chat_client/azure_chat_client_with_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py b/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py index 85aba7d3ab1..3f858814869 100644 --- a/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py +++ b/python/samples/getting_started/agents/azure_chat_client/azure_chat_client_with_thread.py @@ -130,8 +130,8 @@ async def example_with_existing_thread_messages() -> None: async def main() -> None: print("=== Azure Chat Client Agent Thread Management Examples ===\n") - # await example_with_automatic_thread_creation() - # await example_with_thread_persistence() + await example_with_automatic_thread_creation() + await example_with_thread_persistence() await example_with_existing_thread_messages()