Files
maritime-logistics/src/llm/messages.rs
T

77 lines
1.8 KiB
Rust

//! Request/response shapes for the Anthropic Messages API.
//!
//! Hand-rolled rather than pulled from an SDK: pydantic-ai has no Rust
//! equivalent, and the surface area we actually need (text, tool_use,
//! tool_result, forced tool choice) is small enough to own directly.
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub role: String,
pub content: Vec<ContentBlock>,
}
impl Message {
pub fn user_text(text: impl Into<String>) -> Self {
Self {
role: "user".into(),
content: vec![ContentBlock::Text { text: text.into() }],
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text {
text: String,
},
ToolUse {
id: String,
name: String,
input: Value,
},
ToolResult {
tool_use_id: String,
content: String,
},
/// Anything else the API sends back (e.g. a `thinking` block) that this
/// client doesn't act on but must not fail to deserialize.
#[serde(other)]
Unknown,
}
#[derive(Debug, Clone, Serialize)]
pub struct ToolSpec {
pub name: String,
pub description: String,
pub input_schema: Value,
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ToolChoice {
Auto,
Tool { name: String },
}
#[derive(Debug, Serialize)]
pub struct RequestBody<'a> {
pub model: &'a str,
pub max_tokens: u32,
pub system: &'a str,
pub messages: &'a [Message],
pub tools: &'a [ToolSpec],
pub tool_choice: ToolChoice,
}
#[derive(Debug, Deserialize)]
pub struct ResponseBody {
#[serde(default)]
pub content: Vec<ContentBlock>,
#[serde(default)]
pub stop_reason: Option<String>,
}