use std::net::SocketAddr;
use std::path::Path;
use super::{
Config, DeviceKind, EndpointConfig, LocalModelConfig, RawConfig, Secret, WebSearchConfig,
interpolate_value,
};
use crate::error::ConfigError;
impl Config {
pub fn load(path: &Path) -> Result<Config, crate::api_error::ConfigError> {
crate::profile::load_path(path).map_err(crate::api_error::ConfigError::from)
}
pub fn load_profile(
dir: &Path,
name: &crate::profile::ProfileName,
) -> Result<Config, crate::api_error::ConfigError> {
crate::profile::load_named(dir, name).map_err(crate::api_error::ConfigError::from)
}
#[must_use]
pub(crate) fn bind_addr(&self) -> SocketAddr {
self.server.bind
}
#[must_use]
pub(crate) fn server_key(&self) -> Secret {
self.server.key.clone()
}
#[must_use]
pub(crate) fn web_search_config(&self) -> Option<&WebSearchConfig> {
self.tools
.as_ref()
.and_then(|tools| tools.web_search.as_ref())
}
#[must_use]
pub(crate) fn endpoint_concurrency(&self, endpoint: &EndpointConfig) -> Option<usize> {
if let Some(n) = endpoint.concurrency {
return Some(n);
}
let device_id = endpoint.device.as_deref()?;
self.devices
.iter()
.find(|d| d.id == device_id)
.and_then(|d| d.concurrency)
}
pub(crate) fn local_model_concurrency(
&self,
model: &LocalModelConfig,
) -> Result<usize, ConfigError> {
match (&model.device, &model.lane) {
(None, None) => Ok(1),
(Some(device_id), Some(lane_id)) => {
let device = self
.devices
.iter()
.find(|d| d.id == *device_id)
.ok_or_else(|| {
ConfigError::Validation(format!(
"local_model {} names undefined device {device_id}",
model.name
))
})?;
if device.kind != DeviceKind::Local {
return Err(ConfigError::Validation(format!(
"local_model {} must reference a local device, but {device_id} is remote",
model.name
)));
}
let lane = device
.lanes
.iter()
.find(|l| l.id == *lane_id)
.ok_or_else(|| {
ConfigError::Validation(format!(
"local_model {} names undefined lane {lane_id} on device {device_id}",
model.name
))
})?;
if lane.concurrency < 1 {
return Err(ConfigError::Validation(format!(
"device {device_id} lane {lane_id} concurrency must be at least 1"
)));
}
Ok(lane.concurrency)
}
_ => Err(ConfigError::Validation(format!(
"local_model {} must set both device and lane, or neither",
model.name
))),
}
}
pub fn from_toml_str(raw: &str) -> Result<Config, crate::api_error::ConfigError> {
Self::parse_toml(raw).map_err(crate::api_error::ConfigError::from)
}
pub(crate) fn parse_toml(raw: &str) -> Result<Config, ConfigError> {
let document: toml::Value = toml::from_str(raw).map_err(|source| ConfigError::Parse {
path: None,
source: Box::new(source),
})?;
Self::from_value(document)
}
pub(crate) fn from_value(mut document: toml::Value) -> Result<Config, ConfigError> {
interpolate_value(&mut document)?;
let raw: RawConfig = document.try_into().map_err(|source| ConfigError::Parse {
path: None,
source: Box::new(source),
})?;
let config = Config::from(raw);
config.validate()?;
Ok(config)
}
}