use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize)]
pub struct ChatRequest {
pub model: String,
pub messages: Vec<ChatMessage>,
#[serde(skip_serializing_if = "Option::is_none")]
pub temperature: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_tokens: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_p: Option<f32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatResponse {
pub id: String,
pub object: String,
pub created: i64,
pub model: String,
pub choices: Vec<ChatChoice>,
pub usage: Usage,
}
impl ChatResponse {
pub fn text(&self) -> Option<&str> {
self.choices
.first()
.and_then(|c| c.message.content.as_deref())
}
pub fn content(&self) -> String {
self.text().unwrap_or("").to_string()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatChoice {
pub index: u32,
pub message: ChatMessage,
pub finish_reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatChunk {
pub id: String,
pub choices: Vec<ChatChunkChoice>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatChunkChoice {
pub delta: ChatDelta,
pub finish_reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatDelta {
pub content: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Usage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelList {
pub object: String,
pub data: Vec<Model>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Model {
pub id: String,
pub object: String,
pub created: i64,
pub owned_by: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct EmbeddingRequest {
pub input: EmbeddingInput,
pub model: String,
}
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum EmbeddingInput {
Single(String),
Multiple(Vec<String>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingResponse {
pub data: Vec<EmbeddingData>,
pub model: String,
pub usage: EmbeddingUsage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingData {
pub embedding: Vec<f32>,
pub index: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmbeddingUsage {
pub prompt_tokens: u32,
pub total_tokens: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct RagUploadRequest {
pub content: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub filename: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RagUploadResponse {
pub document_id: String,
pub chunks_count: usize,
}
#[derive(Debug, Clone, Serialize)]
pub struct RagQueryRequest {
pub query: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_k: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub debug: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stream: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quality_mode: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RagQueryResponse {
pub answer: String,
pub sources: Vec<RagSource>,
#[serde(default)]
pub audit: Option<RagAuditInfo>,
#[serde(default)]
pub confidence: Option<f32>,
#[serde(default)]
pub grounded: Option<bool>,
}
impl RagQueryResponse {
pub fn confidence(&self) -> Option<f32> {
self.audit
.as_ref()
.map(|a| a.confidence)
.or(self.confidence)
}
pub fn grounded(&self) -> Option<bool> {
self.audit.as_ref().map(|a| a.grounded).or(self.grounded)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RagAuditInfo {
pub confidence: f32,
pub retrieval_path: String,
pub sources_evaluated: usize,
pub sources_used: usize,
pub best_score: f32,
pub grounded: bool,
pub confidence_label: String,
pub model: String,
pub latency_ms: u64,
#[serde(default)]
pub candidates_found: Option<usize>,
#[serde(default)]
pub candidates_after_tenant: Option<usize>,
#[serde(default)]
pub candidates_after_score: Option<usize>,
#[serde(default)]
pub query_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RagSource {
pub document_id: String,
pub content: String,
pub score: f32,
#[serde(default)]
pub chunk_id: Option<String>,
#[serde(default)]
pub metadata: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct OrchestratorRequest {
pub prompt: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OrchestratorResponse {
pub final_output: String,
}
impl ChatRequest {
pub fn new(model: impl Into<String>, messages: Vec<ChatMessage>) -> Self {
Self {
model: model.into(),
messages,
temperature: None,
max_tokens: None,
stream: None,
top_p: None,
stop: None,
}
}
pub fn quick(model: impl Into<String>, message: impl Into<String>) -> Self {
Self::new(model, vec![ChatMessage::user(message)])
}
}
impl ChatMessage {
pub fn user(content: impl Into<String>) -> Self {
Self {
role: "user".into(),
content: Some(content.into()),
name: None,
}
}
pub fn system(content: impl Into<String>) -> Self {
Self {
role: "system".into(),
content: Some(content.into()),
name: None,
}
}
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: "assistant".into(),
content: Some(content.into()),
name: None,
}
}
}