use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct Usage {
pub input_tokens: u32,
pub output_tokens: u32,
}
impl Usage {
pub fn new(input_tokens: u32, output_tokens: u32) -> Self {
Self {
input_tokens,
output_tokens,
}
}
pub fn total_tokens(&self) -> u32 {
self.input_tokens + self.output_tokens
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct CacheUsage {
#[serde(default)]
pub cache_read_input_tokens: u32,
#[serde(default)]
pub cache_creation_input_tokens: u32,
}
impl CacheUsage {
pub fn new(cache_read_input_tokens: u32, cache_creation_input_tokens: u32) -> Self {
Self {
cache_read_input_tokens,
cache_creation_input_tokens,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Model {
pub id: String,
pub r#type: String,
pub created_at: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(default, skip_serializing_if = "serde_json::Map::is_empty")]
pub metadata: serde_json::Map<String, serde_json::Value>,
}
impl Model {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
r#type: "model".to_string(),
created_at: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true),
display_name: None,
metadata: serde_json::Map::new(),
}
}
pub fn with_display_name(mut self, name: impl Into<String>) -> Self {
self.display_name = Some(name.into());
self
}
}
pub mod models {
pub const CLAUDE_SONNET_4_5_20250929: &str = "claude-sonnet-4-5-20250929";
pub const CLAUDE_SONNET_4_5_20250929_STRUCTURED_OUTPUTS: &str =
"claude-sonnet-4-5-20250929-structured-outputs";
pub const CLAUDE_HAIKU_4_5_20251001: &str = "claude-haiku-4-5-20251001";
pub const CLAUDE_OPUS_4_1_20250805: &str = "claude-opus-4-1-20250805";
pub const SONNET_LATEST: &str = CLAUDE_SONNET_4_5_20250929;
pub const HAIKU_LATEST: &str = CLAUDE_HAIKU_4_5_20251001;
pub const OPUS_LATEST: &str = CLAUDE_OPUS_4_1_20250805;
pub const DEFAULT: &str = CLAUDE_SONNET_4_5_20250929;
#[deprecated(since = "0.2.0", note = "Use CLAUDE_SONNET_4_5_20250929 instead")]
pub const CLAUDE_3_5_SONNET_20241022: &str = "claude-3-5-sonnet-20241022";
#[deprecated(since = "0.2.0", note = "Use CLAUDE_HAIKU_4_5_20251001 instead")]
pub const CLAUDE_3_5_HAIKU_20241022: &str = "claude-3-5-haiku-20241022";
#[deprecated(since = "0.2.0", note = "Use CLAUDE_SONNET_4_5_20250929 instead")]
pub const CLAUDE_SONNET_4_5_20250514: &str = "claude-sonnet-4-5-20250514";
#[deprecated(since = "0.2.0", note = "Use CLAUDE_OPUS_4_1_20250805 instead")]
pub const CLAUDE_3_OPUS_20240229: &str = "claude-3-opus-20240229";
#[deprecated(since = "0.2.0", note = "Use CLAUDE_SONNET_4_5_20250929 instead")]
pub const CLAUDE_3_SONNET_20240229: &str = "claude-3-sonnet-20240229";
#[deprecated(since = "0.2.0", note = "Use CLAUDE_HAIKU_4_5_20251001 instead")]
pub const CLAUDE_3_HAIKU_20240307: &str = "claude-3-haiku-20240307";
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum StopReason {
#[serde(rename = "end_turn")]
EndTurn,
#[serde(rename = "max_tokens")]
MaxTokens,
#[serde(rename = "tool_use")]
ToolUse,
#[serde(rename = "stop_sequence")]
StopSequence,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PermissionMode {
Default,
AcceptEdits,
BypassPermissions,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
impl ToolDefinition {
pub fn new(
name: impl Into<String>,
description: impl Into<String>,
input_schema: serde_json::Value,
) -> Self {
Self {
name: name.into(),
description: description.into(),
input_schema,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_usage_total() {
let usage = Usage::new(100, 50);
assert_eq!(usage.total_tokens(), 150);
}
#[test]
fn test_model_creation() {
let model = Model::new("claude-3-5-sonnet");
assert_eq!(model.id, "claude-3-5-sonnet");
assert_eq!(model.r#type, "model");
}
#[test]
fn test_stop_reason_serialization() {
let reasons = vec![
StopReason::EndTurn,
StopReason::MaxTokens,
StopReason::ToolUse,
];
for reason in reasons {
let json = serde_json::to_string(&reason).unwrap();
let deserialized: StopReason = serde_json::from_str(&json).unwrap();
assert_eq!(reason, deserialized);
}
}
}