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