asimov_ollama_module/
lib.rs1#![no_std]
4#![forbid(unsafe_code)]
5
6use asimov_module::{prelude::*, tracing};
7use core::error::Error;
8use serde_json::{Value, json};
9
10#[derive(Clone, Debug, bon::Builder)]
11#[builder(on(String, into))]
12pub struct Options {
13 #[builder(default = "http://localhost:11434")]
14 pub endpoint: String,
15
16 pub model: String,
17}
18
19pub fn generate(input: impl AsRef<str>, options: &Options) -> Result<Vec<String>, Box<dyn Error>> {
20 let req = json!({
21 "model": options.model,
22 "prompt": input.as_ref(),
23 "stream": false
24 });
25
26 let mut resp = ureq::Agent::config_builder()
27 .http_status_as_error(false)
28 .user_agent("asimov-ollama-module")
29 .build()
30 .new_agent()
31 .post(format!("{}/api/generate", options.endpoint))
32 .header("content-type", "application/json")
33 .send_json(&req)
34 .inspect_err(|e| tracing::error!("HTTP request failed: {e}"))?;
35 tracing::debug!(response = ?resp);
36
37 let status = resp.status();
38 tracing::debug!(status = status.to_string());
39
40 let resp: Value = resp
41 .body_mut()
42 .read_json()
43 .inspect_err(|e| tracing::error!("unable to read HTTP response body: {e}"))?;
44 tracing::debug!(body = ?resp);
45
46 if !status.is_success() {
47 tracing::error!("Received an error response: {status}");
48
49 if let Some(message) = resp["error"].as_str() {
53 return Err(message.into());
54 }
55 }
56
57 let mut responses = Vec::new();
73
74 if let Some(response) = resp["response"].as_str() {
75 responses.push(response.to_string())
76 }
77
78 if let Some(done_reason) = resp["done_reason"].as_str() {
79 tracing::debug!(done_reason)
80 }
81
82 Ok(responses)
83}