use saya_config::{AiProvider, ResolvedAi};
use thiserror::Error;
#[derive(Debug, Error)]
pub(crate) enum AgentRuntimeError {
#[error("{0}")]
Provider(String),
#[error("{0}")]
Database(String),
#[error("{0}")]
Agent(String),
#[error("{0}")]
Configuration(String),
}
#[derive(Debug, Clone, Default, PartialEq)]
pub(crate) struct PromptOverrides {
pub(crate) provider: Option<AiProvider>,
pub(crate) model: Option<String>,
pub(crate) allow_data_sharing: Option<bool>,
pub(crate) profile: Option<String>,
pub(crate) included_profiles: Vec<String>,
pub(crate) endpoint: Option<String>,
pub(crate) endpoint_bound: bool,
}
#[cfg(test)]
pub(crate) fn query_data_allowed(provider: AiProvider, allow_data_sharing: bool) -> bool {
query_data_allowed_for_endpoint(provider, None, allow_data_sharing)
}
pub(crate) fn query_data_allowed_for_endpoint(
provider: AiProvider,
base_url: Option<&str>,
allow_data_sharing: bool,
) -> bool {
allow_data_sharing || is_local_endpoint(provider, base_url)
}
pub(crate) fn is_local_endpoint(provider: AiProvider, base_url: Option<&str>) -> bool {
match provider {
AiProvider::Ollama => base_url.is_none_or(|value| {
let Ok(url) = url::Url::parse(value) else {
return false;
};
if url.username() != ""
|| url.password().is_some()
|| url.query().is_some()
|| url.fragment().is_some()
{
return false;
}
let Some(host) = url.host_str() else {
return false;
};
host.eq_ignore_ascii_case("localhost")
|| host
.parse::<std::net::IpAddr>()
.is_ok_and(|address| address.is_loopback())
}),
AiProvider::Openai
| AiProvider::OpenaiCompatible
| AiProvider::Anthropic
| AiProvider::Gemini => false,
}
}
pub(crate) fn effective_ai(base: &ResolvedAi, overrides: &PromptOverrides) -> ResolvedAi {
let mut ai = base.clone();
if let Some(provider) = overrides.provider {
if ai.provider != provider {
ai.base_url = None;
}
ai.provider = provider;
}
if overrides.endpoint_bound {
ai.base_url = overrides.endpoint.clone();
}
if let Some(model) = overrides.model.as_ref() {
ai.model = model.clone();
}
if let Some(value) = overrides.allow_data_sharing {
ai.allow_data_sharing = value;
}
ai
}