//! Outbound request DTOs for the OpenAI/Anthropic-compatible chat completions API. //! //! Flow: `harness`/`runtime` builds a `ChatRequest` from conversation state and //! the active tool set → serializes to JSON via `serde` → sends to the //! provider's `/chat/completions`-style endpoint (streaming or not). //! //! Why: fields mirror the wire format exactly (including `#[serde(rename)]` //! for reserved words like `type`) so no manual (de)serialization glue is //! needed; optional fields use `skip_serializing_if` so unset knobs are //! omitted rather than sent as `null`, matching provider expectations. use serde::{Deserialize, Serialize}; use serde_json::Value; /// Outbound chat completion request body sent to an OpenAI/Anthropic-compatible provider. /// /// Flow: constructed from the current message history plus optional /// generation knobs (temperature, `max_tokens`, tools, etc.) and serialized /// directly into the HTTP request body. /// /// Return: not a function, but the value that becomes the JSON request /// payload for a completion call. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChatRequest { pub model: String, pub messages: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub max_tokens: Option, #[serde(skip_serializing_if = "Option::is_none")] pub temperature: Option, #[serde(skip_serializing_if = "Option::is_none")] pub tools: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub stream: Option, #[serde(skip_serializing_if = "Option::is_none")] pub top_p: Option, #[serde(skip_serializing_if = "Option::is_none")] pub stop: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub stream_options: Option, } /// Streaming options for the request; `include_usage` asks the provider to /// emit a final usage chunk in the SSE stream. /// /// Why: usage tokens are otherwise unavailable in a streamed response since /// they're normally only attached to the final non-streamed completion. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StreamOptions { pub include_usage: bool, } /// Wire format for a single tool definition sent to the provider. /// /// Flow: built from the harness's registered `Tool` impls (see `all_tools()`) /// and attached to `ChatRequest.tools` so the model knows which functions it /// may call. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolDef { #[serde(rename = "type")] pub type_: String, pub function: ToolFunctionDef, } /// Name, description, and JSON schema parameters for a tool definition. /// /// Why: `parameters` is a raw `serde_json::Value` rather than a typed struct /// because each tool defines its own arbitrary JSON schema. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolFunctionDef { pub name: String, pub description: String, pub parameters: Value, }