use std::sync::Arc;
use ikigai_core::EndpointSpace;
use crate::config;
pub fn space() -> EndpointSpace {
ikigai_llm::space(Arc::new(UreqTransport), registry())
}
fn registry() -> ikigai_llm::Registry {
let path = config::config_dir().join("llm.json");
match std::fs::read_to_string(&path) {
Ok(json) => ikigai_llm::Registry::from_json(&json).unwrap_or_else(|e| {
panic!(
"ikigai-dev: {} parse error: {e:?} — fix the file (explanations \
derived against a silently-defaulted model would be mislabeled)",
path.display()
)
}),
Err(_) => {
let mut ollama = ikigai_llm::OpenAiConfig::ollama("llama3.2:3b");
ollama.caps.context = Some(131_072);
ollama.caps.modalities = vec!["text".to_string()];
ollama.caps.params = Some("3B".to_string());
ikigai_llm::Registry::single(ollama)
}
}
}
struct UreqTransport;
#[async_trait::async_trait]
impl ikigai_http::HttpTransport for UreqTransport {
async fn send(
&self,
request: ikigai_http::HttpRequest,
) -> std::result::Result<ikigai_http::HttpResponse, String> {
use std::io::Read;
let agent = ureq::builder().redirects(0).build();
let mut req = agent.request(request.method.as_str(), &request.url);
for (name, value) in &request.headers {
req = req.set(name, value);
}
let outcome = if request.body.is_empty() {
req.call()
} else {
req.send_bytes(&request.body)
};
let resp = match outcome {
Ok(resp) => resp,
Err(ureq::Error::Status(_, resp)) => resp,
Err(e) => return Err(e.to_string()),
};
let status = resp.status();
let headers = resp
.headers_names()
.into_iter()
.filter_map(|name| resp.header(&name).map(|v| (name.clone(), v.to_string())))
.collect();
let mut body = Vec::new();
if request.method != ikigai_http::Method::Head {
resp.into_reader()
.read_to_end(&mut body)
.map_err(|e| format!("reading response body: {e}"))?;
}
Ok(ikigai_http::HttpResponse {
status,
headers,
body,
})
}
}