Skip to main content

rig_core/completion/
request.rs

1//! Completion requests, normalized responses, and provider model contracts.
2//!
3//! ```
4//! use rig_core::completion::CompletionRequest;
5//!
6//! let request = CompletionRequest::new("Who are you?")
7//!     .preamble("You are a concise assistant.")
8//!     .temperature(0.5);
9//! assert_eq!(request.temperature, Some(0.5));
10//! ```
11
12use super::message::{
13    AssistantContent, AssistantMessage, CallId, DocumentMediaType, Origin, StopReason, ToolCall,
14};
15use super::options::{
16    CacheRetention, GenerationOptions, OnUnsupported, Reasoning, ServiceTier, Verbosity,
17};
18use super::provider_options::{ExtensionOptions, ProviderExtension, ProviderOptions, ReplyExtras};
19use crate::error::ProviderError;
20use crate::json_utils::remove_preserving_order;
21use crate::message::ToolChoice;
22use crate::{
23    json_utils,
24    message::{Message, ToolName, UserContent},
25};
26
27use serde::{Deserialize, Serialize};
28use std::collections::HashMap;
29use std::ops::{Add, AddAssign};
30use std::sync::Arc;
31
32/// A document sent with a request as context, rendered before the chat history.
33#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
34pub struct Document {
35    /// Stable document identifier included in the serialized context block.
36    pub id: String,
37    /// Text content passed to the model as retrieval or static context.
38    pub text: String,
39    /// Additional string metadata rendered before the document text.
40    #[serde(flatten)]
41    pub additional_props: HashMap<String, String>,
42}
43
44impl std::fmt::Display for Document {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(
47            f,
48            concat!("<file id: {}>\n", "{}\n", "</file>\n"),
49            self.id,
50            if self.additional_props.is_empty() {
51                self.text.clone()
52            } else {
53                let mut sorted_props = self.additional_props.iter().collect::<Vec<_>>();
54                sorted_props.sort_by(|a, b| a.0.cmp(b.0));
55                let metadata = sorted_props
56                    .iter()
57                    .map(|(k, v)| format!("{k}: {v:?}"))
58                    .collect::<Vec<_>>()
59                    .join(" ");
60                format!("<metadata {} />\n{}", metadata, self.text)
61            }
62        )
63    }
64}
65
66/// A tool the model may call: its name, description and argument schema,
67/// sent to the model, and what the tool does to the world
68/// ([`ToolAnnotations`]), which stays with the host.
69#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
70pub struct ToolDefinition {
71    /// Tool name exposed to the model. It must match the registered tool name.
72    pub name: ToolName,
73    /// Human-readable description sent to the model.
74    pub description: String,
75    /// JSON Schema describing tool arguments.
76    pub parameters: serde_json::Value,
77    /// What calling the tool does to the world, for the host's policies
78    /// (approval, plan mode, running calls side by side). Never sent to
79    /// the model; written only when something is stated.
80    #[serde(default, skip_serializing_if = "ToolAnnotations::is_empty")]
81    pub annotations: ToolAnnotations,
82}
83
84impl ToolDefinition {
85    /// A tool the model may call by `name`, with arguments matching the
86    /// JSON Schema `parameters`, and no annotations.
87    pub fn new(
88        name: ToolName,
89        description: impl Into<String>,
90        parameters: serde_json::Value,
91    ) -> Self {
92        Self {
93            name,
94            description: description.into(),
95            parameters,
96            annotations: ToolAnnotations::default(),
97        }
98    }
99
100    /// The same definition with `annotations`.
101    pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
102        self.annotations = annotations;
103        self
104    }
105}
106
107/// What calling a tool does to the world, as the Model Context Protocol's
108/// tool annotations state it, and spelled as they are on its wire
109/// (`readOnlyHint`, …), so an MCP tool's annotations read straight into
110/// one. Each is a hint a tool's author gives: a host's policies read them
111/// (approval, plan mode, which calls run side by side), never trusting a
112/// tool it does not trust for them. A hint left unset takes the protocol's
113/// default, which the accessors apply: not read-only, destructive, not
114/// idempotent, open world.
115///
116/// ```
117/// use rig_core::completion::ToolAnnotations;
118///
119/// let read = ToolAnnotations::default().with_read_only(true).with_open_world(false);
120/// assert!(read.read_only() && !read.destructive() && !read.open_world());
121/// let unknown = ToolAnnotations::default();
122/// assert!(!unknown.read_only() && unknown.destructive() && unknown.open_world());
123/// let mcp: ToolAnnotations = serde_json::from_value(serde_json::json!({"readOnlyHint": true}))?;
124/// assert!(mcp.read_only());
125/// # Ok::<(), serde_json::Error>(())
126/// ```
127#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, Deserialize, Serialize)]
128#[serde(rename_all = "camelCase")]
129pub struct ToolAnnotations {
130    /// A human-readable title for the tool.
131    #[serde(default, skip_serializing_if = "Option::is_none")]
132    pub title: Option<String>,
133    /// Whether the tool changes nothing in its environment.
134    #[serde(default, skip_serializing_if = "Option::is_none")]
135    pub read_only_hint: Option<bool>,
136    /// Whether a tool that changes things may also destroy or overwrite
137    /// what was there (rather than only add). Meaningless for a read-only
138    /// tool.
139    #[serde(default, skip_serializing_if = "Option::is_none")]
140    pub destructive_hint: Option<bool>,
141    /// Whether calling the tool again with the same arguments has no
142    /// further effect. Meaningless for a read-only tool.
143    #[serde(default, skip_serializing_if = "Option::is_none")]
144    pub idempotent_hint: Option<bool>,
145    /// Whether the tool reaches beyond a closed set of things, such as the
146    /// web or a shell; `false` for one that works on a known store, such
147    /// as the project's files.
148    #[serde(default, skip_serializing_if = "Option::is_none")]
149    pub open_world_hint: Option<bool>,
150}
151
152impl ToolAnnotations {
153    /// Whether nothing is stated.
154    pub fn is_empty(&self) -> bool {
155        *self == Self::default()
156    }
157
158    /// Whether the tool changes nothing: the hint, else `false`.
159    pub fn read_only(&self) -> bool {
160        self.read_only_hint.unwrap_or(false)
161    }
162
163    /// Whether the tool may destroy or overwrite something: never for a
164    /// read-only tool, else the hint, else `true`.
165    pub fn destructive(&self) -> bool {
166        !self.read_only() && self.destructive_hint.unwrap_or(true)
167    }
168
169    /// Whether repeating a call has no further effect: always for a
170    /// read-only tool, else the hint, else `false`.
171    pub fn idempotent(&self) -> bool {
172        self.read_only() || self.idempotent_hint.unwrap_or(false)
173    }
174
175    /// Whether the tool reaches an open world: the hint, else `true`.
176    pub fn open_world(&self) -> bool {
177        self.open_world_hint.unwrap_or(true)
178    }
179
180    /// The same annotations with a title.
181    pub fn with_title(mut self, title: impl Into<String>) -> Self {
182        self.title = Some(title.into());
183        self
184    }
185
186    /// The same annotations stating whether the tool is read-only.
187    pub fn with_read_only(mut self, read_only: bool) -> Self {
188        self.read_only_hint = Some(read_only);
189        self
190    }
191
192    /// The same annotations stating whether the tool is destructive.
193    pub fn with_destructive(mut self, destructive: bool) -> Self {
194        self.destructive_hint = Some(destructive);
195        self
196    }
197
198    /// The same annotations stating whether the tool is idempotent.
199    pub fn with_idempotent(mut self, idempotent: bool) -> Self {
200        self.idempotent_hint = Some(idempotent);
201        self
202    }
203
204    /// The same annotations stating whether the tool reaches an open world.
205    pub fn with_open_world(mut self, open_world: bool) -> Self {
206        self.open_world_hint = Some(open_world);
207        self
208    }
209}
210
211/// Provider-native tool definition.
212///
213/// Stored under `additional_params.tools` and forwarded by providers that support
214/// provider-managed tools.
215#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
216pub struct ProviderToolDefinition {
217    /// Tool type/kind name as expected by the target provider (for example `web_search`).
218    #[serde(rename = "type")]
219    pub kind: String,
220    /// Additional provider-specific configuration for this hosted tool.
221    #[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
222    pub config: serde_json::Map<String, serde_json::Value>,
223}
224
225impl ProviderToolDefinition {
226    /// Creates a provider-hosted tool definition by type.
227    pub fn new(kind: impl Into<String>) -> Self {
228        Self {
229            kind: kind.into(),
230            config: serde_json::Map::new(),
231        }
232    }
233
234    /// Adds a provider-specific configuration key/value.
235    pub fn with_config(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
236        self.config.insert(key.into(), value);
237        self
238    }
239}
240
241/// Normalized generation ending. Unmapped provider values remain in [`Self::Other`].
242/// Failure statuses may accompany parseable output; callers must decide whether
243/// such output is usable rather than treating every response as successful.
244#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245#[serde(rename_all = "snake_case")]
246#[non_exhaustive]
247pub enum FinishReason {
248    /// Natural end of the response.
249    Stop,
250    /// The response hit the output-token limit.
251    Length,
252    /// The model stopped to call one or more tools.
253    ToolCalls,
254    /// The provider filtered the content.
255    ContentFilter,
256    /// A provider-specific reason outside the normalized vocabulary, carried
257    /// verbatim in the provider's own wire spelling.
258    Other(String),
259}
260
261impl FinishReason {
262    /// Changes [`Self::Stop`] to [`Self::ToolCalls`] when output contains a tool
263    /// call. All other reasons remain unchanged. Response builders and streaming
264    /// aggregation apply this reconciliation.
265    pub fn reconcile_with_output(self, has_tool_call: bool) -> Self {
266        if has_tool_call && matches!(self, Self::Stop) {
267            Self::ToolCalls
268        } else {
269            self
270        }
271    }
272
273    /// Returns whether the reason is [`Self::Length`] or [`Self::ContentFilter`].
274    /// These reasons permit answerless turns without treating absent content as
275    /// a malformed response. Unknown reasons are not classified as truncation.
276    pub fn truncated_output(&self) -> bool {
277        matches!(self, Self::Length | Self::ContentFilter)
278    }
279
280    /// Formats an answerless-turn diagnostic with budget or filtering advice
281    /// for known truncation reasons, and a generic explanation otherwise.
282    pub fn no_answer_message(&self) -> String {
283        let remedy = match self {
284            Self::Length => {
285                "the turn ran out of output budget before producing one — \
286                 raise max_tokens for this request"
287            }
288            Self::ContentFilter => {
289                "the provider filtered the response — the content, not the \
290                 budget, is what it objected to"
291            }
292            _ => "the turn ended before producing one",
293        };
294        format!(
295            "the model produced no answer and stopped with \
296             finish_reason={self:?}; {remedy}"
297        )
298    }
299}
300
301/// Assistant content and normalized completion metadata. The choice may be
302/// empty, including for truncated or filtered turns. Provider-specific data is
303/// available through [`Self::raw`] without retaining a concrete model type.
304///
305/// A response goes straight back into the conversation as the assistant
306/// turn: `history.extend(response.message())`.
307#[non_exhaustive]
308#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
309#[serde(from = "CompletionResponseRepr")]
310pub struct CompletionResponse {
311    /// Assistant content returned by the provider, one block per provider
312    /// output item, in provider order. Possibly empty.
313    pub choice: Vec<AssistantContent>,
314    /// Tokens used during prompting and responding
315    pub usage: Usage,
316    /// The wire, provider and requested model that produced the response,
317    /// with the model and response id the provider reported.
318    pub origin: Origin,
319    /// The provider's report that the turn failed, such as a refusal's
320    /// explanation. The turn's message then ends in [`StopReason::Error`]
321    /// and is never replayed.
322    #[serde(default, skip_serializing_if = "Option::is_none")]
323    pub error: Option<String>,
324    /// Why the reply stopped before the provider ended it, when the caller
325    /// stopped reading: the turn's message then ends in
326    /// [`StopReason::Aborted`] and is replayed as its text alone.
327    #[serde(default, skip_serializing_if = "Option::is_none")]
328    pub aborted: Option<String>,
329    /// Request identifier from HTTP headers or SDK metadata, not the body's
330    /// response ID. `None` when the provider reports none.
331    #[serde(default, skip_serializing_if = "Option::is_none")]
332    pub provider_request_id: Option<String>,
333    /// Reported finish reason, reconciled by the setters with tool-call output.
334    /// Read through [`Self::finish_reason`].
335    #[serde(default)]
336    finish_reason: Option<FinishReason>,
337    /// Whether [`FinishReason::Other`] ends the turn as a normal stop. Read
338    /// through [`Self::accepts_unknown_finish_reasons`].
339    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
340    accepts_unknown_finish_reasons: bool,
341    /// The calls the provider ended the reply in the middle of. Read
342    /// through [`Self::unfinished_calls`].
343    #[serde(default, skip_serializing_if = "Vec::is_empty")]
344    unfinished_calls: Vec<CallId>,
345    /// Provider response document for typed inspection through deserialization.
346    /// Parsed wire types may omit unmodeled fields. This data does not override
347    /// normalized fields; callers constructing responses must supply it.
348    pub raw: serde_json::Value,
349}
350
351/// Distinct response and transport identifiers for one model call.
352/// Unreported identifiers remain `None`.
353#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
354pub struct ResponseIdentity {
355    /// Response-wide ID.
356    #[serde(default, skip_serializing_if = "Option::is_none")]
357    pub response_id: Option<String>,
358    /// Transport request ID from HTTP headers or SDK metadata.
359    #[serde(default, skip_serializing_if = "Option::is_none")]
360    pub provider_request_id: Option<String>,
361}
362
363impl CompletionResponse {
364    /// Create a response from its required parts; optional metadata starts
365    /// unset. `raw` is the provider's own document for this response,
366    /// serialized; see [`Self::raw`].
367    pub fn new(
368        choice: Vec<AssistantContent>,
369        usage: Usage,
370        origin: Origin,
371        raw: serde_json::Value,
372    ) -> Self {
373        Self {
374            choice,
375            usage,
376            origin,
377            error: None,
378            aborted: None,
379            provider_request_id: None,
380            finish_reason: None,
381            accepts_unknown_finish_reasons: false,
382            unfinished_calls: Vec::new(),
383            raw,
384        }
385    }
386
387    /// The provider descriptor name (`"openai"`).
388    pub fn provider(&self) -> &str {
389        &self.origin.provider
390    }
391
392    /// `P`'s typed view of [`Self::raw`]. `None` unless `P` is the provider
393    /// that produced the reply.
394    ///
395    /// # Errors
396    ///
397    /// The inner result fails when `raw` does not hold `P`'s extras.
398    pub fn extras<P: ProviderExtension>(&self) -> Option<Result<P::Extras, serde_json::Error>> {
399        (self.origin.provider == P::PROVIDER)
400            .then(|| P::Extras::from_reply(&self.origin.api, &self.raw))
401    }
402
403    /// `P`'s typed view of [`Self::raw`], or the empty view (every field
404    /// `None`) when it cannot be read: when another provider produced the
405    /// reply, or when `raw` does not hold `P`'s extras. A decode failure is
406    /// logged at `debug`; use [`Self::extras`] to tell the cases apart.
407    pub fn extras_lossy<P: ProviderExtension>(&self) -> P::Extras
408    where
409        P::Extras: Default,
410    {
411        match self.extras::<P>() {
412            Some(Ok(extras)) => extras,
413            Some(Err(error)) => {
414                tracing::debug!(
415                    provider = P::PROVIDER,
416                    api = %self.origin.api,
417                    %error,
418                    "reply extras did not decode; reading them as empty"
419                );
420                P::Extras::default()
421            }
422            None => P::Extras::default(),
423        }
424    }
425
426    /// The model the provider reported, when it reported one.
427    pub fn model(&self) -> Option<&str> {
428        self.origin.response_model.as_deref()
429    }
430
431    /// The provider's response id, when it sent one.
432    pub fn response_id(&self) -> Option<&str> {
433        self.origin.response_id.as_deref()
434    }
435
436    /// Why the model stopped generating, when the provider reported it.
437    pub fn finish_reason(&self) -> Option<FinishReason> {
438        self.finish_reason.clone()
439    }
440
441    /// Whether a finish reason outside the normalized vocabulary ends the
442    /// turn as a normal stop, as the request asked
443    /// ([`CompletionRequest::accept_unknown_finish_reasons`]).
444    ///
445    /// [`CompletionRequest::accept_unknown_finish_reasons`]: field@CompletionRequest::accept_unknown_finish_reasons
446    pub fn accepts_unknown_finish_reasons(&self) -> bool {
447        self.accepts_unknown_finish_reasons
448    }
449
450    /// The tool calls the provider ended the reply in the middle of (their
451    /// arguments as far as they came), in the order they opened, such as a
452    /// call cut by [`FinishReason::Length`]. Empty when every call in
453    /// [`Self::choice`] was finished.
454    pub fn unfinished_calls(&self) -> impl Iterator<Item = &ToolCall> {
455        self.tool_calls()
456            .filter(|call| self.unfinished_calls.contains(&call.id))
457    }
458
459    /// The same response, `calls` (ids of calls in [`Self::choice`]) being
460    /// the ones the provider ended the reply in the middle of.
461    pub fn with_unfinished_calls(mut self, calls: impl IntoIterator<Item = CallId>) -> Self {
462        self.unfinished_calls = calls.into_iter().collect();
463        self
464    }
465
466    /// Accept, or refuse, [`FinishReason::Other`] as a normal stop in
467    /// [`Self::stop`]. Filtered content and a reported failure still fail.
468    pub fn accept_unknown_finish_reasons(mut self, accept: bool) -> Self {
469        self.accepts_unknown_finish_reasons = accept;
470        self
471    }
472
473    /// How the turn ended, for history. It fails closed: only a natural
474    /// stop, the token limit and a stop to call tools are successes. A
475    /// reported failure, filtered content and any finish reason outside
476    /// that set are [`StopReason::Error`], unless the response accepts
477    /// unknown reasons ([`Self::accepts_unknown_finish_reasons`]), which then
478    /// stop like [`FinishReason::Stop`]. A reply the caller stopped reading
479    /// is [`StopReason::Aborted`].
480    pub fn stop(&self) -> StopReason {
481        if let Some(error) = &self.error {
482            return StopReason::Error(error.clone());
483        }
484        if let Some(reason) = &self.aborted {
485            return StopReason::Aborted(reason.clone());
486        }
487        match &self.finish_reason {
488            Some(FinishReason::Length) => StopReason::Length,
489            Some(FinishReason::ToolCalls) => StopReason::ToolUse,
490            Some(FinishReason::ContentFilter) => {
491                StopReason::Error("Provider finish_reason: content_filter".to_owned())
492            }
493            Some(FinishReason::Other(reason)) if !self.accepts_unknown_finish_reasons => {
494                StopReason::Error(format!("Provider finish_reason: {reason}"))
495            }
496            Some(FinishReason::Stop | FinishReason::Other(_)) | None
497                if self.tool_calls().next().is_some() =>
498            {
499                StopReason::ToolUse
500            }
501            Some(FinishReason::Stop | FinishReason::Other(_)) | None => StopReason::Stop,
502        }
503    }
504
505    /// This response's identity metadata as one [`ResponseIdentity`] carrier.
506    pub fn identity(&self) -> ResponseIdentity {
507        ResponseIdentity {
508            response_id: self.origin.response_id.clone(),
509            provider_request_id: self.provider_request_id.clone(),
510        }
511    }
512
513    /// Attach the normalized finish reason, reconciled against the choice via
514    /// [`FinishReason::reconcile_with_output`].
515    pub fn with_finish_reason(self, finish_reason: FinishReason) -> Self {
516        self.with_optional_finish_reason(Some(finish_reason))
517    }
518
519    /// The text parts of [`Self::choice`], concatenated in order.
520    pub fn text(&self) -> String {
521        self.choice
522            .iter()
523            .filter_map(|part| match part {
524                AssistantContent::Text(text) => Some(text.text.as_str()),
525                _ => None,
526            })
527            .collect()
528    }
529
530    /// The reasoning text of [`Self::choice`], concatenated in order.
531    /// Redacted reasoning has no text.
532    pub fn reasoning(&self) -> String {
533        self.choice
534            .iter()
535            .filter_map(|part| match part {
536                AssistantContent::Reasoning(reasoning) => Some(reasoning.text.as_str()),
537                _ => None,
538            })
539            .collect()
540    }
541
542    /// The assistant turn to append to the conversation: [`Self::choice`] in
543    /// order with its origin and stop, or `None` for an
544    /// empty choice.
545    pub fn message(&self) -> Option<Message> {
546        if self.choice.is_empty() {
547            return None;
548        }
549        Some(Message::Assistant(AssistantMessage {
550            content: self.choice.clone(),
551            ..self.head()
552        }))
553    }
554
555    /// The turn this response began, holding `content`, for a runtime that
556    /// cut the reply short and answers its calls itself (an agent rolling a
557    /// turn back over an invalid call). It keeps the origin, and stops to
558    /// call tools, since the runtime answers them. A block keeps its provider
559    /// item only when this response holds the same block with it. A response
560    /// the provider did not end, or one the consumer has not wholly taken,
561    /// holds no provider item, so its turn replays canonically.
562    pub fn continued(&self, content: Vec<AssistantContent>) -> AssistantMessage {
563        let content = content
564            .into_iter()
565            .map(|block| {
566                if self.choice.contains(&block) {
567                    block
568                } else {
569                    crate::operation::completion::canonical(block)
570                }
571            })
572            .collect();
573        AssistantMessage::rolled_back(Some(self.origin.clone()), content)
574    }
575
576    /// The turn's origin and stop with no content, for a
577    /// runtime that carries the content separately.
578    pub fn head(&self) -> AssistantMessage {
579        AssistantMessage {
580            content: Vec::new(),
581            origin: Some(self.origin.clone()),
582            stop: Some(self.stop()),
583        }
584    }
585
586    /// The tool calls in [`Self::choice`], in order.
587    pub fn tool_calls(&self) -> impl Iterator<Item = &ToolCall> {
588        self.choice.iter().filter_map(|part| match part {
589            AssistantContent::ToolCall(call) => Some(call),
590            _ => None,
591        })
592    }
593
594    /// Sets or clears the finish reason, reconciling a present reason with the choice.
595    pub fn with_optional_finish_reason(mut self, finish_reason: Option<FinishReason>) -> Self {
596        let has_tool_call = self
597            .choice
598            .iter()
599            .any(|content| matches!(content, AssistantContent::ToolCall(_)));
600        self.finish_reason =
601            finish_reason.map(|reason| reason.reconcile_with_output(has_tool_call));
602        self
603    }
604}
605
606/// Deserialization shape routed through builders for finish-reason reconciliation
607/// and empty-identifier normalization.
608#[derive(Deserialize)]
609struct CompletionResponseRepr {
610    choice: Vec<AssistantContent>,
611    usage: Usage,
612    origin: Origin,
613    #[serde(default)]
614    error: Option<String>,
615    #[serde(default)]
616    aborted: Option<String>,
617    #[serde(default)]
618    provider_request_id: Option<String>,
619    #[serde(default)]
620    finish_reason: Option<FinishReason>,
621    #[serde(default)]
622    accepts_unknown_finish_reasons: bool,
623    #[serde(default)]
624    unfinished_calls: Vec<CallId>,
625    raw: serde_json::Value,
626}
627
628impl From<CompletionResponseRepr> for CompletionResponse {
629    fn from(repr: CompletionResponseRepr) -> Self {
630        let CompletionResponseRepr {
631            choice,
632            usage,
633            mut origin,
634            error,
635            aborted,
636            provider_request_id,
637            finish_reason,
638            accepts_unknown_finish_reasons,
639            unfinished_calls,
640            raw,
641        } = repr;
642        use crate::provider_response::reported;
643        origin.response_id = reported(origin.response_id);
644        origin.response_model = reported(origin.response_model);
645        let mut response =
646            Self::new(choice, usage, origin, raw).with_optional_finish_reason(finish_reason);
647        response.error = error;
648        response.aborted = aborted;
649        response.provider_request_id = reported(provider_request_id);
650        response.accepts_unknown_finish_reasons = accepts_unknown_finish_reasons;
651        response.unfinished_calls = unfinished_calls;
652        response
653    }
654}
655
656/// The token usage a provider reported for one completion.
657///
658/// Every provider mapping keeps one contract, so the counters read the same
659/// way on every provider:
660///
661/// - `cached_input_tokens + cache_creation_input_tokens <= input_tokens`:
662///   input counts every prompt token, cache reads and writes included.
663/// - `reasoning_tokens <= output_tokens`: output counts every generated
664///   token, reasoning included.
665/// - `total_tokens == input_tokens + output_tokens`, absent unless both are
666///   reported.
667///
668/// A counter the provider did not send is `None`; a reported zero is
669/// `Some(0)`. Serialized as the same keys, absent when `None`.
670///
671/// ```
672/// use rig_core::completion::Usage;
673///
674/// let usage = Usage::new().input_tokens(12).output_tokens(3).total_tokens(15);
675/// assert_eq!(usage.input_tokens, Some(12));
676/// assert!(usage.cost.is_none());
677/// ```
678#[non_exhaustive]
679#[derive(Debug, Default, PartialEq, Clone, Copy, Serialize, Deserialize)]
680pub struct Usage {
681    /// Every input token of the request: uncached, read from a cache,
682    /// written to a cache, and any prompt a provider's hosted tools added.
683    #[serde(default, skip_serializing_if = "Option::is_none")]
684    pub input_tokens: Option<u64>,
685    /// Every output token, reasoning included.
686    #[serde(default, skip_serializing_if = "Option::is_none")]
687    pub output_tokens: Option<u64>,
688    /// `input_tokens + output_tokens`; absent unless both are reported.
689    #[serde(default, skip_serializing_if = "Option::is_none")]
690    pub total_tokens: Option<u64>,
691    /// The part of `input_tokens` read from a provider-managed cache.
692    #[serde(default, skip_serializing_if = "Option::is_none")]
693    pub cached_input_tokens: Option<u64>,
694    /// The part of `input_tokens` written to a provider-managed cache.
695    #[serde(default, skip_serializing_if = "Option::is_none")]
696    pub cache_creation_input_tokens: Option<u64>,
697    /// The part of `input_tokens` a provider's hosted tools added to the prompt.
698    #[serde(default, skip_serializing_if = "Option::is_none")]
699    pub tool_use_prompt_tokens: Option<u64>,
700    /// The part of `output_tokens` spent on internal reasoning ("thinking",
701    /// "thoughts").
702    #[serde(default, skip_serializing_if = "Option::is_none")]
703    pub reasoning_tokens: Option<u64>,
704    /// What the turn cost in USD, when known. Never derived from the token
705    /// counters here, and none of them is derived from it. A cost the
706    /// provider reports is its figure. One priced from the model's catalog
707    /// spec ([`Pricing::cost`](crate::catalog::Pricing::cost), from the
708    /// facts the wire was connected with) is the
709    /// standard-tier list price of the counted tokens: it leaves out the
710    /// service tier, long-context price tiers and hosted-tool fees (web
711    /// search, code execution), so it can be lower than the bill.
712    #[serde(default, skip_serializing_if = "Option::is_none")]
713    pub cost: Option<Cost>,
714}
715
716impl Usage {
717    /// No counter and no cost reported.
718    pub const fn new() -> Self {
719        Self {
720            input_tokens: None,
721            output_tokens: None,
722            total_tokens: None,
723            cached_input_tokens: None,
724            cache_creation_input_tokens: None,
725            tool_use_prompt_tokens: None,
726            reasoning_tokens: None,
727            cost: None,
728        }
729    }
730
731    /// Whether the provider reported any counter or a cost.
732    pub fn is_reported(&self) -> bool {
733        *self != Self::default()
734    }
735
736    /// The tokens the call read and wrote, what a conversation's next
737    /// request sends again: [`Self::total_tokens`](field@Self::total_tokens),
738    /// else the input and output tokens. `None` without input tokens.
739    pub fn context_tokens(&self) -> Option<u64> {
740        self.total_tokens.or_else(|| {
741            Some(
742                self.input_tokens?
743                    .saturating_add(self.output_tokens.unwrap_or(0)),
744            )
745        })
746    }
747
748    /// Set, or with `None` clear, [`Self::input_tokens`](field@Self::input_tokens).
749    pub fn input_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
750        self.input_tokens = tokens.into();
751        self
752    }
753
754    /// Set, or with `None` clear, [`Self::output_tokens`](field@Self::output_tokens).
755    pub fn output_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
756        self.output_tokens = tokens.into();
757        self
758    }
759
760    /// Set, or with `None` clear, [`Self::total_tokens`](field@Self::total_tokens).
761    pub fn total_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
762        self.total_tokens = tokens.into();
763        self
764    }
765
766    /// Set, or with `None` clear,
767    /// [`Self::cached_input_tokens`](field@Self::cached_input_tokens).
768    pub fn cached_input_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
769        self.cached_input_tokens = tokens.into();
770        self
771    }
772
773    /// Set, or with `None` clear,
774    /// [`Self::cache_creation_input_tokens`](field@Self::cache_creation_input_tokens).
775    pub fn cache_creation_input_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
776        self.cache_creation_input_tokens = tokens.into();
777        self
778    }
779
780    /// Set, or with `None` clear,
781    /// [`Self::tool_use_prompt_tokens`](field@Self::tool_use_prompt_tokens).
782    pub fn tool_use_prompt_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
783        self.tool_use_prompt_tokens = tokens.into();
784        self
785    }
786
787    /// Set, or with `None` clear, [`Self::reasoning_tokens`](field@Self::reasoning_tokens).
788    pub fn reasoning_tokens(mut self, tokens: impl Into<Option<u64>>) -> Self {
789        self.reasoning_tokens = tokens.into();
790        self
791    }
792
793    /// Set, or with `None` clear, [`Self::cost`](field@Self::cost).
794    pub fn cost(mut self, cost: impl Into<Option<Cost>>) -> Self {
795        self.cost = cost.into();
796        self
797    }
798}
799
800/// What one or more turns cost, in USD. `total` is what was charged, or
801/// when [`Self::is_complete`] is `false`, a lower bound of it.
802///
803/// A part is `None` when its source does not give it: a provider that
804/// reports only a total leaves every part `None`, and one that reports
805/// input and output leaves the cache parts `None`. A cost computed from
806/// the catalog ([`Pricing::cost`](crate::catalog::Pricing::cost)) leaves a
807/// cache part `None` when the catalog lists no rate for it and the usage
808/// has such tokens; its `total` then sums the known parts only, and it is
809/// not complete. A part is never filled with a placeholder `0.0`. Summing
810/// costs keeps a part only when every side has it, and is complete only
811/// when every side is. A `None` part is left out when serialized, and an
812/// absent one reads back as `None`.
813#[non_exhaustive]
814#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
815pub struct Cost {
816    /// Uncached input tokens.
817    #[serde(default, skip_serializing_if = "Option::is_none")]
818    pub input: Option<f64>,
819    /// Output tokens, reasoning included.
820    #[serde(default, skip_serializing_if = "Option::is_none")]
821    pub output: Option<f64>,
822    /// Input tokens read from a cache.
823    #[serde(default, skip_serializing_if = "Option::is_none")]
824    pub cache_read: Option<f64>,
825    /// Input tokens written to a cache.
826    #[serde(default, skip_serializing_if = "Option::is_none")]
827    pub cache_write: Option<f64>,
828    /// The whole charge, or a lower bound of it when the cost is not
829    /// complete.
830    pub total: f64,
831    /// Whether a part with tokens had no price, so `total` leaves it out.
832    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
833    incomplete: bool,
834}
835
836impl Cost {
837    /// Whether [`Self::total`](field@Self::total) is the whole charge.
838    /// `false` when a part the usage has tokens for had no known price, so
839    /// `total` is a lower bound.
840    pub fn is_complete(&self) -> bool {
841        !self.incomplete
842    }
843
844    /// A cost from the catalog's prices, where `None` marks a part whose
845    /// tokens had no price: `total` sums the known parts, and the cost is
846    /// complete only when no part is `None`.
847    pub(crate) fn priced(
848        input: f64,
849        output: f64,
850        cache_read: Option<f64>,
851        cache_write: Option<f64>,
852    ) -> Self {
853        Self {
854            input: Some(input),
855            output: Some(output),
856            cache_read,
857            cache_write,
858            total: input + output + cache_read.unwrap_or(0.0) + cache_write.unwrap_or(0.0),
859            incomplete: cache_read.is_none() || cache_write.is_none(),
860        }
861    }
862
863    /// A cost split into its parts, each known; `total` is their sum.
864    pub fn from_parts(input: f64, output: f64, cache_read: f64, cache_write: f64) -> Self {
865        Self {
866            input: Some(input),
867            output: Some(output),
868            cache_read: Some(cache_read),
869            cache_write: Some(cache_write),
870            total: input + output + cache_read + cache_write,
871            incomplete: false,
872        }
873    }
874
875    /// A cost known only as its total; every part is `None`.
876    pub fn from_total(total: f64) -> Self {
877        Self {
878            input: None,
879            output: None,
880            cache_read: None,
881            cache_write: None,
882            total,
883            incomplete: false,
884        }
885    }
886
887    /// Set, or with `None` clear, [`Self::input`](field@Self::input).
888    /// `total` is left as it is.
889    pub fn input(mut self, input: impl Into<Option<f64>>) -> Self {
890        self.input = input.into();
891        self
892    }
893
894    /// Set, or with `None` clear, [`Self::output`](field@Self::output).
895    /// `total` is left as it is.
896    pub fn output(mut self, output: impl Into<Option<f64>>) -> Self {
897        self.output = output.into();
898        self
899    }
900
901    /// Set, or with `None` clear, [`Self::cache_read`](field@Self::cache_read).
902    /// `total` is left as it is.
903    pub fn cache_read(mut self, cache_read: impl Into<Option<f64>>) -> Self {
904        self.cache_read = cache_read.into();
905        self
906    }
907
908    /// Set, or with `None` clear, [`Self::cache_write`](field@Self::cache_write).
909    /// `total` is left as it is.
910    pub fn cache_write(mut self, cache_write: impl Into<Option<f64>>) -> Self {
911        self.cache_write = cache_write.into();
912        self
913    }
914}
915
916/// Sum one part of two costs: unknown when either side's is unknown, since
917/// a known part plus an unknown one is not known.
918fn add_part(lhs: Option<f64>, rhs: Option<f64>) -> Option<f64> {
919    Some(lhs? + rhs?)
920}
921
922/// `total` always sums. Each part sums only when both sides know it, and is
923/// `None` otherwise, so a summed part never understates what it covers. The
924/// sum is complete only when both sides are.
925impl Add for Cost {
926    type Output = Self;
927
928    fn add(self, other: Self) -> Self::Output {
929        Self {
930            input: add_part(self.input, other.input),
931            output: add_part(self.output, other.output),
932            cache_read: add_part(self.cache_read, other.cache_read),
933            cache_write: add_part(self.cache_write, other.cache_write),
934            total: self.total + other.total,
935            incomplete: self.incomplete || other.incomplete,
936        }
937    }
938}
939
940/// Sum two counters where an unreported side does not turn a reported one
941/// into "unreported".
942fn add_counter(lhs: Option<u64>, rhs: Option<u64>) -> Option<u64> {
943    match (lhs, rhs) {
944        (None, None) => None,
945        (lhs, rhs) => Some(lhs.unwrap_or(0) + rhs.unwrap_or(0)),
946    }
947}
948
949impl Add for Usage {
950    type Output = Self;
951
952    fn add(mut self, other: Self) -> Self::Output {
953        self += other;
954        self
955    }
956}
957
958/// Token counters add where an unreported side adds nothing. Cost sums only
959/// when both sides have one: a turn whose cost is unknown makes the sum
960/// unknown, rather than too low. A side that reports nothing at all
961/// ([`Usage::is_reported`] is `false`) is the identity, so a fold from
962/// [`Usage::default`] keeps its first turn's cost.
963impl AddAssign for Usage {
964    fn add_assign(&mut self, other: Self) {
965        if !other.is_reported() {
966            return;
967        }
968        if !self.is_reported() {
969            *self = other;
970            return;
971        }
972        self.cost = match (self.cost, other.cost) {
973            (Some(lhs), Some(rhs)) => Some(lhs + rhs),
974            _ => None,
975        };
976        self.input_tokens = add_counter(self.input_tokens, other.input_tokens);
977        self.output_tokens = add_counter(self.output_tokens, other.output_tokens);
978        self.total_tokens = add_counter(self.total_tokens, other.total_tokens);
979        self.cached_input_tokens = add_counter(self.cached_input_tokens, other.cached_input_tokens);
980        self.cache_creation_input_tokens = add_counter(
981            self.cache_creation_input_tokens,
982            other.cache_creation_input_tokens,
983        );
984        self.tool_use_prompt_tokens =
985            add_counter(self.tool_use_prompt_tokens, other.tool_use_prompt_tokens);
986        self.reasoning_tokens = add_counter(self.reasoning_tokens, other.reasoning_tokens);
987    }
988}
989
990/// Many calls' [`Usage`] summed for a running account: the token counters,
991/// the known cost, and how many calls the sum holds and how many of them
992/// were not fully priced.
993///
994/// ```
995/// use rig_core::completion::{Usage, UsageTotals};
996///
997/// let mut totals = UsageTotals::default();
998/// totals.record(&Usage::new().input_tokens(100).output_tokens(20).total_tokens(120));
999/// assert_eq!((totals.calls, totals.unpriced, totals.uncached_input()), (1, 1, 100));
1000/// ```
1001#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
1002pub struct UsageTotals {
1003    /// The token counters summed, with no cost: a call that did not
1004    /// report a counter adds nothing to it.
1005    pub tokens: Usage,
1006    /// The known costs summed, in USD.
1007    pub cost: f64,
1008    /// The calls whose cost is not known, or known only in part, so
1009    /// [`Self::cost`](field@Self::cost) is a lower bound when any.
1010    pub unpriced: u64,
1011    /// The calls summed.
1012    pub calls: u64,
1013}
1014
1015impl UsageTotals {
1016    /// Adds a finished call's `usage`.
1017    pub fn record(&mut self, usage: &Usage) {
1018        self.calls += 1;
1019        match usage.cost {
1020            Some(cost) => {
1021                self.cost += cost.total;
1022                if !cost.is_complete() {
1023                    self.unpriced += 1;
1024                }
1025            }
1026            None => self.unpriced += 1,
1027        }
1028        self.tokens += usage.cost(None);
1029    }
1030
1031    /// Adds what `other` summed.
1032    pub fn add(&mut self, other: &UsageTotals) {
1033        self.tokens += other.tokens;
1034        self.cost += other.cost;
1035        self.unpriced += other.unpriced;
1036        self.calls += other.calls;
1037    }
1038
1039    /// Input tokens neither read from nor written to a cache.
1040    pub fn uncached_input(&self) -> u64 {
1041        self.tokens
1042            .input_tokens
1043            .unwrap_or(0)
1044            .saturating_sub(self.tokens.cached_input_tokens.unwrap_or(0))
1045            .saturating_sub(self.tokens.cache_creation_input_tokens.unwrap_or(0))
1046    }
1047
1048    /// The tokens read and written, cached input included.
1049    pub fn total_tokens(&self) -> u64 {
1050        self.tokens
1051            .input_tokens
1052            .unwrap_or(0)
1053            .saturating_add(self.tokens.output_tokens.unwrap_or(0))
1054    }
1055
1056    /// The cost as `$0.123`, ending in `+` when some calls were not priced,
1057    /// or `None` when no call was.
1058    pub fn cost_label(&self) -> Option<String> {
1059        if self.unpriced >= self.calls {
1060            return None;
1061        }
1062        let more = if self.unpriced > 0 { "+" } else { "" };
1063        Some(format!("{}{more}", dollars_label(self.cost)))
1064    }
1065
1066    /// The [cost](Self::cost_label), or as `1.2M tokens` when no call was
1067    /// priced, such as a local or subscription model's.
1068    pub fn cost_or_tokens(&self) -> String {
1069        self.cost_label()
1070            .unwrap_or_else(|| format!("{} tokens", tokens_label(self.total_tokens())))
1071    }
1072}
1073
1074/// One line for a person or a log: the calls, the tokens by kind and the
1075/// cost.
1076impl std::fmt::Display for UsageTotals {
1077    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1078        match self.calls {
1079            1 => f.write_str("1 model call")?,
1080            calls => write!(f, "{calls} model calls")?,
1081        }
1082        write!(f, ": {} in", tokens_label(self.uncached_input()))?;
1083        if let Some(read) = self.tokens.cached_input_tokens.filter(|read| *read > 0) {
1084            write!(f, ", {} cache read", tokens_label(read))?;
1085        }
1086        if let Some(written) = self
1087            .tokens
1088            .cache_creation_input_tokens
1089            .filter(|written| *written > 0)
1090        {
1091            write!(f, ", {} cache written", tokens_label(written))?;
1092        }
1093        write!(
1094            f,
1095            ", {} out",
1096            tokens_label(self.tokens.output_tokens.unwrap_or(0))
1097        )?;
1098        if let Some(thought) = self.tokens.reasoning_tokens.filter(|thought| *thought > 0) {
1099            write!(f, " ({} reasoning)", tokens_label(thought))?;
1100        }
1101        let cost = self.cost_label();
1102        write!(f, "; {}", cost.as_deref().unwrap_or("cost unknown"))
1103    }
1104}
1105
1106/// The context a conversation's next request sends, against the model's
1107/// input limit.
1108#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1109pub struct ContextUse {
1110    /// Tokens in use.
1111    pub tokens: u64,
1112    /// The model's input limit, when known.
1113    pub limit: Option<u32>,
1114}
1115
1116impl ContextUse {
1117    /// The share of the input limit in use, in percent, when the limit is
1118    /// known.
1119    pub fn percent(&self) -> Option<u64> {
1120        self.limit
1121            .filter(|limit| *limit > 0)
1122            .map(|limit| self.tokens.saturating_mul(100) / u64::from(limit))
1123    }
1124}
1125
1126/// `45k/200k (22%)`, or `45k` when the input limit is not known.
1127impl std::fmt::Display for ContextUse {
1128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1129        match (self.limit, self.percent()) {
1130            (Some(limit), Some(percent)) => write!(
1131                f,
1132                "{}/{} ({percent}%)",
1133                tokens_label(self.tokens),
1134                tokens_label(u64::from(limit))
1135            ),
1136            _ => f.write_str(&tokens_label(self.tokens)),
1137        }
1138    }
1139}
1140
1141/// A token count in at most four characters plus a unit: `999`, `1.2k`,
1142/// `45k`, `1.2M`.
1143pub fn tokens_label(count: u64) -> String {
1144    // Shown rounded, so the float conversion's precision does not matter.
1145    let scaled = |unit: f64| count as f64 / unit;
1146    match count {
1147        0..1_000 => count.to_string(),
1148        1_000..10_000 => format!("{:.1}k", scaled(1e3)),
1149        10_000..1_000_000 => format!("{:.0}k", scaled(1e3)),
1150        1_000_000..10_000_000 => format!("{:.1}M", scaled(1e6)),
1151        _ => format!("{:.0}M", scaled(1e6)),
1152    }
1153}
1154
1155/// A cost in USD: tenths of a cent below a dollar, cents above.
1156fn dollars_label(cost: f64) -> String {
1157    if cost < 1.0 {
1158        format!("${cost:.3}")
1159    } else {
1160        format!("${cost:.2}")
1161    }
1162}
1163
1164/// Model capabilities used by runtimes when preparing requests.
1165/// Defaults are conservative; construct through [`Self::new`] and setters.
1166#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1167pub struct ProviderCapabilities {
1168    /// Whether native structured output can remain enabled with tool calls
1169    /// without suppressing them. Defaults to `false`.
1170    pub composes_native_output_with_tools: bool,
1171    /// Whether the model answers a forced tool choice (`Required` or
1172    /// `Specific`) with an error. Rig then does not force the output tool
1173    /// of its own structured-output flow (the extractor). Defaults to `false`.
1174    #[serde(default, skip_serializing_if = "crate::json_utils::is_false")]
1175    pub rejects_forced_tool_choice: bool,
1176}
1177
1178impl ProviderCapabilities {
1179    /// Create the conservative capability set used by default.
1180    pub const fn new() -> Self {
1181        Self {
1182            composes_native_output_with_tools: false,
1183            rejects_forced_tool_choice: false,
1184        }
1185    }
1186
1187    /// Declare whether native structured output composes with tool calls.
1188    pub const fn with_native_output_tool_composition(mut self, supported: bool) -> Self {
1189        self.composes_native_output_with_tools = supported;
1190        self
1191    }
1192
1193    /// Declare whether the model rejects a forced tool choice.
1194    pub const fn with_forced_tool_choice_rejected(mut self, rejected: bool) -> Self {
1195        self.rejects_forced_tool_choice = rejected;
1196        self
1197    }
1198}
1199
1200/// A completion request to any provider's model.
1201#[non_exhaustive]
1202#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1203pub struct CompletionRequest {
1204    /// Optional model override for this request.
1205    pub model: Option<String>,
1206    /// The chat history to be sent to the completion model provider.
1207    /// The very last message is the prompt.
1208    ///
1209    /// It must hold at least one message, and every user and assistant
1210    /// message must carry content. The field is public, so this is a rule
1211    /// rather than a type guarantee: [`Self::validate_message_content`]
1212    /// checks it at the request boundary.
1213    ///
1214    /// Each message is shared, with the conversation it came from and the
1215    /// records of the request, so copying a request copies pointers.
1216    pub chat_history: Vec<Arc<Message>>,
1217    /// The documents to be sent to the completion model provider
1218    pub documents: Vec<Document>,
1219    /// The tools to be sent to the completion model provider, each shared,
1220    /// so copying a request copies pointers.
1221    pub tools: Vec<Arc<ToolDefinition>>,
1222    /// The temperature to be sent to the completion model provider
1223    pub temperature: Option<f64>,
1224    /// The max tokens to be sent to the completion model provider
1225    pub max_tokens: Option<u64>,
1226    /// Which tools the model may or must call before it answers.
1227    pub tool_choice: Option<ToolChoice>,
1228    /// Additional provider-specific parameters to be sent to the completion model provider
1229    pub additional_params: Option<serde_json::Value>,
1230    /// Optional JSON Schema for structured output. When set, providers that support
1231    /// native structured outputs will constrain the model's response to match this schema.
1232    pub output_schema: Option<schemars::Schema>,
1233    /// Opt-in for sensitive request, response, and tool-content telemetry.
1234    /// Defaults to `false` and is excluded from serialization. Enabling it can
1235    /// expose prompts, context, tool results, and model output in span attributes
1236    /// and increase telemetry storage costs. Requires explicit caller consent.
1237    /// Agent drivers record normalized content; direct provider coverage varies,
1238    /// especially for streams consumed after the provider returns.
1239    #[serde(skip)]
1240    pub record_telemetry_content: bool,
1241    /// Whether a finish reason outside the normalized vocabulary
1242    /// ([`FinishReason::Other`]) ends the turn as a normal stop instead of a
1243    /// failure. Defaults to `false`. The response carries the choice, so its
1244    /// [`CompletionResponse::stop`], the runtimes and history replay agree.
1245    /// `Other` also holds genuine failures, such as a malformed tool call or a
1246    /// recitation block, so with this set the tool calls of such a reply run.
1247    /// [`FinishReason::ContentFilter`] still fails the turn.
1248    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1249    pub accept_unknown_finish_reasons: bool,
1250    /// Send other models' reasoning in the history as reasoning where the
1251    /// target reads it ([`Accepts::foreign_reasoning`]), instead of as plain
1252    /// text. Defaults to `false`.
1253    ///
1254    /// [`Accepts::foreign_reasoning`]: crate::completion::Accepts::foreign_reasoning
1255    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1256    pub foreign_reasoning: bool,
1257    /// Portable generation options. Precedence, lowest first: the mapped
1258    /// options, then [`Self::provider_options`], then `additional_params`.
1259    #[serde(default, skip_serializing_if = "GenerationOptions::is_default")]
1260    pub options: GenerationOptions,
1261    /// Typed per-provider options. The wire reads only the entry of its own
1262    /// provider, above the mapped options and below `additional_params`.
1263    #[serde(default, skip_serializing_if = "ProviderOptions::is_empty")]
1264    pub provider_options: ProviderOptions,
1265    /// Where a provider that caches prompts by marker (Anthropic's Messages
1266    /// family, OpenAI's Responses API from GPT-5.6 on) may mark the end of a
1267    /// stable prefix that later requests repeat, beside its own markers, so
1268    /// content after it (per-turn memory, a reminder) does not cost the
1269    /// cached prefix. A provider that takes fewer markers keeps the points
1270    /// furthest into the request; one that caches prefixes without markers
1271    /// ignores them. A provider that marks only some parts of a request moves
1272    /// a point back to the last part it can mark. Empty marks nothing.
1273    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1274    pub cache_points: Vec<CachePoint>,
1275}
1276
1277/// A point in a [`CompletionRequest`] that ends a stable prefix, for
1278/// [`CompletionRequest::cache_points`]. Prefixes run in the order a request
1279/// is sent: tools, then the system prompt, then the messages.
1280#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
1281#[serde(rename_all = "snake_case")]
1282pub enum CachePoint {
1283    /// After the tool definitions.
1284    Tools,
1285    /// After the system prompt.
1286    System,
1287    /// After the first `n` user and assistant messages. System messages
1288    /// are not counted, wherever they sit; `Messages(0)` is [`Self::System`].
1289    /// The point names the request's messages as written: adapting them
1290    /// for a wire, which drops some and joins adjacent user messages, moves
1291    /// the point with them, inside a joined message when it ends there.
1292    Messages(usize),
1293    /// After the first `items` content items of the user or assistant
1294    /// message at `message`, counted as [`Self::Messages`] counts them.
1295    Within {
1296        /// The message, from 0, system messages not counted.
1297        message: usize,
1298        /// How many of its content items the prefix holds.
1299        items: usize,
1300    },
1301}
1302
1303impl CompletionRequest {
1304    /// The system instructions of this request: the content of the leading
1305    /// [`Message::System`] in `chat_history`, which is where
1306    /// [`Self::preamble`] places it.
1307    pub fn system_instructions(&self) -> Option<&str> {
1308        match self.chat_history.first().map(Arc::as_ref) {
1309            Some(Message::System { content }) => Some(content.as_str()),
1310            _ => None,
1311        }
1312    }
1313
1314    /// Reject a request with no messages, a user or assistant message with no
1315    /// content, or a tool result with no content blocks. The error is
1316    /// [`ProviderError::Request`] and names the role and index of the first
1317    /// offending message.
1318    ///
1319    /// Every wire rejects an empty turn, so this turns a remote 400 into a
1320    /// local error. It checks the request direction only: a provider may
1321    /// return empty assistant content, which the reply keeps and the
1322    /// runtime judges. `System` content is a
1323    /// `String` and is not checked. A tool result holding one empty text
1324    /// block is not empty.
1325    ///
1326    /// [`Model::call`](crate::driver::Model::call),
1327    /// [`Model::stream`](crate::driver::Model::stream) and their `_observed`
1328    /// twins run it before encoding, so it covers
1329    /// [`DynModel`](crate::DynModel), every model a handler serves and the
1330    /// agent runtimes built on them. The OpenAI Responses websocket session
1331    /// sends without the driver and runs it on each send. Code that encodes
1332    /// a request some other way should call it first.
1333    pub fn validate_message_content(&self) -> Result<(), ProviderError> {
1334        if self.chat_history.is_empty() {
1335            return Err(ProviderError::request(
1336                "request has an empty chat history; providers require at least one message",
1337            ));
1338        }
1339
1340        let empty_message = |role: &str, index: usize| {
1341            ProviderError::request(format!(
1342                "{role} message at index {index} has no content; \
1343                 providers reject empty content blocks"
1344            ))
1345        };
1346
1347        for (index, message) in self.chat_history.iter().enumerate() {
1348            match &**message {
1349                Message::System { .. } => {}
1350                Message::Assistant(AssistantMessage { content, .. }) => {
1351                    if content.is_empty() {
1352                        return Err(empty_message("assistant", index));
1353                    }
1354                }
1355                Message::User { content } => {
1356                    if content.is_empty() {
1357                        return Err(empty_message("user", index));
1358                    }
1359                }
1360            }
1361        }
1362
1363        Ok(())
1364    }
1365
1366    /// The output schema's `"title"`, else `"response_schema"`: a name for
1367    /// providers that need one beside the JSON Schema (such as OpenAI).
1368    pub fn output_schema_name(&self) -> Option<String> {
1369        self.output_schema.as_ref().map(|schema| {
1370            schema
1371                .as_object()
1372                .and_then(|o| o.get("title"))
1373                .and_then(|v| v.as_str())
1374                .unwrap_or("response_schema")
1375                .to_string()
1376        })
1377    }
1378
1379    /// Returns documents normalized into a message (if any).
1380    /// Most providers do not accept documents directly as input, so it needs to convert into a
1381    /// `Message` so that it can be incorporated into `chat_history`.
1382    pub fn normalized_documents(&self) -> Option<Message> {
1383        Self::normalized_documents_from(&self.documents)
1384    }
1385
1386    fn normalized_documents_from(documents: &[Document]) -> Option<Message> {
1387        if documents.is_empty() {
1388            return None;
1389        }
1390
1391        let content = documents
1392            .iter()
1393            .map(|doc| UserContent::document_text(doc.to_string(), Some(DocumentMediaType::TXT)))
1394            .collect();
1395
1396        Some(Message::User { content })
1397    }
1398}
1399
1400/// Place the documents message `message` at the first non-system position,
1401/// so document context lands after any leading system messages. A user
1402/// message already there takes the documents at its front instead, so the
1403/// history keeps alternating roles. Telemetry and the sent request must
1404/// agree on this placement.
1405pub(crate) fn insert_after_leading_system(chat_history: &mut Vec<Arc<Message>>, message: Message) {
1406    let insert_at = chat_history
1407        .iter()
1408        .position(|message| !matches!(&**message, Message::System { .. }))
1409        .unwrap_or(chat_history.len());
1410    let joins =
1411        |at: &&mut Arc<Message>| matches!(&***at, Message::User { content } if !content.is_empty());
1412    match (
1413        chat_history
1414            .get_mut(insert_at)
1415            .filter(joins)
1416            .map(Arc::make_mut),
1417        message,
1418    ) {
1419        (Some(Message::User { content }), Message::User { content: documents }) => {
1420            content.splice(0..0, documents);
1421        }
1422        (_, message) => chat_history.insert(insert_at, Arc::new(message)),
1423    }
1424}
1425
1426fn merge_provider_tools_into_additional_params(
1427    additional_params: Option<serde_json::Value>,
1428    provider_tools: Vec<ProviderToolDefinition>,
1429) -> Option<serde_json::Value> {
1430    if provider_tools.is_empty() {
1431        return additional_params;
1432    }
1433
1434    let mut provider_tools_json = provider_tools
1435        .into_iter()
1436        .map(|ProviderToolDefinition { kind, mut config }| {
1437            // Force the provider tool type from the strongly-typed field.
1438            config.insert("type".to_string(), serde_json::Value::String(kind));
1439            serde_json::Value::Object(config)
1440        })
1441        .collect::<Vec<_>>();
1442
1443    let mut params_map = match additional_params {
1444        Some(serde_json::Value::Object(map)) => map,
1445        Some(serde_json::Value::Bool(stream)) => {
1446            let mut map = serde_json::Map::new();
1447            map.insert("stream".to_string(), serde_json::Value::Bool(stream));
1448            map
1449        }
1450        _ => serde_json::Map::new(),
1451    };
1452
1453    let mut merged_tools = match remove_preserving_order(&mut params_map, "tools") {
1454        Some(serde_json::Value::Array(existing)) => existing,
1455        _ => Vec::new(),
1456    };
1457    merged_tools.append(&mut provider_tools_json);
1458    params_map.insert("tools".to_string(), serde_json::Value::Array(merged_tools));
1459    Some(serde_json::Value::Object(params_map))
1460}
1461
1462impl CompletionRequest {
1463    /// A request whose conversation is the one user message `prompt`, with
1464    /// no preamble, documents or tools. The setters below add to it and
1465    /// check nothing; [`Self::validate_message_content`] checks the content
1466    /// when the request is sent.
1467    ///
1468    /// Each setter changes the request's public fields as it is called, so
1469    /// order matters where two setters touch the same field: a second
1470    /// [`Self::preamble`] adds a second system message, and
1471    /// [`Self::additional_params`] with a `tools` key (or `None`) replaces
1472    /// provider tools added before it. Set `additional_params` first.
1473    ///
1474    /// ```
1475    /// use rig_core::completion::CompletionRequest;
1476    ///
1477    /// let request = CompletionRequest::new("Who are you?")
1478    ///     .preamble("You are a concise assistant.")
1479    ///     .temperature(0.5);
1480    /// assert_eq!(request.chat_history.len(), 2);
1481    /// assert_eq!(request.temperature, Some(0.5));
1482    /// ```
1483    pub fn new(prompt: impl Into<Message>) -> Self {
1484        Self::conversation(vec![Arc::new(prompt.into())])
1485    }
1486
1487    /// A request for `chat_history` as given, with nothing else set.
1488    fn conversation(chat_history: Vec<Arc<Message>>) -> Self {
1489        Self {
1490            model: None,
1491            chat_history,
1492            documents: Vec::new(),
1493            tools: Vec::new(),
1494            temperature: None,
1495            max_tokens: None,
1496            tool_choice: None,
1497            additional_params: None,
1498            output_schema: None,
1499            record_telemetry_content: false,
1500            accept_unknown_finish_reasons: false,
1501            foreign_reasoning: false,
1502            options: GenerationOptions::default(),
1503            provider_options: ProviderOptions::default(),
1504            cache_points: Vec::new(),
1505        }
1506    }
1507
1508    /// Put `preamble` first in the conversation, as a [`Message::System`],
1509    /// ahead of any system message already there.
1510    pub fn preamble(mut self, preamble: impl Into<String>) -> Self {
1511        self.chat_history
1512            .insert(0, Arc::new(Message::system(preamble.into())));
1513        self
1514    }
1515
1516    /// Override the model for this request.
1517    pub fn model<S: Into<String>>(mut self, model: impl Into<Option<S>>) -> Self {
1518        self.model = model.into().map(Into::into);
1519        self.warn_if_shadowed("model", self.model.is_some());
1520        self
1521    }
1522
1523    /// Add `message` to the conversation, before the prompt (its last
1524    /// message).
1525    pub fn message(self, message: impl Into<Arc<Message>>) -> Self {
1526        self.messages([message])
1527    }
1528
1529    /// Add `messages` to the conversation in order, before the prompt (its
1530    /// last message).
1531    pub fn messages(mut self, messages: impl IntoIterator<Item = impl Into<Arc<Message>>>) -> Self {
1532        let prompt = self.chat_history.pop();
1533        self.chat_history
1534            .extend(messages.into_iter().map(Into::into));
1535        self.chat_history.extend(prompt);
1536        self
1537    }
1538
1539    /// Add a document.
1540    pub fn document(mut self, document: Document) -> Self {
1541        self.documents.push(document);
1542        self
1543    }
1544
1545    /// Add documents in order.
1546    pub fn documents(mut self, documents: impl IntoIterator<Item = Document>) -> Self {
1547        self.documents.extend(documents);
1548        self
1549    }
1550
1551    /// Add a tool.
1552    pub fn tool(self, tool: impl Into<Arc<ToolDefinition>>) -> Self {
1553        self.tools([tool])
1554    }
1555
1556    /// Add tools in order.
1557    pub fn tools(
1558        mut self,
1559        tools: impl IntoIterator<Item = impl Into<Arc<ToolDefinition>>>,
1560    ) -> Self {
1561        let first = self.tools.is_empty();
1562        self.tools.extend(tools.into_iter().map(Into::into));
1563        self.warn_if_shadowed("tools", first && !self.tools.is_empty());
1564        self
1565    }
1566
1567    /// Add a provider-hosted tool: appended to `additional_params.tools`,
1568    /// so a later [`Self::additional_params`] with a `tools` key replaces
1569    /// it.
1570    pub fn provider_tool(self, tool: ProviderToolDefinition) -> Self {
1571        self.provider_tools(vec![tool])
1572    }
1573
1574    /// Add provider-hosted tools in order: appended to
1575    /// `additional_params.tools`.
1576    pub fn provider_tools(mut self, tools: Vec<ProviderToolDefinition>) -> Self {
1577        self.additional_params =
1578            merge_provider_tools_into_additional_params(self.additional_params.take(), tools);
1579        self
1580    }
1581
1582    /// Merge provider-specific parameters into the request's, key by key;
1583    /// `None` clears them, provider tools included. Provider conversion
1584    /// determines precedence over typed fields, and a key that overrides a
1585    /// typed field this request sets is logged.
1586    pub fn additional_params(
1587        mut self,
1588        additional_params: impl Into<Option<serde_json::Value>>,
1589    ) -> Self {
1590        let additional_params = additional_params.into();
1591        for key in shadowed_typed_fields(
1592            additional_params.as_ref(),
1593            &[
1594                ("temperature", self.temperature.is_some()),
1595                ("max_tokens", self.max_tokens.is_some()),
1596                ("tool_choice", self.tool_choice.is_some()),
1597                ("model", self.model.is_some()),
1598                ("tools", !self.tools.is_empty()),
1599                ("response_format", self.output_schema.is_some()),
1600            ],
1601        ) {
1602            warn_shadowed(key);
1603        }
1604        self.additional_params =
1605            json_utils::merge_params(self.additional_params.take(), additional_params);
1606        self
1607    }
1608
1609    /// Set, or with `None` clear, the temperature.
1610    pub fn temperature(mut self, temperature: impl Into<Option<f64>>) -> Self {
1611        self.temperature = temperature.into();
1612        self.warn_if_shadowed("temperature", self.temperature.is_some());
1613        self
1614    }
1615
1616    /// Set, or with `None` clear, the output-token limit. Provider-specific
1617    /// defaults and requirements apply.
1618    pub fn max_tokens(mut self, max_tokens: impl Into<Option<u64>>) -> Self {
1619        self.max_tokens = max_tokens.into();
1620        self.warn_if_shadowed("max_tokens", self.max_tokens.is_some());
1621        self
1622    }
1623
1624    /// Set the tool-selection policy.
1625    pub fn tool_choice(mut self, tool_choice: ToolChoice) -> Self {
1626        self.tool_choice = Some(tool_choice);
1627        self.warn_if_shadowed("tool_choice", true);
1628        self
1629    }
1630
1631    /// Set, or with `None` clear, a native structured-output schema for
1632    /// providers that support one. The returned content is not
1633    /// deserialized.
1634    pub fn output_schema(mut self, schema: impl Into<Option<schemars::Schema>>) -> Self {
1635        self.output_schema = schema.into();
1636        self.warn_if_shadowed("response_format", self.output_schema.is_some());
1637        self
1638    }
1639
1640    /// Opt in to sensitive content telemetry, off by default. See
1641    /// [`Self::record_telemetry_content`] for what that exposes.
1642    pub fn record_content_telemetry(mut self, enabled: bool) -> Self {
1643        self.record_telemetry_content = enabled;
1644        self
1645    }
1646
1647    /// Accept, or with `false` refuse, finish reasons outside the normalized
1648    /// vocabulary as a normal stop. See
1649    /// [`Self::accept_unknown_finish_reasons`] for what that lets through.
1650    ///
1651    /// [`Self::accept_unknown_finish_reasons`]: field@Self::accept_unknown_finish_reasons
1652    pub fn accept_unknown_finish_reasons(mut self, accept: bool) -> Self {
1653        self.accept_unknown_finish_reasons = accept;
1654        self
1655    }
1656
1657    /// Replace the portable generation options with `options`, a reusable
1658    /// value. Calls apply in order: this replaces every field, so a
1659    /// shortcut such as [`Self::seed`] called before it is lost, and one
1660    /// called after it sets its one field on top.
1661    ///
1662    /// ```
1663    /// use rig_core::completion::{CompletionRequest, Effort, GenerationOptions};
1664    ///
1665    /// let shared = GenerationOptions::new().reasoning(Effort::High).seed(1);
1666    /// let request = CompletionRequest::new("hi").seed(7).options(shared.clone()).seed(2);
1667    /// assert_eq!(request.options, shared.seed(2));
1668    /// ```
1669    pub fn options(mut self, options: GenerationOptions) -> Self {
1670        self.options = options;
1671        self
1672    }
1673
1674    /// Set the reasoning level or budget in [`Self::options`](field@Self::options), as
1675    /// [`GenerationOptions::reasoning`](GenerationOptions::reasoning) does,
1676    /// keeping its other fields. See
1677    /// [`Self::options`](method@Self::options) for the order of calls.
1678    pub fn reasoning(mut self, reasoning: impl Into<Reasoning>) -> Self {
1679        self.options = std::mem::take(&mut self.options).reasoning(reasoning);
1680        self
1681    }
1682
1683    /// Set the cache retention in [`Self::options`](field@Self::options), as
1684    /// [`GenerationOptions::cache`](GenerationOptions::cache) does,
1685    /// keeping its other fields. See
1686    /// [`Self::options`](method@Self::options) for the order of calls.
1687    pub fn cache(mut self, cache: CacheRetention) -> Self {
1688        self.options = std::mem::take(&mut self.options).cache(cache);
1689        self
1690    }
1691
1692    /// Set the service tier in [`Self::options`](field@Self::options), as
1693    /// [`GenerationOptions::service_tier`](GenerationOptions::service_tier) does,
1694    /// keeping its other fields. See
1695    /// [`Self::options`](method@Self::options) for the order of calls.
1696    pub fn service_tier(mut self, tier: ServiceTier) -> Self {
1697        self.options = std::mem::take(&mut self.options).service_tier(tier);
1698        self
1699    }
1700
1701    /// Set the answer verbosity in [`Self::options`](field@Self::options), as
1702    /// [`GenerationOptions::verbosity`](GenerationOptions::verbosity) does,
1703    /// keeping its other fields. See
1704    /// [`Self::options`](method@Self::options) for the order of calls.
1705    pub fn verbosity(mut self, verbosity: Verbosity) -> Self {
1706        self.options = std::mem::take(&mut self.options).verbosity(verbosity);
1707        self
1708    }
1709
1710    /// Set whether the model may call several tools in one turn in
1711    /// [`Self::options`](field@Self::options), as
1712    /// [`GenerationOptions::parallel_tool_calls`] does, keeping its other
1713    /// fields. See
1714    /// [`Self::options`](method@Self::options) for the order of calls.
1715    pub fn parallel_tool_calls(mut self, parallel: bool) -> Self {
1716        self.options = std::mem::take(&mut self.options).parallel_tool_calls(parallel);
1717        self
1718    }
1719
1720    /// Set the nucleus sampling probability mass in [`Self::options`](field@Self::options), as
1721    /// [`GenerationOptions::top_p`](GenerationOptions::top_p) does,
1722    /// keeping its other fields. See
1723    /// [`Self::options`](method@Self::options) for the order of calls.
1724    pub fn top_p(mut self, top_p: f64) -> Self {
1725        self.options = std::mem::take(&mut self.options).top_p(top_p);
1726        self
1727    }
1728
1729    /// Set the sampling seed in [`Self::options`](field@Self::options), as
1730    /// [`GenerationOptions::seed`](GenerationOptions::seed) does,
1731    /// keeping its other fields. See
1732    /// [`Self::options`](method@Self::options) for the order of calls.
1733    pub fn seed(mut self, seed: u64) -> Self {
1734        self.options = std::mem::take(&mut self.options).seed(seed);
1735        self
1736    }
1737
1738    /// Set the stop sequences in [`Self::options`](field@Self::options), as
1739    /// [`GenerationOptions::stop`](GenerationOptions::stop) does,
1740    /// keeping its other fields. See
1741    /// [`Self::options`](method@Self::options) for the order of calls.
1742    pub fn stop<S: Into<String>>(mut self, stop: impl IntoIterator<Item = S>) -> Self {
1743        self.options = std::mem::take(&mut self.options).stop(stop);
1744        self
1745    }
1746
1747    /// Set what happens to an option the wire or model cannot honour in
1748    /// [`Self::options`](field@Self::options), as
1749    /// [`GenerationOptions::on_unsupported`] does, keeping its other fields. See
1750    /// [`Self::options`](method@Self::options) for the order of calls.
1751    pub fn on_unsupported(mut self, policy: OnUnsupported) -> Self {
1752        self.options = std::mem::take(&mut self.options).on_unsupported(policy);
1753        self
1754    }
1755
1756    /// Replace the typed per-provider options.
1757    ///
1758    /// Calls apply in order: this replaces every entry, so an entry set by
1759    /// an earlier [`Self::provider_option`] is lost, and a later
1760    /// [`Self::provider_option`] replaces its provider's entry on top.
1761    pub fn provider_options(mut self, options: ProviderOptions) -> Self {
1762        self.provider_options = options;
1763        self
1764    }
1765
1766    /// Store `options` as the entry of their provider
1767    /// ([`ExtensionOptions::Ext`]), replacing that provider's entry and
1768    /// keeping every other, as [`ProviderOptions::set`] does. Options that
1769    /// do not serialize fail the request's encode.
1770    ///
1771    /// The entry is always stored under `O::Ext`'s key, the built-in
1772    /// provider the options type belongs to. A third-party provider whose
1773    /// extension reuses a built-in options type (say `OpenAiOptions` for an
1774    /// OpenAI-compatible gateway) must store them with
1775    /// [`ProviderOptions::with::<P>`](ProviderOptions::with) instead, or its
1776    /// wire never reads them.
1777    ///
1778    /// ```
1779    /// use rig_core::completion::{CompletionRequest, ProviderOptions};
1780    /// use rig_core::providers::openrouter::extension::{
1781    ///     OpenRouterExt, OpenRouterOptions, ProviderPreferences,
1782    /// };
1783    ///
1784    /// let request = CompletionRequest::new("hi").provider_option(
1785    ///     OpenRouterOptions::new().provider(ProviderPreferences::new().allow_fallbacks(false)),
1786    /// );
1787    /// assert!(request.provider_options.contains::<OpenRouterExt>());
1788    /// ```
1789    pub fn provider_option<O: ExtensionOptions>(mut self, options: O) -> Self {
1790        self.provider_options = std::mem::take(&mut self.provider_options).set(options);
1791        self
1792    }
1793
1794    /// The input messages telemetry records: the conversation with the
1795    /// documents inserted after any leading system messages.
1796    pub fn messages_for_telemetry(&self) -> Vec<Arc<Message>> {
1797        let mut messages = self.chat_history.clone();
1798        if let Some(documents) = self.normalized_documents() {
1799            insert_after_leading_system(&mut messages, documents);
1800        }
1801        messages
1802    }
1803
1804    /// Log a typed field `key` that `additional_params` already overrides.
1805    fn warn_if_shadowed(&self, key: &'static str, set: bool) {
1806        if !shadowed_typed_fields(self.additional_params.as_ref(), &[(key, set)]).is_empty() {
1807            warn_shadowed(key);
1808        }
1809    }
1810}
1811
1812fn warn_shadowed(key: &str) {
1813    if matches!(key, "tools" | "response_format") {
1814        tracing::warn!(
1815            key,
1816            "additional_params also carries `{key}`; the provider decides how it combines with the typed field"
1817        );
1818    } else {
1819        tracing::warn!(
1820            key,
1821            "additional_params overrides the typed `{key}` field set on the same request"
1822        );
1823    }
1824}
1825
1826impl From<&str> for CompletionRequest {
1827    fn from(prompt: &str) -> Self {
1828        Self::new(prompt)
1829    }
1830}
1831
1832impl From<String> for CompletionRequest {
1833    fn from(prompt: String) -> Self {
1834        Self::new(prompt)
1835    }
1836}
1837
1838impl From<Message> for CompletionRequest {
1839    fn from(prompt: Message) -> Self {
1840        Self::new(prompt)
1841    }
1842}
1843
1844/// The conversation as given, ending with the prompt. An empty one fails
1845/// [`CompletionRequest::validate_message_content`].
1846impl From<Vec<Message>> for CompletionRequest {
1847    fn from(chat_history: Vec<Message>) -> Self {
1848        Self::conversation(chat_history.into_iter().map(Arc::new).collect())
1849    }
1850}
1851
1852/// The passthrough keys that will override a typed field the caller also set.
1853/// The override itself is the documented precedence (see
1854/// [`CompletionRequest::additional_params`]); naming the collisions
1855/// makes an accidental one visible instead of silent.
1856pub(crate) fn shadowed_typed_fields<'a>(
1857    additional_params: Option<&serde_json::Value>,
1858    typed: &[(&'a str, bool)],
1859) -> Vec<&'a str> {
1860    let Some(serde_json::Value::Object(params)) = additional_params else {
1861        return Vec::new();
1862    };
1863    typed
1864        .iter()
1865        .filter(|(key, set)| *set && params.contains_key(*key))
1866        .map(|(key, _)| *key)
1867        .collect()
1868}
1869
1870#[cfg(test)]
1871mod tests;
1872
1873#[cfg(test)]
1874mod response_identity_tests;
1875
1876#[cfg(test)]
1877mod plain_value_tests;