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