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")]
60pub enum ContentPart {
61 Text {
63 text: String,
65 },
66 Artifact {
68 uri: String,
70 mime_type: String,
72 },
73 Image { image: Image },
75 ToolCall(ToolCall),
77 ToolResult {
79 call_id: String,
81 result: Value,
83 is_error: bool,
85 },
86}
87
88#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
90pub struct Message {
91 pub role: MessageRole,
93 pub content: Vec<ContentPart>,
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 pub continuation: Option<Continuation>,
98}
99
100#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
102pub struct ToolDefinition {
103 pub name: String,
104 pub description: String,
105 pub input_schema: Value,
106}
107#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
109pub struct ToolCall {
110 pub id: String,
112 pub name: String,
114 pub arguments: Value,
116}
117
118#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
120pub struct CompletionRequest {
121 pub use_case: UseCase,
123 pub model_mode: ModelMode,
125 pub messages: Vec<Message>,
127 pub tools: Vec<ToolDefinition>,
129 pub constraints: ModelConstraints,
131 pub max_output_tokens: Option<u32>,
133 pub diagnostics: bool,
135}
136
137#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
142pub struct ModelProfile {
143 pub profile_key: String,
145 pub context_window_tokens: u32,
147}
148
149#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
151#[serde(rename_all = "snake_case")]
152#[derive(Default)]
153pub enum FinishReason {
154 #[default]
156 Stop,
157 ToolCalls,
159 Length,
161 Other,
163}
164
165#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
167pub struct TokenUsage {
168 pub input_tokens: u64,
170 pub output_tokens: u64,
172 pub cached_input_tokens: Option<u64>,
174 pub reasoning_output_tokens: Option<u64>,
176 pub credits: Option<u64>,
178}
179
180#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
182pub struct Completion {
183 pub message: Message,
185 pub finish_reason: FinishReason,
187 pub usage: Option<TokenUsage>,
189 pub diagnostics: Option<Value>,
191}
192
193pub mod service;
194
195impl MessageRole {
196 pub const fn as_str(self) -> &'static str {
197 match self {
198 Self::System => "system",
199 Self::User => "user",
200 Self::Assistant => "assistant",
201 Self::Tool => "tool",
202 }
203 }
204}
205
206impl Default for Message {
207 fn default() -> Self {
208 Self::text(MessageRole::User, "")
209 }
210}
211impl Message {
212 pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
213 Self {
214 role,
215 content: vec![ContentPart::Text { text: text.into() }],
216 continuation: None,
217 }
218 }
219 pub fn text_content(&self) -> String {
220 self.content
221 .iter()
222 .filter_map(|part| match part {
223 ContentPart::Text { text } => Some(text.as_str()),
224 _ => None,
225 })
226 .collect::<Vec<_>>()
227 .join("\n")
228 }
229}
230
231impl TokenUsage {
232 pub fn total_tokens(self) -> u64 {
233 self.input_tokens.saturating_add(self.output_tokens)
234 }
235}
236impl Message {
237 pub fn with_images(mut self, images: impl IntoIterator<Item = Image>) -> Self {
238 self.content
239 .extend(images.into_iter().map(|image| ContentPart::Image { image }));
240 self
241 }
242}
243
244pub const MESSAGE_SCHEMA: &str = include_str!("../schema/message.v1.schema.json");