mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python telemetry (#223)
* initial work on telemetry * moved tool operation const * missing quotes * working otel with samples * updated readme and other assets * added tests * added tests * small updates * updated genaiattributes docs * updated tests * additional warning * cleanup of tests
This commit is contained in:
committed by
GitHub
Unverified
parent
3ee9dddfa2
commit
0ce8eb1e2f
@@ -4,7 +4,7 @@ import json
|
||||
from collections.abc import AsyncIterable, Mapping, MutableSequence, Sequence
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
from typing import Any, cast
|
||||
from typing import Any, ClassVar, cast
|
||||
|
||||
from openai import AsyncOpenAI, AsyncStream
|
||||
from openai.types import CompletionUsage
|
||||
@@ -29,21 +29,19 @@ from .._types import (
|
||||
UsageDetails,
|
||||
)
|
||||
from ..exceptions import ServiceInitializationError, ServiceInvalidResponseError
|
||||
from ..telemetry import use_telemetry
|
||||
from ._shared import OpenAIConfigBase, OpenAIHandler, OpenAIModelTypes, OpenAISettings
|
||||
|
||||
__all__ = ["OpenAIChatClient"]
|
||||
|
||||
|
||||
# region OpenAIChatClientBase
|
||||
|
||||
|
||||
# Implements agent_framework.ChatClient protocol, through ChatClientBase
|
||||
# region Base Client
|
||||
@use_telemetry
|
||||
@use_tool_calling
|
||||
class OpenAIChatClientBase(OpenAIHandler, ChatClientBase):
|
||||
"""OpenAI Chat completion class."""
|
||||
|
||||
# region Overriding base class methods
|
||||
# most of the methods are overridden from the ChatClientBase class, otherwise it is mentioned
|
||||
MODEL_PROVIDER_NAME: ClassVar[str] = "openai" # type: ignore[reportIncompatibleVariableOverride, misc]
|
||||
|
||||
async def _inner_get_response(
|
||||
self,
|
||||
@@ -100,8 +98,6 @@ class OpenAIChatClientBase(OpenAIHandler, ChatClientBase):
|
||||
for choice in chunk.choices
|
||||
)
|
||||
|
||||
# endregion
|
||||
|
||||
# region content creation
|
||||
|
||||
def _create_chat_message_content(
|
||||
@@ -220,34 +216,34 @@ class OpenAIChatClientBase(OpenAIHandler, ChatClientBase):
|
||||
# Flatten the list of lists into a single list
|
||||
return list(chain.from_iterable(list_of_list))
|
||||
|
||||
# endregion
|
||||
# region Parsers
|
||||
|
||||
def _openai_chat_message_parser(self, message: ChatMessage) -> list[dict[str, Any]]:
|
||||
"""Parse a chat message into the openai format."""
|
||||
all_messages: list[dict[str, Any]] = []
|
||||
args: dict[str, Any] = {
|
||||
"role": message.role.value if isinstance(message.role, ChatRole) else message.role,
|
||||
}
|
||||
if message.additional_properties:
|
||||
args["metadata"] = message.additional_properties
|
||||
for content in message.contents:
|
||||
args: dict[str, Any] = {
|
||||
"role": message.role.value if isinstance(message.role, ChatRole) else message.role,
|
||||
}
|
||||
if message.additional_properties:
|
||||
args["metadata"] = message.additional_properties
|
||||
match content:
|
||||
case FunctionResultContent():
|
||||
new_args = args.copy()
|
||||
new_args["tool_call_id"] = content.call_id
|
||||
new_args["content"] = content.result
|
||||
all_messages.append(new_args)
|
||||
case FunctionCallContent():
|
||||
function_call = self._openai_content_parser(content)
|
||||
if "tool_calls" not in args:
|
||||
args["tool_calls"] = []
|
||||
args["tool_calls"].append(function_call) # type: ignore
|
||||
if all_messages and "tool_calls" in all_messages[-1]:
|
||||
# If the last message already has tool calls, append to it
|
||||
all_messages[-1]["tool_calls"].append(self._openai_content_parser(content))
|
||||
else:
|
||||
args["tool_calls"] = [self._openai_content_parser(content)] # type: ignore
|
||||
case FunctionResultContent():
|
||||
args["tool_call_id"] = content.call_id
|
||||
args["content"] = content.result
|
||||
case _:
|
||||
if "content" not in args:
|
||||
args["content"] = []
|
||||
# this is a list to allow multi-modal content
|
||||
args["content"].append(self._openai_content_parser(content)) # type: ignore
|
||||
if "content" in args or "tool_calls" in args:
|
||||
all_messages.append(args)
|
||||
if "content" in args or "tool_calls" in args:
|
||||
all_messages.append(args)
|
||||
return all_messages
|
||||
|
||||
def _openai_content_parser(self, content: AIContents) -> dict[str, Any]:
|
||||
@@ -268,10 +264,16 @@ class OpenAIChatClientBase(OpenAIHandler, ChatClientBase):
|
||||
case _:
|
||||
return content.model_dump(exclude_none=True)
|
||||
|
||||
def service_url(self) -> str | None:
|
||||
"""Get the URL of the service.
|
||||
|
||||
# endregion
|
||||
Override this in the subclass to return the proper URL.
|
||||
If the service does not have a URL, return None.
|
||||
"""
|
||||
return str(self.client.base_url) if self.client else None
|
||||
|
||||
# region OpenAIChatClient
|
||||
|
||||
# region Public client
|
||||
|
||||
|
||||
class OpenAIChatClient(OpenAIConfigBase, OpenAIChatClientBase):
|
||||
|
||||
Reference in New Issue
Block a user