pub mod anthropic;
pub mod ollama;
pub mod openai;
use std::collections::HashMap;
use crate::diagnostic::StopReason;
use crate::error::Result;
#[cfg(feature = "streaming")]
use crate::streaming::FlexStream;
use crate::value::FlexValue;
#[derive(Debug, Clone)]
pub struct NormalizedResponse {
pub id: String,
pub model: String,
pub content: Vec<ContentBlock>,
pub stop_reason: StopReason,
pub usage: Usage,
pub raw: FlexValue,
}
#[derive(Debug, Clone)]
pub enum ContentBlock {
Text {
text: String,
},
ToolUse {
id: String,
name: String,
input: FlexValue,
},
Unknown {
block_type: String,
data: FlexValue,
},
}
#[derive(Debug, Clone, Default)]
pub struct Usage {
pub input_tokens: u64,
pub output_tokens: u64,
pub cache_read_tokens: Option<u64>,
pub cache_creation_tokens: Option<u64>,
pub extra: HashMap<String, serde_json::Value>,
}
pub trait ProviderAdapter: Send + Sync {
fn parse_response(&self, body: &FlexValue) -> Result<NormalizedResponse>;
fn emit_response(&self, response: &NormalizedResponse) -> serde_json::Value;
#[cfg(feature = "streaming")]
fn stream_parser(&self) -> FlexStream;
}
impl ContentBlock {
pub fn is_text(&self) -> bool {
matches!(self, ContentBlock::Text { .. })
}
pub fn is_tool_use(&self) -> bool {
matches!(self, ContentBlock::ToolUse { .. })
}
pub fn as_text(&self) -> Option<&str> {
match self {
ContentBlock::Text { text } => Some(text),
_ => None,
}
}
pub fn as_tool_use(&self) -> Option<(&str, &str, &FlexValue)> {
match self {
ContentBlock::ToolUse { id, name, input } => Some((id, name, input)),
_ => None,
}
}
}
impl NormalizedResponse {
pub fn text(&self) -> String {
self.content
.iter()
.filter_map(|b| b.as_text())
.collect::<Vec<_>>()
.join("")
}
pub fn tool_uses(&self) -> Vec<&ContentBlock> {
self.content.iter().filter(|b| b.is_tool_use()).collect()
}
pub fn has_tool_use(&self) -> bool {
self.content.iter().any(|b| b.is_tool_use())
}
}