1use serde::{Deserialize, Serialize};
3use serde_json::Value;
4mod continuation;
5mod policy;
6pub use continuation::Continuation;
7pub use policy::{
8 controls, input_modality_for_kind, input_modality_for_mime, normalize_capability_input,
9 normalize_constraint_input, normalize_input_token, payload_input_modalities, resolve,
10 BackendCapability, EffectiveGeneration, GenerationControls, GenerationParameters,
11 GenerationSupport, ModelCapabilities, DEFAULT_MAX_OUTPUT_TOKENS, INPUT_AUDIO, INPUT_FILE,
12 INPUT_IMAGE, INPUT_VIDEO, REASONING_EFFORT_LADDER,
13};
14pub use service::Image;
15
16#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
18#[serde(transparent)]
19pub struct UseCase(pub String);
20
21#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
23#[serde(transparent)]
24pub struct ModelMode(pub String);
25
26#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
30#[serde(deny_unknown_fields)]
31pub struct ModelConstraints {
32 #[serde(default)]
34 pub input: Vec<String>,
35 pub tool_calling: bool,
37 pub structured_output: bool,
39}
40
41#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
43#[serde(rename_all = "snake_case")]
44#[derive(Default)]
45pub enum MessageRole {
46 System,
48 #[default]
50 User,
51 Assistant,
53 Tool,
55}
56
57#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
59#[serde(tag = "type", rename_all = "snake_case")]
60#[serde(deny_unknown_fields)]
61pub enum ContentPart {
62 Text {
64 text: String,
66 },
67 Artifact {
69 uri: String,
71 mime_type: String,
73 },
74 Image { image: Image },
76 ToolCall(ToolCall),
78 ToolResult {
80 call_id: String,
82 result: Value,
84 is_error: bool,
86 },
87}
88
89#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
91#[serde(deny_unknown_fields)]
92pub struct Message {
93 pub role: MessageRole,
95 pub content: Vec<ContentPart>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub continuation: Option<Continuation>,
100}
101
102#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
104pub struct ToolDefinition {
105 pub name: String,
106 pub description: String,
107 pub input_schema: Value,
108}
109#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
111#[serde(deny_unknown_fields)]
112pub struct ToolCall {
113 pub id: String,
115 pub name: String,
117 pub arguments: Value,
119}
120
121#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
123pub struct CompletionRequest {
124 pub use_case: UseCase,
126 pub model_mode: ModelMode,
128 pub messages: Vec<Message>,
130 pub tools: Vec<ToolDefinition>,
132 pub constraints: ModelConstraints,
134 pub max_output_tokens: Option<u32>,
136 pub diagnostics: bool,
138}
139
140#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
145pub struct ModelProfile {
146 pub profile_key: String,
148 pub context_window_tokens: u32,
150}
151
152#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
154#[serde(rename_all = "snake_case")]
155#[derive(Default)]
156pub enum FinishReason {
157 #[default]
159 Stop,
160 ToolCalls,
162 Length,
164 Other,
166}
167
168#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
170pub struct TokenUsage {
171 pub input_tokens: u64,
173 pub output_tokens: u64,
175 pub cached_input_tokens: Option<u64>,
177 pub reasoning_output_tokens: Option<u64>,
179 pub credits: Option<u64>,
181}
182
183#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
185pub struct Completion {
186 pub message: Message,
188 pub finish_reason: FinishReason,
190 pub usage: Option<TokenUsage>,
192 pub diagnostics: Option<Value>,
194}
195
196pub mod service;
197
198impl MessageRole {
199 pub const fn as_str(self) -> &'static str {
200 match self {
201 Self::System => "system",
202 Self::User => "user",
203 Self::Assistant => "assistant",
204 Self::Tool => "tool",
205 }
206 }
207}
208
209impl Default for Message {
210 fn default() -> Self {
211 Self::text(MessageRole::User, "")
212 }
213}
214impl Message {
215 pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
216 Self {
217 role,
218 content: vec![ContentPart::Text { text: text.into() }],
219 continuation: None,
220 }
221 }
222 pub fn text_content(&self) -> String {
223 self.content
224 .iter()
225 .filter_map(|part| match part {
226 ContentPart::Text { text } => Some(text.as_str()),
227 _ => None,
228 })
229 .collect::<Vec<_>>()
230 .join("\n")
231 }
232}
233
234impl TokenUsage {
235 pub fn total_tokens(self) -> u64 {
236 self.input_tokens.saturating_add(self.output_tokens)
237 }
238}
239impl Message {
240 pub fn with_images(mut self, images: impl IntoIterator<Item = Image>) -> Self {
241 self.content
242 .extend(images.into_iter().map(|image| ContentPart::Image { image }));
243 self
244 }
245}
246
247pub const MESSAGE_SCHEMA: &str = include_str!("../schema/message.v1.schema.json");