Skip to main content

providers_kit/
generation.rs

1//! Portable generation, messages, function continuation, and streaming values.
2//!
3//! Construct a [`GenerateRequest`] for an already selected [`crate::ModelBackend`].
4//! Returned assistant [`Message`] values retain the exact associated continuation;
5//! append them unchanged, then append correlated [`FunctionResult`] values in a
6//! tool message. providers-kit declares functions but never executes them.
7//!
8//! [`GenerateResponse`] is authoritative at completion. [`GenerateEventStream`]
9//! provides bounded incremental observation from supplied Models; partial text or
10//! hosted projections cannot reconstruct opaque replay state. Serialization rejects
11//! messages carrying continuation instead of silently dropping it.
12//!
13//! # Consume a stream and retain the authoritative response
14//!
15//! This requires OpenAI credentials and access to the exact selected text model.
16//! A complete stream contains one `Completed`; earlier text is only incremental
17//! observation. Keep the returned assistant message, not a reconstruction from deltas.
18//! A terminal reason such as `Length` or `ContentFilter` is not an ordinary successful
19//! answer. This example accepts only `Stop` and does not compare wording to its prompt.
20//!
21//! ```no_run
22//! use futures_util::StreamExt;
23//! use providers_kit::{Provider, ProviderBackend, ProviderConfig, ModelBackend, ModelId};
24//! use providers_kit::config::{ApiKey, OpenAiConfig, ProviderKind, TransportConfig};
25//! use providers_kit::generation::{ContentPart, FinishReason, GenerateEvent, GenerateRequest, Message};
26//! # #[tokio::main(flavor = "current_thread")]
27//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
28//! let provider = Provider::from_config(ProviderConfig::new(
29//!     ProviderKind::OpenAi(OpenAiConfig::new(ApiKey::new("replace-with-your-key")?)),
30//!     TransportConfig::default(),
31//! ))?;
32//! let work = async {
33//!     let model = provider.model(ModelId::new("gpt-6-luna")?)?;
34//!     let request = GenerateRequest::new(vec![Message::user_text("Explain a rainbow briefly.")?])?;
35//!     let mut stream = model.generate_stream(&request).await?;
36//!     let mut text = String::new();
37//!     let mut completed = None;
38//!     while let Some(event) = stream.next().await {
39//!         match event? {
40//!             GenerateEvent::Started { model: returned } => assert_eq!(returned, *model.model_id()),
41//!             GenerateEvent::ContentDelta { delta } => text.push_str(&delta),
42//!             GenerateEvent::Completed(response) => {
43//!                 assert!(completed.is_none(), "duplicate completion");
44//!                 completed = Some(response);
45//!             }
46//!             GenerateEvent::ThinkingDelta { .. } | GenerateEvent::HostedTool(_) | GenerateEvent::Usage(_) => {}
47//!         }
48//!     }
49//!     let response = completed.ok_or("stream ended before Completed")?;
50//!     assert_eq!(response.model, *model.model_id());
51//!     assert_eq!(response.finish_reason, Some(FinishReason::Stop));
52//!     let final_text: String = response.message.content().iter().filter_map(|part| match part {
53//!         ContentPart::Text { text } => Some(text.as_str()), _ => None,
54//!     }).collect();
55//!     assert!(!final_text.trim().is_empty());
56//!     assert_eq!(text, final_text); // Fidelity within Kit output, not native parity.
57//!     Ok::<_, Box<dyn std::error::Error>>(response.message)
58//! }.await;
59//! let cleanup = provider.shutdown().await;
60//! if let Err(error) = &cleanup {
61//!     eprintln!("Cleanup failed: {error}");
62//! }
63//! let _unchanged_assistant = work?;
64//! cleanup?;
65//! # Ok(())
66//! # }
67//! ```
68//!
69//! # Correlated function continuation
70//!
71//! providers-kit declares functions; the application executes them. This bounded
72//! two-generation example requires the selected OpenAI model, credentials, and
73//! function support. Validate the call before dispatching application behavior. The
74//! result copies its exact id/name, and the assistant is moved unchanged into history.
75//! It includes opaque reasoning/replay state that visible text cannot reconstruct.
76//!
77//! ```no_run
78//! use providers_kit::{Provider, ProviderBackend, ProviderConfig, ModelBackend, ModelId};
79//! use providers_kit::config::{ApiKey, OpenAiConfig, ProviderKind, TransportConfig};
80//! use providers_kit::generation::{ContentPart, FinishReason, FunctionResult, GenerateRequest,
81//!     GenerationConfig, JsonSchema, Message, ProviderFunctionTool, ProviderToolSpec, ToolChoice};
82//! # #[tokio::main(flavor = "current_thread")]
83//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
84//! let provider = Provider::from_config(ProviderConfig::new(
85//!     ProviderKind::OpenAi(OpenAiConfig::new(ApiKey::new("replace-with-your-key")?)),
86//!     TransportConfig::default(),
87//! ))?;
88//! let work = async {
89//!     let model = provider.model(ModelId::new("gpt-6-luna")?)?;
90//!     let lookup = ProviderFunctionTool::new("lookup", Some("Read the local item".into()),
91//!         JsonSchema::new(serde_json::json!({"type":"object","properties":{},"additionalProperties":false}))?)?;
92//!     let initial = Message::user_text("Look up the local item and summarize it.")?;
93//!     let request = GenerateRequest::new(vec![initial.clone()])?
94//!         .with_tools(vec![ProviderToolSpec::Function(lookup)])?
95//!         .with_generation(GenerationConfig::default().with_tool_choice(ToolChoice::Required))?;
96//!     let first = model.generate(&request).await?;
97//!     assert_eq!(first.model, *model.model_id());
98//!     assert_eq!(first.finish_reason, Some(FinishReason::ToolCall));
99//!     let [call] = first.message.function_calls() else { return Err("expected one lookup".into()); };
100//!     assert_eq!(call.name(), "lookup");
101//!     assert_eq!(call.arguments(), &serde_json::json!({}));
102//!     let local_item = serde_json::json!({"name":"paper","quantity":3});
103//!     let result = FunctionResult::new(call.id().map(str::to_owned), call.name(),
104//!         vec![ContentPart::Json { value: local_item }], false)?;
105//!     assert_eq!(result.call_id(), call.id());
106//!     let followup = GenerateRequest::new(vec![initial, first.message, Message::tool(vec![result])?])?;
107//!     let second = model.generate(&followup).await?;
108//!     assert_eq!(second.model, *model.model_id());
109//!     assert_eq!(second.finish_reason, Some(FinishReason::Stop));
110//!     assert!(second.message.function_calls().is_empty());
111//!     assert!(second.message.content().iter().any(|part| matches!(part,
112//!         ContentPart::Text { text } if !text.trim().is_empty())));
113//!     Ok::<(), Box<dyn std::error::Error>>(())
114//! }.await;
115//! let cleanup = provider.shutdown().await;
116//! if let Err(error) = &cleanup {
117//!     eprintln!("Cleanup failed: {error}");
118//! }
119//! work?;
120//! cleanup?;
121//! # Ok(())
122//! # }
123//! ```
124//!
125//! # Structured output and reasoning
126//!
127//! Request shape is local and testable. Model compatibility is checked when the
128//! request executes: an omitted reasoning override remains omitted; unsupported
129//! sampling/reasoning/schema combinations fail rather than silently changing settings.
130//! A schema constrains shape; ordinary prompt wording is not an equality guarantee.
131//!
132//! ```
133//! use providers_kit::generation::{GenerateRequest, GenerationConfig, JsonSchema, Message,
134//!     ReasoningConfig, ReasoningEffort, ResponseFormat};
135//! let schema = JsonSchema::new(serde_json::json!({
136//!     "type":"object", "properties":{"summary":{"type":"string"}},
137//!     "required":["summary"], "additionalProperties":false
138//! }))?;
139//! let request = GenerateRequest::<()>::new(vec![Message::user_text("Summarize the supplied note.")?])?
140//!     .with_response_format(ResponseFormat::JsonSchema(schema))
141//!     .with_reasoning(ReasoningConfig { effort: Some(ReasoningEffort::Low), ..Default::default() })
142//!     .with_generation(GenerationConfig::default()
143//!         .with_max_output_tokens(std::num::NonZeroU32::new(4096).unwrap()))?;
144//! assert!(matches!(request.response_format(), Some(ResponseFormat::JsonSchema(_))));
145//! assert_eq!(request.reasoning().unwrap().effort, Some(ReasoningEffort::Low));
146//! assert!(request.generation().temperature().is_none());
147//! # Ok::<(), providers_kit::KitError>(())
148//! ```
149
150//! The same request pattern can be executed on an exact supporting model. This
151//! requires credentials and current access to `gpt-6-luna`; no sampling override
152//! is inferred from the reasoning setting. Inspect both terminal outcome and the
153//! returned JSON rather than assuming the prompt's prose guarantees a value.
154//!
155//! ```no_run
156//! use providers_kit::{Provider, ProviderBackend, ProviderConfig, ModelBackend, ModelId};
157//! use providers_kit::config::{ApiKey, OpenAiConfig, ProviderKind, TransportConfig};
158//! use providers_kit::generation::{ContentPart, FinishReason, GenerateRequest, JsonSchema,
159//!     Message, ReasoningConfig, ReasoningEffort, ResponseFormat};
160//! # #[tokio::main(flavor = "current_thread")]
161//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
162//! let provider = Provider::from_config(ProviderConfig::new(
163//!     ProviderKind::OpenAi(OpenAiConfig::new(ApiKey::new("replace-with-your-key")?)),
164//!     TransportConfig::default(),
165//! ))?;
166//! let work = async {
167//!     let model = provider.model(ModelId::new("gpt-6-luna")?)?;
168//!     let schema = JsonSchema::new(serde_json::json!({"type":"object",
169//!         "properties":{"summary":{"type":"string"}}, "required":["summary"], "additionalProperties":false}))?;
170//!     let request = GenerateRequest::new(vec![Message::user_text("Summarize: rain supports plant growth.")?])?
171//!         .with_response_format(ResponseFormat::JsonSchema(schema))
172//!         .with_reasoning(ReasoningConfig { effort: Some(ReasoningEffort::Low), ..Default::default() });
173//!     let response = model.generate(&request).await?;
174//!     assert_eq!(response.model, *model.model_id());
175//!     assert_eq!(response.finish_reason, Some(FinishReason::Stop));
176//!     let text: String = response.message.content().iter().filter_map(|part| match part {
177//!         ContentPart::Text { text } => Some(text.as_str()), _ => None,
178//!     }).collect();
179//!     let value: serde_json::Value = serde_json::from_str(&text)?;
180//!     assert!(value["summary"].is_string());
181//!     assert_eq!(value.as_object().ok_or("expected an object")?.len(), 1);
182//!     Ok::<(), Box<dyn std::error::Error>>(())
183//! }.await;
184//! let cleanup = provider.shutdown().await;
185//! if let Err(error) = &cleanup {
186//!     eprintln!("Cleanup failed: {error}");
187//! }
188//! work?;
189//! cleanup?;
190//! # Ok(())
191//! # }
192//! ```
193
194use std::collections::BTreeSet;
195use std::fmt;
196use std::num::NonZeroU32;
197use std::pin::Pin;
198use std::task::{Context, Poll};
199
200use futures_util::Stream;
201use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::Error as _};
202use serde_json::Value;
203use url::Url;
204
205use crate::KitError;
206use crate::core::content::base64_bytes;
207use crate::core::{GenerateRequestView, selected_tools_for_choice};
208use crate::error::ValidationViolation;
209use crate::hosted::{HostedTool, HostedToolOutput};
210use crate::models::ModelId;
211
212/// Reference that can be submitted as provider-managed or remote model input.
213#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
214#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
215pub enum FileReference {
216    /// Provider-managed resource identifier.
217    Provider {
218        /// Exact provider-assigned identifier or input URI.
219        id: String,
220        /// Optional media type for selecting the reusable input block kind.
221        media_type: Option<String>,
222    },
223    /// Externally hosted resource.
224    Url {
225        /// Exact resource URL.
226        url: Url,
227        /// Optional media-type hint.
228        media_type: Option<String>,
229    },
230}
231
232/// Artifact returned by generation or hosted execution.
233#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
234#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
235pub enum Artifact {
236    /// Inline artifact bytes.
237    Inline {
238        /// Media type describing the bytes.
239        media_type: String,
240        /// Optional human-readable file name.
241        file_name: Option<String>,
242        /// Optional model-authored description.
243        description: Option<String>,
244        /// Raw artifact bytes, serialized as base64.
245        #[serde(with = "base64_bytes")]
246        data: Vec<u8>,
247    },
248    /// Provider-managed or remote artifact reference.
249    File {
250        /// Reference usable by a later request.
251        reference: FileReference,
252        /// Optional human-readable file name.
253        file_name: Option<String>,
254        /// Optional model-authored description.
255        description: Option<String>,
256    },
257}
258
259/// Ordered portable message content.
260#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
261#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
262pub enum ContentPart {
263    /// Plain UTF-8 text.
264    Text {
265        /// Text content.
266        text: String,
267    },
268    /// Provider-authored thinking replayed with continuation state.
269    Thinking {
270        /// Provider-visible thinking text, when returned.
271        text: Option<String>,
272    },
273    /// Remote image input.
274    ImageUrl {
275        /// Absolute image URL.
276        url: Url,
277    },
278    /// Inline image bytes.
279    Image {
280        /// Image media type.
281        media_type: String,
282        /// Raw image bytes.
283        #[serde(with = "base64_bytes")]
284        data: Vec<u8>,
285    },
286    /// Inline audio bytes.
287    Audio {
288        /// Audio media type.
289        media_type: String,
290        /// Raw audio bytes.
291        #[serde(with = "base64_bytes")]
292        data: Vec<u8>,
293    },
294    /// Inline document bytes.
295    Document {
296        /// Document media type.
297        media_type: String,
298        /// Raw document bytes.
299        #[serde(with = "base64_bytes")]
300        data: Vec<u8>,
301        /// Optional file name presented to the provider.
302        file_name: Option<String>,
303    },
304    /// Provider-managed or remote file input.
305    File {
306        /// Exact provider or remote file reference.
307        reference: FileReference,
308    },
309    /// Structured function-result content.
310    Json {
311        /// Structured JSON value.
312        value: Value,
313    },
314}
315
316/// Portable message role.
317#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
318#[serde(rename_all = "snake_case")]
319pub enum Role {
320    /// System instruction.
321    System,
322    /// Developer instruction.
323    Developer,
324    /// User input.
325    User,
326    /// Model-authored assistant output.
327    Assistant,
328    /// Caller-supplied function output.
329    Tool,
330}
331
332/// Opaque provider-authored state required to continue one model turn.
333///
334/// Returned assistant messages carry the selected Model's exact continuation type.
335/// Append that message unchanged before the corresponding tool-result message.
336/// Native continuation contents have no public constructor or serialization contract.
337/// Serializing any message that carries continuation fails rather than discarding it.
338/// Caller models may choose their own Clone + Send + Sync continuation type.
339///
340/// # Examples
341///
342/// ```
343/// use providers_kit::generation::{GenerationContinuation, Message};
344/// let message = Message::<GenerationContinuation>::assistant_text("Hello")?;
345/// assert!(message.continuation().is_none());
346/// assert!(serde_json::to_value(&message).is_ok());
347///
348/// # Ok::<(), providers_kit::KitError>(())
349/// ```
350///
351#[derive(Clone, PartialEq)]
352pub struct GenerationContinuation {
353    pub(crate) model_id: ModelId,
354    pub(crate) inner: NativeContinuation,
355}
356
357#[derive(Clone, PartialEq)]
358pub(crate) enum NativeContinuation {
359    OpenAi { output_items: Vec<Value> },
360    Anthropic { content_blocks: Vec<Value> },
361    Gemini { signed_parts: Vec<Value> },
362    Ollama { thinking: String },
363}
364
365impl fmt::Debug for GenerationContinuation {
366    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
367        formatter
368            .debug_struct("GenerationContinuation")
369            .field("model_id", &self.model_id)
370            .field("state", &"<redacted>")
371            .finish()
372    }
373}
374
375/// Portable message with private function and continuation state.
376///
377/// A caller Model can return its own continuation type. Preserve that exact
378/// assistant value before appending the matching function-result message:
379///
380/// ```
381/// use providers_kit::generation::{
382///     ContentPart, FunctionCall, FunctionResult, GenerateRequest, Message,
383/// };
384/// let user = Message::<u32>::user_text("Look up record a")?;
385/// // Representative output constructed by a caller Model with Continuation = u32.
386/// let call = FunctionCall::new(Some("call-1".into()), "lookup", serde_json::json!({"key":"a"}))?;
387/// let assistant = Message::assistant(Vec::new(), vec![call])?.with_continuation(7)?;
388/// let result = FunctionResult::new(Some("call-1".into()), "lookup",
389///     vec![ContentPart::Json { value: serde_json::json!({"value":2}) }], false)?;
390/// let next = GenerateRequest::new(vec![user, assistant, Message::tool(vec![result])?])?;
391/// assert_eq!(next.messages()[1].continuation(), Some(&7));
392/// assert_eq!(next.messages()[2].function_results()[0].call_id(), Some("call-1"));
393/// assert!(serde_json::to_value(&next).is_err());
394///
395/// # Ok::<(), providers_kit::KitError>(())
396/// ```
397///
398/// Role, function calls/results, and continuation are validated together. Supplied
399/// continuations stay opaque; caller Models use their own exact associated type.
400/// Assistant messages returned by a Model must be replayed unchanged.
401///
402/// See [`crate::generation::GenerationContinuation`] for a complete example.
403///
404#[derive(Clone, PartialEq)]
405pub struct Message<C = GenerationContinuation> {
406    role: Role,
407    content: Vec<ContentPart>,
408    function_calls: Vec<FunctionCall>,
409    function_results: Vec<FunctionResult>,
410    continuation: Option<C>,
411}
412
413impl<C> fmt::Debug for Message<C> {
414    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
415        formatter
416            .debug_struct("Message")
417            .field("role", &self.role)
418            .field("content", &self.content)
419            .field("function_calls", &self.function_calls)
420            .field("function_results", &self.function_results)
421            .field(
422                "continuation",
423                &self.continuation.is_some().then_some("<redacted>"),
424            )
425            .finish()
426    }
427}
428
429impl<C> Message<C> {
430    fn from_role(role: Role, content: Vec<ContentPart>) -> Self {
431        Self {
432            role,
433            content,
434            function_calls: Vec::new(),
435            function_results: Vec::new(),
436            continuation: None,
437        }
438    }
439
440    /// Creates a text-only system instruction.
441    ///
442    /// # Errors
443    ///
444    /// Returns an input error for empty or invalid content, or content incompatible with the role. Instruction messages permit only text; user messages reject thinking and raw JSON.
445    ///
446    /// # Examples
447    ///
448    /// ```
449    /// use providers_kit::generation::{Message, ContentPart, Role};
450    /// let message = Message::<()>::system(vec![ContentPart::Text { text: "Hello".into() }])?;
451    /// assert_eq!(message.role(), Role::System);
452    ///
453    /// # Ok::<(), providers_kit::KitError>(())
454    /// ```
455    ///
456    pub fn system(content: Vec<ContentPart>) -> Result<Self, KitError> {
457        Self::instruction(Role::System, content)
458    }
459
460    /// Creates a text-only developer instruction.
461    ///
462    /// # Errors
463    ///
464    /// Returns an input error for empty or invalid content, or content incompatible with the role. Instruction messages permit only text; user messages reject thinking and raw JSON.
465    ///
466    /// # Examples
467    ///
468    /// ```
469    /// use providers_kit::generation::{Message, ContentPart, Role};
470    /// let message = Message::<()>::developer(vec![ContentPart::Text { text: "Hello".into() }])?;
471    /// assert_eq!(message.role(), Role::Developer);
472    ///
473    /// # Ok::<(), providers_kit::KitError>(())
474    /// ```
475    ///
476    pub fn developer(content: Vec<ContentPart>) -> Result<Self, KitError> {
477        Self::instruction(Role::Developer, content)
478    }
479
480    fn instruction(role: Role, content: Vec<ContentPart>) -> Result<Self, KitError> {
481        let message = Self::from_role(role, content);
482        message.validate_shape()?;
483        Ok(message)
484    }
485
486    /// Creates a user message.
487    ///
488    /// # Errors
489    ///
490    /// Returns an input error for empty or invalid content, or content incompatible with the role. Instruction messages permit only text; user messages reject thinking and raw JSON.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// use providers_kit::generation::{Message, ContentPart, Role};
496    /// let message = Message::<()>::user(vec![ContentPart::Text { text: "Hello".into() }])?;
497    /// assert_eq!(message.role(), Role::User);
498    ///
499    /// # Ok::<(), providers_kit::KitError>(())
500    /// ```
501    ///
502    pub fn user(content: Vec<ContentPart>) -> Result<Self, KitError> {
503        let message = Self::from_role(Role::User, content);
504        message.validate_shape()?;
505        Ok(message)
506    }
507
508    /// Creates an assistant message with visible content and/or function calls.
509    ///
510    /// # Errors
511    ///
512    /// Returns an input error if both content and calls are empty, content is malformed, or the role cannot represent the supplied values.
513    ///
514    /// # Examples
515    ///
516    /// ```
517    /// use providers_kit::generation::FunctionCall;
518    /// let call = FunctionCall::new(Some("call-1".into()), "lookup", serde_json::json!({"key":"a"}))?;
519    /// use providers_kit::generation::Message;
520    /// let message = Message::<()>::assistant(Vec::new(), vec![call])?;
521    /// assert_eq!(message.function_calls().len(), 1);
522    ///
523    /// # Ok::<(), providers_kit::KitError>(())
524    /// ```
525    ///
526    pub fn assistant(
527        content: Vec<ContentPart>,
528        function_calls: Vec<FunctionCall>,
529    ) -> Result<Self, KitError> {
530        let message = Self {
531            function_calls,
532            ..Self::from_role(Role::Assistant, content)
533        };
534        message.validate_shape()?;
535        Ok(message)
536    }
537
538    /// Creates a tool message containing validated function results.
539    ///
540    /// # Errors
541    ///
542    /// Returns an input error when no function results are supplied or the message shape is invalid.
543    ///
544    /// # Examples
545    ///
546    /// ```
547    /// use providers_kit::generation::{FunctionResult, ContentPart};
548    /// let result = FunctionResult::new(
549    ///     Some("call-1".into()),
550    ///     "lookup",
551    ///     vec![ContentPart::Json {
552    ///         value: serde_json::json!({"value":2}),
553    ///     }],
554    ///     false,
555    /// )?;
556    /// use providers_kit::generation::Message;
557    /// let message = Message::<()>::tool(vec![result])?;
558    /// assert_eq!(message.function_results()[0].call_id(), Some("call-1"));
559    ///
560    /// # Ok::<(), providers_kit::KitError>(())
561    /// ```
562    ///
563    pub fn tool(results: Vec<FunctionResult>) -> Result<Self, KitError> {
564        let message = Self {
565            function_results: results,
566            ..Self::from_role(Role::Tool, Vec::new())
567        };
568        message.validate_shape()?;
569        Ok(message)
570    }
571
572    /// Creates one user text message.
573    ///
574    /// # Errors
575    ///
576    /// Returns an input error when the text is empty or whitespace-only.
577    ///
578    /// # Examples
579    ///
580    /// ```
581    /// use providers_kit::generation::{Message, Role};
582    /// let message = Message::<()>::user_text("Hello")?;
583    /// assert_eq!(message.role(), Role::User);
584    /// assert_eq!(message.content().len(), 1);
585    ///
586    /// # Ok::<(), providers_kit::KitError>(())
587    /// ```
588    ///
589    pub fn user_text(text: impl Into<String>) -> Result<Self, KitError> {
590        Self::user(vec![ContentPart::Text { text: text.into() }])
591    }
592
593    /// Creates one assistant text message.
594    ///
595    /// # Errors
596    ///
597    /// Returns an input error when the text is empty or whitespace-only.
598    ///
599    /// # Examples
600    ///
601    /// ```
602    /// use providers_kit::generation::{Message, Role};
603    /// let message = Message::<()>::assistant_text("Hello")?;
604    /// assert_eq!(message.role(), Role::Assistant);
605    /// assert_eq!(message.content().len(), 1);
606    ///
607    /// # Ok::<(), providers_kit::KitError>(())
608    /// ```
609    ///
610    pub fn assistant_text(text: impl Into<String>) -> Result<Self, KitError> {
611        Self::assistant(vec![ContentPart::Text { text: text.into() }], Vec::new())
612    }
613
614    /// Attaches continuation state to an assistant message.
615    ///
616    /// Continuation is valid only on assistant messages and is never silently serialized.
617    /// Use this constructor for a caller Model's continuation; preserve supplied returned
618    /// messages unchanged rather than reconstructing their private replay state.
619    ///
620    /// # Errors
621    ///
622    /// Returns an input error when the message role is not Assistant.
623    ///
624    /// # Examples
625    ///
626    /// ```
627    /// use providers_kit::generation::Message;
628    /// let message = Message::assistant_text("continue")?.with_continuation(7_u32)?;
629    /// assert_eq!(message.continuation(), Some(&7));
630    /// assert!(serde_json::to_value(&message).is_err());
631    /// assert!(Message::user_text("hello")?.with_continuation(7_u32).is_err());
632    ///
633    /// # Ok::<(), providers_kit::KitError>(())
634    /// ```
635    ///
636    pub fn with_continuation(mut self, continuation: C) -> Result<Self, KitError> {
637        if self.role != Role::Assistant {
638            return Err(KitError::invalid_input(
639                ValidationViolation::Incompatible {
640                    field: "message.continuation",
641                    other: "message.role",
642                },
643                "continuation can be attached only to assistant messages",
644            ));
645        }
646        self.continuation = Some(continuation);
647        Ok(self)
648    }
649
650    /// Returns the speaker role.
651    ///
652    /// # Examples
653    ///
654    /// ```
655    /// use providers_kit::generation::{Message, ContentPart, Role};
656    /// let message = Message::<()>::user_text("Hello")?;
657    /// assert_eq!(message.role(), Role::User);
658    ///
659    /// # Ok::<(), providers_kit::KitError>(())
660    /// ```
661    ///
662    #[must_use]
663    pub const fn role(&self) -> Role {
664        self.role
665    }
666
667    /// Returns ordered visible content.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// use providers_kit::generation::{Message, ContentPart, Role};
673    /// let message = Message::<()>::user_text("Hello")?;
674    /// assert!(matches!(&message.content()[0], ContentPart::Text { text } if text == "Hello"));
675    ///
676    /// # Ok::<(), providers_kit::KitError>(())
677    /// ```
678    ///
679    #[must_use]
680    pub fn content(&self) -> &[ContentPart] {
681        &self.content
682    }
683
684    /// Returns model-requested function calls.
685    ///
686    /// # Examples
687    ///
688    /// ```
689    /// use providers_kit::generation::FunctionCall;
690    /// let call = FunctionCall::new(Some("call-1".into()), "lookup", serde_json::json!({"key":"a"}))?;
691    /// let message = providers_kit::generation::Message::<()>::assistant(Vec::new(), vec![call])?;
692    /// assert_eq!(message.function_calls()[0].name(), "lookup");
693    ///
694    /// # Ok::<(), providers_kit::KitError>(())
695    /// ```
696    ///
697    #[must_use]
698    pub fn function_calls(&self) -> &[FunctionCall] {
699        &self.function_calls
700    }
701
702    /// Returns caller-supplied function results.
703    ///
704    /// See [`crate::generation::Message::tool`] for a complete example.
705    ///
706    #[must_use]
707    pub fn function_results(&self) -> &[FunctionResult] {
708        &self.function_results
709    }
710
711    /// Returns opaque continuation state when this is a returned assistant message.
712    ///
713    /// See [`crate::generation::Message::with_continuation`] for a complete example.
714    ///
715    #[must_use]
716    pub const fn continuation(&self) -> Option<&C> {
717        self.continuation.as_ref()
718    }
719
720    pub(crate) fn try_map_continuation<D>(
721        self,
722        map: &mut impl FnMut(C) -> Result<D, KitError>,
723    ) -> Result<Message<D>, KitError> {
724        Ok(Message {
725            role: self.role,
726            content: self.content,
727            function_calls: self.function_calls,
728            function_results: self.function_results,
729            continuation: self.continuation.map(map).transpose()?,
730        })
731    }
732
733    pub(crate) fn returned_assistant(
734        content: Vec<ContentPart>,
735        function_calls: Vec<FunctionCall>,
736        continuation: Option<C>,
737    ) -> Self {
738        Self {
739            role: Role::Assistant,
740            content,
741            function_calls,
742            function_results: Vec::new(),
743            continuation,
744        }
745    }
746
747    pub(crate) fn validate_for_request(&self) -> Result<(), KitError> {
748        self.validate_shape()?;
749        if self
750            .content
751            .iter()
752            .any(|part| matches!(part, ContentPart::Thinking { .. }))
753            && self.continuation.is_none()
754        {
755            return Err(KitError::invalid_input(
756                ValidationViolation::MissingField {
757                    field: "message.continuation",
758                },
759                "thinking input requires assistant continuation state",
760            ));
761        }
762        Ok(())
763    }
764
765    fn validate_shape(&self) -> Result<(), KitError> {
766        for part in &self.content {
767            part.validate()?;
768        }
769        match self.role {
770            Role::System | Role::Developer => {
771                if self.content.is_empty()
772                    || self
773                        .content
774                        .iter()
775                        .any(|part| !matches!(part, ContentPart::Text { .. }))
776                {
777                    return Err(KitError::invalid_input(
778                        ValidationViolation::InvalidValue {
779                            field: "message.content",
780                            rule: "instructions require non-empty text-only content",
781                        },
782                        "instruction messages require non-empty text-only content",
783                    ));
784                }
785                if !self.function_calls.is_empty()
786                    || !self.function_results.is_empty()
787                    || self.continuation.is_some()
788                {
789                    return Err(KitError::invalid_input(
790                        ValidationViolation::InvalidValue {
791                            field: "message.role",
792                            rule: "instructions cannot carry functions or continuation",
793                        },
794                        "instruction messages cannot carry functions or continuation state",
795                    ));
796                }
797            }
798            Role::User => {
799                if self.content.is_empty() {
800                    return Err(KitError::invalid_input(
801                        ValidationViolation::Empty {
802                            field: "message.content",
803                        },
804                        "user message content must not be empty",
805                    ));
806                }
807                if self.content.iter().any(|part| {
808                    matches!(
809                        part,
810                        ContentPart::Thinking { .. } | ContentPart::Json { .. }
811                    )
812                }) {
813                    return Err(KitError::invalid_input(
814                        ValidationViolation::InvalidValue {
815                            field: "message.content",
816                            rule: "user content cannot include thinking or raw JSON",
817                        },
818                        "user messages cannot contain thinking or raw JSON content",
819                    ));
820                }
821                if !self.function_calls.is_empty()
822                    || !self.function_results.is_empty()
823                    || self.continuation.is_some()
824                {
825                    return Err(KitError::invalid_input(
826                        ValidationViolation::InvalidValue {
827                            field: "message.role",
828                            rule: "user messages cannot carry functions or continuation",
829                        },
830                        "user messages cannot carry functions or continuation state",
831                    ));
832                }
833            }
834            Role::Assistant => {
835                if self.content.is_empty()
836                    && self.function_calls.is_empty()
837                    && self.continuation.is_none()
838                {
839                    return Err(KitError::invalid_input(
840                        ValidationViolation::InvalidValue {
841                            field: "message",
842                            rule: "assistant requires content, calls, or continuation",
843                        },
844                        "assistant message requires content, a function call, or retained continuation",
845                    ));
846                }
847                if self.content.iter().any(|part| {
848                    !matches!(
849                        part,
850                        ContentPart::Text { .. } | ContentPart::Thinking { .. }
851                    )
852                }) {
853                    return Err(KitError::invalid_input(
854                        ValidationViolation::InvalidValue {
855                            field: "message.content",
856                            rule: "assistant input requires text or continued thinking",
857                        },
858                        "assistant input supports only text and continued thinking content",
859                    ));
860                }
861                if !self.function_results.is_empty() {
862                    return Err(KitError::invalid_input(
863                        ValidationViolation::Incompatible {
864                            field: "message.function_results",
865                            other: "message.role",
866                        },
867                        "assistant messages cannot carry function results",
868                    ));
869                }
870            }
871            Role::Tool => {
872                if !self.content.is_empty()
873                    || !self.function_calls.is_empty()
874                    || self.function_results.is_empty()
875                    || self.continuation.is_some()
876                {
877                    return Err(KitError::invalid_input(
878                        ValidationViolation::InvalidValue {
879                            field: "message",
880                            rule: "tool messages require only function results",
881                        },
882                        "tool messages require function results and no direct content, calls, or continuation",
883                    ));
884                }
885            }
886        }
887        Ok(())
888    }
889}
890
891impl GenerationContinuation {
892    pub(crate) fn open_ai(model_id: ModelId, output_items: Vec<Value>) -> Self {
893        Self {
894            model_id,
895            inner: NativeContinuation::OpenAi { output_items },
896        }
897    }
898
899    pub(crate) fn anthropic(model_id: ModelId, content_blocks: Vec<Value>) -> Self {
900        Self {
901            model_id,
902            inner: NativeContinuation::Anthropic { content_blocks },
903        }
904    }
905
906    pub(crate) fn gemini(model_id: ModelId, signed_parts: Vec<Value>) -> Self {
907        Self {
908            model_id,
909            inner: NativeContinuation::Gemini { signed_parts },
910        }
911    }
912
913    pub(crate) fn ollama(model_id: ModelId, thinking: String) -> Self {
914        Self {
915            model_id,
916            inner: NativeContinuation::Ollama { thinking },
917        }
918    }
919}
920
921impl<C> Serialize for Message<C> {
922    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
923    where
924        S: Serializer,
925    {
926        if self.continuation.is_some() {
927            return Err(S::Error::custom(
928                "provider continuation cannot be serialized; retain the returned message in memory",
929            ));
930        }
931        #[derive(Serialize)]
932        struct Wire<'a> {
933            role: Role,
934            content: &'a [ContentPart],
935            function_calls: &'a [FunctionCall],
936            function_results: &'a [FunctionResult],
937        }
938        Wire {
939            role: self.role,
940            content: &self.content,
941            function_calls: &self.function_calls,
942            function_results: &self.function_results,
943        }
944        .serialize(serializer)
945    }
946}
947
948impl<'de, C> Deserialize<'de> for Message<C> {
949    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
950    where
951        D: Deserializer<'de>,
952    {
953        #[derive(Deserialize)]
954        #[serde(deny_unknown_fields)]
955        struct Wire {
956            role: Role,
957            #[serde(default)]
958            content: Vec<ContentPart>,
959            #[serde(default)]
960            function_calls: Vec<FunctionCall>,
961            #[serde(default)]
962            function_results: Vec<FunctionResult>,
963        }
964
965        let wire = Wire::deserialize(deserializer)?;
966        let message = Self {
967            role: wire.role,
968            content: wire.content,
969            function_calls: wire.function_calls,
970            function_results: wire.function_results,
971            continuation: None,
972        };
973        message
974            .validate_for_request()
975            .map_err(serde::de::Error::custom)?;
976        Ok(message)
977    }
978}
979
980/// Validated strict object JSON schema.
981///
982/// # Examples
983///
984/// ```
985/// use providers_kit::generation::JsonSchema;
986/// let schema = JsonSchema::new(
987///     serde_json::json!({"type":"object", "properties":{"answer":{"type":"string"}}}),
988/// )?;
989/// assert_eq!(schema.schema()["type"], "object");
990///
991/// # Ok::<(), providers_kit::KitError>(())
992/// ```
993///
994#[derive(Clone, Debug, PartialEq)]
995pub struct JsonSchema(Value);
996
997impl JsonSchema {
998    /// Validates and owns an object-shaped JSON schema.
999    ///
1000    /// # Errors
1001    ///
1002    /// Returns an input error unless the schema is a JSON object whose root declares `type: object`. The selected operation performs any stricter schema or provider validation.
1003    ///
1004    /// See [`crate::generation::JsonSchema`] for a complete example.
1005    ///
1006    pub fn new(schema: Value) -> Result<Self, KitError> {
1007        let object = schema.as_object().ok_or_else(|| {
1008            KitError::invalid_input(
1009                ValidationViolation::InvalidFormat {
1010                    field: "json_schema",
1011                    rule: "JSON object",
1012                },
1013                "JSON schema must be encoded as an object",
1014            )
1015        })?;
1016        if !matches!(object.get("type"), Some(Value::String(kind)) if kind == "object") {
1017            return Err(KitError::invalid_input(
1018                ValidationViolation::InvalidValue {
1019                    field: "json_schema.type",
1020                    rule: "must declare object",
1021                },
1022                "JSON schema root must declare `type: object`",
1023            ));
1024        }
1025        Ok(Self(schema))
1026    }
1027
1028    /// Borrows the validated schema document.
1029    ///
1030    /// See [`crate::generation::JsonSchema`] for a complete example.
1031    ///
1032    #[must_use]
1033    pub const fn schema(&self) -> &Value {
1034        &self.0
1035    }
1036
1037    /// Consumes the wrapper and returns the schema document.
1038    ///
1039    /// # Examples
1040    ///
1041    /// ```
1042    /// use providers_kit::generation::JsonSchema;
1043    /// let schema = JsonSchema::new(
1044    ///     serde_json::json!({"type":"object", "properties":{"answer":{"type":"string"}}}),
1045    /// )?;
1046    /// assert_eq!(schema.into_value()["type"], "object");
1047    ///
1048    /// # Ok::<(), providers_kit::KitError>(())
1049    /// ```
1050    ///
1051    #[must_use]
1052    pub fn into_value(self) -> Value {
1053        self.0
1054    }
1055}
1056
1057impl Serialize for JsonSchema {
1058    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1059    where
1060        S: Serializer,
1061    {
1062        self.0.serialize(serializer)
1063    }
1064}
1065
1066impl<'de> Deserialize<'de> for JsonSchema {
1067    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1068    where
1069        D: Deserializer<'de>,
1070    {
1071        Self::new(Value::deserialize(deserializer)?).map_err(serde::de::Error::custom)
1072    }
1073}
1074
1075/// Requested output format for a generation request.
1076#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1077#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
1078pub enum ResponseFormat {
1079    /// Plain text output.
1080    Text,
1081    /// A provider-validated JSON object without a caller schema.
1082    JsonObject,
1083    /// JSON validated against a strict caller schema.
1084    JsonSchema(JsonSchema),
1085}
1086
1087/// Portable reasoning effort hint for providers that support explicit thinking controls.
1088///
1089/// # Examples
1090/// ```rust
1091/// let effort = providers_kit::generation::ReasoningEffort::High;
1092/// assert!(matches!(effort, providers_kit::generation::ReasoningEffort::High));
1093/// ```
1094#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1095#[serde(rename_all = "snake_case")]
1096pub enum ReasoningEffort {
1097    /// Requests the lightest available reasoning budget.
1098    Minimal,
1099    /// Requests a low reasoning budget when the provider exposes several tiers.
1100    Low,
1101    /// Requests a balanced reasoning budget between latency and depth.
1102    Medium,
1103    /// Requests a high reasoning budget for harder prompts.
1104    High,
1105    /// Requests the deepest reasoning tier the provider allows.
1106    Maximum,
1107}
1108
1109/// Controls how much reasoning summary a provider should return.
1110///
1111/// # Examples
1112/// ```rust
1113/// let mode = providers_kit::generation::ReasoningSummaryMode::Summary;
1114/// assert!(matches!(mode, providers_kit::generation::ReasoningSummaryMode::Summary));
1115/// ```
1116#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1117#[serde(rename_all = "snake_case")]
1118pub enum ReasoningSummaryMode {
1119    /// Lets the provider decide whether and how to summarize its reasoning.
1120    Auto,
1121    /// Suppresses any reasoning summary in the provider response when supported.
1122    Hidden,
1123    /// Requests a concise reasoning summary.
1124    Summary,
1125    /// Requests the most detailed reasoning summary the provider exposes.
1126    Verbose,
1127}
1128
1129/// Optional reasoning controls attached to a generation request.
1130///
1131/// # Examples
1132/// ```rust
1133/// let config = providers_kit::generation::ReasoningConfig {
1134///     effort: Some(providers_kit::generation::ReasoningEffort::High),
1135///     ..Default::default()
1136/// };
1137/// assert_eq!(config.effort, Some(providers_kit::generation::ReasoningEffort::High));
1138/// ```
1139#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
1140#[serde(deny_unknown_fields)]
1141pub struct ReasoningConfig {
1142    /// Requested reasoning effort tier when the backend supports explicit controls.
1143    pub effort: Option<ReasoningEffort>,
1144    /// Upper bound for provider-native reasoning tokens when that concept exists.
1145    pub max_reasoning_tokens: Option<u32>,
1146    /// Requested reasoning-summary presentation mode.
1147    pub summary_mode: ReasoningSummaryMode,
1148}
1149
1150impl Default for ReasoningConfig {
1151    fn default() -> Self {
1152        Self {
1153            effort: None,
1154            max_reasoning_tokens: None,
1155            summary_mode: ReasoningSummaryMode::Auto,
1156        }
1157    }
1158}
1159
1160/// Policy for how strongly the allowed-tool list should be enforced.
1161///
1162/// # Examples
1163/// ```rust
1164/// let mode = providers_kit::generation::AllowedToolMode::Required;
1165/// assert!(matches!(mode, providers_kit::generation::AllowedToolMode::Required));
1166/// ```
1167#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1168pub enum AllowedToolMode {
1169    /// Lets the provider decide how strictly to enforce the allowed-tool list.
1170    Auto,
1171    /// Requires the provider to keep tool calls inside the allowed-tool list.
1172    Required,
1173}
1174
1175impl Serialize for AllowedToolMode {
1176    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1177    where
1178        S: Serializer,
1179    {
1180        self.as_wire().serialize(serializer)
1181    }
1182}
1183
1184impl<'de> Deserialize<'de> for AllowedToolMode {
1185    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1186    where
1187        D: Deserializer<'de>,
1188    {
1189        let value = String::deserialize(deserializer)?;
1190        Self::from_wire(&value)
1191    }
1192}
1193
1194impl AllowedToolMode {
1195    pub(crate) fn as_wire(self) -> &'static str {
1196        match self {
1197            Self::Auto => "auto",
1198            Self::Required => "required",
1199        }
1200    }
1201
1202    fn from_wire<E>(value: &str) -> Result<Self, E>
1203    where
1204        E: serde::de::Error,
1205    {
1206        match value {
1207            "required" => Ok(Self::Required),
1208            "auto" => Ok(Self::Auto),
1209            other => Err(E::custom(format!("unknown allowed tool mode `{other}`"))),
1210        }
1211    }
1212}
1213
1214/// Portable tool-selection policy for a request.
1215///
1216/// Provider renderers map this policy into their native tool-choice shape after
1217/// the selected model has validated whether tools, strict schemas, and forced
1218/// choices are supported together.
1219///
1220/// # Examples
1221/// ```rust
1222/// let choice = providers_kit::generation::ToolChoice::Tool {
1223///     name: "lookup_weather".into(),
1224/// };
1225/// assert!(matches!(choice, providers_kit::generation::ToolChoice::Tool { .. }));
1226/// ```
1227#[derive(Clone, Debug, PartialEq, Eq)]
1228pub enum ToolChoice {
1229    /// Lets the provider choose whether tools should be used.
1230    Auto,
1231    /// Disables tool calling for the request.
1232    None,
1233    /// Requires at least one tool call before completion.
1234    Required,
1235    /// Allows any declared tool to be called.
1236    Any,
1237    /// Forces the provider to call one specific tool.
1238    Tool {
1239        /// Declared tool name that must be used.
1240        name: String,
1241    },
1242    /// Restricts tool calls to an explicit subset of tool names.
1243    Allowed {
1244        /// Enforcement mode for the allowed-tool subset.
1245        mode: AllowedToolMode,
1246        /// Tool names that remain eligible for selection.
1247        names: Vec<String>,
1248    },
1249}
1250
1251impl Serialize for ToolChoice {
1252    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1253    where
1254        S: Serializer,
1255    {
1256        self.as_wire().serialize(serializer)
1257    }
1258}
1259
1260impl<'de> Deserialize<'de> for ToolChoice {
1261    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1262    where
1263        D: Deserializer<'de>,
1264    {
1265        match Value::deserialize(deserializer)? {
1266            Value::String(value) => match value.as_str() {
1267                "auto" => Ok(Self::Auto),
1268                "none" => Ok(Self::None),
1269                "required" => Ok(Self::Required),
1270                "any" => Ok(Self::Any),
1271                other => Err(serde::de::Error::custom(format!(
1272                    "unknown tool choice variant `{other}`"
1273                ))),
1274            },
1275            Value::Object(object) => {
1276                serde_json::from_value::<ToolChoiceObjectWire>(Value::Object(object))
1277                    .map_err(serde::de::Error::custom)?
1278                    .into_tool_choice()
1279            }
1280            _ => Err(serde::de::Error::custom(
1281                "tool choice must be encoded as a string or object",
1282            )),
1283        }
1284    }
1285}
1286
1287impl ToolChoice {
1288    fn as_wire(&self) -> ToolChoiceWireRef<'_> {
1289        match self {
1290            Self::Auto => ToolChoiceWireRef::String("auto"),
1291            Self::None => ToolChoiceWireRef::String("none"),
1292            Self::Required => ToolChoiceWireRef::String("required"),
1293            Self::Any => ToolChoiceWireRef::String("any"),
1294            Self::Tool { name } => ToolChoiceWireRef::Object(ToolChoiceObjectWireRef {
1295                kind: "tool",
1296                mode: None,
1297                name: Some(name),
1298                names: None,
1299            }),
1300            Self::Allowed { mode, names } => ToolChoiceWireRef::Object(ToolChoiceObjectWireRef {
1301                kind: "allowed",
1302                mode: Some(mode.as_wire()),
1303                name: None,
1304                names: Some(names.as_slice()),
1305            }),
1306        }
1307    }
1308}
1309
1310#[derive(Serialize)]
1311#[serde(untagged)]
1312enum ToolChoiceWireRef<'a> {
1313    String(&'static str),
1314    Object(ToolChoiceObjectWireRef<'a>),
1315}
1316
1317#[derive(Serialize)]
1318struct ToolChoiceObjectWireRef<'a> {
1319    #[serde(rename = "type")]
1320    kind: &'static str,
1321    #[serde(skip_serializing_if = "Option::is_none")]
1322    mode: Option<&'static str>,
1323    #[serde(skip_serializing_if = "Option::is_none")]
1324    name: Option<&'a str>,
1325    #[serde(skip_serializing_if = "Option::is_none")]
1326    names: Option<&'a [String]>,
1327}
1328
1329#[derive(Deserialize)]
1330#[serde(deny_unknown_fields)]
1331struct ToolChoiceObjectWire {
1332    #[serde(rename = "type", default)]
1333    kind: Option<String>,
1334    #[serde(default)]
1335    mode: Option<String>,
1336    #[serde(default)]
1337    name: Option<String>,
1338    #[serde(default)]
1339    names: Option<Value>,
1340}
1341
1342impl ToolChoiceObjectWire {
1343    fn into_tool_choice<E>(self) -> Result<ToolChoice, E>
1344    where
1345        E: serde::de::Error,
1346    {
1347        let kind = self
1348            .kind
1349            .ok_or_else(|| E::custom("tool choice objects require a string `type` field"))?;
1350        match kind.as_str() {
1351            "tool" => {
1352                let name = self
1353                    .name
1354                    .filter(|name| !name.trim().is_empty())
1355                    .ok_or_else(|| E::custom("tool choice objects require a non-empty `name`"))?;
1356                Ok(ToolChoice::Tool { name })
1357            }
1358            "allowed" => Ok(ToolChoice::Allowed {
1359                mode: self
1360                    .mode
1361                    .as_deref()
1362                    .map(AllowedToolMode::from_wire)
1363                    .transpose()?
1364                    .unwrap_or(AllowedToolMode::Auto),
1365                names: parse_allowed_tool_names(self.names)?,
1366            }),
1367            other => Err(E::custom(format!(
1368                "unknown tool choice object type `{other}`"
1369            ))),
1370        }
1371    }
1372}
1373
1374fn parse_allowed_tool_names<E>(value: Option<Value>) -> Result<Vec<String>, E>
1375where
1376    E: serde::de::Error,
1377{
1378    match value {
1379        None => Ok(Vec::new()),
1380        Some(Value::Array(items)) => items
1381            .into_iter()
1382            .map(|item| match item {
1383                Value::String(name) if !name.trim().is_empty() => Ok(name),
1384                Value::String(_) => {
1385                    Err(E::custom("allowed tool entries must be non-empty strings"))
1386                }
1387                _ => Err(E::custom("allowed tool entries must be strings")),
1388            })
1389            .collect(),
1390        Some(_) => Err(E::custom("allowed tool names must be encoded as an array")),
1391    }
1392}
1393
1394/// Validated client-executed function declaration shown to a model.
1395///
1396/// This declares a caller-executed function. providers-kit never executes the callback;
1397/// the caller supplies correlated [`FunctionResult`] values after returned calls.
1398///
1399/// # Examples
1400///
1401/// ```
1402/// use providers_kit::generation::{ProviderFunctionTool, JsonSchema};
1403/// let tool = ProviderFunctionTool::new(
1404///     "lookup",
1405///     Some("Find a record".into()),
1406///     JsonSchema::new(serde_json::json!({"type":"object"}))?,
1407/// )?;
1408/// assert_eq!(tool.name(), "lookup");
1409/// assert_eq!(tool.description(), Some("Find a record"));
1410/// assert_eq!(tool.input_schema().schema()["type"], "object");
1411///
1412/// # Ok::<(), providers_kit::KitError>(())
1413/// ```
1414///
1415#[derive(Clone, Debug, PartialEq, Serialize)]
1416pub struct ProviderFunctionTool {
1417    name: String,
1418    description: Option<String>,
1419    input_schema: JsonSchema,
1420}
1421
1422impl ProviderFunctionTool {
1423    /// Creates a function declaration with a stable non-empty name.
1424    ///
1425    /// # Errors
1426    ///
1427    /// Returns an input error for an invalid function name or blank description. The input schema has already been validated by [`JsonSchema::new`].
1428    ///
1429    /// See [`crate::generation::ProviderFunctionTool`] for a complete example.
1430    ///
1431    pub fn new(
1432        name: impl Into<String>,
1433        description: Option<String>,
1434        input_schema: JsonSchema,
1435    ) -> Result<Self, KitError> {
1436        let name = name.into();
1437        FunctionCall::validate_name("tools.function.name", &name)?;
1438        if description
1439            .as_ref()
1440            .is_some_and(|value| value.trim().is_empty())
1441        {
1442            return Err(KitError::invalid_input(
1443                ValidationViolation::Empty {
1444                    field: "tools.function.description",
1445                },
1446                "function tool description must be omitted or non-empty",
1447            ));
1448        }
1449        Ok(Self {
1450            name,
1451            description,
1452            input_schema,
1453        })
1454    }
1455
1456    /// Returns the provider-visible function name.
1457    ///
1458    /// See [`crate::generation::ProviderFunctionTool`] for a complete example.
1459    ///
1460    #[must_use]
1461    pub fn name(&self) -> &str {
1462        &self.name
1463    }
1464
1465    /// Returns the optional model-visible description.
1466    ///
1467    /// See [`crate::generation::ProviderFunctionTool`] for a complete example.
1468    ///
1469    #[must_use]
1470    pub fn description(&self) -> Option<&str> {
1471        self.description.as_deref()
1472    }
1473
1474    /// Returns the strict argument schema.
1475    ///
1476    /// See [`crate::generation::ProviderFunctionTool`] for a complete example.
1477    ///
1478    #[must_use]
1479    pub const fn input_schema(&self) -> &JsonSchema {
1480        &self.input_schema
1481    }
1482
1483    /// Consumes the declaration into its validated parts.
1484    ///
1485    /// # Examples
1486    ///
1487    /// ```
1488    /// use providers_kit::generation::{ProviderFunctionTool, JsonSchema};
1489    /// let tool = ProviderFunctionTool::new(
1490    ///     "lookup",
1491    ///     Some("Find a record".into()),
1492    ///     JsonSchema::new(serde_json::json!({"type":"object"}))?,
1493    /// )?;
1494    /// let (name, description, schema) = tool.into_parts();
1495    /// assert_eq!(name, "lookup");
1496    /// assert_eq!(description.as_deref(), Some("Find a record"));
1497    /// assert_eq!(schema.schema()["type"], "object");
1498    ///
1499    /// # Ok::<(), providers_kit::KitError>(())
1500    /// ```
1501    ///
1502    #[must_use]
1503    pub fn into_parts(self) -> (String, Option<String>, JsonSchema) {
1504        (self.name, self.description, self.input_schema)
1505    }
1506}
1507
1508impl<'de> Deserialize<'de> for ProviderFunctionTool {
1509    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1510    where
1511        D: Deserializer<'de>,
1512    {
1513        #[derive(Deserialize)]
1514        #[serde(deny_unknown_fields)]
1515        struct Wire {
1516            name: String,
1517            description: Option<String>,
1518            input_schema: JsonSchema,
1519        }
1520        let wire = Wire::deserialize(deserializer)?;
1521        Self::new(wire.name, wire.description, wire.input_schema).map_err(serde::de::Error::custom)
1522    }
1523}
1524
1525/// Tool declaration supplied to a generation request.
1526#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1527#[serde(tag = "type", content = "declaration", rename_all = "snake_case")]
1528pub enum ProviderToolSpec {
1529    /// Client-executed function.
1530    Function(ProviderFunctionTool),
1531    /// Provider-hosted execution.
1532    Hosted(HostedTool),
1533}
1534
1535impl ProviderToolSpec {
1536    pub(crate) const fn hosted_tool(&self) -> Option<&HostedTool> {
1537        match self {
1538            Self::Function(_) => None,
1539            Self::Hosted(tool) => Some(tool),
1540        }
1541    }
1542}
1543
1544/// Validated client function call requested by a model.
1545///
1546/// # Examples
1547///
1548/// ```
1549/// use providers_kit::generation::FunctionCall;
1550/// let call = FunctionCall::new(Some("call-1".into()), "lookup", serde_json::json!({"key":"a"}))?;
1551/// assert_eq!(call.id(), Some("call-1"));
1552/// assert_eq!(call.name(), "lookup");
1553/// assert_eq!(call.arguments()["key"], "a");
1554///
1555/// # Ok::<(), providers_kit::KitError>(())
1556/// ```
1557///
1558#[derive(Clone, Debug, PartialEq, Serialize)]
1559pub struct FunctionCall {
1560    id: Option<String>,
1561    name: String,
1562    arguments: Value,
1563}
1564
1565impl<'de> Deserialize<'de> for FunctionCall {
1566    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1567    where
1568        D: Deserializer<'de>,
1569    {
1570        #[derive(Deserialize)]
1571        #[serde(deny_unknown_fields)]
1572        struct Wire {
1573            id: Option<String>,
1574            name: String,
1575            arguments: Value,
1576        }
1577
1578        let wire = Wire::deserialize(deserializer)?;
1579        Self::new(wire.id, wire.name, wire.arguments).map_err(serde::de::Error::custom)
1580    }
1581}
1582
1583impl FunctionCall {
1584    /// Creates a validated function call.
1585    ///
1586    /// # Errors
1587    ///
1588    /// Returns an input error for an invalid portable function name, invalid optional call id, or arguments that are not a JSON object. Keep returned call correlation unchanged in its result.
1589    ///
1590    /// See [`crate::generation::FunctionCall`] for a complete example.
1591    ///
1592    pub fn new(
1593        id: Option<String>,
1594        name: impl Into<String>,
1595        arguments: Value,
1596    ) -> Result<Self, KitError> {
1597        let name = name.into();
1598        Self::validate_name("function_call.name", &name)?;
1599        if let Some(id) = id.as_deref() {
1600            Self::validate_id("function_call.id", id)?;
1601        }
1602        if !arguments.is_object() {
1603            return Err(KitError::invalid_input(
1604                ValidationViolation::InvalidFormat {
1605                    field: "function_call.arguments",
1606                    rule: "JSON object",
1607                },
1608                "function arguments must be a JSON object",
1609            ));
1610        }
1611        Ok(Self {
1612            id,
1613            name,
1614            arguments,
1615        })
1616    }
1617
1618    /// Returns the provider correlation id, when one was supplied.
1619    ///
1620    /// See [`crate::generation::FunctionCall`] for a complete example.
1621    ///
1622    #[must_use]
1623    pub fn id(&self) -> Option<&str> {
1624        self.id.as_deref()
1625    }
1626
1627    /// Returns the function name.
1628    ///
1629    /// See [`crate::generation::FunctionCall`] for a complete example.
1630    ///
1631    #[must_use]
1632    pub fn name(&self) -> &str {
1633        &self.name
1634    }
1635
1636    /// Returns the validated object-shaped arguments.
1637    ///
1638    /// See [`crate::generation::FunctionCall`] for a complete example.
1639    ///
1640    #[must_use]
1641    pub fn arguments(&self) -> &Value {
1642        &self.arguments
1643    }
1644
1645    pub(crate) fn validate_name(label: &'static str, value: &str) -> Result<(), KitError> {
1646        let mut bytes = value.bytes();
1647        let first = bytes.next();
1648        if value.len() > 64
1649            || !first.is_some_and(|byte| byte.is_ascii_alphabetic() || byte == b'_')
1650            || !bytes.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-'))
1651        {
1652            Err(KitError::invalid_input(
1653                ValidationViolation::InvalidFormat {
1654                    field: label,
1655                    rule: "1..=64 ASCII letters, digits, underscores, or hyphens; first character letter or underscore",
1656                },
1657                format!(
1658                    "{label} must use 1..=64 portable ASCII letters, digits, underscores, or hyphens and start with a letter or underscore"
1659                ),
1660            ))
1661        } else {
1662            Ok(())
1663        }
1664    }
1665
1666    fn validate_id(label: &'static str, value: &str) -> Result<(), KitError> {
1667        if value.is_empty()
1668            || value.len() > 256
1669            || value.trim() != value
1670            || value.chars().any(char::is_control)
1671        {
1672            Err(KitError::invalid_input(
1673                ValidationViolation::InvalidFormat {
1674                    field: label,
1675                    rule: "1..=256 unpadded UTF-8 bytes without control characters",
1676                },
1677                format!(
1678                    "{label} must contain 1..=256 unpadded UTF-8 bytes without control characters"
1679                ),
1680            ))
1681        } else {
1682            Ok(())
1683        }
1684    }
1685}
1686
1687/// Validated caller-supplied result for a client function call.
1688///
1689/// # Examples
1690///
1691/// ```
1692/// use providers_kit::generation::{FunctionResult, ContentPart};
1693/// let result = FunctionResult::new(
1694///     Some("call-1".into()),
1695///     "lookup",
1696///     vec![ContentPart::Json {
1697///         value: serde_json::json!({"value":2}),
1698///     }],
1699///     false,
1700/// )?;
1701/// assert_eq!(result.call_id(), Some("call-1"));
1702/// assert_eq!(result.name(), "lookup");
1703/// assert_eq!(result.content().len(), 1);
1704/// assert!(!result.is_error());
1705///
1706/// # Ok::<(), providers_kit::KitError>(())
1707/// ```
1708///
1709#[derive(Clone, Debug, PartialEq, Serialize)]
1710pub struct FunctionResult {
1711    call_id: Option<String>,
1712    name: String,
1713    content: Vec<ContentPart>,
1714    is_error: bool,
1715}
1716
1717impl<'de> Deserialize<'de> for FunctionResult {
1718    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1719    where
1720        D: Deserializer<'de>,
1721    {
1722        #[derive(Deserialize)]
1723        #[serde(deny_unknown_fields)]
1724        struct Wire {
1725            call_id: Option<String>,
1726            name: String,
1727            content: Vec<ContentPart>,
1728            is_error: bool,
1729        }
1730
1731        let wire = Wire::deserialize(deserializer)?;
1732        Self::new(wire.call_id, wire.name, wire.content, wire.is_error)
1733            .map_err(serde::de::Error::custom)
1734    }
1735}
1736
1737impl FunctionResult {
1738    /// Creates a validated function result.
1739    ///
1740    /// # Errors
1741    ///
1742    /// Returns an input error for an invalid name/id, empty or invalid content, or provider thinking content. Accepted function failures use `is_error = true` with nonempty model-visible content.
1743    ///
1744    /// See [`crate::generation::FunctionResult`] for a complete example.
1745    ///
1746    pub fn new(
1747        call_id: Option<String>,
1748        name: impl Into<String>,
1749        content: Vec<ContentPart>,
1750        is_error: bool,
1751    ) -> Result<Self, KitError> {
1752        let name = name.into();
1753        FunctionCall::validate_name("function_result.name", &name)?;
1754        if let Some(call_id) = call_id.as_deref() {
1755            FunctionCall::validate_id("function_result.call_id", call_id)?;
1756        }
1757        if content.is_empty() {
1758            return Err(KitError::invalid_input(
1759                ValidationViolation::Empty {
1760                    field: "function_result.content",
1761                },
1762                "function result content must not be empty",
1763            ));
1764        }
1765        for part in &content {
1766            part.validate()?;
1767        }
1768        if content
1769            .iter()
1770            .any(|part| matches!(part, ContentPart::Thinking { .. }))
1771        {
1772            return Err(KitError::invalid_input(
1773                ValidationViolation::Unsupported {
1774                    field: "function_result.content",
1775                    rule: "thinking content is not permitted",
1776                },
1777                "function results cannot contain provider thinking state",
1778            ));
1779        }
1780        Ok(Self {
1781            call_id,
1782            name,
1783            content,
1784            is_error,
1785        })
1786    }
1787
1788    /// Returns the originating call id, when available.
1789    ///
1790    /// See [`crate::generation::FunctionResult`] for a complete example.
1791    ///
1792    #[must_use]
1793    pub fn call_id(&self) -> Option<&str> {
1794        self.call_id.as_deref()
1795    }
1796
1797    /// Returns the function name.
1798    ///
1799    /// See [`crate::generation::FunctionResult`] for a complete example.
1800    ///
1801    #[must_use]
1802    pub fn name(&self) -> &str {
1803        &self.name
1804    }
1805
1806    /// Returns ordered model-visible result content.
1807    ///
1808    /// See [`crate::generation::FunctionResult`] for a complete example.
1809    ///
1810    #[must_use]
1811    pub fn content(&self) -> &[ContentPart] {
1812        &self.content
1813    }
1814
1815    /// Reports whether the completed function execution failed.
1816    ///
1817    /// See [`crate::generation::FunctionResult`] for a complete example.
1818    ///
1819    #[must_use]
1820    pub const fn is_error(&self) -> bool {
1821        self.is_error
1822    }
1823}
1824
1825/// Optional sampling and tool-choice overrides.
1826///
1827/// These are portable requested controls. Local validation does not establish support
1828/// for every selected model; unsupported combinations fail at the model operation.
1829///
1830/// # Examples
1831///
1832/// ```
1833/// use providers_kit::generation::GenerationConfig;
1834/// let config = GenerationConfig::default().with_temperature(0.5)?.with_seed(42);
1835/// assert_eq!(config.temperature(), Some(0.5));
1836/// assert_eq!(config.seed(), Some(42));
1837///
1838/// # Ok::<(), providers_kit::KitError>(())
1839/// ```
1840///
1841#[derive(Clone, Debug, PartialEq, Serialize, Default)]
1842#[serde(deny_unknown_fields)]
1843pub struct GenerationConfig {
1844    temperature: Option<f32>,
1845    top_p: Option<f32>,
1846    top_k: Option<NonZeroU32>,
1847    max_output_tokens: Option<NonZeroU32>,
1848    stop_sequences: Vec<String>,
1849    presence_penalty: Option<f32>,
1850    frequency_penalty: Option<f32>,
1851    seed: Option<u64>,
1852    tool_choice: Option<ToolChoice>,
1853}
1854
1855impl GenerationConfig {
1856    /// Sets a finite non-negative sampling temperature.
1857    ///
1858    /// # Errors
1859    ///
1860    /// Returns an input error unless temperature is finite and between 0 and 2 inclusive.
1861    ///
1862    /// # Examples
1863    ///
1864    /// ```
1865    /// use providers_kit::generation::GenerationConfig;
1866    /// let config = GenerationConfig::default().with_temperature(0.5)?;
1867    /// assert_eq!(config.temperature(), Some(0.5));
1868    ///
1869    /// # Ok::<(), providers_kit::KitError>(())
1870    /// ```
1871    ///
1872    pub fn with_temperature(mut self, value: f32) -> Result<Self, KitError> {
1873        if !value.is_finite() || value < 0.0 {
1874            return Err(KitError::invalid_input(
1875                ValidationViolation::OutOfRange {
1876                    field: "generation.temperature",
1877                    rule: "finite and non-negative",
1878                },
1879                "temperature must be finite and non-negative",
1880            ));
1881        }
1882        self.temperature = Some(value);
1883        Ok(self)
1884    }
1885
1886    /// Sets a finite nucleus-sampling probability in `(0, 1]`.
1887    ///
1888    /// # Errors
1889    ///
1890    /// Returns an input error unless top_p is finite and between 0 and 1 inclusive.
1891    ///
1892    /// # Examples
1893    ///
1894    /// ```
1895    /// use providers_kit::generation::GenerationConfig;
1896    /// let config = GenerationConfig::default().with_top_p(0.8)?;
1897    /// assert_eq!(config.top_p(), Some(0.8));
1898    ///
1899    /// # Ok::<(), providers_kit::KitError>(())
1900    /// ```
1901    ///
1902    pub fn with_top_p(mut self, value: f32) -> Result<Self, KitError> {
1903        if !(value.is_finite() && 0.0 < value && value <= 1.0) {
1904            return Err(KitError::invalid_input(
1905                ValidationViolation::OutOfRange {
1906                    field: "generation.top_p",
1907                    rule: "finite and in (0, 1]",
1908                },
1909                "top_p must be in (0, 1]",
1910            ));
1911        }
1912        self.top_p = Some(value);
1913        Ok(self)
1914    }
1915
1916    /// Sets a non-zero top-k cutoff.
1917    ///
1918    /// # Examples
1919    ///
1920    /// ```
1921    /// use providers_kit::generation::GenerationConfig;
1922    /// let config = GenerationConfig::default().with_top_k(std::num::NonZeroU32::new(20).unwrap());
1923    /// assert_eq!(config.top_k(), std::num::NonZeroU32::new(20));
1924    ///
1925    /// # Ok::<(), providers_kit::KitError>(())
1926    /// ```
1927    ///
1928    #[must_use]
1929    pub fn with_top_k(mut self, value: NonZeroU32) -> Self {
1930        self.top_k = Some(value);
1931        self
1932    }
1933
1934    /// Sets a non-zero maximum output-token count.
1935    ///
1936    /// # Examples
1937    ///
1938    /// ```
1939    /// use providers_kit::generation::GenerationConfig;
1940    /// let config =
1941    ///     GenerationConfig::default().with_max_output_tokens(std::num::NonZeroU32::new(256).unwrap());
1942    /// assert_eq!(config.max_output_tokens(), std::num::NonZeroU32::new(256));
1943    ///
1944    /// # Ok::<(), providers_kit::KitError>(())
1945    /// ```
1946    ///
1947    #[must_use]
1948    pub fn with_max_output_tokens(mut self, value: NonZeroU32) -> Self {
1949        self.max_output_tokens = Some(value);
1950        self
1951    }
1952
1953    /// Replaces validated unique stop sequences.
1954    ///
1955    /// # Errors
1956    ///
1957    /// Returns an input error for a blank or repeated sequence. An empty list clears existing stop sequences.
1958    ///
1959    /// # Examples
1960    ///
1961    /// ```
1962    /// use providers_kit::generation::GenerationConfig;
1963    /// let config = GenerationConfig::default().with_stop_sequences(vec!["END".into()])?;
1964    /// assert_eq!(config.stop_sequences(), &["END"]);
1965    ///
1966    /// # Ok::<(), providers_kit::KitError>(())
1967    /// ```
1968    ///
1969    pub fn with_stop_sequences(mut self, values: Vec<String>) -> Result<Self, KitError> {
1970        let mut seen = BTreeSet::new();
1971        for value in &values {
1972            if value.trim().is_empty() {
1973                return Err(KitError::invalid_input(
1974                    ValidationViolation::Empty {
1975                        field: "generation.stop_sequences",
1976                    },
1977                    "stop sequences must not be empty",
1978                ));
1979            }
1980            if !seen.insert(value) {
1981                return Err(KitError::invalid_input(
1982                    ValidationViolation::Duplicate {
1983                        field: "generation.stop_sequences",
1984                    },
1985                    "stop sequences must be unique",
1986                ));
1987            }
1988        }
1989        self.stop_sequences = values;
1990        Ok(self)
1991    }
1992
1993    /// Sets a finite presence penalty in `[-2, 2]`.
1994    ///
1995    /// # Errors
1996    ///
1997    /// Returns an input error unless the penalty is finite and between -2 and 2 inclusive.
1998    ///
1999    /// # Examples
2000    ///
2001    /// ```
2002    /// use providers_kit::generation::GenerationConfig;
2003    /// let config = GenerationConfig::default().with_presence_penalty(0.25)?;
2004    /// assert_eq!(config.presence_penalty(), Some(0.25));
2005    ///
2006    /// # Ok::<(), providers_kit::KitError>(())
2007    /// ```
2008    ///
2009    pub fn with_presence_penalty(mut self, value: f32) -> Result<Self, KitError> {
2010        self.presence_penalty = Some(Self::penalty("generation.presence_penalty", value)?);
2011        Ok(self)
2012    }
2013
2014    /// Sets a finite frequency penalty in `[-2, 2]`.
2015    ///
2016    /// # Errors
2017    ///
2018    /// Returns an input error unless the penalty is finite and between -2 and 2 inclusive.
2019    ///
2020    /// # Examples
2021    ///
2022    /// ```
2023    /// use providers_kit::generation::GenerationConfig;
2024    /// let config = GenerationConfig::default().with_frequency_penalty(0.25)?;
2025    /// assert_eq!(config.frequency_penalty(), Some(0.25));
2026    ///
2027    /// # Ok::<(), providers_kit::KitError>(())
2028    /// ```
2029    ///
2030    pub fn with_frequency_penalty(mut self, value: f32) -> Result<Self, KitError> {
2031        self.frequency_penalty = Some(Self::penalty("generation.frequency_penalty", value)?);
2032        Ok(self)
2033    }
2034
2035    fn penalty(name: &'static str, value: f32) -> Result<f32, KitError> {
2036        if value.is_finite() && (-2.0..=2.0).contains(&value) {
2037            Ok(value)
2038        } else {
2039            Err(KitError::invalid_input(
2040                ValidationViolation::OutOfRange {
2041                    field: name,
2042                    rule: "finite and in [-2, 2]",
2043                },
2044                format!("{name} must be finite and in [-2, 2]"),
2045            ))
2046        }
2047    }
2048
2049    /// Sets a deterministic sampling seed.
2050    ///
2051    /// # Examples
2052    ///
2053    /// ```
2054    /// use providers_kit::generation::GenerationConfig;
2055    /// let config = GenerationConfig::default().with_seed(42);
2056    /// assert_eq!(config.seed(), Some(42));
2057    ///
2058    /// # Ok::<(), providers_kit::KitError>(())
2059    /// ```
2060    ///
2061    #[must_use]
2062    pub fn with_seed(mut self, value: u64) -> Self {
2063        self.seed = Some(value);
2064        self
2065    }
2066
2067    /// Sets a validated tool-selection policy.
2068    ///
2069    /// # Examples
2070    ///
2071    /// ```
2072    /// use providers_kit::generation::GenerationConfig;
2073    /// let config =
2074    ///     GenerationConfig::default().with_tool_choice(providers_kit::generation::ToolChoice::Auto);
2075    /// assert_eq!(config.tool_choice(), Some(&providers_kit::generation::ToolChoice::Auto));
2076    ///
2077    /// # Ok::<(), providers_kit::KitError>(())
2078    /// ```
2079    ///
2080    #[must_use]
2081    pub fn with_tool_choice(mut self, value: ToolChoice) -> Self {
2082        self.tool_choice = Some(value);
2083        self
2084    }
2085
2086    #[must_use]
2087    /// Returns the requested sampling temperature.
2088    ///
2089    /// See [`crate::generation::GenerationConfig::with_temperature`] for a complete example.
2090    ///
2091    pub const fn temperature(&self) -> Option<f32> {
2092        self.temperature
2093    }
2094
2095    #[must_use]
2096    /// Returns the requested nucleus-sampling probability.
2097    ///
2098    /// See [`crate::generation::GenerationConfig::with_top_p`] for a complete example.
2099    ///
2100    pub const fn top_p(&self) -> Option<f32> {
2101        self.top_p
2102    }
2103
2104    #[must_use]
2105    /// Returns the requested top-k cutoff.
2106    ///
2107    /// See [`crate::generation::GenerationConfig::with_top_k`] for a complete example.
2108    ///
2109    pub const fn top_k(&self) -> Option<NonZeroU32> {
2110        self.top_k
2111    }
2112
2113    #[must_use]
2114    /// Returns the requested maximum output-token count.
2115    ///
2116    /// See [`crate::generation::GenerationConfig::with_max_output_tokens`] for a complete example.
2117    ///
2118    pub const fn max_output_tokens(&self) -> Option<NonZeroU32> {
2119        self.max_output_tokens
2120    }
2121
2122    #[must_use]
2123    /// Returns validated stop sequences in caller order.
2124    ///
2125    /// See [`crate::generation::GenerationConfig::with_stop_sequences`] for a complete example.
2126    ///
2127    pub fn stop_sequences(&self) -> &[String] {
2128        &self.stop_sequences
2129    }
2130
2131    #[must_use]
2132    /// Returns the requested presence penalty.
2133    ///
2134    /// See [`crate::generation::GenerationConfig::with_presence_penalty`] for a complete example.
2135    ///
2136    pub const fn presence_penalty(&self) -> Option<f32> {
2137        self.presence_penalty
2138    }
2139
2140    #[must_use]
2141    /// Returns the requested frequency penalty.
2142    ///
2143    /// See [`crate::generation::GenerationConfig::with_frequency_penalty`] for a complete example.
2144    ///
2145    pub const fn frequency_penalty(&self) -> Option<f32> {
2146        self.frequency_penalty
2147    }
2148
2149    #[must_use]
2150    /// Returns the requested sampling seed.
2151    ///
2152    /// See [`crate::generation::GenerationConfig::with_seed`] for a complete example.
2153    ///
2154    pub const fn seed(&self) -> Option<u64> {
2155        self.seed
2156    }
2157
2158    #[must_use]
2159    /// Returns the requested tool-selection policy.
2160    ///
2161    /// See [`crate::generation::GenerationConfig::with_tool_choice`] for a complete example.
2162    ///
2163    pub const fn tool_choice(&self) -> Option<&ToolChoice> {
2164        self.tool_choice.as_ref()
2165    }
2166}
2167
2168impl<'de> Deserialize<'de> for GenerationConfig {
2169    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2170    where
2171        D: Deserializer<'de>,
2172    {
2173        #[derive(Deserialize, Default)]
2174        #[serde(deny_unknown_fields)]
2175        struct Wire {
2176            temperature: Option<f32>,
2177            top_p: Option<f32>,
2178            top_k: Option<NonZeroU32>,
2179            max_output_tokens: Option<NonZeroU32>,
2180            #[serde(default)]
2181            stop_sequences: Vec<String>,
2182            presence_penalty: Option<f32>,
2183            frequency_penalty: Option<f32>,
2184            seed: Option<u64>,
2185            tool_choice: Option<ToolChoice>,
2186        }
2187        let wire = Wire::deserialize(deserializer)?;
2188        let mut config = Self::default();
2189        if let Some(value) = wire.temperature {
2190            config = config
2191                .with_temperature(value)
2192                .map_err(serde::de::Error::custom)?;
2193        }
2194        if let Some(value) = wire.top_p {
2195            config = config.with_top_p(value).map_err(serde::de::Error::custom)?;
2196        }
2197        config.top_k = wire.top_k;
2198        config.max_output_tokens = wire.max_output_tokens;
2199        config = config
2200            .with_stop_sequences(wire.stop_sequences)
2201            .map_err(serde::de::Error::custom)?;
2202        if let Some(value) = wire.presence_penalty {
2203            config = config
2204                .with_presence_penalty(value)
2205                .map_err(serde::de::Error::custom)?;
2206        }
2207        if let Some(value) = wire.frequency_penalty {
2208            config = config
2209                .with_frequency_penalty(value)
2210                .map_err(serde::de::Error::custom)?;
2211        }
2212        config.seed = wire.seed;
2213        config.tool_choice = wire.tool_choice;
2214        Ok(config)
2215    }
2216}
2217
2218/// Validated model-bound generation request.
2219///
2220/// Requests borrow no provider resources and carry no model selector. The selected
2221/// Model owns exact identity and continuation type; use the same type across all
2222/// messages in this request.
2223///
2224/// # Examples
2225///
2226/// ```
2227/// use providers_kit::generation::{GenerateRequest, Message};
2228/// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2229/// assert_eq!(request.messages().len(), 1);
2230///
2231/// # Ok::<(), providers_kit::KitError>(())
2232/// ```
2233///
2234#[derive(Clone, Debug, PartialEq, Serialize)]
2235#[serde(bound(serialize = ""), deny_unknown_fields)]
2236pub struct GenerateRequest<C = GenerationContinuation> {
2237    messages: Vec<Message<C>>,
2238    tools: Vec<ProviderToolSpec>,
2239    response_format: Option<ResponseFormat>,
2240    reasoning: Option<ReasoningConfig>,
2241    generation: GenerationConfig,
2242}
2243
2244impl<C> GenerateRequest<C> {
2245    /// Creates and validates a request containing at least one ordinary message.
2246    ///
2247    /// # Errors
2248    ///
2249    /// Returns an input error when messages are empty, contain only instructions, place instructions after ordinary messages, or violate a message role/content invariant.
2250    ///
2251    /// See [`crate::generation::GenerateRequest`] for a complete example.
2252    ///
2253    pub fn new(messages: Vec<Message<C>>) -> Result<Self, KitError> {
2254        let request = Self {
2255            messages,
2256            tools: Vec::new(),
2257            response_format: None,
2258            reasoning: None,
2259            generation: GenerationConfig::default(),
2260        };
2261        request.validate()?;
2262        Ok(request)
2263    }
2264
2265    /// Replaces model-visible tool declarations.
2266    ///
2267    /// # Errors
2268    ///
2269    /// Returns an input error for duplicate function declarations or a tool selection inconsistent with the inventory. Hosted-tool and model compatibility are checked by the selected model.
2270    ///
2271    /// # Examples
2272    ///
2273    /// ```
2274    /// use providers_kit::generation::{ProviderFunctionTool, JsonSchema};
2275    /// let tool = ProviderFunctionTool::new(
2276    ///     "lookup",
2277    ///     Some("Find a record".into()),
2278    ///     JsonSchema::new(serde_json::json!({"type":"object"}))?,
2279    /// )?;
2280    /// use providers_kit::generation::{GenerateRequest, Message, ProviderToolSpec};
2281    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Look up a record")?])?
2282    ///     .with_tools(vec![ProviderToolSpec::Function(tool)])?;
2283    /// assert_eq!(request.tools().len(), 1);
2284    ///
2285    /// # Ok::<(), providers_kit::KitError>(())
2286    /// ```
2287    ///
2288    pub fn with_tools(mut self, tools: Vec<ProviderToolSpec>) -> Result<Self, KitError> {
2289        self.tools = tools;
2290        self.validate_tools()?;
2291        Ok(self)
2292    }
2293
2294    /// Replaces structured-output configuration.
2295    ///
2296    /// # Examples
2297    ///
2298    /// ```
2299    /// use providers_kit::generation::{GenerateRequest, Message};
2300    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2301    /// use providers_kit::generation::ResponseFormat;
2302    /// let request = request.with_response_format(ResponseFormat::JsonObject);
2303    /// assert!(matches!(request.response_format(), Some(ResponseFormat::JsonObject)));
2304    ///
2305    /// # Ok::<(), providers_kit::KitError>(())
2306    /// ```
2307    ///
2308    #[must_use]
2309    pub fn with_response_format(mut self, value: ResponseFormat) -> Self {
2310        self.response_format = Some(value);
2311        self
2312    }
2313
2314    /// Replaces reasoning configuration.
2315    ///
2316    /// # Examples
2317    ///
2318    /// ```
2319    /// use providers_kit::generation::{GenerateRequest, Message};
2320    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2321    /// use providers_kit::generation::{ReasoningConfig, ReasoningEffort};
2322    /// let request = request.with_reasoning(ReasoningConfig {
2323    ///     effort: Some(ReasoningEffort::Low),
2324    ///     ..Default::default()
2325    /// });
2326    /// assert_eq!(request.reasoning().unwrap().effort, Some(ReasoningEffort::Low));
2327    ///
2328    /// # Ok::<(), providers_kit::KitError>(())
2329    /// ```
2330    ///
2331    #[must_use]
2332    pub fn with_reasoning(mut self, value: ReasoningConfig) -> Self {
2333        self.reasoning = Some(value);
2334        self
2335    }
2336
2337    /// Replaces generation controls after validating tool references.
2338    ///
2339    /// # Errors
2340    ///
2341    /// Returns an input error when the generation/tool-choice settings do not match the declared tools, including an undeclared selected function or required choice without tools.
2342    ///
2343    /// # Examples
2344    ///
2345    /// ```
2346    /// use providers_kit::generation::{GenerateRequest, Message};
2347    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2348    /// use providers_kit::generation::GenerationConfig;
2349    /// let request = request.with_generation(GenerationConfig::default().with_seed(42))?;
2350    /// assert_eq!(request.generation().seed(), Some(42));
2351    ///
2352    /// # Ok::<(), providers_kit::KitError>(())
2353    /// ```
2354    ///
2355    pub fn with_generation(mut self, value: GenerationConfig) -> Result<Self, KitError> {
2356        self.generation = value;
2357        self.validate_tools()?;
2358        Ok(self)
2359    }
2360
2361    #[must_use]
2362    /// Returns validated messages in request order.
2363    ///
2364    /// See [`crate::generation::GenerateRequest`] for a complete example.
2365    ///
2366    pub fn messages(&self) -> &[Message<C>] {
2367        &self.messages
2368    }
2369
2370    #[must_use]
2371    /// Returns declared client and hosted tools.
2372    ///
2373    /// # Examples
2374    ///
2375    /// ```
2376    /// use providers_kit::generation::{GenerateRequest, Message};
2377    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2378    /// assert!(request.tools().is_empty());
2379    ///
2380    /// # Ok::<(), providers_kit::KitError>(())
2381    /// ```
2382    ///
2383    pub fn tools(&self) -> &[ProviderToolSpec] {
2384        &self.tools
2385    }
2386
2387    #[must_use]
2388    /// Returns the requested structured-output format.
2389    ///
2390    /// # Examples
2391    ///
2392    /// ```
2393    /// use providers_kit::generation::{GenerateRequest, Message};
2394    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2395    /// assert!(request.response_format().is_none());
2396    ///
2397    /// # Ok::<(), providers_kit::KitError>(())
2398    /// ```
2399    ///
2400    pub const fn response_format(&self) -> Option<&ResponseFormat> {
2401        self.response_format.as_ref()
2402    }
2403
2404    #[must_use]
2405    /// Returns the requested reasoning controls.
2406    ///
2407    /// # Examples
2408    ///
2409    /// ```
2410    /// use providers_kit::generation::{GenerateRequest, Message};
2411    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2412    /// assert!(request.reasoning().is_none());
2413    ///
2414    /// # Ok::<(), providers_kit::KitError>(())
2415    /// ```
2416    ///
2417    pub const fn reasoning(&self) -> Option<&ReasoningConfig> {
2418        self.reasoning.as_ref()
2419    }
2420
2421    #[must_use]
2422    /// Returns sampling and tool-choice controls.
2423    ///
2424    /// # Examples
2425    ///
2426    /// ```
2427    /// use providers_kit::generation::{GenerateRequest, Message};
2428    /// let request = GenerateRequest::<()>::new(vec![Message::user_text("Hello")?])?;
2429    /// assert_eq!(request.generation().seed(), None);
2430    ///
2431    /// # Ok::<(), providers_kit::KitError>(())
2432    /// ```
2433    ///
2434    pub const fn generation(&self) -> &GenerationConfig {
2435        &self.generation
2436    }
2437
2438    /// Maps only opaque continuation values while preserving validated request data.
2439    ///
2440    /// This is value conversion for caller-owned concrete composition. It does not
2441    /// inspect a supplied continuation or authorize dropping its replay information.
2442    ///
2443    /// # Errors
2444    ///
2445    /// Returns the first callback error unchanged. Messages without continuation do not invoke the callback.
2446    ///
2447    /// # Examples
2448    ///
2449    /// ```
2450    /// use providers_kit::generation::{GenerateRequest, Message};
2451    /// let assistant = Message::assistant_text("continue")?.with_continuation(7_u32)?;
2452    /// let request = GenerateRequest::new(vec![assistant])?;
2453    /// let mapped = request.try_map_continuation(|value| Ok(value.to_string()))?;
2454    /// assert_eq!(mapped.messages()[0].continuation().map(String::as_str), Some("7"));
2455    ///
2456    /// # Ok::<(), providers_kit::KitError>(())
2457    /// ```
2458    ///
2459    pub fn try_map_continuation<D>(
2460        self,
2461        mut map: impl FnMut(C) -> Result<D, KitError>,
2462    ) -> Result<GenerateRequest<D>, KitError> {
2463        Ok(GenerateRequest {
2464            messages: self
2465                .messages
2466                .into_iter()
2467                .map(|message| message.try_map_continuation(&mut map))
2468                .collect::<Result<_, _>>()?,
2469            tools: self.tools,
2470            response_format: self.response_format,
2471            reasoning: self.reasoning,
2472            generation: self.generation,
2473        })
2474    }
2475
2476    fn validate(&self) -> Result<(), KitError> {
2477        if self.messages.is_empty() {
2478            return Err(KitError::invalid_input(
2479                ValidationViolation::Empty {
2480                    field: "generation.messages",
2481                },
2482                "generation request messages must not be empty",
2483            ));
2484        }
2485        let mut saw_ordinary = false;
2486        for message in &self.messages {
2487            match message.role() {
2488                crate::generation::Role::System | crate::generation::Role::Developer
2489                    if saw_ordinary =>
2490                {
2491                    return Err(KitError::invalid_input(
2492                        ValidationViolation::InvalidValue {
2493                            field: "generation.messages",
2494                            rule: "instructions must form a leading prefix",
2495                        },
2496                        "system and developer instructions must form one leading prefix",
2497                    ));
2498                }
2499                crate::generation::Role::System | crate::generation::Role::Developer => {}
2500                _ => saw_ordinary = true,
2501            }
2502            message.validate_for_request()?;
2503        }
2504        if !saw_ordinary {
2505            return Err(KitError::invalid_input(
2506                ValidationViolation::InvalidValue {
2507                    field: "generation.messages",
2508                    rule: "requires a non-instruction message",
2509                },
2510                "generation request requires at least one non-instruction message",
2511            ));
2512        }
2513        self.validate_tools()
2514    }
2515
2516    fn validate_tools(&self) -> Result<(), KitError> {
2517        let mut names = BTreeSet::new();
2518        for tool in &self.tools {
2519            if let ProviderToolSpec::Function(function) = tool
2520                && !names.insert(function.name())
2521            {
2522                return Err(KitError::invalid_input(
2523                    ValidationViolation::Duplicate {
2524                        field: "tools.function.name",
2525                    },
2526                    format!("duplicate function tool name `{}`", function.name()),
2527                ));
2528            }
2529        }
2530        selected_tools_for_choice(&self.tools, self.generation.tool_choice())?;
2531        Ok(())
2532    }
2533
2534    #[must_use]
2535    pub(crate) fn as_view(&self) -> GenerateRequestView<'_, C> {
2536        GenerateRequestView {
2537            messages: &self.messages,
2538            tools: &self.tools,
2539            response_format: self.response_format.as_ref(),
2540            reasoning: self.reasoning.as_ref(),
2541            generation: &self.generation,
2542        }
2543    }
2544}
2545
2546impl<'de, C> Deserialize<'de> for GenerateRequest<C> {
2547    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2548    where
2549        D: Deserializer<'de>,
2550    {
2551        #[derive(Deserialize)]
2552        #[serde(bound(deserialize = ""), deny_unknown_fields)]
2553        struct Wire<C> {
2554            messages: Vec<Message<C>>,
2555            #[serde(default)]
2556            tools: Vec<ProviderToolSpec>,
2557            response_format: Option<ResponseFormat>,
2558            reasoning: Option<ReasoningConfig>,
2559            #[serde(default)]
2560            generation: GenerationConfig,
2561        }
2562        let wire = Wire::deserialize(deserializer)?;
2563        let mut request = Self::new(wire.messages).map_err(serde::de::Error::custom)?;
2564        request.tools = wire.tools;
2565        request.response_format = wire.response_format;
2566        request.reasoning = wire.reasoning;
2567        request.generation = wire.generation;
2568        request.validate().map_err(serde::de::Error::custom)?;
2569        Ok(request)
2570    }
2571}
2572
2573/// Portable completion reason.
2574#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
2575#[serde(rename_all = "snake_case")]
2576pub enum FinishReason {
2577    /// The model reached a natural stop.
2578    Stop,
2579    /// The configured or provider output limit was reached.
2580    Length,
2581    /// The model stopped to request a function call.
2582    ToolCall,
2583    /// Provider safety filtering stopped output.
2584    ContentFilter,
2585    /// A provider-authored terminal error stopped output.
2586    Error,
2587    /// Generation paused for provider-managed continuation.
2588    Paused,
2589}
2590
2591/// Citation attached to generated output.
2592#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2593#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
2594pub enum Citation {
2595    /// Citation of an external URL.
2596    Url {
2597        /// Cited URL.
2598        url: String,
2599        /// Provider-reported title.
2600        title: Option<String>,
2601        /// Provider-reported excerpt.
2602        snippet: Option<String>,
2603        /// Inclusive output start index, when reported.
2604        start_index: Option<usize>,
2605        /// Exclusive output end index, when reported.
2606        end_index: Option<usize>,
2607    },
2608    /// Citation of a provider-managed or remote file.
2609    File {
2610        /// Exact cited file reference.
2611        reference: FileReference,
2612        /// Provider-reported title.
2613        title: Option<String>,
2614        /// Provider-reported excerpt.
2615        snippet: Option<String>,
2616        /// Inclusive output start index, when reported.
2617        start_index: Option<usize>,
2618        /// Exclusive output end index, when reported.
2619        end_index: Option<usize>,
2620    },
2621}
2622
2623/// Final normalized generation result.
2624///
2625/// A returned assistant message is the replay owner. Preserve it unchanged through
2626/// function continuation; projections of text, hosted results, or artifacts cannot
2627/// reconstruct native state. Serialization fails while continuation is present.
2628///
2629/// # Examples
2630///
2631/// ```
2632/// use providers_kit::{ModelId, generation::{GenerateResponse, Message, FinishReason, Usage}};
2633/// let response = GenerateResponse {
2634///     model: ModelId::new("exact-model")?,
2635///     message: Message::assistant_text("done")?.with_continuation(7_u32)?,
2636///     finish_reason: Some(FinishReason::Stop),
2637///     citations: Vec::new(),
2638///     artifacts: Vec::new(),
2639///     hosted_tool_outputs: Vec::new(),
2640///     usage: Usage::default(),
2641/// };
2642/// let mapped = response.try_map_continuation(|value| Ok(value.to_string()))?;
2643/// assert_eq!(mapped.message.continuation().map(String::as_str), Some("7"));
2644/// assert_eq!(mapped.model.as_str(), "exact-model");
2645///
2646/// # Ok::<(), providers_kit::KitError>(())
2647/// ```
2648///
2649#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2650#[serde(bound(serialize = "", deserialize = ""))]
2651pub struct GenerateResponse<C = GenerationContinuation> {
2652    /// Exact model that produced the response.
2653    pub model: ModelId,
2654    /// Returned assistant message and opaque continuation state.
2655    pub message: Message<C>,
2656    /// Provider-reported completion reason.
2657    pub finish_reason: Option<FinishReason>,
2658    /// Citations attached to generated output.
2659    pub citations: Vec<Citation>,
2660    /// Generated artifacts.
2661    pub artifacts: Vec<Artifact>,
2662    /// Normalized provider-hosted tool outputs.
2663    pub hosted_tool_outputs: Vec<HostedToolOutput>,
2664    /// Provider-reported token accounting.
2665    pub usage: Usage,
2666}
2667
2668impl<C> GenerateResponse<C> {
2669    /// Maps only opaque continuation values.
2670    ///
2671    /// # Errors
2672    ///
2673    /// Returns the callback error unchanged if converting the response continuation fails; other response fields retain their exact values.
2674    ///
2675    /// See [`crate::generation::GenerateResponse`] for a complete example.
2676    ///
2677    pub fn try_map_continuation<D>(
2678        self,
2679        mut map: impl FnMut(C) -> Result<D, KitError>,
2680    ) -> Result<GenerateResponse<D>, KitError> {
2681        Ok(GenerateResponse {
2682            model: self.model,
2683            message: self.message.try_map_continuation(&mut map)?,
2684            finish_reason: self.finish_reason,
2685            citations: self.citations,
2686            artifacts: self.artifacts,
2687            hosted_tool_outputs: self.hosted_tool_outputs,
2688            usage: self.usage,
2689        })
2690    }
2691}
2692
2693/// Incremental generation event.
2694#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2695#[serde(
2696    bound(serialize = "", deserialize = ""),
2697    tag = "event",
2698    rename_all = "snake_case"
2699)]
2700pub enum GenerateEvent<C = GenerationContinuation> {
2701    /// Stream metadata is ready.
2702    Started {
2703        /// Exact model producing the stream.
2704        model: ModelId,
2705    },
2706    /// Incremental visible text.
2707    ContentDelta {
2708        /// Text appended by this event.
2709        delta: String,
2710    },
2711    /// Incremental provider-authored thinking summary.
2712    ThinkingDelta {
2713        /// Thinking text appended by this event.
2714        delta: String,
2715    },
2716    /// Incremental provider-hosted tool activity.
2717    HostedTool(Box<HostedToolOutput>),
2718    /// Updated provider usage accounting.
2719    Usage(Usage),
2720    /// Terminal normalized response.
2721    Completed(Box<GenerateResponse<C>>),
2722}
2723
2724impl<C> GenerateEvent<C> {
2725    /// Maps continuation state carried by a completion event.
2726    ///
2727    /// # Errors
2728    ///
2729    /// Returns the callback error for a completion continuation. Other events retain their data without invoking the callback.
2730    ///
2731    /// # Examples
2732    ///
2733    /// ```
2734    /// use providers_kit::{ModelId, generation::{GenerateResponse, Message, FinishReason, Usage}};
2735    /// let response = GenerateResponse {
2736    ///     model: ModelId::new("exact-model")?,
2737    ///     message: Message::assistant_text("done")?.with_continuation(7_u32)?,
2738    ///     finish_reason: Some(FinishReason::Stop),
2739    ///     citations: Vec::new(),
2740    ///     artifacts: Vec::new(),
2741    ///     hosted_tool_outputs: Vec::new(),
2742    ///     usage: Usage::default(),
2743    /// };
2744    /// use providers_kit::generation::GenerateEvent;
2745    /// let event = GenerateEvent::Completed(Box::new(response));
2746    /// let mapped = event.try_map_continuation(|value| Ok(value.to_string()))?;
2747    /// assert!(
2748    ///     matches!(mapped, GenerateEvent::Completed(response) if response.message.continuation().map(String::as_str) == Some("7"))
2749    /// );
2750    ///
2751    /// # Ok::<(), providers_kit::KitError>(())
2752    /// ```
2753    ///
2754    pub fn try_map_continuation<D>(
2755        self,
2756        map: impl FnMut(C) -> Result<D, KitError>,
2757    ) -> Result<GenerateEvent<D>, KitError> {
2758        Ok(match self {
2759            Self::Started { model } => GenerateEvent::Started { model },
2760            Self::ContentDelta { delta } => GenerateEvent::ContentDelta { delta },
2761            Self::ThinkingDelta { delta } => GenerateEvent::ThinkingDelta { delta },
2762            Self::HostedTool(output) => GenerateEvent::HostedTool(output),
2763            Self::Usage(usage) => GenerateEvent::Usage(usage),
2764            Self::Completed(response) => {
2765                GenerateEvent::Completed(Box::new(response.try_map_continuation(map)?))
2766            }
2767        })
2768    }
2769}
2770
2771/// Value-erased stream of normalized generation events.
2772///
2773/// This value adapter does not enforce a backend protocol. Caller Models own
2774/// bounded production, terminal ordering, admission release and cancellation.
2775/// Supplied model streams produce no events after Completed and own no forwarding task.
2776///
2777/// # Examples
2778///
2779/// ```
2780///
2781/// # #[tokio::main(flavor = "current_thread")]
2782/// # async fn main() -> Result<(), providers_kit::KitError> {
2783/// use providers_kit::generation::{GenerateEvent, GenerateEventStream};
2784/// use futures_util::{stream, StreamExt};
2785/// let source = stream::iter([Ok(GenerateEvent::<()>::ContentDelta { delta: "hello".into() })]);
2786/// let mut stream = GenerateEventStream::new(source);
2787/// assert!(
2788///     matches!(stream.next().await.transpose()?, Some(GenerateEvent::ContentDelta { delta }) if delta == "hello")
2789/// );
2790/// assert!(stream.next().await.is_none());
2791///
2792/// # Ok(())
2793/// # }
2794/// ```
2795///
2796pub struct GenerateEventStream<C = GenerationContinuation> {
2797    inner: Pin<Box<dyn Stream<Item = Result<GenerateEvent<C>, KitError>> + Send + 'static>>,
2798}
2799
2800impl<C: 'static> GenerateEventStream<C> {
2801    /// Wraps a concrete event stream without erasing model behavior.
2802    ///
2803    /// This value adapter does not enforce a backend protocol. Caller Models own
2804    /// bounded production, terminal ordering, admission release and cancellation.
2805    /// Supplied model streams produce no events after Completed and own no forwarding task.
2806    ///
2807    /// See [`crate::generation::GenerateEventStream`] for a complete example.
2808    ///
2809    pub fn new<S>(stream: S) -> Self
2810    where
2811        S: Stream<Item = Result<GenerateEvent<C>, KitError>> + Send + 'static,
2812    {
2813        Self {
2814            inner: Box::pin(stream),
2815        }
2816    }
2817}
2818
2819impl<C> Stream for GenerateEventStream<C> {
2820    type Item = Result<GenerateEvent<C>, KitError>;
2821
2822    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
2823        self.inner.as_mut().poll_next(cx)
2824    }
2825}
2826
2827/// Token accounting reported by a provider response.
2828///
2829/// Fields stay `None` when the provider did not report that exact value.
2830/// The Kit does not derive aggregate totals or request counts from other
2831/// fields.
2832///
2833/// # Examples
2834/// ```rust
2835/// let usage = providers_kit::generation::Usage {
2836///     input_tokens: Some(12),
2837///     output_tokens: Some(34),
2838///     reasoning_tokens: None,
2839///     cached_input_tokens: Some(5),
2840///     total_tokens: Some(46),
2841/// };
2842/// assert_eq!(usage.cached_input_tokens, Some(5));
2843/// assert_eq!(usage.total_tokens, Some(46));
2844/// ```
2845#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
2846pub struct Usage {
2847    /// Input tokens billed or counted for the request.
2848    pub input_tokens: Option<u64>,
2849    /// Output tokens generated by the provider.
2850    pub output_tokens: Option<u64>,
2851    /// Reasoning or thinking tokens reported separately by the provider.
2852    pub reasoning_tokens: Option<u64>,
2853    /// Input tokens served from prompt cache rather than recomputed.
2854    pub cached_input_tokens: Option<u64>,
2855    /// Total token count when the provider reports an aggregate value.
2856    pub total_tokens: Option<u64>,
2857}
2858
2859impl Usage {
2860    pub(crate) fn reported_tokens(input_tokens: Option<u64>, output_tokens: Option<u64>) -> Self {
2861        Self {
2862            input_tokens,
2863            output_tokens,
2864            ..Self::default()
2865        }
2866    }
2867
2868    pub(crate) fn with_reported_total(mut self, total_tokens: Option<u64>) -> Self {
2869        self.total_tokens = total_tokens;
2870        self
2871    }
2872
2873    pub(crate) fn with_cached_input(mut self, cached_input_tokens: Option<u64>) -> Self {
2874        self.cached_input_tokens = cached_input_tokens;
2875        self
2876    }
2877
2878    pub(crate) fn with_reasoning(mut self, reasoning_tokens: Option<u64>) -> Self {
2879        self.reasoning_tokens = reasoning_tokens;
2880        self
2881    }
2882}