Skip to main content

rig_core/completion/
message.rs

1use crate::error::ProviderError;
2use serde::{Deserialize, Serialize};
3use std::{convert::Infallible, str::FromStr};
4use thiserror::Error;
5
6/// A provider-agnostic chat message.
7///
8/// Messages are role-tagged and may contain one or many content items, including
9/// text, images, audio, documents, tool calls, and tool results. Provider modules
10/// are responsible for translating these generic messages into provider-native
11/// request bodies. That conversion may be lossy when a provider does not support
12/// a particular content type.
13#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
14#[serde(tag = "role", rename_all = "lowercase")]
15pub enum Message {
16    /// System message containing instruction text.
17    System { content: String },
18
19    /// User message containing one or more content types defined by `UserContent`.
20    User { content: Vec<UserContent> },
21
22    /// An assistant turn: its blocks in the order the provider produced
23    /// them, and where they came from.
24    Assistant(AssistantMessage),
25}
26
27pub mod citation;
28mod identity;
29mod native;
30
31pub use citation::{Citation, DocumentRange, Source, SourceLocation, Span};
32
33pub use identity::{CallId, EmptyCallId, EmptyToolName, LocalCallId, ProviderCallId, ToolName};
34pub use native::{Api, Fingerprint, Native, Opaque, Origin, StopReason};
35
36/// One assistant turn.
37///
38/// `content` holds one block per provider output item, in the order the
39/// provider produced them. `origin` names the wire, provider and model that
40/// produced the turn; a hand-built turn has none and always replays from its
41/// canonical fields.
42#[non_exhaustive]
43#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
44pub struct AssistantMessage {
45    /// The blocks, in provider order.
46    pub content: Vec<AssistantContent>,
47    /// Who produced the turn.
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub origin: Option<Origin>,
50    /// How the turn ended.
51    #[serde(default, skip_serializing_if = "Option::is_none")]
52    pub stop: Option<StopReason>,
53}
54
55impl AssistantMessage {
56    /// A hand-built turn of `content`: no origin or stop.
57    pub fn new(content: Vec<AssistantContent>) -> Self {
58        Self {
59            content,
60            ..Self::default()
61        }
62    }
63
64    /// The turn a runtime rolls back over: what arrived of a reply from
65    /// `origin`, stopped to call tools, since the runtime answers its calls.
66    /// `content` keeps only the provider items its fold kept.
67    pub fn rolled_back(origin: Option<Origin>, content: Vec<AssistantContent>) -> Self {
68        Self {
69            content,
70            origin,
71            stop: Some(StopReason::ToolUse),
72        }
73    }
74
75    /// The turn a run failed on before the reply ended: kept in history,
76    /// never replayed.
77    pub fn aborted(
78        origin: Option<Origin>,
79        content: Vec<AssistantContent>,
80        reason: impl Into<String>,
81    ) -> Self {
82        Self {
83            content,
84            origin,
85            stop: Some(StopReason::Aborted(reason.into())),
86        }
87    }
88
89    /// This turn holding `content` in place of its blocks.
90    pub fn with_content(mut self, content: Vec<AssistantContent>) -> Self {
91        self.content = content;
92        self
93    }
94
95    /// This turn with `origin` as who produced it.
96    pub fn with_origin(mut self, origin: impl Into<Option<Origin>>) -> Self {
97        self.origin = origin.into();
98        self
99    }
100
101    /// This turn with `stop` as how it ended.
102    pub fn with_stop(mut self, stop: impl Into<Option<StopReason>>) -> Self {
103        self.stop = stop.into();
104        self
105    }
106
107    /// The tool calls, in order.
108    pub fn tool_calls(&self) -> impl Iterator<Item = &ToolCall> {
109        self.content.iter().filter_map(|part| match part {
110            AssistantContent::ToolCall(call) => Some(call),
111            _ => None,
112        })
113    }
114}
115
116/// Whether no block of the choice is an answer
117/// ([`AssistantContent::is_answer`]): reasoning, provider-only items and
118/// blank text are not, even when retained in history.
119pub fn turn_delivered_no_answer(choice: &[AssistantContent]) -> bool {
120    !choice.iter().any(AssistantContent::is_answer)
121}
122
123/// Why a run fails on a turn, or `None` when it goes on. A turn that will
124/// not replay ([`StopReason::is_failure`], the rule `adapt` skips it by)
125/// runs none of its tool calls. A turn the provider failed
126/// ([`StopReason::Error`]) fails even when it holds an answer, so the caller
127/// never takes as a success a turn the next request leaves out. A turn that
128/// delivered no answer ([`turn_delivered_no_answer`]) also fails when the
129/// output budget or a filter cut it (`finish` is truncating). A turn that
130/// ended at the token limit with a call is finished, so its call runs.
131/// Every runtime reads this one rule.
132pub fn turn_failure(
133    choice: &[AssistantContent],
134    stop: Option<&StopReason>,
135    finish: Option<&crate::completion::FinishReason>,
136) -> Option<String> {
137    let failed = stop.filter(|stop| stop.is_failure());
138    let calls = choice
139        .iter()
140        .any(|block| matches!(block, AssistantContent::ToolCall(_)));
141    if let Some(StopReason::Error(reason) | StopReason::Aborted(reason)) = failed
142        && calls
143    {
144        return Some(format!(
145            "the turn failed, so none of its tool calls ran: {reason}"
146        ));
147    }
148    let answered = !turn_delivered_no_answer(choice);
149    if !answered && let Some(finish) = finish.filter(|finish| finish.truncated_output()) {
150        return Some(finish.no_answer_message());
151    }
152    match failed {
153        Some(StopReason::Error(reason)) if answered => {
154            Some(format!("the provider failed the turn: {reason}"))
155        }
156        Some(StopReason::Error(reason)) => Some(format!(
157            "the provider failed the turn without an answer: {reason}"
158        )),
159        _ => None,
160    }
161}
162
163/// User text, tool results, or media. Supported source kinds and media types
164/// depend on the target provider.
165#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
166#[serde(tag = "type", rename_all = "lowercase")]
167pub enum UserContent {
168    /// Plain text user content.
169    Text(Text),
170    /// Result of a tool call returned as user-visible context to the model.
171    ToolResult(ToolResult),
172    /// Image content.
173    Image(Image),
174    /// Audio content.
175    Audio(Audio),
176    /// Video content.
177    Video(Video),
178    /// Document content.
179    Document(Document),
180}
181
182/// One block of an assistant turn: one provider output item.
183/// Deserialization requires the lowercase `type` tag.
184#[non_exhaustive]
185#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
186#[serde(tag = "type", rename_all = "lowercase")]
187pub enum AssistantContent {
188    /// Answer text.
189    Text(Text),
190    /// A tool call requested by the assistant.
191    ToolCall(ToolCall),
192    /// Reasoning the model showed.
193    Reasoning(Reasoning),
194    /// An image the assistant produced.
195    Image(Image),
196    /// A provider item with no canonical meaning.
197    Opaque(Opaque),
198}
199
200impl AssistantContent {
201    /// This block without its provider item: what a different provider can
202    /// use, and what its fingerprint covers.
203    pub fn canonical(&self) -> Self {
204        let mut block = self.clone();
205        if let Some(native) = block.native_slot() {
206            *native = None;
207        }
208        block
209    }
210
211    /// Whether the block answers the user: text that is not blank, a call
212    /// or an image. Reasoning and provider-only items are not an answer.
213    pub fn is_answer(&self) -> bool {
214        match self {
215            Self::Text(text) => !text.text.trim().is_empty(),
216            Self::ToolCall(_) | Self::Image(_) => true,
217            Self::Reasoning(_) | Self::Opaque(_) => false,
218        }
219    }
220
221    /// Whether the block has nothing to send, so replay drops it: blank text
222    /// or reasoning, and redacted reasoning, without a provider item that is
223    /// still current (with one, its identity pairs it with what follows, and
224    /// pi replays it whatever its text), and an opaque item that does not
225    /// replay.
226    pub fn is_blank(&self) -> bool {
227        match self {
228            Self::Text(Text { text, .. }) => text.trim().is_empty() && self.native_item().is_none(),
229            Self::Reasoning(Reasoning { text, redacted, .. }) => {
230                (*redacted || text.trim().is_empty()) && self.native_item().is_none()
231            }
232            Self::Opaque(opaque) => !opaque.replay,
233            Self::ToolCall(_) | Self::Image(_) => false,
234        }
235    }
236
237    /// The fingerprint of the block's canonical fields, through a fixed,
238    /// versioned projection rather than the block's serde layout, so a field
239    /// added to a canonical type never stales stored items. A rig-issued
240    /// call id counts as one placeholder: rig issues a fresh id each time it
241    /// decodes a call the provider sent without one.
242    pub fn fingerprint(&self) -> Fingerprint {
243        Fingerprint::of(&self.projection())
244    }
245
246    /// Projection v1 of the canonical fields.
247    fn projection(&self) -> serde_json::Value {
248        use serde_json::json;
249        match self {
250            Self::Text(text) => json!(["v1", "text", text.text]),
251            Self::Reasoning(reasoning) => {
252                json!(["v1", "reasoning", reasoning.text, reasoning.redacted])
253            }
254            Self::ToolCall(call) => {
255                let id = match &call.id {
256                    CallId::Provider(id) => id.as_str().to_owned(),
257                    CallId::Local(_) => "~local".to_owned(),
258                };
259                json!([
260                    "v1",
261                    "call",
262                    id,
263                    call.function.name.as_str(),
264                    call.function.arguments,
265                    call.function.invalid_arguments,
266                ])
267            }
268            Self::Image(image) => {
269                json!(["v1", "image", image.media_type, image.detail, image.data,])
270            }
271            Self::Opaque(_) => json!(["v1", "opaque"]),
272        }
273    }
274
275    fn native_slot(&mut self) -> Option<&mut Option<Native>> {
276        match self {
277            Self::Text(text) => Some(&mut text.native),
278            Self::ToolCall(call) => Some(&mut call.native),
279            Self::Reasoning(reasoning) => Some(&mut reasoning.native),
280            Self::Image(image) => Some(&mut image.native),
281            Self::Opaque(_) => None,
282        }
283    }
284
285    /// The provider item this block was decoded from, held for its current
286    /// canonical form. An [`Opaque`] block has no separate item.
287    pub fn with_native(mut self, item: serde_json::Value) -> Self {
288        let fingerprint = self.fingerprint();
289        if let Some(native) = self.native_slot() {
290            *native = Some(Native { item, fingerprint });
291        }
292        self
293    }
294
295    /// The provider item of an edited block: no longer current, but its
296    /// identity keys still name the item it was.
297    pub(crate) fn stale_item(&self) -> Option<&serde_json::Value> {
298        let native = match self {
299            Self::Text(text) => text.native.as_ref(),
300            Self::ToolCall(call) => call.native.as_ref(),
301            Self::Reasoning(reasoning) => reasoning.native.as_ref(),
302            Self::Image(image) => image.native.as_ref(),
303            Self::Opaque(_) => None,
304        }?;
305        (native.fingerprint != self.fingerprint()).then_some(&native.item)
306    }
307
308    /// The provider item, while the block is still what it was decoded
309    /// from. An edited block has none: encoders rebuild it.
310    pub fn native_item(&self) -> Option<&serde_json::Value> {
311        let native = match self {
312            Self::Text(text) => text.native.as_ref(),
313            Self::ToolCall(call) => call.native.as_ref(),
314            Self::Reasoning(reasoning) => reasoning.native.as_ref(),
315            Self::Image(image) => image.native.as_ref(),
316            Self::Opaque(_) => None,
317        }?;
318        (native.fingerprint == self.fingerprint()).then_some(&native.item)
319    }
320}
321
322/// Reasoning the model showed: its text, or a redacted block with none.
323/// Signatures, encrypted payloads and ids are provider data and live in
324/// `native`.
325#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
326pub struct Reasoning {
327    /// The reasoning text, summaries included.
328    pub text: String,
329    /// Whether the provider withheld the text.
330    #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
331    pub redacted: bool,
332    /// The provider item this block was decoded from.
333    #[serde(
334        default,
335        skip_serializing_if = "Option::is_none",
336        deserialize_with = "native::lenient"
337    )]
338    pub native: Option<Native>,
339}
340
341impl Reasoning {
342    /// Reasoning text with no provider item.
343    pub fn new(text: impl Into<String>) -> Self {
344        Self {
345            text: text.into(),
346            ..Self::default()
347        }
348    }
349}
350
351/// The result of a tool call, sent back to the model.
352///
353/// Build it from the call it answers with [`ToolCall::result`], so its id
354/// and name match the call.
355#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
356pub struct ToolResult {
357    /// The id of the answered call.
358    pub call: CallId,
359    /// Executed tool name, which may differ from the model-requested name after
360    /// hook repair. Required for provider replay independently of call identity.
361    pub name: ToolName,
362    /// One or more content items produced by the tool.
363    pub content: Vec<ToolResultContent>,
364    /// Whether the tool failed, was refused, or never ran: `content` then
365    /// says why.
366    #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
367    pub is_error: bool,
368}
369
370/// Describes one typed item in a tool result.
371#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
372#[serde(tag = "type", rename_all = "lowercase")]
373pub enum ToolResultContent {
374    /// Literal text. Providers must not reinterpret it as structured JSON.
375    Text(Text),
376    /// An image supplied explicitly by the tool.
377    Image(Image),
378    /// Structured JSON supplied explicitly by the tool runtime.
379    Json {
380        /// The structured value.
381        value: serde_json::Value,
382    },
383}
384
385impl ToolResultContent {
386    /// Borrow literal text content.
387    pub fn as_text(&self) -> Option<&str> {
388        match self {
389            Self::Text(text) => Some(&text.text),
390            Self::Image(_) | Self::Json { .. } => None,
391        }
392    }
393
394    /// Borrow structured JSON content.
395    pub fn as_json(&self) -> Option<&serde_json::Value> {
396        match self {
397            Self::Json { value } => Some(value),
398            Self::Text(_) | Self::Image(_) => None,
399        }
400    }
401
402    /// Deserialize JSON content into a typed value.
403    ///
404    /// Structured JSON is decoded directly. Literal text is parsed only because
405    /// the caller explicitly requested JSON decoding, which supports transcripts
406    /// recorded before structured tool output was preserved canonically. This
407    /// helper never changes the content sent to a model or provider.
408    pub fn deserialize_json<T>(&self) -> Result<T, serde_json::Error>
409    where
410        T: serde::de::DeserializeOwned,
411    {
412        match self {
413            Self::Json { value } => T::deserialize(value),
414            Self::Text(text) => serde_json::from_str(&text.text),
415            Self::Image(_) => Err(<serde_json::Error as serde::de::Error>::custom(
416                "cannot decode image tool-result content as JSON",
417            )),
418        }
419    }
420}
421
422/// A tool call: its id, and the function and arguments requested.
423#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
424pub struct ToolCall {
425    /// The call's one identity: the provider's id, or one rig issued when
426    /// the provider sent none.
427    pub id: CallId,
428    /// Function name and JSON arguments requested by the model.
429    pub function: ToolFunction,
430    /// The provider item this call was decoded from.
431    #[serde(
432        default,
433        skip_serializing_if = "Option::is_none",
434        deserialize_with = "native::lenient"
435    )]
436    pub native: Option<Native>,
437}
438
439impl ToolCall {
440    /// A call with `id` and no provider item.
441    pub fn new(id: CallId, function: ToolFunction) -> Self {
442        Self {
443            id,
444            function,
445            native: None,
446        }
447    }
448
449    /// A call the provider identified by `wire_id`; rig issues an id when it
450    /// is empty.
451    pub fn from_wire(wire_id: impl Into<String>, function: ToolFunction) -> Self {
452        Self::new(CallId::from_wire(wire_id), function)
453    }
454
455    /// The result answering this call: its id and name, and `content`.
456    pub fn result(&self, content: Vec<ToolResultContent>) -> ToolResult {
457        ToolResult {
458            call: self.id.clone(),
459            name: self.function.name.clone(),
460            content,
461            is_error: false,
462        }
463    }
464
465    /// The result reporting that this call failed, with `content` saying
466    /// why.
467    pub fn error_result(&self, content: Vec<ToolResultContent>) -> ToolResult {
468        ToolResult {
469            is_error: true,
470            ..self.result(content)
471        }
472    }
473}
474
475/// A tool function to call: its name and its arguments, always a JSON
476/// object.
477///
478/// Arguments the model sent that are not an object are kept as text in
479/// `invalid_arguments`, with `arguments` holding what could be read of them
480/// (`{}` when nothing could), so a malformed call never fails a reply and
481/// every wire receives an object.
482///
483/// ```
484/// use rig_core::message::{ToolFunction, ToolName};
485///
486/// let name = ToolName::new("search")?;
487/// let call = ToolFunction::parse(name.clone(), r#"{"q": "ab"#);
488/// assert_eq!(call.arguments["q"], "ab");
489/// assert_eq!(call.invalid_arguments.as_deref(), Some(r#"{"q": "ab"#));
490///
491/// let call = ToolFunction::new(name, serde_json::json!("{\"q\":1}"));
492/// assert_eq!(call.arguments["q"], 1);
493/// assert!(call.invalid_arguments.is_none());
494/// # Ok::<(), rig_core::message::EmptyToolName>(())
495/// ```
496#[derive(Clone, Debug, Serialize, PartialEq)]
497pub struct ToolFunction {
498    /// Tool/function name to invoke.
499    pub name: ToolName,
500    /// The arguments.
501    pub arguments: serde_json::Map<String, serde_json::Value>,
502    /// The arguments as the model sent them, when they were not a JSON
503    /// object.
504    #[serde(default, skip_serializing_if = "Option::is_none")]
505    pub invalid_arguments: Option<String>,
506}
507
508impl ToolFunction {
509    /// A call to `name` with `arguments`. An object is kept; a string
510    /// holding an object is unwrapped once; `null` is `{}`; anything else is
511    /// `{}` with its JSON text in `invalid_arguments`.
512    pub fn new(name: ToolName, arguments: serde_json::Value) -> Self {
513        use serde_json::Value;
514        let (arguments, invalid_arguments) = match arguments {
515            Value::Object(arguments) => (arguments, None),
516            Value::Null => (serde_json::Map::new(), None),
517            Value::String(text) => {
518                return Self::parse(name, &text);
519            }
520            other => (serde_json::Map::new(), Some(other.to_string())),
521        };
522        Self {
523            name,
524            arguments,
525            invalid_arguments,
526        }
527    }
528
529    /// A call to `name` with the argument JSON `text`. Blank text is `{}`.
530    /// Text that is not an object keeps what a cut-off object still states,
531    /// or `{}`, and is kept in `invalid_arguments`. A string holding an
532    /// object is unwrapped once.
533    pub fn parse(name: ToolName, text: &str) -> Self {
534        use serde_json::Value;
535        let parsed = crate::json_utils::parse_tool_arguments(text);
536        let (arguments, invalid) = match parsed {
537            Ok(Value::Object(arguments)) => (arguments, false),
538            Ok(Value::Null) => (serde_json::Map::new(), false),
539            Ok(Value::String(inner)) => match serde_json::from_str(&inner) {
540                Ok(Value::Object(arguments)) => (arguments, false),
541                _ => (serde_json::Map::new(), true),
542            },
543            Ok(_) => (serde_json::Map::new(), true),
544            Err(_) => (
545                crate::json_utils::parse_partial_object(text).unwrap_or_default(),
546                true,
547            ),
548        };
549        Self {
550            name,
551            arguments,
552            invalid_arguments: invalid.then(|| text.to_owned()),
553        }
554    }
555
556    /// The arguments as a JSON value.
557    pub fn arguments_value(&self) -> serde_json::Value {
558        serde_json::Value::Object(self.arguments.clone())
559    }
560}
561
562impl<'de> Deserialize<'de> for ToolFunction {
563    /// Arguments stored in any JSON shape are read through
564    /// [`ToolFunction::new`], so a history saved before arguments were
565    /// always objects still loads.
566    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
567        #[derive(Deserialize)]
568        struct Repr {
569            name: ToolName,
570            #[serde(default)]
571            arguments: serde_json::Value,
572            #[serde(default)]
573            invalid_arguments: Option<String>,
574        }
575        let Repr {
576            name,
577            arguments,
578            invalid_arguments,
579        } = Repr::deserialize(deserializer)?;
580        let mut function = Self::new(name, arguments);
581        if invalid_arguments.is_some() {
582            function.invalid_arguments = invalid_arguments;
583        }
584        Ok(function)
585    }
586}
587
588/// Text. On an assistant turn, `native` holds the provider item the block
589/// was decoded from, and [`Text::citations`] what the provider says supports
590/// it; user and tool-result text leave both empty.
591#[non_exhaustive]
592#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
593#[serde(from = "TextRepr")]
594pub struct Text {
595    /// Text content.
596    pub text: String,
597    /// The citations and the fingerprint of the text they fit; read through
598    /// [`Text::citations`].
599    #[serde(default, skip_serializing_if = "Option::is_none")]
600    citations: Option<citation::Citations>,
601    /// The provider item this block was decoded from.
602    #[serde(
603        default,
604        skip_serializing_if = "Option::is_none",
605        deserialize_with = "native::lenient"
606    )]
607    pub native: Option<Native>,
608}
609
610/// A stored [`Text`], read leniently: unreadable citations or native load
611/// as none.
612#[derive(Deserialize)]
613struct TextRepr {
614    text: String,
615    #[serde(default, deserialize_with = "citation::lenient")]
616    citations: Option<citation::Citations>,
617    #[serde(default, deserialize_with = "native::lenient")]
618    native: Option<Native>,
619}
620
621impl From<TextRepr> for Text {
622    fn from(repr: TextRepr) -> Self {
623        let TextRepr {
624            text,
625            citations,
626            native,
627        } = repr;
628        Self {
629            text,
630            citations,
631            native,
632        }
633        .checked()
634    }
635}
636
637impl Text {
638    /// Text with no provider item and no citations.
639    pub fn new(text: impl Into<String>) -> Self {
640        Self {
641            text: text.into(),
642            citations: None,
643            native: None,
644        }
645    }
646
647    /// Returns the inner text string.
648    pub fn text(&self) -> &str {
649        &self.text
650    }
651}
652
653impl std::fmt::Display for Text {
654    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
655        let Self { text, .. } = self;
656        write!(f, "{text}")
657    }
658}
659
660/// Image content containing image data and metadata about it. On an
661/// assistant turn, `native` holds the provider item it was decoded from.
662#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
663pub struct Image {
664    /// Image source data.
665    pub data: DocumentSourceKind,
666    /// Image media type, if known.
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub media_type: Option<ImageMediaType>,
669    /// Provider-specific image detail preference.
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub detail: Option<ImageDetail>,
672    /// The provider item this image was decoded from.
673    #[serde(
674        default,
675        skip_serializing_if = "Option::is_none",
676        deserialize_with = "native::lenient"
677    )]
678    pub native: Option<Native>,
679}
680
681/// The kind of image source (to be used).
682#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
683#[serde(tag = "type", content = "value", rename_all = "camelCase")]
684pub enum DocumentSourceKind {
685    /// A file URL/URI.
686    Url(String),
687    /// A base-64 encoded string.
688    Base64(String),
689    /// A provider-side uploaded file identifier.
690    FileId(String),
691    /// Raw bytes
692    Raw(Vec<u8>),
693    /// A string (or a string literal).
694    String(String),
695    #[default]
696    /// An unknown file source (there's nothing there).
697    Unknown,
698}
699
700impl DocumentSourceKind {
701    /// Create a URL-backed source.
702    pub fn url(url: impl Into<String>) -> Self {
703        Self::Url(url.into())
704    }
705
706    /// Create a base64-backed source.
707    pub fn base64(base64_string: impl Into<String>) -> Self {
708        Self::Base64(base64_string.into())
709    }
710
711    /// Create a provider file ID-backed source.
712    pub fn file_id(file_id: impl Into<String>) -> Self {
713        Self::FileId(file_id.into())
714    }
715
716    /// Create a string-backed source.
717    pub fn string(input: impl Into<String>) -> Self {
718        Self::String(input.into())
719    }
720
721    /// Return the contained URL, base64 string, or file ID, if this source stores one.
722    pub fn try_into_inner(self) -> Option<String> {
723        match self {
724            Self::Url(s) | Self::Base64(s) | Self::FileId(s) => Some(s),
725            _ => None,
726        }
727    }
728}
729
730impl std::fmt::Display for DocumentSourceKind {
731    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
732        match self {
733            Self::Url(string) => write!(f, "{string}"),
734            Self::Base64(string) => write!(f, "{string}"),
735            Self::FileId(string) => write!(f, "{string}"),
736            Self::String(string) => write!(f, "{string}"),
737            Self::Raw(_) => write!(f, "<binary data>"),
738            Self::Unknown => write!(f, "<unknown>"),
739        }
740    }
741}
742
743/// Audio content containing audio data and metadata about it.
744#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
745pub struct Audio {
746    /// Audio source data.
747    pub data: DocumentSourceKind,
748    /// Audio media type, if known.
749    #[serde(skip_serializing_if = "Option::is_none")]
750    pub media_type: Option<AudioMediaType>,
751}
752
753/// Video content containing video data and metadata about it.
754#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
755pub struct Video {
756    /// Video source data.
757    pub data: DocumentSourceKind,
758    /// Video media type, if known.
759    #[serde(skip_serializing_if = "Option::is_none")]
760    pub media_type: Option<VideoMediaType>,
761    /// Provider-specific video fields, a JSON object (Gemini's
762    /// `video_metadata`).
763    #[serde(default, skip_serializing_if = "Option::is_none")]
764    pub additional_params: Option<serde_json::Value>,
765}
766
767/// Document content containing document data and metadata about it.
768#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
769pub struct Document {
770    /// Document source data.
771    pub data: DocumentSourceKind,
772    /// Document media type, if known.
773    #[serde(skip_serializing_if = "Option::is_none")]
774    pub media_type: Option<DocumentMediaType>,
775    /// Provider-specific document fields, a JSON object (Anthropic's
776    /// `title`, `context` and `citations`).
777    #[serde(default, skip_serializing_if = "Option::is_none")]
778    pub additional_params: Option<serde_json::Value>,
779}
780
781/// Content representation as base64, text, or a URL.
782#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
783#[serde(rename_all = "lowercase")]
784pub enum ContentFormat {
785    #[default]
786    Base64,
787    String,
788    Url,
789}
790
791/// Helper enum that tracks the media type of the content.
792#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
793pub enum MediaType {
794    Image(ImageMediaType),
795    Audio(AudioMediaType),
796    Document(DocumentMediaType),
797    Video(VideoMediaType),
798}
799
800/// Describes the image media type of the content. Not every provider supports every media type.
801/// Convertible to and from MIME type strings.
802#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
803#[serde(rename_all = "lowercase")]
804pub enum ImageMediaType {
805    JPEG,
806    PNG,
807    GIF,
808    WEBP,
809    HEIC,
810    HEIF,
811    SVG,
812}
813
814/// Describes the document media type of the content. Not every provider supports every media type.
815/// Includes also programming languages as document types for providers who support code running.
816/// Convertible to and from MIME type strings.
817#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
818#[serde(rename_all = "lowercase")]
819pub enum DocumentMediaType {
820    PDF,
821    TXT,
822    RTF,
823    HTML,
824    CSS,
825    MARKDOWN,
826    CSV,
827    XML,
828    Javascript,
829    Python,
830}
831
832impl DocumentMediaType {
833    pub fn is_code(&self) -> bool {
834        matches!(self, Self::Javascript | Self::Python)
835    }
836}
837
838/// Describes the audio media type of the content. Not every provider supports every media type.
839/// Convertible to and from MIME type strings.
840#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
841#[serde(rename_all = "lowercase")]
842pub enum AudioMediaType {
843    WAV,
844    MP3,
845    AIFF,
846    AAC,
847    OGG,
848    FLAC,
849    M4A,
850    PCM16,
851    PCM24,
852}
853
854/// Describes the video media type of the content. Not every provider supports every media type.
855/// Convertible to and from MIME type strings.
856#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
857#[serde(rename_all = "lowercase")]
858pub enum VideoMediaType {
859    AVI,
860    MP4,
861    MPEG,
862    MOV,
863    WEBM,
864}
865
866/// Describes the detail of the image content, which can be low, high, or auto (open-ai specific).
867#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
868#[serde(rename_all = "lowercase")]
869pub enum ImageDetail {
870    Low,
871    High,
872    #[default]
873    Auto,
874}
875
876impl Message {
877    /// Clones the first text block of a user message, or returns `None`.
878    pub fn rag_text(&self) -> Option<String> {
879        match self {
880            Message::User { content } => {
881                for item in content.iter() {
882                    if let UserContent::Text(Text { text, .. }) = item {
883                        return Some(text.clone());
884                    }
885                }
886                None
887            }
888            Message::System { .. } => None,
889            _ => None,
890        }
891    }
892
893    /// Creates a system instruction message.
894    pub fn system(text: impl Into<String>) -> Self {
895        Message::System {
896            content: text.into(),
897        }
898    }
899
900    /// Creates a user message containing one text block.
901    pub fn user(text: impl Into<String>) -> Self {
902        Message::User {
903            content: vec![UserContent::text(text)],
904        }
905    }
906
907    /// Creates a hand-built assistant message containing one text block.
908    pub fn assistant(text: impl Into<String>) -> Self {
909        Message::Assistant(AssistantMessage::new(vec![AssistantContent::text(text)]))
910    }
911
912    /// Creates a user message containing a text tool result answering the
913    /// call `call` to the tool `name`. To answer a call you hold, prefer
914    /// [`ToolCall::result`] with [`Self::tool_results`].
915    pub fn tool_result(call: CallId, name: ToolName, content: impl Into<String>) -> Self {
916        Message::User {
917            content: vec![UserContent::tool_result(
918                call,
919                name,
920                vec![ToolResultContent::text(content)],
921            )],
922        }
923    }
924
925    /// Creates a user message carrying `results`, in order.
926    pub fn tool_results(results: Vec<ToolResult>) -> Self {
927        Message::User {
928            content: results.into_iter().map(UserContent::ToolResult).collect(),
929        }
930    }
931}
932
933/// Generates media constructors without fetching or decoding source data.
934macro_rules! media_ctors {
935    () => {};
936    (
937        $(#[$meta:meta])* $name:ident => Image($kind:ident: $data:ty);
938        $($rest:tt)*
939    ) => {
940        $(#[$meta])*
941        pub fn $name(
942            data: impl Into<$data>,
943            media_type: Option<ImageMediaType>,
944            detail: Option<ImageDetail>,
945        ) -> Self {
946            Self::Image(Image {
947                data: DocumentSourceKind::$kind(data.into()),
948                media_type,
949                detail,
950                native: None,
951            })
952        }
953        media_ctors! { $($rest)* }
954    };
955    (
956        $(#[$meta:meta])* $name:ident => $variant:ident(params $mt:ty, $kind:ident: $data:ty);
957        $($rest:tt)*
958    ) => {
959        $(#[$meta])*
960        pub fn $name(data: impl Into<$data>, media_type: Option<$mt>) -> Self {
961            Self::$variant($variant {
962                data: DocumentSourceKind::$kind(data.into()),
963                media_type,
964                additional_params: None,
965            })
966        }
967        media_ctors! { $($rest)* }
968    };
969    (
970        $(#[$meta:meta])* $name:ident => $variant:ident($mt:ty, $kind:ident: $data:ty);
971        $($rest:tt)*
972    ) => {
973        $(#[$meta])*
974        pub fn $name(data: impl Into<$data>, media_type: Option<$mt>) -> Self {
975            Self::$variant($variant {
976                data: DocumentSourceKind::$kind(data.into()),
977                media_type,
978            })
979        }
980        media_ctors! { $($rest)* }
981    };
982}
983
984impl UserContent {
985    /// Creates user text content.
986    pub fn text(text: impl Into<String>) -> Self {
987        UserContent::Text(text.into().into())
988    }
989
990    media_ctors! {
991        /// Creates user image content from base64-encoded data.
992        image_base64 => Image(Base64: String);
993        /// Creates user image content from unencoded bytes.
994        image_raw => Image(Raw: Vec<u8>);
995        /// Creates user image content referencing a URL.
996        image_url => Image(Url: String);
997        /// Creates user audio content from base64-encoded data.
998        audio_base64 => Audio(AudioMediaType, Base64: String);
999        /// Creates user audio content from unencoded bytes.
1000        audio_raw => Audio(AudioMediaType, Raw: Vec<u8>);
1001        /// Creates user audio content referencing a URL.
1002        audio_url => Audio(AudioMediaType, Url: String);
1003        /// Creates user video content from base64-encoded data.
1004        video_base64 => Video(params VideoMediaType, Base64: String);
1005        /// Creates user video content from unencoded bytes.
1006        video_raw => Video(params VideoMediaType, Raw: Vec<u8>);
1007        /// Creates user video content referencing a URL.
1008        video_url => Video(params VideoMediaType, Url: String);
1009        /// Creates user document content from base64-encoded data.
1010        document_base64 => Document(params DocumentMediaType, Base64: String);
1011        /// Creates user document content from unencoded bytes.
1012        document_raw => Document(params DocumentMediaType, Raw: Vec<u8>);
1013        /// Creates user document content referencing a URL.
1014        document_url => Document(params DocumentMediaType, Url: String);
1015        /// Creates user document content from literal text, such as a plain
1016        /// text or Markdown file. Binary formats belong in
1017        /// [`Self::document_base64`] or [`Self::document_raw`].
1018        document_text => Document(params DocumentMediaType, String: String);
1019    }
1020
1021    /// Creates a tool result answering the call `call` to the tool `name`.
1022    pub fn tool_result(call: CallId, name: ToolName, content: Vec<ToolResultContent>) -> Self {
1023        UserContent::ToolResult(ToolResult {
1024            call,
1025            name,
1026            content,
1027            is_error: false,
1028        })
1029    }
1030}
1031
1032impl AssistantContent {
1033    /// Creates assistant text content.
1034    pub fn text(text: impl Into<String>) -> Self {
1035        AssistantContent::Text(text.into().into())
1036    }
1037
1038    media_ctors! {
1039        /// Creates assistant image content from base64-encoded data.
1040        image_base64 => Image(Base64: String);
1041    }
1042
1043    /// Creates a tool call from a provider-issued ID, name, and arguments.
1044    /// Rig issues an id when `id` is empty.
1045    pub fn tool_call(id: impl Into<String>, name: ToolName, arguments: serde_json::Value) -> Self {
1046        AssistantContent::ToolCall(ToolCall::from_wire(id, ToolFunction::new(name, arguments)))
1047    }
1048
1049    /// Creates reasoning text with no provider item.
1050    pub fn reasoning(reasoning: impl Into<String>) -> Self {
1051        AssistantContent::Reasoning(Reasoning::new(reasoning))
1052    }
1053}
1054
1055impl ToolResultContent {
1056    /// Creates literal text tool-result content.
1057    pub fn text(text: impl Into<String>) -> Self {
1058        ToolResultContent::Text(text.into().into())
1059    }
1060
1061    /// Creates structured JSON tool-result content.
1062    pub fn json(value: serde_json::Value) -> Self {
1063        ToolResultContent::Json { value }
1064    }
1065
1066    media_ctors! {
1067        /// Creates tool-result image content from base64-encoded data.
1068        image_base64 => Image(Base64: String);
1069        /// Creates tool-result image content from raw, unencoded bytes.
1070        image_raw => Image(Raw: Vec<u8>);
1071        /// Creates tool-result image content referencing a URL.
1072        image_url => Image(Url: String);
1073    }
1074}
1075
1076/// Trait for converting between MIME types and media types.
1077pub trait MimeType {
1078    fn from_mime_type(mime_type: &str) -> Option<Self>
1079    where
1080        Self: Sized;
1081    fn to_mime_type(&self) -> &'static str;
1082}
1083
1084impl MimeType for MediaType {
1085    fn from_mime_type(mime_type: &str) -> Option<Self> {
1086        ImageMediaType::from_mime_type(mime_type)
1087            .map(MediaType::Image)
1088            .or_else(|| DocumentMediaType::from_mime_type(mime_type).map(MediaType::Document))
1089            .or_else(|| AudioMediaType::from_mime_type(mime_type).map(MediaType::Audio))
1090            .or_else(|| VideoMediaType::from_mime_type(mime_type).map(MediaType::Video))
1091    }
1092
1093    fn to_mime_type(&self) -> &'static str {
1094        match self {
1095            MediaType::Image(media_type) => media_type.to_mime_type(),
1096            MediaType::Audio(media_type) => media_type.to_mime_type(),
1097            MediaType::Document(media_type) => media_type.to_mime_type(),
1098            MediaType::Video(media_type) => media_type.to_mime_type(),
1099        }
1100    }
1101}
1102
1103// Emits both directions of a [`MimeType`] impl from a single pair list, so a
1104// variant's parse and emit spellings cannot drift apart. Extra `| "alias"`
1105// spellings parse to the same variant; only the first (canonical) string is
1106// emitted by `to_mime_type`.
1107macro_rules! impl_mime_type {
1108    ($ty:ident { $($variant:ident => $canonical:literal $(| $alias:literal)*),+ $(,)? }) => {
1109        impl MimeType for $ty {
1110            fn from_mime_type(mime_type: &str) -> Option<Self> {
1111                match mime_type {
1112                    $($canonical $(| $alias)* => Some($ty::$variant),)+
1113                    _ => None,
1114                }
1115            }
1116
1117            fn to_mime_type(&self) -> &'static str {
1118                match self {
1119                    $($ty::$variant => $canonical,)+
1120                }
1121            }
1122        }
1123    };
1124}
1125
1126impl_mime_type!(ImageMediaType {
1127    JPEG => "image/jpeg",
1128    PNG => "image/png",
1129    GIF => "image/gif",
1130    WEBP => "image/webp",
1131    HEIC => "image/heic",
1132    HEIF => "image/heif",
1133    SVG => "image/svg+xml",
1134});
1135
1136impl_mime_type!(DocumentMediaType {
1137    PDF => "application/pdf",
1138    TXT => "text/plain",
1139    RTF => "text/rtf",
1140    HTML => "text/html",
1141    CSS => "text/css",
1142    MARKDOWN => "text/markdown" | "text/md",
1143    CSV => "text/csv",
1144    XML => "text/xml",
1145    Javascript => "application/x-javascript" | "text/x-javascript",
1146    Python => "application/x-python" | "text/x-python",
1147});
1148
1149impl_mime_type!(AudioMediaType {
1150    WAV => "audio/wav",
1151    MP3 => "audio/mp3",
1152    AIFF => "audio/aiff",
1153    AAC => "audio/aac",
1154    OGG => "audio/ogg",
1155    FLAC => "audio/flac",
1156    M4A => "audio/m4a",
1157    PCM16 => "audio/pcm16",
1158    PCM24 => "audio/pcm24",
1159});
1160
1161impl_mime_type!(VideoMediaType {
1162    AVI => "video/avi",
1163    MP4 => "video/mp4",
1164    MPEG => "video/mpeg",
1165    MOV => "video/mov",
1166    WEBM => "video/webm",
1167});
1168
1169impl std::str::FromStr for ImageDetail {
1170    type Err = ();
1171
1172    fn from_str(s: &str) -> Result<Self, Self::Err> {
1173        match s.to_lowercase().as_str() {
1174            "low" => Ok(ImageDetail::Low),
1175            "high" => Ok(ImageDetail::High),
1176            "auto" => Ok(ImageDetail::Auto),
1177            _ => Err(()),
1178        }
1179    }
1180}
1181
1182/// `From` impls for [`Text`] from string-like types.
1183macro_rules! text_from {
1184    ($($src:ty),+ $(,)?) => {$(
1185        impl From<$src> for Text {
1186            fn from(text: $src) -> Self {
1187                Text::new(text)
1188            }
1189        }
1190    )+};
1191}
1192
1193text_from!(String, &String, &str);
1194
1195/// `From<String>` impls that forward into a content type's `text` constructor.
1196macro_rules! text_content_from_string {
1197    ($($ty:ident),+ $(,)?) => {$(
1198        impl From<String> for $ty {
1199            fn from(text: String) -> Self {
1200                $ty::text(text)
1201            }
1202        }
1203    )+};
1204}
1205
1206text_content_from_string!(ToolResultContent, AssistantContent, UserContent);
1207
1208/// One-line `From<T> for Message` forwards: convert the value, wrap it in the
1209/// named content variant, and build a single-content message.
1210macro_rules! single_content_message_from {
1211    (User { $($src:ty => $variant:ident),+ $(,)? }) => {$(
1212        impl From<$src> for Message {
1213            fn from(value: $src) -> Self {
1214                Message::User {
1215                    content: vec![UserContent::$variant(value.into())],
1216                }
1217            }
1218        }
1219    )+};
1220    (Assistant { $($src:ty => $variant:ident),+ $(,)? }) => {$(
1221        impl From<$src> for Message {
1222            fn from(value: $src) -> Self {
1223                Message::Assistant(AssistantMessage::new(vec![AssistantContent::$variant(
1224                    value.into(),
1225                )]))
1226            }
1227        }
1228    )+};
1229}
1230
1231single_content_message_from!(User {
1232    String => Text,
1233    &str => Text,
1234    &String => Text,
1235    Text => Text,
1236    Image => Image,
1237    Audio => Audio,
1238    Document => Document,
1239    ToolResult => ToolResult,
1240});
1241
1242single_content_message_from!(Assistant {
1243    ToolCall => ToolCall,
1244});
1245
1246impl FromStr for Text {
1247    type Err = Infallible;
1248
1249    fn from_str(s: &str) -> Result<Self, Self::Err> {
1250        Ok(s.into())
1251    }
1252}
1253
1254impl From<&Message> for Message {
1255    fn from(msg: &Message) -> Self {
1256        msg.clone()
1257    }
1258}
1259
1260impl From<AssistantContent> for Message {
1261    fn from(content: AssistantContent) -> Self {
1262        Message::Assistant(AssistantMessage::new(vec![content]))
1263    }
1264}
1265
1266impl From<AssistantMessage> for Message {
1267    fn from(message: AssistantMessage) -> Self {
1268        Message::Assistant(message)
1269    }
1270}
1271
1272impl From<UserContent> for Message {
1273    fn from(content: UserContent) -> Self {
1274        Message::User {
1275            content: vec![content],
1276        }
1277    }
1278}
1279
1280impl From<Vec<AssistantContent>> for Message {
1281    fn from(content: Vec<AssistantContent>) -> Self {
1282        Message::Assistant(AssistantMessage::new(content))
1283    }
1284}
1285
1286impl From<Vec<UserContent>> for Message {
1287    fn from(content: Vec<UserContent>) -> Self {
1288        Message::User { content }
1289    }
1290}
1291
1292#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
1293#[serde(rename_all = "snake_case")]
1294pub enum ToolChoice {
1295    #[default]
1296    Auto,
1297    None,
1298    Required,
1299    Specific {
1300        function_names: Vec<ToolName>,
1301    },
1302}
1303
1304/// Error type to represent issues with converting messages to and from specific provider messages.
1305#[derive(Debug, Error)]
1306pub enum MessageError {
1307    #[error("Message conversion error: {0}")]
1308    ConversionError(String),
1309}
1310
1311impl From<MessageError> for ProviderError {
1312    fn from(error: MessageError) -> Self {
1313        ProviderError::request(error)
1314    }
1315}
1316
1317#[cfg(test)]
1318mod tests;