otherone_agent/
response_parser.rs1use otherone_ai::types::{ChatResponse, ParsedResponse, ToolCallsWrapper};
6
7pub fn parse_ai_response(
12 response: &ChatResponse,
13 provider: &otherone_ai::types::ProviderType,
14) -> Result<ParsedResponse, String> {
15 match provider {
16 otherone_ai::types::ProviderType::OpenAI
17 | otherone_ai::types::ProviderType::OpenRouter
18 | otherone_ai::types::ProviderType::Local => parse_openai_response(response),
19 otherone_ai::types::ProviderType::Fetch => parse_fetch_response(response),
20 otherone_ai::types::ProviderType::Anthropic => parse_anthropic_response(response),
21 }
22}
23
24fn parse_openai_response(response: &ChatResponse) -> Result<ParsedResponse, String> {
26 if response.choices.is_empty() {
27 return Err("Invalid OpenAI response format: missing choices".to_string());
28 }
29
30 let choice = &response.choices[0];
31 let message = choice.message.as_ref().unwrap();
32 let content = message.content.as_deref().unwrap_or("");
33 let role = message.role.as_deref().unwrap_or("assistant");
34
35 let token_consumption = response
36 .usage
37 .as_ref()
38 .map(|u| u.total_tokens.unwrap_or(0))
39 .unwrap_or(0);
40
41 let tools = message.tool_calls.as_ref().map(|tc| ToolCallsWrapper {
42 tool_calls: tc.clone(),
43 });
44
45 Ok(ParsedResponse {
46 content: content.to_string(),
47 role: role.to_string(),
48 token_consumption,
49 tools,
50 thinking: None,
51 raw_response: None,
52 })
53}
54
55fn parse_anthropic_response(response: &ChatResponse) -> Result<ParsedResponse, String> {
57 if response.choices.is_empty() {
58 return Err("Invalid Anthropic response format: missing choices".to_string());
59 }
60
61 let choice = &response.choices[0];
62 let message = choice.message.as_ref().unwrap();
63 let content = message.content.as_deref().unwrap_or("");
64 let role = message.role.as_deref().unwrap_or("assistant");
65
66 let token_consumption = response
67 .usage
68 .as_ref()
69 .map(|u| u.total_tokens.unwrap_or(0))
70 .unwrap_or(0);
71
72 Ok(ParsedResponse {
73 content: content.to_string(),
74 role: role.to_string(),
75 token_consumption,
76 tools: None,
77 thinking: None,
78 raw_response: None,
79 })
80}
81
82fn parse_fetch_response(response: &ChatResponse) -> Result<ParsedResponse, String> {
84 parse_openai_response(response)
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use otherone_ai::types::{ChatResponse, Choice, ResponseMessage, Usage};
92
93 #[test]
94 fn test_parse_openai_response() {
95 let response = ChatResponse {
96 id: Some("chat-123".to_string()),
97 object: Some("chat.completion".to_string()),
98 created: Some(1234567890),
99 model: Some("gpt-4".to_string()),
100 choices: vec![Choice {
101 index: 0,
102 message: Some(ResponseMessage {
103 role: Some("assistant".to_string()),
104 content: Some("Hello!".to_string()),
105 tool_calls: None,
106 }),
107 delta: None,
108 finish_reason: Some("stop".to_string()),
109 }],
110 usage: Some(Usage {
111 prompt_tokens: Some(10),
112 completion_tokens: Some(5),
113 total_tokens: Some(15),
114 }),
115 };
116
117 let parsed = parse_openai_response(&response).unwrap();
118 assert_eq!(parsed.content, "Hello!");
119 assert_eq!(parsed.role, "assistant");
120 assert_eq!(parsed.token_consumption, 15);
121 assert!(parsed.tools.is_none());
122 }
123}