use serde::{Deserialize, Serialize};
use serde_json::Value;
mod continuation;
mod policy;
pub use continuation::Continuation;
pub use policy::{
controls, input_modality_for_kind, input_modality_for_mime, normalize_capability_input,
normalize_constraint_input, normalize_input_token, payload_input_modalities, resolve,
BackendCapability, EffectiveGeneration, GenerationControls, GenerationParameters,
GenerationSupport, ModelCapabilities, DEFAULT_MAX_OUTPUT_TOKENS, INPUT_AUDIO, INPUT_FILE,
INPUT_IMAGE, INPUT_VIDEO, REASONING_EFFORT_LADDER,
};
pub use service::Image;
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct UseCase(pub String);
#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ModelMode(pub String);
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ModelConstraints {
#[serde(default)]
pub input: Vec<String>,
pub tool_calling: bool,
pub structured_output: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum MessageRole {
System,
#[default]
User,
Assistant,
Tool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentPart {
Text {
text: String,
},
Artifact {
uri: String,
mime_type: String,
},
Image { image: Image },
ToolCall(ToolCall),
ToolResult {
call_id: String,
result: Value,
is_error: bool,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub role: MessageRole,
pub content: Vec<ContentPart>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub continuation: Option<Continuation>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub input_schema: Value,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ToolCall {
pub id: String,
pub name: String,
pub arguments: Value,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct CompletionRequest {
pub use_case: UseCase,
pub model_mode: ModelMode,
pub messages: Vec<Message>,
pub tools: Vec<ToolDefinition>,
pub constraints: ModelConstraints,
pub max_output_tokens: Option<u32>,
pub diagnostics: bool,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ModelProfile {
pub profile_key: String,
pub context_window_tokens: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Default)]
pub enum FinishReason {
#[default]
Stop,
ToolCalls,
Length,
Other,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct TokenUsage {
pub input_tokens: u64,
pub output_tokens: u64,
pub cached_input_tokens: Option<u64>,
pub reasoning_output_tokens: Option<u64>,
pub credits: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Completion {
pub message: Message,
pub finish_reason: FinishReason,
pub usage: Option<TokenUsage>,
pub diagnostics: Option<Value>,
}
pub mod service;
impl MessageRole {
pub const fn as_str(self) -> &'static str {
match self {
Self::System => "system",
Self::User => "user",
Self::Assistant => "assistant",
Self::Tool => "tool",
}
}
}
impl Default for Message {
fn default() -> Self {
Self::text(MessageRole::User, "")
}
}
impl Message {
pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
Self {
role,
content: vec![ContentPart::Text { text: text.into() }],
continuation: None,
}
}
pub fn text_content(&self) -> String {
self.content
.iter()
.filter_map(|part| match part {
ContentPart::Text { text } => Some(text.as_str()),
_ => None,
})
.collect::<Vec<_>>()
.join("\n")
}
}
impl TokenUsage {
pub fn total_tokens(self) -> u64 {
self.input_tokens.saturating_add(self.output_tokens)
}
}
impl Message {
pub fn with_images(mut self, images: impl IntoIterator<Item = Image>) -> Self {
self.content
.extend(images.into_iter().map(|image| ContentPart::Image { image }));
self
}
}
pub const MESSAGE_SCHEMA: &str = include_str!("../schema/message.v1.schema.json");