3030import com .fasterxml .jackson .annotation .JsonProperty ;
3131import com .fasterxml .jackson .annotation .JsonSubTypes ;
3232import com .fasterxml .jackson .annotation .JsonTypeInfo ;
33+ import com .fasterxml .jackson .databind .annotation .JsonDeserialize ;
34+ import com .fasterxml .jackson .databind .annotation .JsonSerialize ;
3335import reactor .core .publisher .Flux ;
3436import reactor .core .publisher .Mono ;
3537
3638import org .springframework .ai .anthropic .api .StreamHelper .ChatCompletionResponseBuilder ;
39+ import org .springframework .ai .anthropic .api .tool .Tool ;
40+ import org .springframework .ai .anthropic .util .ContentFieldDeserializer ;
41+ import org .springframework .ai .anthropic .util .ContentFieldSerializer ;
3742import org .springframework .ai .model .ChatModelDescription ;
3843import org .springframework .ai .model .ModelOptionsUtils ;
3944import org .springframework .ai .observation .conventions .AiProvider ;
@@ -632,7 +637,12 @@ public ChatCompletionRequestBuilder topK(Integer topK) {
632637 }
633638
634639 public ChatCompletionRequestBuilder tools (List <Tool > tools ) {
635- this .tools = tools ;
640+ if (this .tools == null ) {
641+ this .tools = tools ;
642+ }
643+ else {
644+ this .tools .addAll (tools );
645+ }
636646 return this ;
637647 }
638648
@@ -717,7 +727,11 @@ public record ContentBlock(
717727
718728 // tool_result response only
719729 @ JsonProperty ("tool_use_id" ) String toolUseId ,
720- @ JsonProperty ("content" ) String content ,
730+
731+ @ JsonSerialize (using = ContentFieldSerializer .class )
732+ @ JsonDeserialize (using = ContentFieldDeserializer .class )
733+ @ JsonProperty ("content" )
734+ Object content ,
721735
722736 // Thinking only
723737 @ JsonProperty ("signature" ) String signature ,
@@ -728,6 +742,15 @@ public record ContentBlock(
728742 ) {
729743 // @formatter:on
730744
745+ @ JsonInclude (Include .NON_NULL )
746+ @ JsonIgnoreProperties (ignoreUnknown = true )
747+ public record WebSearchToolContentBlock (@ JsonProperty ("type" ) String type , @ JsonProperty ("title" ) String title ,
748+ @ JsonProperty ("url" ) String url , @ JsonProperty ("encrypted_content" ) String EncryptedContent ,
749+ @ JsonProperty ("page_age" ) String pageAge ) {
750+
751+ }
752+ // @formatter:on
753+
731754 /**
732755 * Create content block
733756 * @param mediaType The media type of the content.
@@ -813,6 +836,18 @@ public enum Type {
813836 @ JsonProperty ("tool_result" )
814837 TOOL_RESULT ("tool_result" ),
815838
839+ /**
840+ * Server Tool request
841+ */
842+ @ JsonProperty ("server_tool_use" )
843+ SERVER_TOOL_USE ("server_tool_use" ),
844+
845+ /**
846+ * Web search tool result
847+ */
848+ @ JsonProperty ("web_search_tool_result" )
849+ WEB_SEARCH_TOOL_RESULT ("web_search_tool_result" ),
850+
816851 /**
817852 * Text message.
818853 */
@@ -926,22 +961,6 @@ public Source(String url) {
926961 /// CONTENT_BLOCK EVENTS
927962 ///////////////////////////////////////
928963
929- /**
930- * Tool description.
931- *
932- * @param name The name of the tool.
933- * @param description A description of the tool.
934- * @param inputSchema The input schema of the tool.
935- */
936- @ JsonInclude (Include .NON_NULL )
937- public record Tool (
938- // @formatter:off
939- @ JsonProperty ("name" ) String name ,
940- @ JsonProperty ("description" ) String description ,
941- @ JsonProperty ("input_schema" ) Map <String , Object > inputSchema ) {
942- // @formatter:on
943- }
944-
945964 // CB START EVENT
946965
947966 /**
@@ -987,16 +1006,25 @@ public record ChatCompletionResponse(
9871006 public record Usage (
9881007 // @formatter:off
9891008 @ JsonProperty ("input_tokens" ) Integer inputTokens ,
990- @ JsonProperty ("output_tokens" ) Integer outputTokens ) {
991- // @formatter:off
1009+ @ JsonProperty ("output_tokens" ) Integer outputTokens ,
1010+ @ JsonProperty ("server_tool_use" ) ServerToolUse serverToolUse ) {
1011+ // @formatter:on
1012+ }
1013+
1014+ @ JsonInclude (Include .NON_NULL )
1015+ @ JsonIgnoreProperties (ignoreUnknown = true )
1016+ public record ServerToolUse (
1017+ // @formatter:off
1018+ @ JsonProperty ("web_search_requests" ) Integer webSearchRequests ) {
1019+ // @formatter:on
9921020 }
9931021
994- /// ECB STOP
1022+ /// ECB STOP
9951023
9961024 /**
9971025 * Special event used to aggregate multiple tool use events into a single event with
9981026 * list of aggregated ContentBlockToolUse.
999- */
1027+ */
10001028 public static class ToolUseAggregationEvent implements StreamEvent {
10011029
10021030 private Integer index ;
@@ -1015,17 +1043,17 @@ public EventType type() {
10151043 }
10161044
10171045 /**
1018- * Get tool content blocks.
1019- * @return The tool content blocks.
1020- */
1046+ * Get tool content blocks.
1047+ * @return The tool content blocks.
1048+ */
10211049 public List <ContentBlockStartEvent .ContentBlockToolUse > getToolContentBlocks () {
10221050 return this .toolContentBlocks ;
10231051 }
10241052
10251053 /**
1026- * Check if the event is empty.
1027- * @return True if the event is empty, false otherwise.
1028- */
1054+ * Check if the event is empty.
1055+ * @return True if the event is empty, false otherwise.
1056+ */
10291057 public boolean isEmpty () {
10301058 return (this .index == null || this .id == null || this .name == null
10311059 || !StringUtils .hasText (this .partialJson ));
@@ -1054,7 +1082,8 @@ ToolUseAggregationEvent appendPartialJson(String partialJson) {
10541082 void squashIntoContentBlock () {
10551083 Map <String , Object > map = (StringUtils .hasText (this .partialJson ))
10561084 ? ModelOptionsUtils .jsonToMap (this .partialJson ) : Map .of ();
1057- this .toolContentBlocks .add (new ContentBlockStartEvent .ContentBlockToolUse ("tool_use" , this .id , this .name , map ));
1085+ this .toolContentBlocks
1086+ .add (new ContentBlockStartEvent .ContentBlockToolUse ("tool_use" , this .id , this .name , map ));
10581087 this .index = null ;
10591088 this .id = null ;
10601089 this .name = null ;
@@ -1063,28 +1092,29 @@ void squashIntoContentBlock() {
10631092
10641093 @ Override
10651094 public String toString () {
1066- return "EventToolUseBuilder [index=" + this .index + ", id=" + this .id + ", name=" + this .name + ", partialJson="
1067- + this .partialJson + ", toolUseMap=" + this .toolContentBlocks + "]" ;
1095+ return "EventToolUseBuilder [index=" + this .index + ", id=" + this .id + ", name=" + this .name
1096+ + ", partialJson=" + this .partialJson + ", toolUseMap=" + this .toolContentBlocks + "]" ;
10681097 }
10691098
10701099 }
10711100
1072- ///////////////////////////////////////
1073- /// MESSAGE EVENTS
1074- ///////////////////////////////////////
1101+ ///////////////////////////////////////
1102+ /// MESSAGE EVENTS
1103+ ///////////////////////////////////////
10751104
1076- // MESSAGE START EVENT
1105+ // MESSAGE START EVENT
10771106
10781107 /**
10791108 * Content block start event.
1109+ *
10801110 * @param type The event type.
10811111 * @param index The index of the content block.
10821112 * @param contentBlock The content block body.
1083- */
1113+ */
10841114 @ JsonInclude (Include .NON_NULL )
10851115 @ JsonIgnoreProperties (ignoreUnknown = true )
10861116 public record ContentBlockStartEvent (
1087- // @formatter:off
1117+ // @formatter:off
10881118 @ JsonProperty ("type" ) EventType type ,
10891119 @ JsonProperty ("index" ) Integer index ,
10901120 @ JsonProperty ("content_block" ) ContentBlockBody contentBlock ) implements StreamEvent {
0 commit comments