Source code for autogen_ext.models.anthropic._anthropic_client
importasyncioimportbase64importinspectimportjsonimportloggingimportreimportwarnings# from asyncio import Taskfromtypingimport(Any,AsyncGenerator,Coroutine,Dict,List,Literal,Mapping,Optional,Sequence,Set,Union,cast,)importtiktokenfromanthropicimportAsyncAnthropic,AsyncStreamfromanthropic.typesimport(ContentBlock,ImageBlockParam,Message,MessageParam,RawMessageStreamEvent,# type: ignoreTextBlock,TextBlockParam,ToolParam,ToolResultBlockParam,ToolUseBlock,)fromanthropic.types.image_block_paramimportSourcefromautogen_coreimport(EVENT_LOGGER_NAME,TRACE_LOGGER_NAME,CancellationToken,Component,FunctionCall,Image,)fromautogen_core.loggingimportLLMCallEvent,LLMStreamEndEvent,LLMStreamStartEventfromautogen_core.modelsimport(AssistantMessage,ChatCompletionClient,CreateResult,FinishReasons,FunctionExecutionResultMessage,LLMMessage,ModelCapabilities,# type: ignoreModelInfo,RequestUsage,SystemMessage,UserMessage,validate_model_info,)fromautogen_core.toolsimportTool,ToolSchemafrompydanticimportBaseModel,SecretStrfromtyping_extensionsimportSelf,Unpackfrom.import_model_infofrom.configimportAnthropicClientConfiguration,AnthropicClientConfigurationConfigModellogger=logging.getLogger(EVENT_LOGGER_NAME)trace_logger=logging.getLogger(TRACE_LOGGER_NAME)# Common parameters for message creationanthropic_message_params={"system","messages","max_tokens","temperature","top_p","top_k","stop_sequences","tools","tool_choice","stream","metadata",}disallowed_create_args={"stream","messages"}required_create_args:Set[str]={"model"}anthropic_init_kwargs=set(inspect.getfullargspec(AsyncAnthropic.__init__).kwonlyargs)def_anthropic_client_from_config(config:Mapping[str,Any])->AsyncAnthropic:# Filter config to only include valid parametersclient_config={k:vfork,vinconfig.items()ifkinanthropic_init_kwargs}returnAsyncAnthropic(**client_config)def_create_args_from_config(config:Mapping[str,Any])->Dict[str,Any]:create_args={k:vfork,vinconfig.items()ifkinanthropic_message_paramsork=="model"}create_args_keys=set(create_args.keys())ifnotrequired_create_args.issubset(create_args_keys):raiseValueError(f"Required create args are missing: {required_create_args-create_args_keys}")ifdisallowed_create_args.intersection(create_args_keys):raiseValueError(f"Disallowed create args are present: {disallowed_create_args.intersection(create_args_keys)}")returncreate_argsdeftype_to_role(message:LLMMessage)->str:ifisinstance(message,SystemMessage):return"system"elifisinstance(message,UserMessage):return"user"elifisinstance(message,AssistantMessage):return"assistant"else:return"tool"defget_mime_type_from_image(image:Image)->Literal["image/jpeg","image/png","image/gif","image/webp"]:"""Get a valid Anthropic media type from an Image object."""# Get base64 data firstbase64_data=image.to_base64()# Decode the base64 stringimage_data=base64.b64decode(base64_data)# Check the first few bytes for known signaturesifimage_data.startswith(b"\xff\xd8\xff"):return"image/jpeg"elifimage_data.startswith(b"\x89PNG\r\n\x1a\n"):return"image/png"elifimage_data.startswith(b"GIF87a")orimage_data.startswith(b"GIF89a"):return"image/gif"elifimage_data.startswith(b"RIFF")andimage_data[8:12]==b"WEBP":return"image/webp"else:# Default to JPEG as a fallbackreturn"image/jpeg"defuser_message_to_anthropic(message:UserMessage)->MessageParam:assert_valid_name(message.source)ifisinstance(message.content,str):return{"role":"user","content":message.content,}else:blocks:List[Union[TextBlockParam,ImageBlockParam]]=[]forpartinmessage.content:ifisinstance(part,str):blocks.append(TextBlockParam(type="text",text=part))elifisinstance(part,Image):blocks.append(ImageBlockParam(type="image",source=Source(type="base64",media_type=get_mime_type_from_image(part),data=part.to_base64(),),))else:raiseValueError(f"Unknown content type: {part}")return{"role":"user","content":blocks,}defsystem_message_to_anthropic(message:SystemMessage)->str:returnmessage.contentdefassistant_message_to_anthropic(message:AssistantMessage)->MessageParam:assert_valid_name(message.source)ifisinstance(message.content,list):# Tool callstool_use_blocks:List[ToolUseBlock]=[]forfunc_callinmessage.content:# Parse the arguments and convert to dict if it's a JSON stringargs=func_call.argumentsifisinstance(args,str):try:args_dict=json.loads(args)exceptjson.JSONDecodeError:args_dict={"text":args}else:args_dict=argstool_use_blocks.append(ToolUseBlock(type="tool_use",id=func_call.id,name=func_call.name,input=args_dict,))# Include thought if availablecontent_blocks:List[ContentBlock]=[]ifhasattr(message,"thought")andmessage.thoughtisnotNone:content_blocks.append(TextBlock(type="text",text=message.thought))content_blocks.extend(tool_use_blocks)return{"role":"assistant","content":content_blocks,}else:# Simple text contentreturn{"role":"assistant","content":message.content,}deftool_message_to_anthropic(message:FunctionExecutionResultMessage)->List[MessageParam]:# Create a single user message containing all tool resultscontent_blocks:List[ToolResultBlockParam]=[]forresultinmessage.content:content_blocks.append(ToolResultBlockParam(type="tool_result",tool_use_id=result.call_id,content=result.content,))return[{"role":"user",# Changed from "tool" to "user""content":content_blocks,}]defto_anthropic_type(message:LLMMessage)->Union[str,List[MessageParam],MessageParam]:ifisinstance(message,SystemMessage):returnsystem_message_to_anthropic(message)elifisinstance(message,UserMessage):returnuser_message_to_anthropic(message)elifisinstance(message,AssistantMessage):returnassistant_message_to_anthropic(message)else:returntool_message_to_anthropic(message)defconvert_tools(tools:Sequence[Tool|ToolSchema])->List[ToolParam]:result:List[ToolParam]=[]fortoolintools:ifisinstance(tool,Tool):tool_schema=tool.schemaelse:assertisinstance(tool,dict)tool_schema=tool# Convert parameters to match Anthropic's schema formattool_params:Dict[str,Any]={}if"parameters"intool_schema:params=tool_schema["parameters"]# Transfer propertiesif"properties"inparams:tool_params["properties"]=params["properties"]# Transfer required fieldsif"required"inparams:tool_params["required"]=params["required"]# Handle schema typeif"type"inparams:tool_params["type"]=params["type"]else:tool_params["type"]="object"result.append(ToolParam(name=tool_schema["name"],input_schema=tool_params,description=tool_schema.get("description",""),))# Check if the tool has a valid nameassert_valid_name(tool_schema["name"])returnresultdefnormalize_name(name:str)->str:""" def __init__(self, **kwargs: Unpack[AnthropicClientConfiguration]): if "model" not in kwargs: raise ValueError("model is required for AnthropicChatCompletionClient") self._raw_config: Dict[str, Any] = dict(kwargs).copy() copied_args = dict(kwargs).copy() model_info: Optional[ModelInfo] = None if "model_info" in kwargs: model_info = kwargs["model_info"] del copied_args["model_info"] client = _anthropic_client_from_config(copied_args) create_args = _create_args_from_config(copied_args) super().__init__( client=client, create_args=create_args, model_info=model_info, ) def __getstate__(self) -> Dict[str, Any]: state = self.__dict__.copy() state["_client"] = None return state def __setstate__(self, state: Dict[str, Any]) -> None: self.__dict__.update(state) self._client = _anthropic_client_from_config(state["_raw_config"]) def _to_config(self) -> AnthropicClientConfigurationConfigModel: copied_config = self._raw_config.copy() return AnthropicClientConfigurationConfigModel(**copied_config) @classmethod def _from_config(cls, config: AnthropicClientConfigurationConfigModel) -> Self: copied_config = config.model_copy().model_dump(exclude_none=True) return cls(**copied_config) Normalize names by replacing invalid characters with underscore. """returnre.sub(r"[^a-zA-Z0-9_-]","_",name)[:64]defassert_valid_name(name:str)->str:""" Ensure that configured names are valid, raises ValueError if not. """ifnotre.match(r"^[a-zA-Z0-9_-]+$",name):raiseValueError(f"Invalid name: {name}. Only letters, numbers, '_' and '-' are allowed.")iflen(name)>64:raiseValueError(f"Invalid name: {name}. Name must be less than 64 characters.")returnnamedefnormalize_stop_reason(stop_reason:str|None)->FinishReasons:ifstop_reasonisNone:return"unknown"# Convert to lowercase for comparisonstop_reason=stop_reason.lower()# Map Anthropic stop reasons to standard reasonsKNOWN_STOP_MAPPINGS:Dict[str,FinishReasons]={"end_turn":"stop","max_tokens":"length","stop_sequence":"stop","tool_use":"function_calls",}returnKNOWN_STOP_MAPPINGS.get(stop_reason,"unknown")def_add_usage(usage1:RequestUsage,usage2:RequestUsage)->RequestUsage:returnRequestUsage(prompt_tokens=usage1.prompt_tokens+usage2.prompt_tokens,completion_tokens=usage1.completion_tokens+usage2.completion_tokens,)
[docs]classBaseAnthropicChatCompletionClient(ChatCompletionClient):def__init__(self,client:AsyncAnthropic,*,create_args:Dict[str,Any],model_info:Optional[ModelInfo]=None,):self._client=clientifmodel_infoisNone:try:self._model_info=_model_info.get_info(create_args["model"])exceptKeyErroraserr:raiseValueError("model_info is required when model name is not recognized")fromerrelse:self._model_info=model_info# Validate model_infovalidate_model_info(self._model_info)self._create_args=create_argsself._total_usage=RequestUsage(prompt_tokens=0,completion_tokens=0)self._actual_usage=RequestUsage(prompt_tokens=0,completion_tokens=0)
[docs]asyncdefcreate(self,messages:Sequence[LLMMessage],*,tools:Sequence[Tool|ToolSchema]=[],json_output:Optional[bool|type[BaseModel]]=None,extra_create_args:Mapping[str,Any]={},cancellation_token:Optional[CancellationToken]=None,)->CreateResult:# Copy create args and update with extra argscreate_args=self._create_args.copy()create_args.update(extra_create_args)# Check for vision capability if images are presentifself.model_info["vision"]isFalse:formessageinmessages:ifisinstance(message,UserMessage):ifisinstance(message.content,list)andany(isinstance(x,Image)forxinmessage.content):raiseValueError("Model does not support vision and image was provided")# Handle JSON output formatifjson_outputisnotNone:ifself.model_info["json_output"]isFalseandjson_outputisTrue:raiseValueError("Model does not support JSON output")ifjson_outputisTrue:create_args["response_format"]={"type":"json_object"}elifisinstance(json_output,type):raiseValueError("Structured output is currently not supported for Anthropic models")# Process system message separatelysystem_message=Noneanthropic_messages:List[MessageParam]=[]formessageinmessages:ifisinstance(message,SystemMessage):ifsystem_messageisnotNone:raiseValueError("Multiple system messages are not supported")system_message=to_anthropic_type(message)else:anthropic_message=to_anthropic_type(message)ifisinstance(anthropic_message,list):anthropic_messages.extend(anthropic_message)elifisinstance(anthropic_message,str):msg=MessageParam(role="user"ifisinstance(message,UserMessage)else"assistant",content=anthropic_message)anthropic_messages.append(msg)else:anthropic_messages.append(anthropic_message)# Check for function calling supportifself.model_info["function_calling"]isFalseandlen(tools)>0:raiseValueError("Model does not support function calling")# Set up the requestrequest_args:Dict[str,Any]={"model":create_args["model"],"messages":anthropic_messages,"max_tokens":create_args.get("max_tokens",4096),"temperature":create_args.get("temperature",1.0),}# Add system message if presentifsystem_messageisnotNone:request_args["system"]=system_messagehas_tool_results=any(isinstance(msg,FunctionExecutionResultMessage)formsginmessages)# Store and add tools if presentiflen(tools)>0:converted_tools=convert_tools(tools)self._last_used_tools=converted_toolsrequest_args["tools"]=converted_toolselifhas_tool_results:# anthropic requires tools to be present even if there is any tool userequest_args["tools"]=self._last_used_tools# Optional parametersforparamin["top_p","top_k","stop_sequences","metadata"]:ifparamincreate_args:request_args[param]=create_args[param]# Execute the requestfuture:asyncio.Task[Message]=asyncio.ensure_future(self._client.messages.create(**request_args))# type: ignoreifcancellation_tokenisnotNone:cancellation_token.link_future(future)# type: ignoreresult:Message=cast(Message,awaitfuture)# type: ignore# Extract usage statisticsusage=RequestUsage(prompt_tokens=result.usage.input_tokens,completion_tokens=result.usage.output_tokens,)logger.info(LLMCallEvent(messages=cast(List[Dict[str,Any]],anthropic_messages),response=result.model_dump(),prompt_tokens=usage.prompt_tokens,completion_tokens=usage.completion_tokens,))# Process the responsecontent:Union[str,List[FunctionCall]]thought=None# Check if the response includes tool usestool_uses=[blockforblockinresult.contentifgetattr(block,"type",None)=="tool_use"]iftool_uses:# Handle tool use responsecontent=[]# Check for text content that should be treated as thoughttext_blocks:List[TextBlock]=[blockforblockinresult.contentifisinstance(block,TextBlock)]iftext_blocks:thought="".join([block.textforblockintext_blocks])# Process tool use blocksfortool_useintool_uses:ifisinstance(tool_use,ToolUseBlock):tool_input=tool_use.inputifisinstance(tool_input,dict):tool_input=json.dumps(tool_input)else:tool_input=str(tool_input)iftool_inputisnotNoneelse""content.append(FunctionCall(id=tool_use.id,name=normalize_name(tool_use.name),arguments=tool_input,))else:# Handle text responsecontent="".join([block.textifisinstance(block,TextBlock)else""forblockinresult.content])# Create the final resultresponse=CreateResult(finish_reason=normalize_stop_reason(result.stop_reason),content=content,usage=usage,cached=False,thought=thought,)# Update usage statisticsself._total_usage=_add_usage(self._total_usage,usage)self._actual_usage=_add_usage(self._actual_usage,usage)returnresponse
[docs]asyncdefcreate_stream(self,messages:Sequence[LLMMessage],*,tools:Sequence[Tool|ToolSchema]=[],json_output:Optional[bool|type[BaseModel]]=None,extra_create_args:Mapping[str,Any]={},cancellation_token:Optional[CancellationToken]=None,max_consecutive_empty_chunk_tolerance:int=0,)->AsyncGenerator[Union[str,CreateResult],None]:""" Creates an AsyncGenerator that yields a stream of completions based on the provided messages and tools. """# Copy create args and update with extra argscreate_args=self._create_args.copy()create_args.update(extra_create_args)# Check for vision capability if images are presentifself.model_info["vision"]isFalse:formessageinmessages:ifisinstance(message,UserMessage):ifisinstance(message.content,list)andany(isinstance(x,Image)forxinmessage.content):raiseValueError("Model does not support vision and image was provided")# Handle JSON output formatifjson_outputisnotNone:ifself.model_info["json_output"]isFalseandjson_outputisTrue:raiseValueError("Model does not support JSON output")ifjson_outputisTrue:create_args["response_format"]={"type":"json_object"}ifisinstance(json_output,type):raiseValueError("Structured output is currently not supported for Anthropic models")# Process system message separatelysystem_message=Noneanthropic_messages:List[MessageParam]=[]formessageinmessages:ifisinstance(message,SystemMessage):ifsystem_messageisnotNone:raiseValueError("Multiple system messages are not supported")system_message=to_anthropic_type(message)else:anthropic_message=to_anthropic_type(message)ifisinstance(anthropic_message,list):anthropic_messages.extend(anthropic_message)elifisinstance(anthropic_message,str):msg=MessageParam(role="user"ifisinstance(message,UserMessage)else"assistant",content=anthropic_message)anthropic_messages.append(msg)else:anthropic_messages.append(anthropic_message)# Check for function calling supportifself.model_info["function_calling"]isFalseandlen(tools)>0:raiseValueError("Model does not support function calling")# Set up the requestrequest_args:Dict[str,Any]={"model":create_args["model"],"messages":anthropic_messages,"max_tokens":create_args.get("max_tokens",4096),"temperature":create_args.get("temperature",1.0),"stream":True,}# Add system message if presentifsystem_messageisnotNone:request_args["system"]=system_message# Check if any message is a tool resulthas_tool_results=any(isinstance(msg,FunctionExecutionResultMessage)formsginmessages)# Add tools if presentiflen(tools)>0:converted_tools=convert_tools(tools)self._last_used_tools=converted_toolsrequest_args["tools"]=converted_toolselifhas_tool_results:request_args["tools"]=self._last_used_tools# Optional parametersforparamin["top_p","top_k","stop_sequences","metadata"]:ifparamincreate_args:request_args[param]=create_args[param]# Stream the responsestream_future:asyncio.Task[AsyncStream[RawMessageStreamEvent]]=asyncio.ensure_future(cast(Coroutine[Any,Any,AsyncStream[RawMessageStreamEvent]],self._client.messages.create(**request_args)))ifcancellation_tokenisnotNone:cancellation_token.link_future(stream_future)# type: ignorestream:AsyncStream[RawMessageStreamEvent]=cast(AsyncStream[RawMessageStreamEvent],awaitstream_future)# type: ignoretext_content:List[str]=[]tool_calls:Dict[str,Dict[str,Any]]={}# Track tool calls by IDcurrent_tool_id:Optional[str]=Noneinput_tokens:int=0output_tokens:int=0stop_reason:Optional[str]=Nonefirst_chunk=True# Process the streamasyncforchunkinstream:iffirst_chunk:first_chunk=False# Emit the start event.logger.info(LLMStreamStartEvent(messages=cast(List[Dict[str,Any]],anthropic_messages),))# Handle different event typesifchunk.type=="content_block_start":ifchunk.content_block.type=="tool_use":# Start of a tool use blockcurrent_tool_id=chunk.content_block.idtool_calls[current_tool_id]={"id":chunk.content_block.id,"name":chunk.content_block.name,"input":"",# Will be populated from deltas}elifchunk.type=="content_block_delta":ifhasattr(chunk.delta,"type")andchunk.delta.type=="text_delta":# Handle text contentdelta_text=chunk.delta.texttext_content.append(delta_text)ifdelta_text:yielddelta_text# Handle tool input deltas - they come as InputJSONDeltaelifhasattr(chunk.delta,"type")andchunk.delta.type=="input_json_delta":ifcurrent_tool_idisnotNoneandhasattr(chunk.delta,"partial_json"):# Accumulate partial JSON for the current tooltool_calls[current_tool_id]["input"]+=chunk.delta.partial_jsonelifchunk.type=="content_block_stop":# End of a content block (could be text or tool)current_tool_id=Noneelifchunk.type=="message_delta":ifhasattr(chunk.delta,"stop_reason")andchunk.delta.stop_reason:stop_reason=chunk.delta.stop_reason# Get usage info if availableifhasattr(chunk,"usage")andhasattr(chunk.usage,"output_tokens"):output_tokens=chunk.usage.output_tokenselifchunk.type=="message_start":ifhasattr(chunk,"message")andhasattr(chunk.message,"usage"):ifhasattr(chunk.message.usage,"input_tokens"):input_tokens=chunk.message.usage.input_tokensifhasattr(chunk.message.usage,"output_tokens"):output_tokens=chunk.message.usage.output_tokens# Prepare the final responseusage=RequestUsage(prompt_tokens=input_tokens,completion_tokens=output_tokens,)# Determine content based on what was receivedcontent:Union[str,List[FunctionCall]]thought=Noneiftool_calls:# We received tool callsiftext_content:# Text before tool calls is treated as thoughtthought="".join(text_content)# Convert tool calls to FunctionCall objectscontent=[]for_,tool_dataintool_calls.items():# Parse the JSON input if neededinput_str=tool_data["input"]try:# If it's valid JSON, parse it; otherwise use as-isifinput_str.strip().startswith("{")andinput_str.strip().endswith("}"):parsed_input=json.loads(input_str)input_str=json.dumps(parsed_input)# Re-serialize to ensure valid JSONexceptjson.JSONDecodeError:# Keep as string if not valid JSONpasscontent.append(FunctionCall(id=tool_data["id"],name=normalize_name(tool_data["name"]),arguments=input_str,))else:# Just text contentcontent="".join(text_content)# Create the final resultresult=CreateResult(finish_reason=normalize_stop_reason(stop_reason),content=content,usage=usage,cached=False,thought=thought,)# Emit the end event.logger.info(LLMStreamEndEvent(response=result.model_dump(),prompt_tokens=usage.prompt_tokens,completion_tokens=usage.completion_tokens,))# Update usage statisticsself._total_usage=_add_usage(self._total_usage,usage)self._actual_usage=_add_usage(self._actual_usage,usage)yieldresult
[docs]defcount_tokens(self,messages:Sequence[LLMMessage],*,tools:Sequence[Tool|ToolSchema]=[])->int:""" Estimate the number of tokens used by messages and tools. Note: This is an estimation based on common tokenization patterns and may not perfectly match Anthropic's exact token counting for Claude models. """# Use cl100k_base encoding as an approximation for Claude's tokenizertry:encoding=tiktoken.get_encoding("cl100k_base")exceptException:encoding=tiktoken.get_encoding("gpt2")# Fallbacknum_tokens=0# System message tokens (if any)system_content=Noneformessageinmessages:ifisinstance(message,SystemMessage):system_content=message.contentbreakifsystem_content:num_tokens+=len(encoding.encode(system_content))+15# Approximate system message overhead# Message tokensformessageinmessages:ifisinstance(message,SystemMessage):continue# Already counted# Base token cost per messagenum_tokens+=10# Approximate message role & formatting overhead# Content tokensifisinstance(message,UserMessage)orisinstance(message,AssistantMessage):ifisinstance(message.content,str):num_tokens+=len(encoding.encode(message.content))elifisinstance(message.content,list):# Handle different content typesforpartinmessage.content:ifisinstance(part,str):num_tokens+=len(encoding.encode(part))elifisinstance(part,Image):# Estimate vision tokens (simplified)num_tokens+=512# Rough estimation for image tokenselifisinstance(part,FunctionCall):num_tokens+=len(encoding.encode(part.name))num_tokens+=len(encoding.encode(part.arguments))num_tokens+=10# Function call overheadelifisinstance(message,FunctionExecutionResultMessage):forresultinmessage.content:num_tokens+=len(encoding.encode(result.content))num_tokens+=10# Function result overhead# Tool tokensfortoolintools:ifisinstance(tool,Tool):tool_schema=tool.schemaelse:tool_schema=tool# Name and descriptionnum_tokens+=len(encoding.encode(tool_schema["name"]))if"description"intool_schema:num_tokens+=len(encoding.encode(tool_schema["description"]))# Parametersif"parameters"intool_schema:params=tool_schema["parameters"]if"properties"inparams:forprop_name,prop_schemainparams["properties"].items():num_tokens+=len(encoding.encode(prop_name))if"type"inprop_schema:num_tokens+=len(encoding.encode(prop_schema["type"]))if"description"inprop_schema:num_tokens+=len(encoding.encode(prop_schema["description"]))# Special handling for enumsif"enum"inprop_schema:forvalueinprop_schema["enum"]:ifisinstance(value,str):num_tokens+=len(encoding.encode(value))else:num_tokens+=2# Non-string enum values# Tool overheadnum_tokens+=20returnnum_tokens
[docs]defremaining_tokens(self,messages:Sequence[LLMMessage],*,tools:Sequence[Tool|ToolSchema]=[])->int:"""Calculate the remaining tokens based on the model's token limit."""token_limit=_model_info.get_token_limit(self._create_args["model"])returntoken_limit-self.count_tokens(messages,tools=tools)
@propertydefcapabilities(self)->ModelCapabilities:# type: ignorewarnings.warn("capabilities is deprecated, use model_info instead",DeprecationWarning,stacklevel=2)returnself._model_info@propertydefmodel_info(self)->ModelInfo:returnself._model_info
[docs]classAnthropicChatCompletionClient(BaseAnthropicChatCompletionClient,Component[AnthropicClientConfigurationConfigModel]):""" Chat completion client for Anthropic's Claude models. Args: model (str): The Claude model to use (e.g., "claude-3-sonnet-20240229", "claude-3-opus-20240229") api_key (str, optional): Anthropic API key. Required if not in environment variables. base_url (str, optional): Override the default API endpoint. max_tokens (int, optional): Maximum tokens in the response. Default is 4096. temperature (float, optional): Controls randomness. Lower is more deterministic. Default is 1.0. top_p (float, optional): Controls diversity via nucleus sampling. Default is 1.0. top_k (int, optional): Controls diversity via top-k sampling. Default is -1 (disabled). model_info (ModelInfo, optional): The capabilities of the model. Required if using a custom model. To use this client, you must install the Anthropic extension: .. code-block:: bash pip install "autogen-ext[anthropic]" Example: .. code-block:: python import asyncio from autogen_ext.models.anthropic import AnthropicChatCompletionClient from autogen_core.models import UserMessage async def main(): anthropic_client = AnthropicChatCompletionClient( model="claude-3-sonnet-20240229", api_key="your-api-key", # Optional if ANTHROPIC_API_KEY is set in environment ) result = await anthropic_client.create([UserMessage(content="What is the capital of France?", source="user")]) # type: ignore print(result) if __name__ == "__main__": asyncio.run(main()) To load the client from a configuration: .. code-block:: python from autogen_core.models import ChatCompletionClient config = { "provider": "AnthropicChatCompletionClient", "config": {"model": "claude-3-sonnet-20240229"}, } client = ChatCompletionClient.load_component(config) """component_type="model"component_config_schema=AnthropicClientConfigurationConfigModelcomponent_provider_override="autogen_ext.models.anthropic.AnthropicChatCompletionClient"def__init__(self,**kwargs:Unpack[AnthropicClientConfiguration]):if"model"notinkwargs:raiseValueError("model is required for AnthropicChatCompletionClient")self._raw_config:Dict[str,Any]=dict(kwargs).copy()copied_args=dict(kwargs).copy()model_info:Optional[ModelInfo]=Noneif"model_info"inkwargs:model_info=kwargs["model_info"]delcopied_args["model_info"]client=_anthropic_client_from_config(copied_args)create_args=_create_args_from_config(copied_args)super().__init__(client=client,create_args=create_args,model_info=model_info,)def__getstate__(self)->Dict[str,Any]:state=self.__dict__.copy()state["_client"]=Nonereturnstatedef__setstate__(self,state:Dict[str,Any])->None:self.__dict__.update(state)self._client=_anthropic_client_from_config(state["_raw_config"])
[docs]@classmethoddef_from_config(cls,config:AnthropicClientConfigurationConfigModel)->Self:copied_config=config.model_copy().model_dump(exclude_none=True)# Handle api_key as SecretStrif"api_key"incopied_configandisinstance(config.api_key,SecretStr):copied_config["api_key"]=config.api_key.get_secret_value()returncls(**copied_config)