use crate::provider::{ContentPart, Message, Role, ToolDefinition};
use serde_json::{Value, json};
pub fn convert_messages(messages: &[Message]) -> (Vec<Value>, Vec<Value>) {
let mut system_parts: Vec<Value> = Vec::new();
let mut api_messages: Vec<Value> = Vec::new();
for msg in messages {
match msg.role {
Role::System => append_system(msg, &mut system_parts),
Role::User => append_user(msg, &mut api_messages),
Role::Assistant => append_assistant(msg, &mut api_messages),
Role::Tool => append_tool(msg, &mut api_messages),
}
}
(system_parts, api_messages)
}
pub fn convert_tools(tools: &[ToolDefinition]) -> Vec<Value> {
tools
.iter()
.map(|t| {
json!({
"toolSpec": {
"name": t.name,
"description": t.description,
"inputSchema": {
"json": t.parameters
}
}
})
})
.collect()
}
fn append_system(msg: &Message, system_parts: &mut Vec<Value>) {
let text: String = msg
.content
.iter()
.filter_map(|p| match p {
ContentPart::Text { text } => Some(text.clone()),
_ => None,
})
.collect::<Vec<_>>()
.join("\n");
if !text.trim().is_empty() {
system_parts.push(json!({"text": text}));
}
}
fn append_user(msg: &Message, api_messages: &mut Vec<Value>) {
let mut content_parts: Vec<Value> = Vec::new();
for part in &msg.content {
if let ContentPart::Text { text } = part
&& !text.trim().is_empty()
{
content_parts.push(json!({"text": text}));
}
}
if content_parts.is_empty() {
return;
}
if let Some(last) = api_messages.last_mut()
&& last.get("role").and_then(|r| r.as_str()) == Some("user")
&& let Some(arr) = last.get_mut("content").and_then(|c| c.as_array_mut())
{
arr.extend(content_parts);
return;
}
api_messages.push(json!({
"role": "user",
"content": content_parts
}));
}
fn append_assistant(msg: &Message, api_messages: &mut Vec<Value>) {
let mut content_parts: Vec<Value> = Vec::new();
for part in &msg.content {
match part {
ContentPart::Text { text } => {
if !text.trim().is_empty() {
content_parts.push(json!({"text": text}));
}
}
ContentPart::ToolCall {
id,
name,
arguments,
..
} => {
let input: Value =
serde_json::from_str(arguments).unwrap_or_else(|_| json!({"raw": arguments}));
content_parts.push(json!({
"toolUse": {
"toolUseId": id,
"name": name,
"input": input
}
}));
}
_ => {}
}
}
if content_parts.is_empty() {
return;
}
if let Some(last) = api_messages.last_mut()
&& last.get("role").and_then(|r| r.as_str()) == Some("assistant")
&& let Some(arr) = last.get_mut("content").and_then(|c| c.as_array_mut())
{
arr.extend(content_parts);
return;
}
api_messages.push(json!({
"role": "assistant",
"content": content_parts
}));
}
fn append_tool(msg: &Message, api_messages: &mut Vec<Value>) {
let mut content_parts: Vec<Value> = Vec::new();
for part in &msg.content {
if let ContentPart::ToolResult {
tool_call_id,
content,
} = part
{
let content = if content.trim().is_empty() {
"(empty tool result)".to_string()
} else {
content.clone()
};
content_parts.push(json!({
"toolResult": {
"toolUseId": tool_call_id,
"content": [{"text": content}],
"status": "success"
}
}));
}
}
if content_parts.is_empty() {
return;
}
if let Some(last) = api_messages.last_mut()
&& last.get("role").and_then(|r| r.as_str()) == Some("user")
&& let Some(arr) = last.get_mut("content").and_then(|c| c.as_array_mut())
{
arr.extend(content_parts);
return;
}
api_messages.push(json!({
"role": "user",
"content": content_parts
}));
}