Skip to content

Commit c9d588a

Browse files
committed
support anthropic
1 parent 72d7376 commit c9d588a

File tree

2 files changed

+53
-25
lines changed

2 files changed

+53
-25
lines changed

camel/models/anthropic_model.py

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import json
1515
import os
1616
import time
17-
import warnings
1817
from typing import Any, Dict, List, Optional, Type, Union, cast
1918

2019
from openai import AsyncStream, Stream
@@ -636,6 +635,37 @@ def _convert_anthropic_stream_to_openai_chunk(
636635
usage=usage,
637636
)
638637

638+
@staticmethod
639+
def _add_additional_properties_false(schema: Dict[str, Any]) -> None:
640+
r"""Recursively add additionalProperties: false to all object types."""
641+
if schema.get("type") == "object":
642+
schema["additionalProperties"] = False
643+
for value in schema.values():
644+
if isinstance(value, dict):
645+
AnthropicModel._add_additional_properties_false(value)
646+
elif isinstance(value, list):
647+
for item in value:
648+
if isinstance(item, dict):
649+
AnthropicModel._add_additional_properties_false(item)
650+
651+
@staticmethod
652+
def _build_output_config(
653+
response_format: Type[BaseModel],
654+
) -> Dict[str, Any]:
655+
r"""Convert a Pydantic model to Anthropic's output_config format."""
656+
schema = response_format.model_json_schema()
657+
# Remove unsupported fields
658+
schema.pop("$defs", None)
659+
schema.pop("definitions", None)
660+
# Anthropic requires additionalProperties: false on all object types
661+
AnthropicModel._add_additional_properties_false(schema)
662+
return {
663+
"format": {
664+
"type": "json_schema",
665+
"schema": schema,
666+
}
667+
}
668+
639669
def _convert_openai_tools_to_anthropic(
640670
self, tools: Optional[List[Dict[str, Any]]]
641671
) -> Optional[List[Dict[str, Any]]]:
@@ -678,7 +708,7 @@ def _run(
678708
messages (List[OpenAIMessage]): Message list with the chat history
679709
in OpenAI API format.
680710
response_format (Optional[Type[BaseModel]]): The format of the
681-
response. (Not supported by Anthropic API directly)
711+
response.
682712
tools (Optional[List[Dict[str, Any]]]): The schema of the tools to
683713
use for the request.
684714
@@ -687,15 +717,6 @@ def _run(
687717
`ChatCompletion` in the non-stream mode, or
688718
`Stream[ChatCompletionChunk]` in the stream mode.
689719
"""
690-
if response_format is not None:
691-
warnings.warn(
692-
"The 'response_format' parameter is not supported by the "
693-
"Anthropic API and will be ignored. Consider using tools "
694-
"for structured output instead.",
695-
UserWarning,
696-
stacklevel=2,
697-
)
698-
699720
# Update Langfuse trace with current agent session and metadata
700721
agent_session_id = get_current_agent_session_id()
701722
if agent_session_id:
@@ -764,6 +785,12 @@ def _run(
764785
if key in self.model_config_dict:
765786
request_params[key] = self.model_config_dict[key]
766787

788+
# Add structured output via output_config
789+
if response_format is not None:
790+
request_params["output_config"] = self._build_output_config(
791+
response_format
792+
)
793+
767794
# Convert tools
768795
anthropic_tools = self._convert_openai_tools_to_anthropic(tools)
769796
if anthropic_tools:
@@ -803,7 +830,7 @@ async def _arun(
803830
messages (List[OpenAIMessage]): Message list with the chat history
804831
in OpenAI API format.
805832
response_format (Optional[Type[BaseModel]]): The format of the
806-
response. (Not supported by Anthropic API directly)
833+
response.
807834
tools (Optional[List[Dict[str, Any]]]): The schema of the tools to
808835
use for the request.
809836
@@ -812,15 +839,6 @@ async def _arun(
812839
`ChatCompletion` in the non-stream mode, or
813840
`AsyncStream[ChatCompletionChunk]` in the stream mode.
814841
"""
815-
if response_format is not None:
816-
warnings.warn(
817-
"The 'response_format' parameter is not supported by the "
818-
"Anthropic API and will be ignored. Consider using tools "
819-
"for structured output instead.",
820-
UserWarning,
821-
stacklevel=2,
822-
)
823-
824842
# Update Langfuse trace with current agent session and metadata
825843
agent_session_id = get_current_agent_session_id()
826844
if agent_session_id:
@@ -889,6 +907,12 @@ async def _arun(
889907
if key in self.model_config_dict:
890908
request_params[key] = self.model_config_dict[key]
891909

910+
# Add structured output via output_config
911+
if response_format is not None:
912+
request_params["output_config"] = self._build_output_config(
913+
response_format
914+
)
915+
892916
# Convert tools
893917
anthropic_tools = self._convert_openai_tools_to_anthropic(tools)
894918
if anthropic_tools:

examples/agents/chatagent_stream_structured_output.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@
3030

3131

3232
class Result(BaseModel):
33-
sum_result: str = Field(description="Result of the addition")
34-
product_result: str = Field(description="Result of the multiplication")
35-
division_result: str = Field(description="Result of the division")
36-
capital_result: str = Field(description="Result of the capital search")
33+
sum_result: str = Field(description="Only the result of the addition")
34+
product_result: str = Field(
35+
description="Only the result of the multiplication"
36+
)
37+
division_result: str = Field(description="Only the result of the division")
38+
capital_result: str = Field(
39+
description="Only the result of the capital search"
40+
)
3741

3842

3943
USER_MESSAGE = (

0 commit comments

Comments
 (0)