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