ai_sdk_provider/language_model/
content.rs

1use super::tool_result_output::ToolResultOutput;
2use crate::SharedProviderMetadata;
3use serde::{Deserialize, Serialize};
4
5/// Represents a single content element in a language model's response or message.
6///
7/// Content can take multiple forms depending on what the model generates or what is included
8/// in a message. This enum provides a unified interface for handling different types of content
9/// including text, files, tool calls, and citations.
10///
11/// # Variants
12///
13/// * `Text` - Plain text content generated by the model
14/// * `Reasoning` - Internal reasoning content from specialized reasoning models (e.g., o1)
15/// * `File` - Media content including images, audio, video, or other file types
16/// * `Source` - Citation or reference to an external source document
17/// * `ToolCall` - A tool/function call invocation from the model
18/// * `ToolResult` - The result of executing a tool or function
19///
20/// # Usage
21///
22/// Content elements are typically generated as part of a larger response and should be
23/// examined to determine the appropriate handling for each type.
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25#[serde(tag = "type", rename_all = "kebab-case")]
26pub enum Content {
27    /// Plain text content generated by the model
28    Text(TextPart),
29    /// Internal reasoning content from specialized reasoning models
30    Reasoning(ReasoningPart),
31    /// Media content including images, audio, video, or other file types
32    File(FilePart),
33    /// Citation or reference to an external source document
34    Source(SourcePart),
35    /// A tool or function call invocation from the model
36    ToolCall(ToolCallPart),
37    /// The result of executing a tool or function
38    ToolResult(ToolResultPart),
39}
40
41/// Plain text content part from a language model response.
42///
43/// Represents textual content generated by the model. This is the most common content type
44/// and contains the main textual output of the language model.
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46pub struct TextPart {
47    /// The actual text content
48    pub text: String,
49
50    /// Optional provider-specific metadata that may include usage information, model details,
51    /// or other provider-specific properties
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub provider_metadata: Option<SharedProviderMetadata>,
54}
55
56/// Internal reasoning content from advanced reasoning models.
57///
58/// Some specialized language models (such as o1) expose their internal reasoning process
59/// as structured content. This part contains the model's step-by-step reasoning before
60/// providing its final answer. Useful for understanding model behavior and debugging.
61///
62/// Note: Not all models support reasoning content. It will only be present when using
63/// models that are specifically designed for reasoning tasks.
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
65pub struct ReasoningPart {
66    /// The reasoning text describing the model's internal thought process
67    pub reasoning: String,
68
69    /// Optional provider-specific metadata
70    #[serde(skip_serializing_if = "Option::is_none")]
71    pub provider_metadata: Option<SharedProviderMetadata>,
72}
73
74/// File or media content part from a language model response.
75///
76/// Represents binary files, images, audio, video, or other media content generated or
77/// referenced by the model. The content can be provided as raw binary data or as a URL
78/// pointing to the file location.
79///
80/// # Example Use Cases
81///
82/// - Images generated by vision models
83/// - Audio files from speech synthesis models
84/// - Documents or archives created by processing models
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct FilePart {
88    /// File data, either as raw binary bytes or as a URL
89    pub data: super::FileData,
90
91    /// MIME type identifier (e.g., "image/jpeg", "audio/mp3", "application/pdf")
92    pub media_type: String,
93
94    /// Optional provider-specific metadata
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub provider_metadata: Option<SharedProviderMetadata>,
97}
98
99/// Citation or reference to an external source document.
100///
101/// When a model retrieves or references external sources (e.g., from a knowledge base,
102/// search results, or document repository), it includes source citations in the response.
103/// These allow you to trace the origins of information and verify facts used by the model.
104///
105/// # Fields
106///
107/// * `source_type` - The category of source (URL-based or document-based)
108/// * `id` - Unique identifier for the source within the response
109/// * `url` - Optional direct link to access the source
110/// * `title` - Optional human-readable title or name of the source
111/// * `provider_metadata` - Optional provider-specific details about the source
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct SourcePart {
115    /// Categorizes the source as either URL-based or document-based
116    pub source_type: SourceType,
117    /// Unique identifier for this source citation
118    pub id: String,
119
120    /// Optional URL pointing directly to the source material
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub url: Option<String>,
123
124    /// Optional descriptive title or name of the source
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub title: Option<String>,
127
128    /// Optional provider-specific metadata about the source
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub provider_metadata: Option<SharedProviderMetadata>,
131}
132
133/// Categorizes the type of source being cited.
134///
135/// Distinguishes between different source origins to help determine how to access
136/// or display the referenced material.
137///
138/// # Variants
139///
140/// * `Url` - Source is accessible via a direct HTTP/HTTPS URL
141/// * `Document` - Source is a document stored in a knowledge base or document repository
142#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
143#[serde(rename_all = "lowercase")]
144pub enum SourceType {
145    /// Source is accessible via a direct HTTP/HTTPS URL
146    Url,
147    /// Source is a document stored in a knowledge base or document repository
148    Document,
149}
150
151/// A request from the language model to invoke an external tool or function.
152///
153/// When a model detects that it needs to call a tool to accomplish a task, it emits
154/// a tool call with the tool name and arguments. The application is responsible for
155/// executing the tool and returning the results via a ToolResultPart.
156///
157/// # Fields
158///
159/// * `tool_call_id` - Unique identifier for correlating this call with its result
160/// * `tool_name` - Name of the tool to invoke (must match a provided tool definition)
161/// * `input` - Stringified JSON containing the tool arguments
162/// * `provider_executed` - If true, the provider automatically executed the tool
163/// * `dynamic` - If true, indicates a dynamic or runtime-generated tool call
164/// * `provider_metadata` - Optional provider-specific details
165///
166/// # Processing Tool Calls
167///
168/// When you receive a tool call, you should:
169/// 1. Extract the tool name and arguments
170/// 2. Locate the corresponding tool definition
171/// 3. Execute the tool with the provided arguments
172/// 4. Return a ToolResultPart with the results
173#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub struct ToolCallPart {
176    /// Unique identifier that correlates this tool call with its result
177    pub tool_call_id: String,
178    /// Name identifying which tool should be invoked
179    pub tool_name: String,
180    /// Stringified JSON containing the arguments for the tool
181    pub input: String,
182
183    /// Indicates whether the provider has already executed this tool
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub provider_executed: Option<bool>,
186
187    /// Indicates whether this is a dynamic or runtime-generated tool call
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub dynamic: Option<bool>,
190
191    /// Optional provider-specific metadata about the tool call
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub provider_metadata: Option<SharedProviderMetadata>,
194}
195
196/// The result of executing a tool or function in response to a model's tool call.
197///
198/// After the model requests a tool invocation via ToolCallPart, the results of that
199/// execution are communicated back through this message type. It contains the output
200/// from the tool execution, which may be text, structured data, or an error message.
201///
202/// # Fields
203///
204/// * `tool_call_id` - References the ToolCallPart this result corresponds to
205/// * `tool_name` - Name of the tool that was executed
206/// * `output` - The result from tool execution (text, structured, or error)
207/// * `preliminary` - If true, this is a partial result; more results may follow
208/// * `provider_metadata` - Optional provider-specific details about execution
209///
210/// # Including Tool Results in Messages
211///
212/// Tool results must be included in the message history when making subsequent
213/// requests, so the model can see what happened and make appropriate follow-up decisions.
214#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215#[serde(rename_all = "camelCase")]
216pub struct ToolResultPart {
217    /// Identifier linking this result to the corresponding tool call
218    pub tool_call_id: String,
219    /// Name of the tool that was executed
220    pub tool_name: String,
221    /// The structured output or result from executing the tool
222    pub output: ToolResultOutput,
223
224    /// If true, this is a preliminary result and more results may be forthcoming
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub preliminary: Option<bool>,
227
228    /// Optional provider-specific metadata about the tool execution
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub provider_metadata: Option<SharedProviderMetadata>,
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_content_text_serialization() {
239        let content = Content::Text(TextPart {
240            text: "Hello".into(),
241            provider_metadata: None,
242        });
243        let json = serde_json::to_value(&content).unwrap();
244        assert_eq!(json["type"], "text");
245        assert_eq!(json["text"], "Hello");
246    }
247
248    #[test]
249    fn test_content_tool_call() {
250        let content = Content::ToolCall(ToolCallPart {
251            tool_call_id: "call-123".into(),
252            tool_name: "search".into(),
253            input: r#"{"query":"test"}"#.into(),
254            provider_executed: None,
255            dynamic: None,
256            provider_metadata: None,
257        });
258        let json = serde_json::to_value(&content).unwrap();
259        assert_eq!(json["type"], "tool-call");
260        assert_eq!(json["toolCallId"], "call-123");
261        assert_eq!(json["toolName"], "search");
262    }
263
264    #[test]
265    fn test_content_reasoning() {
266        let content = Content::Reasoning(ReasoningPart {
267            reasoning: "Let me think...".into(),
268            provider_metadata: None,
269        });
270        let json = serde_json::to_value(&content).unwrap();
271        assert_eq!(json["type"], "reasoning");
272        assert_eq!(json["reasoning"], "Let me think...");
273    }
274
275    #[test]
276    fn test_source_type_serialization() {
277        let source = SourcePart {
278            source_type: SourceType::Url,
279            id: "src-1".into(),
280            url: Some("https://example.com".into()),
281            title: None,
282            provider_metadata: None,
283        };
284        let json = serde_json::to_value(&source).unwrap();
285        assert_eq!(json["sourceType"], "url");
286    }
287
288    #[test]
289    fn test_tool_result_text_output() {
290        let result = ToolResultPart {
291            tool_call_id: "call-123".into(),
292            tool_name: "test_tool".into(),
293            output: ToolResultOutput::Text {
294                value: "success".into(),
295                provider_metadata: None,
296            },
297            preliminary: None,
298            provider_metadata: None,
299        };
300        let json = serde_json::to_value(&result).unwrap();
301        assert_eq!(json["toolName"], "test_tool");
302        assert_eq!(json["output"]["type"], "text");
303        assert_eq!(json["output"]["value"], "success");
304    }
305
306    #[test]
307    fn test_tool_result_error_output() {
308        let result = ToolResultPart {
309            tool_call_id: "call-123".into(),
310            tool_name: "test_tool".into(),
311            output: ToolResultOutput::ErrorText {
312                value: "error occurred".into(),
313                provider_metadata: None,
314            },
315            preliminary: None,
316            provider_metadata: None,
317        };
318        let json = serde_json::to_value(&result).unwrap();
319        assert_eq!(json["output"]["type"], "error-text");
320        assert_eq!(json["output"]["value"], "error occurred");
321        assert_eq!(json["toolName"], "test_tool");
322    }
323}