1- use super :: { LlmResponse , LlmResult , LmProvider , Message } ;
1+ use super :: {
2+ ContentBlock , LlmResponse , LlmResult , LmProvider , Message , MessageContent , StopReason ,
3+ } ;
24
35pub struct ClaudeProvider {
46 api_key : String ,
@@ -17,30 +19,51 @@ impl ClaudeProvider {
1719}
1820
1921impl LmProvider for ClaudeProvider {
20- fn invoke ( & self , system_prompt : & str , messages : & [ Message ] , max_tokens : u32 ) -> LlmResult < ' _ > {
22+ fn invoke (
23+ & self ,
24+ system_prompt : & str ,
25+ messages : & [ Message ] ,
26+ max_tokens : u32 ,
27+ tools : Option < & [ serde_json:: Value ] > ,
28+ ) -> LlmResult < ' _ > {
2129 let system_prompt = system_prompt. to_string ( ) ;
2230 let messages = messages. to_vec ( ) ;
31+ let tools = tools. map ( |t| t. to_vec ( ) ) ;
2332 Box :: pin ( async move {
2433 let api_messages: Vec < serde_json:: Value > = messages
2534 . iter ( )
2635 . map ( |m| {
36+ let role = match m. role {
37+ super :: Role :: User => "user" ,
38+ super :: Role :: Assistant => "assistant" ,
39+ } ;
40+ let content = match & m. content {
41+ MessageContent :: Text ( s) => serde_json:: Value :: String ( s. clone ( ) ) ,
42+ MessageContent :: Blocks ( blocks) => {
43+ serde_json:: to_value ( blocks) . unwrap_or ( serde_json:: Value :: Null )
44+ }
45+ } ;
2746 serde_json:: json!( {
28- "role" : match m. role {
29- super :: Role :: User => "user" ,
30- super :: Role :: Assistant => "assistant" ,
31- } ,
32- "content" : m. content,
47+ "role" : role,
48+ "content" : content,
3349 } )
3450 } )
3551 . collect ( ) ;
3652
37- let body = serde_json:: json!( {
53+ let mut body = serde_json:: json!( {
3854 "model" : self . model,
3955 "max_tokens" : max_tokens,
4056 "system" : system_prompt,
4157 "messages" : api_messages,
4258 } ) ;
4359
60+ // Include tool definitions if provided
61+ if let Some ( ref tool_defs) = tools {
62+ if !tool_defs. is_empty ( ) {
63+ body[ "tools" ] = serde_json:: Value :: Array ( tool_defs. clone ( ) ) ;
64+ }
65+ }
66+
4467 let response = self
4568 . client
4669 . post ( "https://api.anthropic.com/v1/messages" )
@@ -65,12 +88,18 @@ impl LmProvider for ClaudeProvider {
6588 let response_json: serde_json:: Value = serde_json:: from_str ( & response_text)
6689 . map_err ( |e| Box :: new ( e) as Box < dyn std:: error:: Error + Send + Sync > ) ?;
6790
68- let content = response_json[ "content" ]
69- . as_array ( )
70- . and_then ( |arr| arr. first ( ) )
71- . and_then ( |block| block[ "text" ] . as_str ( ) )
72- . unwrap_or ( "" )
73- . to_string ( ) ;
91+ // Parse content blocks from the response
92+ let content_blocks = parse_content_blocks ( & response_json) ;
93+
94+ // Parse stop_reason
95+ let stop_reason = match response_json[ "stop_reason" ] . as_str ( ) {
96+ Some ( "end_turn" ) => StopReason :: EndTurn ,
97+ Some ( "tool_use" ) => StopReason :: ToolUse ,
98+ Some ( "max_tokens" ) => StopReason :: MaxTokens ,
99+ Some ( "stop_sequence" ) => StopReason :: StopSequence ,
100+ Some ( other) => StopReason :: Other ( other. to_string ( ) ) ,
101+ None => StopReason :: EndTurn ,
102+ } ;
74103
75104 let model = response_json[ "model" ]
76105 . as_str ( )
@@ -85,7 +114,8 @@ impl LmProvider for ClaudeProvider {
85114 . map ( |v| v as u32 ) ;
86115
87116 Ok ( LlmResponse {
88- content,
117+ content : content_blocks,
118+ stop_reason,
89119 model,
90120 input_tokens,
91121 output_tokens,
@@ -101,3 +131,30 @@ impl LmProvider for ClaudeProvider {
101131 true
102132 }
103133}
134+
135+ /// Parse the `content` array from a Claude API response into ContentBlock values.
136+ fn parse_content_blocks ( response_json : & serde_json:: Value ) -> Vec < ContentBlock > {
137+ let Some ( content_array) = response_json[ "content" ] . as_array ( ) else {
138+ return vec ! [ ] ;
139+ } ;
140+
141+ content_array
142+ . iter ( )
143+ . filter_map ( |block| {
144+ let block_type = block[ "type" ] . as_str ( ) ?;
145+ match block_type {
146+ "text" => {
147+ let text = block[ "text" ] . as_str ( ) . unwrap_or ( "" ) . to_string ( ) ;
148+ Some ( ContentBlock :: Text { text } )
149+ }
150+ "tool_use" => {
151+ let id = block[ "id" ] . as_str ( ) ?. to_string ( ) ;
152+ let name = block[ "name" ] . as_str ( ) ?. to_string ( ) ;
153+ let input = block[ "input" ] . clone ( ) ;
154+ Some ( ContentBlock :: ToolUse { id, name, input } )
155+ }
156+ _ => None ,
157+ }
158+ } )
159+ . collect ( )
160+ }
0 commit comments