1use serde::{Deserialize, Serialize};
3use serde_json::Value;
4mod continuation;
5mod policy;
6pub use continuation::Continuation;
7pub use policy::{GenerationParameters, ModelCapabilities};
8pub use service::Image;
9
10#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
12#[serde(transparent)]
13pub struct UseCase(pub String);
14
15#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
17#[serde(transparent)]
18pub struct ModelMode(pub String);
19
20#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
23#[serde(deny_unknown_fields)]
24pub struct ModelConstraints {
25 pub vision: bool,
27 pub tool_calling: bool,
29 pub structured_output: bool,
31}
32
33#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36#[derive(Default)]
37pub enum MessageRole {
38 System,
40 #[default]
42 User,
43 Assistant,
45 Tool,
47}
48
49#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
51#[serde(tag = "type", rename_all = "snake_case")]
52pub enum ContentPart {
53 Text {
55 text: String,
57 },
58 Artifact {
60 uri: String,
62 mime_type: String,
64 },
65 Image { image: Image },
67 ToolCall(ToolCall),
69 ToolResult {
71 call_id: String,
73 result: Value,
75 is_error: bool,
77 },
78}
79
80#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
82pub struct Message {
83 pub role: MessageRole,
85 pub content: Vec<ContentPart>,
87 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub continuation: Option<Continuation>,
90}
91
92#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
94pub struct ToolDefinition {
95 pub name: String,
96 pub description: String,
97 pub input_schema: Value,
98}
99#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
101pub struct ToolCall {
102 pub id: String,
104 pub name: String,
106 pub arguments: Value,
108}
109
110#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
112pub struct CompletionRequest {
113 pub use_case: UseCase,
115 pub model_mode: ModelMode,
117 pub messages: Vec<Message>,
119 pub tools: Vec<ToolDefinition>,
121 pub constraints: ModelConstraints,
123 pub max_output_tokens: Option<u32>,
125 pub diagnostics: bool,
127}
128
129#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
134pub struct ModelProfile {
135 pub profile_key: String,
137 pub context_window_tokens: u32,
139}
140
141#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
143#[serde(rename_all = "snake_case")]
144#[derive(Default)]
145pub enum FinishReason {
146 #[default]
148 Stop,
149 ToolCalls,
151 Length,
153 Other,
155}
156
157#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
159pub struct TokenUsage {
160 pub input_tokens: u64,
162 pub output_tokens: u64,
164 pub cached_input_tokens: Option<u64>,
166 pub reasoning_output_tokens: Option<u64>,
168 pub credits: Option<u64>,
170}
171
172#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
174pub struct Completion {
175 pub message: Message,
177 pub finish_reason: FinishReason,
179 pub usage: Option<TokenUsage>,
181 pub diagnostics: Option<Value>,
183}
184
185pub mod service;
186
187impl MessageRole {
188 pub const fn as_str(self) -> &'static str {
189 match self {
190 Self::System => "system",
191 Self::User => "user",
192 Self::Assistant => "assistant",
193 Self::Tool => "tool",
194 }
195 }
196}
197
198impl Default for Message {
199 fn default() -> Self {
200 Self::text(MessageRole::User, "")
201 }
202}
203impl Message {
204 pub fn text(role: MessageRole, text: impl Into<String>) -> Self {
205 Self {
206 role,
207 content: vec![ContentPart::Text { text: text.into() }],
208 continuation: None,
209 }
210 }
211 pub fn text_content(&self) -> String {
212 self.content
213 .iter()
214 .filter_map(|part| match part {
215 ContentPart::Text { text } => Some(text.as_str()),
216 _ => None,
217 })
218 .collect::<Vec<_>>()
219 .join("\n")
220 }
221}
222
223impl TokenUsage {
224 pub fn total_tokens(self) -> u64 {
225 self.input_tokens.saturating_add(self.output_tokens)
226 }
227}
228impl Message {
229 pub fn with_images(mut self, images: impl IntoIterator<Item = Image>) -> Self {
230 self.content
231 .extend(images.into_iter().map(|image| ContentPart::Image { image }));
232 self
233 }
234}
235
236pub const MESSAGE_SCHEMA: &str = include_str!("../schema/message.v1.schema.json");