|
- import json
- import logging
- import os
- from dataclasses import asdict
- from datetime import datetime
- from typing import Any, Dict, List, Literal
-
- from autogen_core.application.logging.events import LLMCallEvent
- from autogen_core.components import Image
- from autogen_core.components.models import (
- AzureOpenAIChatCompletionClient,
- ChatCompletionClient,
- ModelCapabilities,
- OpenAIChatCompletionClient,
- )
-
- from .messages import (
- AgentEvent,
- AssistantContent,
- FunctionExecutionContent,
- OrchestrationEvent,
- SystemContent,
- UserContent,
- WebSurferEvent,
- )
-
- ENVIRON_KEY_CHAT_COMPLETION_PROVIDER = "CHAT_COMPLETION_PROVIDER"
- ENVIRON_KEY_CHAT_COMPLETION_KWARGS_JSON = "CHAT_COMPLETION_KWARGS_JSON"
-
- # The singleton _default_azure_ad_token_provider, which will be created if needed
- _default_azure_ad_token_provider = None
-
-
- # Create a model client based on information provided in environment variables.
- def create_completion_client_from_env(env: Dict[str, str] | None = None, **kwargs: Any) -> ChatCompletionClient:
- global _default_azure_ad_token_provider
-
- """
- Create a model client based on information provided in environment variables.
- env (Optional): When provied, read from this dictionary rather than os.environ
- kwargs**: ChatClient arguments to override (e.g., model)
-
- NOTE: If 'azure_ad_token_provider' is included, and euquals the string 'DEFAULT' then replace it with
- azure.identity.get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default")
- """
-
- # If a dictionary was not provided, load it from the environment
- if env is None:
- env = dict()
- env.update(os.environ)
-
- # Load the kwargs, and override with provided kwargs
- _kwargs = json.loads(env.get(ENVIRON_KEY_CHAT_COMPLETION_KWARGS_JSON, "{}"))
- _kwargs.update(kwargs)
-
- # If model capabilities were provided, deserialize them as well
- if "model_capabilities" in _kwargs:
- _kwargs["model_capabilities"] = ModelCapabilities(
- vision=_kwargs["model_capabilities"].get("vision"),
- function_calling=_kwargs["model_capabilities"].get("function_calling"),
- json_output=_kwargs["model_capabilities"].get("json_output"),
- )
-
- # Figure out what provider we are using. Default to OpenAI
- _provider = env.get(ENVIRON_KEY_CHAT_COMPLETION_PROVIDER, "openai").lower().strip()
-
- # Instantiate the correct client
- if _provider == "openai":
- return OpenAIChatCompletionClient(**_kwargs)
- elif _provider == "azure":
- if _kwargs.get("azure_ad_token_provider", "").lower() == "default":
- if _default_azure_ad_token_provider is None:
- from azure.identity import DefaultAzureCredential, get_bearer_token_provider
-
- _default_azure_ad_token_provider = get_bearer_token_provider(
- DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
- )
- _kwargs["azure_ad_token_provider"] = _default_azure_ad_token_provider
- return AzureOpenAIChatCompletionClient(**_kwargs)
- else:
- raise ValueError(f"Unknown OAI provider '{_provider}'")
-
-
- # Convert UserContent to a string
- def message_content_to_str(
- message_content: UserContent | AssistantContent | SystemContent | FunctionExecutionContent,
- ) -> str:
- if isinstance(message_content, str):
- return message_content
- elif isinstance(message_content, List):
- converted: List[str] = list()
- for item in message_content:
- if isinstance(item, str):
- converted.append(item.rstrip())
- elif isinstance(item, Image):
- converted.append("<Image>")
- else:
- converted.append(str(item).rstrip())
- return "\n".join(converted)
- else:
- raise AssertionError("Unexpected response type.")
-
-
- # TeamOne log event handler
- class LogHandler(logging.FileHandler):
- def __init__(self, filename: str = "log.jsonl") -> None:
- super().__init__(filename)
-
- def emit(self, record: logging.LogRecord) -> None:
- try:
- ts = datetime.fromtimestamp(record.created).isoformat()
- if isinstance(record.msg, OrchestrationEvent):
- console_message = (
- f"\n{'-'*75} \n" f"\033[91m[{ts}], {record.msg.source}:\033[0m\n" f"\n{record.msg.message}"
- )
- print(console_message, flush=True)
- record.msg = json.dumps(
- {
- "timestamp": ts,
- "source": record.msg.source,
- "message": record.msg.message,
- "type": "OrchestrationEvent",
- }
- )
- super().emit(record)
- elif isinstance(record.msg, AgentEvent):
- console_message = (
- f"\n{'-'*75} \n" f"\033[91m[{ts}], {record.msg.source}:\033[0m\n" f"\n{record.msg.message}"
- )
- print(console_message, flush=True)
- record.msg = json.dumps(
- {
- "timestamp": ts,
- "source": record.msg.source,
- "message": record.msg.message,
- "type": "AgentEvent",
- }
- )
- super().emit(record)
- elif isinstance(record.msg, WebSurferEvent):
- console_message = f"\033[96m[{ts}], {record.msg.source}: {record.msg.message}\033[0m"
- print(console_message, flush=True)
- payload: Dict[str, Any] = {
- "timestamp": ts,
- "type": "WebSurferEvent",
- }
- payload.update(asdict(record.msg))
- record.msg = json.dumps(payload)
- super().emit(record)
- elif isinstance(record.msg, LLMCallEvent):
- record.msg = json.dumps(
- {
- "timestamp": ts,
- "prompt_tokens": record.msg.prompt_tokens,
- "completion_tokens": record.msg.completion_tokens,
- "type": "LLMCallEvent",
- }
- )
- super().emit(record)
- except Exception:
- self.handleError(record)
-
-
- class SentinelMeta(type):
- """
- A baseclass for sentinels that plays well with type hints.
- Define new sentinels like this:
-
- ```
- class MY_DEFAULT(metaclass=SentinelMeta):
- pass
-
-
- foo: list[str] | None | type[MY_DEFAULT] = MY_DEFAULT
- ```
-
- Reference: https://stackoverflow.com/questions/69239403/type-hinting-parameters-with-a-sentinel-value-as-the-default
- """
-
- def __repr__(cls) -> str:
- return f"<{cls.__name__}>"
-
- def __bool__(cls) -> Literal[False]:
- return False
|