Skip to main content

llmsdk_provider/language_model/
prompt.rs

1//! Prompt / message types passed to a [`super::LanguageModel`].
2//!
3//! Mirrors `language-model-v4-prompt.ts`. JSON wire format is preserved
4//! 1:1 with ai-sdk so the same provider HTTP body works across SDKs.
5// Rust guideline compliant 2026-02-21
6
7use serde::{Deserialize, Serialize};
8
9use crate::json::JsonValue;
10use crate::shared::{FileData, ProviderOptions};
11
12/// An ordered list of [`Message`]s sent to the model.
13pub type Prompt = Vec<Message>;
14
15/// One turn in a conversation.
16///
17/// Tagged by `role` on the wire. Each variant carries its own content
18/// shape, mirroring ai-sdk's per-role part lists.
19#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20#[serde(tag = "role", rename_all = "lowercase")]
21pub enum Message {
22    /// System instruction.
23    System {
24        /// System prompt text.
25        content: String,
26        /// Provider-specific options for this message.
27        #[serde(
28            default,
29            rename = "providerOptions",
30            skip_serializing_if = "Option::is_none"
31        )]
32        provider_options: Option<ProviderOptions>,
33    },
34    /// User message; multimodal.
35    User {
36        /// Ordered parts.
37        content: Vec<UserPart>,
38        /// Provider-specific options for this message.
39        #[serde(
40            default,
41            rename = "providerOptions",
42            skip_serializing_if = "Option::is_none"
43        )]
44        provider_options: Option<ProviderOptions>,
45    },
46    /// Assistant message; can include reasoning and tool calls.
47    Assistant {
48        /// Ordered parts.
49        content: Vec<AssistantPart>,
50        /// Provider-specific options for this message.
51        #[serde(
52            default,
53            rename = "providerOptions",
54            skip_serializing_if = "Option::is_none"
55        )]
56        provider_options: Option<ProviderOptions>,
57    },
58    /// Tool message carrying tool results back to the model.
59    Tool {
60        /// Ordered parts.
61        content: Vec<ToolMessagePart>,
62        /// Provider-specific options for this message.
63        #[serde(
64            default,
65            rename = "providerOptions",
66            skip_serializing_if = "Option::is_none"
67        )]
68        provider_options: Option<ProviderOptions>,
69    },
70}
71
72/// Content part allowed in a [`Message::User`].
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
74#[serde(tag = "type", rename_all = "kebab-case")]
75pub enum UserPart {
76    /// Plain text.
77    Text(TextPart),
78    /// File attachment (image, audio, pdf, ...).
79    File(FilePart),
80}
81
82/// Content part allowed in a [`Message::Assistant`].
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
84#[serde(tag = "type", rename_all = "kebab-case")]
85pub enum AssistantPart {
86    /// Plain text reply.
87    Text(TextPart),
88    /// File generated by the model.
89    File(FilePart),
90    /// Reasoning trace.
91    Reasoning {
92        /// Reasoning text.
93        text: String,
94        /// Provider-specific options.
95        #[serde(
96            default,
97            rename = "providerOptions",
98            skip_serializing_if = "Option::is_none"
99        )]
100        provider_options: Option<ProviderOptions>,
101    },
102    /// File generated as part of a reasoning trace.
103    ReasoningFile {
104        /// File payload.
105        data: FileData,
106        /// IANA media type.
107        #[serde(rename = "mediaType")]
108        media_type: String,
109        /// Provider-specific options.
110        #[serde(
111            default,
112            rename = "providerOptions",
113            skip_serializing_if = "Option::is_none"
114        )]
115        provider_options: Option<ProviderOptions>,
116    },
117    /// Provider-specific opaque part. `kind` is `provider.subtype`.
118    Custom {
119        /// Custom kind tag, e.g. `"openai.web_search_result"`.
120        kind: String,
121        /// Provider-specific options.
122        #[serde(
123            default,
124            rename = "providerOptions",
125            skip_serializing_if = "Option::is_none"
126        )]
127        provider_options: Option<ProviderOptions>,
128    },
129    /// Tool invocation the model wants the client (or provider) to execute.
130    ToolCall(ToolCallPart),
131    /// Inline tool result (used when the same turn carries the result).
132    ToolResult(ToolResultPart),
133}
134
135/// Content part allowed in a [`Message::Tool`].
136#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
137#[serde(tag = "type", rename_all = "kebab-case")]
138pub enum ToolMessagePart {
139    /// Result for a previously requested tool call.
140    ToolResult(ToolResultPart),
141    /// Approval response for a provider-executed tool call.
142    ToolApprovalResponse(ToolApprovalResponsePart),
143}
144
145/// Plain text part.
146#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
147pub struct TextPart {
148    /// Text content.
149    pub text: String,
150    /// Provider-specific options.
151    #[serde(
152        default,
153        rename = "providerOptions",
154        skip_serializing_if = "Option::is_none"
155    )]
156    pub provider_options: Option<ProviderOptions>,
157}
158
159/// File attachment part.
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161pub struct FilePart {
162    /// Optional filename hint.
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub filename: Option<String>,
165    /// File payload.
166    pub data: FileData,
167    /// IANA media type (full `type/subtype` or top-level only).
168    #[serde(rename = "mediaType")]
169    pub media_type: String,
170    /// Provider-specific options.
171    #[serde(
172        default,
173        rename = "providerOptions",
174        skip_serializing_if = "Option::is_none"
175    )]
176    pub provider_options: Option<ProviderOptions>,
177}
178
179/// Tool call requested by the assistant.
180#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
181pub struct ToolCallPart {
182    /// Unique id for matching with [`ToolResultPart`].
183    #[serde(rename = "toolCallId")]
184    pub tool_call_id: String,
185    /// Tool name.
186    #[serde(rename = "toolName")]
187    pub tool_name: String,
188    /// Arguments matching the tool's input schema.
189    pub input: JsonValue,
190    /// `true` when the provider will execute the tool itself.
191    #[serde(
192        default,
193        rename = "providerExecuted",
194        skip_serializing_if = "Option::is_none"
195    )]
196    pub provider_executed: Option<bool>,
197    /// `true` when the tool name is only known at runtime (e.g. MCP tools
198    /// proxied through a `provider` tool). Mirrors the `dynamic` field on
199    /// [`super::stream_part::StreamPart::ToolCall`].
200    #[serde(default, skip_serializing_if = "Option::is_none")]
201    pub dynamic: Option<bool>,
202    /// Provider-specific options.
203    #[serde(
204        default,
205        rename = "providerOptions",
206        skip_serializing_if = "Option::is_none"
207    )]
208    pub provider_options: Option<ProviderOptions>,
209}
210
211/// Result of a tool invocation.
212#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
213pub struct ToolResultPart {
214    /// Id of the originating tool call.
215    #[serde(rename = "toolCallId")]
216    pub tool_call_id: String,
217    /// Tool name.
218    #[serde(rename = "toolName")]
219    pub tool_name: String,
220    /// Output payload.
221    pub output: super::content::ToolResultOutput,
222    /// Provider-specific options.
223    #[serde(
224        default,
225        rename = "providerOptions",
226        skip_serializing_if = "Option::is_none"
227    )]
228    pub provider_options: Option<ProviderOptions>,
229}
230
231/// User decision on a provider-executed tool call.
232#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
233pub struct ToolApprovalResponsePart {
234    /// Id of the originating approval request.
235    #[serde(rename = "approvalId")]
236    pub approval_id: String,
237    /// `true` to allow execution.
238    pub approved: bool,
239    /// Optional human-readable reason.
240    #[serde(default, skip_serializing_if = "Option::is_none")]
241    pub reason: Option<String>,
242    /// Provider-specific options.
243    #[serde(
244        default,
245        rename = "providerOptions",
246        skip_serializing_if = "Option::is_none"
247    )]
248    pub provider_options: Option<ProviderOptions>,
249}