use std::collections::BTreeMap;
use schemars::{JsonSchema, schema_for};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{ExtensionMap, Message};
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct ModelRef {
pub provider: String,
pub name: String,
}
impl ModelRef {
pub fn new(provider: impl Into<String>, name: impl Into<String>) -> Self {
Self {
provider: provider.into(),
name: name.into(),
}
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum FeaturePolicy {
#[default]
Strict,
AllowEmulation,
BestEffort,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct GenerationOptions {
pub temperature: Option<f64>,
pub top_p: Option<f64>,
pub max_output_tokens: Option<u64>,
pub seed: Option<u64>,
pub stop: Vec<String>,
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum OutputFormat {
#[default]
Text,
Json,
JsonSchema {
name: String,
schema: Value,
strict: bool,
},
}
impl OutputFormat {
pub fn typed<T>(name: impl Into<String>) -> Self
where
T: JsonSchema,
{
Self::JsonSchema {
name: name.into(),
schema: schema_for!(T).to_value(),
strict: true,
}
}
pub fn typed_with_strictness<T>(name: impl Into<String>, strict: bool) -> Self
where
T: JsonSchema,
{
Self::JsonSchema {
name: name.into(),
schema: schema_for!(T).to_value(),
strict,
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ToolSpec {
pub name: String,
pub description: String,
pub input_schema: Value,
pub output_schema: Option<Value>,
pub metadata: ExtensionMap,
}
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum ToolChoice {
#[default]
Auto,
None,
Required,
Named {
name: String,
},
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ModelRequest {
pub model: ModelRef,
pub messages: Vec<Message>,
pub tools: Vec<ToolSpec>,
pub tool_choice: ToolChoice,
pub output_format: OutputFormat,
pub generation: GenerationOptions,
pub feature_policy: FeaturePolicy,
pub provider_options: BTreeMap<String, Value>,
pub metadata: ExtensionMap,
}
impl ModelRequest {
pub fn new(model: ModelRef, message: Message) -> Self {
Self {
model,
messages: vec![message],
tools: Vec::new(),
tool_choice: ToolChoice::Auto,
output_format: OutputFormat::Text,
generation: GenerationOptions::default(),
feature_policy: FeaturePolicy::Strict,
provider_options: BTreeMap::new(),
metadata: BTreeMap::new(),
}
}
#[must_use]
pub fn message(mut self, message: Message) -> Self {
self.messages.push(message);
self
}
#[must_use]
pub fn tool(mut self, tool: ToolSpec) -> Self {
self.tools.push(tool);
self
}
#[must_use]
pub fn output_format(mut self, output_format: OutputFormat) -> Self {
self.output_format = output_format;
self
}
#[must_use]
pub fn structured_output<T>(self, name: impl Into<String>) -> Self
where
T: JsonSchema,
{
self.output_format(OutputFormat::typed::<T>(name))
}
#[must_use]
pub const fn feature_policy(mut self, feature_policy: FeaturePolicy) -> Self {
self.feature_policy = feature_policy;
self
}
}