use std::collections::BTreeMap;
use serde::Deserialize;
use serde_json::{Map, Value};
use crate::canonical::{Content, ReasoningEffort};
use crate::store::Secret;
mod row;
pub use row::PartialProvider;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum OutMode {
Text,
Ndjson,
Raw,
}
impl OutMode {
pub fn parse(s: &str) -> Option<OutMode> {
match s {
"text" => Some(OutMode::Text),
"ndjson" => Some(OutMode::Ndjson),
"raw" => Some(OutMode::Raw),
_ => None,
}
}
}
#[derive(Default, Clone, Debug, PartialEq)]
pub struct PartialConfig {
pub provider: Option<String>,
pub default_provider: Option<String>,
pub model: Option<String>,
pub base_url: Option<String>,
pub api_key: Option<Secret>,
pub output: Option<OutMode>,
pub raw_in: Option<bool>,
pub thinking: Option<bool>,
pub max_tokens: Option<u32>,
pub temperature: Option<f32>,
pub top_p: Option<f32>,
pub reasoning: Option<ReasoningEffort>,
pub stream: Option<bool>,
pub timeout: Option<u64>,
pub system: Option<Vec<Content>>,
pub providers: BTreeMap<String, PartialProvider>,
pub extra: Map<String, Value>,
}
impl PartialConfig {
pub fn or(self, other: PartialConfig) -> PartialConfig {
PartialConfig {
provider: self.provider.or(other.provider),
default_provider: self.default_provider.or(other.default_provider),
model: self.model.or(other.model),
base_url: self.base_url.or(other.base_url),
api_key: self.api_key.or(other.api_key),
output: self.output.or(other.output),
raw_in: self.raw_in.or(other.raw_in),
thinking: self.thinking.or(other.thinking),
max_tokens: self.max_tokens.or(other.max_tokens),
temperature: self.temperature.or(other.temperature),
top_p: self.top_p.or(other.top_p),
reasoning: self.reasoning.or(other.reasoning),
stream: self.stream.or(other.stream),
timeout: self.timeout.or(other.timeout),
system: self.system.or(other.system),
providers: merge_providers(self.providers, other.providers),
extra: or_map(self.extra, other.extra),
}
}
}
fn merge_providers(
mut hi: BTreeMap<String, PartialProvider>,
lo: BTreeMap<String, PartialProvider>,
) -> BTreeMap<String, PartialProvider> {
for (key, lo_row) in lo {
let merged = match hi.remove(&key) {
Some(hi_row) => hi_row.or(lo_row),
None => lo_row,
};
hi.insert(key, merged);
}
hi
}
pub(crate) fn or_map(mut hi: Map<String, Value>, lo: Map<String, Value>) -> Map<String, Value> {
for (key, value) in lo {
hi.entry(key).or_insert(value);
}
hi
}