providers_kit/config/ollama.rs
1use url::Url;
2
3use super::ApiKey;
4use crate::KitError;
5use crate::error::ValidationViolation;
6
7const DEFAULT_OLLAMA_LOCAL_BASE_URL: &str = "http://localhost:11434/api";
8const DEFAULT_OLLAMA_CLOUD_BASE_URL: &str = "https://ollama.com/api";
9
10/// Ollama local or cloud configuration.
11///
12/// Construction is passive and does not verify a running local or cloud service.
13///
14/// # Examples
15///
16/// ```
17/// use providers_kit::config::{ApiKey, OllamaConfig};
18/// let config = OllamaConfig::local();
19/// let _config = config.with_base_url("http://127.0.0.1:11434/api".parse().unwrap())?;
20///
21/// # Ok::<(), providers_kit::KitError>(())
22/// ```
23///
24#[derive(Clone)]
25pub struct OllamaConfig {
26 base_url: Url,
27 api_key: Option<ApiKey>,
28}
29
30impl OllamaConfig {
31 /// Creates an unauthenticated local Ollama configuration.
32 ///
33 /// ```
34 /// let config = providers_kit::config::OllamaConfig::local();
35 /// let _ = config;
36 /// ```
37 #[must_use]
38 pub fn local() -> Self {
39 Self {
40 base_url: Url::parse(DEFAULT_OLLAMA_LOCAL_BASE_URL)
41 .expect("crate-owned local Ollama base URL should parse"),
42 api_key: None,
43 }
44 }
45
46 /// Creates an authenticated local or proxy configuration.
47 ///
48 /// # Examples
49 ///
50 /// ```
51 /// use providers_kit::config::{ApiKey, OllamaConfig};
52 /// let config = OllamaConfig::local_with_key(ApiKey::new("example-key")?);
53 /// let _config = config.with_base_url("http://127.0.0.1:11434/api".parse().unwrap())?;
54 ///
55 /// # Ok::<(), providers_kit::KitError>(())
56 /// ```
57 ///
58 #[must_use]
59 pub fn local_with_key(api_key: ApiKey) -> Self {
60 Self {
61 base_url: Url::parse(DEFAULT_OLLAMA_LOCAL_BASE_URL)
62 .expect("crate-owned local Ollama base URL should parse"),
63 api_key: Some(api_key),
64 }
65 }
66
67 /// Creates an Ollama Cloud configuration.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use providers_kit::config::{ApiKey, OllamaConfig};
73 /// let config = OllamaConfig::cloud(ApiKey::new("example-key")?);
74 /// let _config = config.with_base_url("http://127.0.0.1:11434/api".parse().unwrap())?;
75 ///
76 /// # Ok::<(), providers_kit::KitError>(())
77 /// ```
78 ///
79 #[must_use]
80 pub fn cloud(api_key: ApiKey) -> Self {
81 Self {
82 base_url: Url::parse(DEFAULT_OLLAMA_CLOUD_BASE_URL)
83 .expect("crate-owned Ollama Cloud base URL should parse"),
84 api_key: Some(api_key),
85 }
86 }
87
88 /// Replaces the API base URL.
89 ///
90 /// # Errors
91 ///
92 /// Returns a configuration error unless the URL path is exactly `/api`. The URL is not contacted or checked for endpoint capabilities here.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// use providers_kit::config::OllamaConfig;
98 /// assert!(OllamaConfig::local().with_base_url("http://localhost:11434/".parse().unwrap()).is_err());
99 /// let _config = OllamaConfig::local().with_base_url("http://localhost:11434/api".parse().unwrap())?;
100 ///
101 /// # Ok::<(), providers_kit::KitError>(())
102 /// ```
103 ///
104 pub fn with_base_url(mut self, base_url: Url) -> Result<Self, KitError> {
105 if base_url.path() != "/api" {
106 return Err(KitError::config(
107 ValidationViolation::InvalidValue {
108 field: "base_url.path",
109 rule: "exact /api path",
110 },
111 "Ollama base URL must point at the exact /api path",
112 ));
113 }
114 self.base_url = base_url;
115 Ok(self)
116 }
117
118 pub(crate) fn base_url(&self) -> &Url {
119 &self.base_url
120 }
121
122 pub(crate) fn api_key(&self) -> Option<&ApiKey> {
123 self.api_key.as_ref()
124 }
125}