use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub effort: Option<EffortLevel>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum EffortLevel {
Low,
Medium,
High,
#[serde(rename = "xhigh", alias = "x_high")]
XHigh,
Max,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CitationConfig {
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Citation {
#[serde(rename = "char_location")]
CharLocation { cited_text: String, start_index: u32, end_index: u32, document_index: u32 },
#[serde(rename = "page_location")]
PageLocation {
cited_text: String,
start_page_number: u32,
end_page_number: u32,
document_index: u32,
},
#[serde(rename = "content_block_location")]
ContentBlockLocation {
cited_text: String,
start_block_index: u32,
end_block_index: u32,
document_index: u32,
},
#[serde(rename = "search_result_location")]
SearchResultLocation {
cited_text: String,
start_index: u32,
end_index: u32,
search_result_index: u32,
},
#[serde(rename = "web_search_result_location")]
WebSearchResultLocation {
cited_text: String,
encrypted_index: String,
title: String,
url: String,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinkingBlock {
pub thinking: String,
pub signature: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RedactedThinkingBlock {
pub data: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn effort_level_low_round_trip() {
let value = EffortLevel::Low;
let json = serde_json::to_string(&value).unwrap();
assert_eq!(json, r#""low""#);
let deserialized: EffortLevel = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, EffortLevel::Low);
}
#[test]
fn effort_level_medium_round_trip() {
let value = EffortLevel::Medium;
let json = serde_json::to_string(&value).unwrap();
assert_eq!(json, r#""medium""#);
let deserialized: EffortLevel = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, EffortLevel::Medium);
}
#[test]
fn effort_level_high_round_trip() {
let value = EffortLevel::High;
let json = serde_json::to_string(&value).unwrap();
assert_eq!(json, r#""high""#);
let deserialized: EffortLevel = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, EffortLevel::High);
}
#[test]
fn effort_level_xhigh_round_trip() {
let value = EffortLevel::XHigh;
let json = serde_json::to_string(&value).unwrap();
assert_eq!(json, r#""xhigh""#);
let deserialized: EffortLevel = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, EffortLevel::XHigh);
}
#[test]
fn effort_level_xhigh_deserialize_x_high_alias() {
let deserialized: EffortLevel = serde_json::from_str(r#""x_high""#).unwrap();
assert_eq!(deserialized, EffortLevel::XHigh);
}
#[test]
fn effort_level_max_round_trip() {
let value = EffortLevel::Max;
let json = serde_json::to_string(&value).unwrap();
assert_eq!(json, r#""max""#);
let deserialized: EffortLevel = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, EffortLevel::Max);
}
#[test]
fn output_config_with_effort() {
let config = OutputConfig { effort: Some(EffortLevel::High) };
let json = serde_json::to_string(&config).unwrap();
assert_eq!(json, r#"{"effort":"high"}"#);
let deserialized: OutputConfig = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.effort, Some(EffortLevel::High));
}
#[test]
fn output_config_without_effort() {
let config = OutputConfig { effort: None };
let json = serde_json::to_string(&config).unwrap();
assert_eq!(json, r#"{}"#);
let deserialized: OutputConfig = serde_json::from_str(&json).unwrap();
assert!(deserialized.effort.is_none());
}
#[test]
fn citation_config_true() {
let config = CitationConfig { enabled: true };
let json = serde_json::to_string(&config).unwrap();
assert_eq!(json, r#"{"enabled":true}"#);
let deserialized: CitationConfig = serde_json::from_str(&json).unwrap();
assert!(deserialized.enabled);
}
#[test]
fn citation_config_false() {
let config = CitationConfig { enabled: false };
let json = serde_json::to_string(&config).unwrap();
assert_eq!(json, r#"{"enabled":false}"#);
let deserialized: CitationConfig = serde_json::from_str(&json).unwrap();
assert!(!deserialized.enabled);
}
#[test]
fn citation_char_location_round_trip() {
let value = Citation::CharLocation {
cited_text: "foo".into(),
start_index: 10,
end_index: 13,
document_index: 0,
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"type":"char_location","cited_text":"foo","start_index":10,"end_index":13,"document_index":0}"#;
assert_eq!(json, expected);
let deserialized: Citation = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, Citation::CharLocation { .. }));
}
#[test]
fn citation_page_location_round_trip() {
let value = Citation::PageLocation {
cited_text: "bar".into(),
start_page_number: 1,
end_page_number: 2,
document_index: 0,
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"type":"page_location","cited_text":"bar","start_page_number":1,"end_page_number":2,"document_index":0}"#;
assert_eq!(json, expected);
let deserialized: Citation = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, Citation::PageLocation { .. }));
}
#[test]
fn citation_content_block_location_round_trip() {
let value = Citation::ContentBlockLocation {
cited_text: "baz".into(),
start_block_index: 0,
end_block_index: 1,
document_index: 0,
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"type":"content_block_location","cited_text":"baz","start_block_index":0,"end_block_index":1,"document_index":0}"#;
assert_eq!(json, expected);
let deserialized: Citation = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, Citation::ContentBlockLocation { .. }));
}
#[test]
fn citation_search_result_location_round_trip() {
let value = Citation::SearchResultLocation {
cited_text: "qux".into(),
start_index: 0,
end_index: 3,
search_result_index: 1,
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"type":"search_result_location","cited_text":"qux","start_index":0,"end_index":3,"search_result_index":1}"#;
assert_eq!(json, expected);
let deserialized: Citation = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, Citation::SearchResultLocation { .. }));
}
#[test]
fn citation_web_search_result_location_round_trip() {
let value = Citation::WebSearchResultLocation {
cited_text: "hello".into(),
encrypted_index: "abc123".into(),
title: "Example".into(),
url: "https://example.com".into(),
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"type":"web_search_result_location","cited_text":"hello","encrypted_index":"abc123","title":"Example","url":"https://example.com"}"#;
assert_eq!(json, expected);
let deserialized: Citation = serde_json::from_str(&json).unwrap();
assert!(matches!(deserialized, Citation::WebSearchResultLocation { .. }));
}
#[test]
fn thinking_block_round_trip() {
let value = ThinkingBlock {
thinking: "Let me reason about this...".into(),
signature: "sig_abc123".into(),
};
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"thinking":"Let me reason about this...","signature":"sig_abc123"}"#;
assert_eq!(json, expected);
let deserialized: ThinkingBlock = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.thinking, "Let me reason about this...");
assert_eq!(deserialized.signature, "sig_abc123");
}
#[test]
fn redacted_thinking_block_round_trip() {
let value = RedactedThinkingBlock { data: "encrypted_data_here".into() };
let json = serde_json::to_string(&value).unwrap();
let expected = r#"{"data":"encrypted_data_here"}"#;
assert_eq!(json, expected);
let deserialized: RedactedThinkingBlock = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.data, "encrypted_data_here");
}
}