Small fixes and basic example

This commit is contained in:
Dmytro Struk
2025-07-30 16:20:33 -07:00
Unverified
parent 278a2a5f80
commit 08b01483c3
4 changed files with 93 additions and 4 deletions
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
from ._assistants_client import * # noqa: F403
from ._chat_client import * # noqa: F403
from ._exceptions import * # noqa: F403
from ._responses_client import * # noqa: F403
@@ -1,6 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.
import json
import sys
from collections.abc import AsyncIterable, Mapping, MutableMapping, MutableSequence
from typing import Any, ClassVar
@@ -39,6 +40,13 @@ from .._types import (
from ..exceptions import ServiceInitializationError
from ._shared import OpenAIConfigBase, OpenAISettings
if sys.version_info >= (3, 11):
from typing import Self # pragma: no cover
else:
from typing_extensions import Self # pragma: no cover
__all__ = ["OpenAIAssistantsClient"]
@use_tool_calling
class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
@@ -112,6 +120,18 @@ class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
client=async_client,
)
async def __aenter__(self) -> "Self":
"""Async context manager entry."""
return self
async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: Any) -> None:
"""Async context manager exit - clean up any assistants we created."""
await self.close()
async def close(self) -> None:
"""Clean up any assistants we created."""
await self._cleanup_assistant_if_needed()
async def _inner_get_response(
self,
*,
@@ -236,7 +256,6 @@ class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
async with stream as response_stream:
async for response in response_stream:
if response.event == "thread.run.created":
response_id = response.data.id
yield ChatResponseUpdate(
contents=[],
conversation_id=thread_id,
@@ -245,6 +264,8 @@ class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
response_id=response_id,
role=ChatRole.ASSISTANT,
)
elif response.event == "thread.run.step.created" and isinstance(response.data, RunStep):
response_id = response.data.run_id
elif response.event == "thread.message.delta" and isinstance(response.data, MessageDeltaEvent):
delta = response.data.delta
role = ChatRole.USER if delta.role == "user" else ChatRole.ASSISTANT
@@ -272,7 +293,7 @@ class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
)
elif (
response.event == "thread.run.completed"
and isinstance(response.data, RunStep)
and isinstance(response.data, Run)
and response.data.usage is not None
):
usage = response.data.usage
@@ -334,7 +355,9 @@ class OpenAIAssistantsClient(OpenAIConfigBase, ChatClientBase):
run_options["model"] = chat_options.ai_model_id
run_options["top_p"] = chat_options.top_p
run_options["temperature"] = chat_options.temperature
run_options["parallel_tool_calls"] = chat_options.allow_multiple_tool_calls
if chat_options.allow_multiple_tool_calls is not None:
run_options["parallel_tool_calls"] = chat_options.allow_multiple_tool_calls
if chat_options.tool_choice is not None:
tool_definitions: list[MutableMapping[str, Any]] = []
@@ -31,7 +31,7 @@ async def non_streaming_example() -> None:
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Result: {result}\n")
print(f"Agent: {result}\n")
async def streaming_example() -> None:
@@ -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.openai import OpenAIAssistantsClient
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 ===")
# Since no assistant ID is provided, the assistant will be automatically created
# and deleted after getting a response
async with ChatClientAgent(
chat_client=OpenAIAssistantsClient(),
instructions="You are a helpful weather agent.",
tools=get_weather,
) as agent:
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
async def streaming_example() -> None:
"""Example of streaming response (get results as they are generated)."""
print("=== Streaming Response Example ===")
# Since no assistant ID is provided, the assistant will be automatically created
# and deleted after getting a response
async with ChatClientAgent(
chat_client=OpenAIAssistantsClient(),
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("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 Assistants Chat Client Agent Example ===")
await non_streaming_example()
await streaming_example()
if __name__ == "__main__":
asyncio.run(main())