Python: added create_agent and removed need for Field in annotations (#380)

* added create_agent

* add minimal sample

* allow multiple annotations

* improved docstring
This commit is contained in:
Eduard van Valkenburg
2025-08-08 19:55:20 +02:00
committed by GitHub
Unverified
parent 757c48e367
commit 8f27e63df6
5 changed files with 86 additions and 16 deletions
@@ -4,7 +4,7 @@ import asyncio
from abc import ABC, abstractmethod
from collections.abc import AsyncIterable, Awaitable, Callable, MutableMapping, MutableSequence, Sequence
from functools import wraps
from typing import Any, Generic, Literal, Protocol, TypeVar, runtime_checkable
from typing import TYPE_CHECKING, Any, Generic, Literal, Protocol, TypeVar, runtime_checkable
from pydantic import BaseModel
@@ -23,6 +23,9 @@ from ._types import (
GeneratedEmbeddings,
)
if TYPE_CHECKING:
from ._agents import ChatClientAgent
TInput = TypeVar("TInput", contravariant=True)
TEmbedding = TypeVar("TEmbedding")
TChatClientBase = TypeVar("TChatClientBase", bound="ChatClientBase")
@@ -641,6 +644,36 @@ class ChatClientBase(AFBaseModel, ABC):
"""
return None
def create_agent(
self,
*,
name: str,
instructions: str,
tools: AITool
| list[AITool]
| Callable[..., Any]
| list[Callable[..., Any]]
| MutableMapping[str, Any]
| list[MutableMapping[str, Any]]
| None = None,
**kwargs: Any,
) -> "ChatClientAgent":
"""Create an agent with the given name and instructions.
Args:
name: The name of the agent.
instructions: The instructions for the agent.
tools: Optional list of tools to associate with the agent.
**kwargs: Additional keyword arguments to pass to the agent.
See ChatClientAgent for all the available options.
Returns:
An instance of ChatClientAgent.
"""
from ._agents import ChatClientAgent
return ChatClientAgent(chat_client=self, name=name, instructions=instructions, tools=tools, **kwargs)
# region: Embedding Client
+22 -3
View File
@@ -4,10 +4,10 @@ import inspect
from collections.abc import Awaitable, Callable
from functools import wraps
from time import perf_counter
from typing import Any, Generic, Protocol, TypeVar, runtime_checkable
from typing import Annotated, Any, Generic, Protocol, TypeVar, get_args, get_origin, runtime_checkable
from opentelemetry import metrics, trace
from pydantic import BaseModel, create_model
from pydantic import BaseModel, Field, create_model
from ._logging import get_logger
from .telemetry import GenAIAttributes, start_as_current_span
@@ -137,6 +137,25 @@ class AIFunction(AITool, Generic[ArgsT, ReturnT]):
logger.info("Function completed. Duration: %fs", duration)
def _parse_annotation(annotation: Any) -> Any:
"""Parse a type annotation and return the corresponding type.
If the second annotation (after the type) is a string, then we convert that to a pydantic Field description.
The rest are returned as-is, allowing for multiple annotations.
"""
origin = get_origin(annotation)
if origin is not None:
args = get_args(annotation)
# For other generics, return the origin type (e.g., list for List[int])
if len(args) > 1 and isinstance(args[1], str):
# Create a new Annotated type with the updated Field
args_list = list(args)
if len(args_list) == 2:
return Annotated[args_list[0], Field(description=args_list[1])]
return Annotated[args_list[0], Field(description=args_list[1]), tuple(args_list[2:])]
return annotation
def ai_function(
func: Callable[..., ReturnT | Awaitable[ReturnT]] | None = None,
*,
@@ -174,7 +193,7 @@ def ai_function(
sig = inspect.signature(f)
fields = {
pname: (
param.annotation if param.annotation is not inspect.Parameter.empty else str,
_parse_annotation(param.annotation) if param.annotation is not inspect.Parameter.empty else str,
param.default if param.default is not inspect.Parameter.empty else ...,
)
for pname, param in sig.parameters.items()