Skip to main content

oai_sdk/
model.rs

1// Copyright 2026 Cloudflavor GmbH
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::chat::Message as ChatMessage;
16use crate::client::ModelClient;
17use crate::client::handle_error_response;
18use crate::client::json_lines_stream;
19use crate::error::{OllamaError, Result};
20use serde::{Deserialize, Serialize};
21use std::collections::HashMap;
22use tokio_stream::Stream;
23
24/// Information about a model.
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ModelInfo {
27    pub name: String,
28    pub model: String,
29    pub modified_at: String,
30    pub size: u64,
31    pub digest: String,
32    pub details: ModelDetails,
33    /// The remote model name (for cloud-hosted models).
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub remote_model: Option<String>,
36    /// The remote host URL (for cloud-hosted models).
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub remote_host: Option<String>,
39}
40
41/// Details about a model.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ModelDetails {
44    pub parent_model: String,
45    pub format: String,
46    pub family: String,
47    pub families: Option<Vec<String>>,
48    pub parameter_size: String,
49    pub quantization_level: String,
50}
51
52/// Response for listing models.
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct ListModelsResponse {
55    pub models: Vec<ModelInfo>,
56}
57
58/// Request for showing model information.
59#[derive(Debug, Clone, Serialize, Deserialize, Default)]
60pub struct ShowModelRequest {
61    pub model: String,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub verbose: Option<bool>,
64}
65
66/// Response for showing model information.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ShowModelResponse {
69    #[serde(default)]
70    pub modelfile: String,
71    #[serde(default)]
72    pub parameters: String,
73    #[serde(default)]
74    pub template: String,
75    pub details: ModelDetails,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub model_info: Option<HashMap<String, serde_json::Value>>,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub capabilities: Option<Vec<String>>,
80}
81
82/// Request for copying a model.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct CopyModelRequest {
85    pub source: String,
86    pub destination: String,
87}
88
89/// Request for deleting a model.
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct DeleteModelRequest {
92    pub model: String,
93}
94
95/// Request for pulling a model.
96#[derive(Debug, Clone, Serialize, Deserialize, Default)]
97pub struct PullModelRequest {
98    pub model: String,
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub insecure: Option<bool>,
101    #[serde(skip_serializing_if = "Option::is_none")]
102    pub stream: Option<bool>,
103}
104
105/// Request for pushing a model.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct PushModelRequest {
108    pub model: String,
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub insecure: Option<bool>,
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub stream: Option<bool>,
113}
114
115/// Request for creating a model.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct CreateModelRequest {
118    pub model: String,
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub from: Option<String>,
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub files: Option<HashMap<String, String>>,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    pub adapters: Option<HashMap<String, String>>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub template: Option<String>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub license: Option<License>,
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub system: Option<String>,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub parameters: Option<HashMap<String, serde_json::Value>>,
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub messages: Option<Vec<ChatMessage>>,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub stream: Option<bool>,
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub quantize: Option<String>,
139}
140
141/// License information.
142#[derive(Debug, Clone, Serialize, Deserialize)]
143#[serde(untagged)]
144pub enum License {
145    Single(String),
146    Multiple(Vec<String>),
147}
148
149/// A running model.
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct RunningModel {
152    pub name: String,
153    pub model: String,
154    pub size: u64,
155    pub digest: String,
156    pub details: ModelDetails,
157    pub expires_at: String,
158    pub size_vram: u64,
159}
160
161/// Response for listing running models.
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ListRunningModelsResponse {
164    pub models: Vec<RunningModel>,
165}
166
167/// Response for version information.
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct VersionResponse {
170    pub version: String,
171}
172
173/// Status response for streaming operations.
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct StatusResponse {
176    pub status: String,
177}
178
179impl ModelClient {
180    /// List local models.
181    pub async fn list_models(&self) -> Result<Vec<ModelInfo>> {
182        let url = self
183            .base_url
184            .join("api/tags")
185            .map_err(OllamaError::UrlError)?;
186        let response = self
187            .client
188            .get(url)
189            .send()
190            .await
191            .map_err(OllamaError::RequestError)?;
192
193        let models: ListModelsResponse = self.handle_response(response, None).await?;
194        Ok(models.models)
195    }
196
197    /// Show information about a model.
198    pub async fn show_model(&self, request: ShowModelRequest) -> Result<ShowModelResponse> {
199        let url = self
200            .base_url
201            .join("api/show")
202            .map_err(OllamaError::UrlError)?;
203        let response = self
204            .client
205            .post(url)
206            .json(&request)
207            .send()
208            .await
209            .map_err(OllamaError::RequestError)?;
210
211        self.handle_response(response, Some(&request.model)).await
212    }
213
214    /// Copy a model.
215    pub async fn copy_model(&self, request: CopyModelRequest) -> Result<()> {
216        let url = self
217            .base_url
218            .join("api/copy")
219            .map_err(OllamaError::UrlError)?;
220        let response = self
221            .client
222            .post(url)
223            .json(&request)
224            .send()
225            .await
226            .map_err(OllamaError::RequestError)?;
227
228        self.handle_void_response(response).await
229    }
230
231    /// Delete a model.
232    pub async fn delete_model(&self, request: DeleteModelRequest) -> Result<()> {
233        let url = self
234            .base_url
235            .join("api/delete")
236            .map_err(OllamaError::UrlError)?;
237        let response = self
238            .client
239            .delete(url)
240            .json(&request)
241            .send()
242            .await
243            .map_err(OllamaError::RequestError)?;
244
245        self.handle_void_response(response).await
246    }
247
248    /// Pull a model.
249    pub async fn pull_model(
250        &self,
251        mut request: PullModelRequest,
252    ) -> Result<impl Stream<Item = Result<StatusResponse>> + '_> {
253        let url = self
254            .base_url
255            .join("api/pull")
256            .map_err(OllamaError::UrlError)?;
257        request.stream = Some(true);
258
259        let response = self
260            .client
261            .post(url)
262            .json(&request)
263            .send()
264            .await
265            .map_err(OllamaError::RequestError)?;
266
267        if !response.status().is_success() {
268            return Err(handle_error_response(response, Some(&request.model)).await);
269        }
270
271        Ok(json_lines_stream(response))
272    }
273
274    /// Push a model.
275    pub async fn push_model(
276        &self,
277        request: PushModelRequest,
278    ) -> Result<impl Stream<Item = Result<StatusResponse>> + '_> {
279        let url = self
280            .base_url
281            .join("api/push")
282            .map_err(OllamaError::UrlError)?;
283        let response = self
284            .client
285            .post(url)
286            .json(&request)
287            .send()
288            .await
289            .map_err(OllamaError::RequestError)?;
290
291        if !response.status().is_success() {
292            return Err(handle_error_response(response, Some(&request.model)).await);
293        }
294
295        Ok(json_lines_stream(response))
296    }
297
298    /// Create a model.
299    pub async fn create_model(
300        &self,
301        request: CreateModelRequest,
302    ) -> Result<impl Stream<Item = Result<StatusResponse>> + '_> {
303        let url = self
304            .base_url
305            .join("api/create")
306            .map_err(OllamaError::UrlError)?;
307        let response = self
308            .client
309            .post(url)
310            .json(&request)
311            .send()
312            .await
313            .map_err(OllamaError::RequestError)?;
314
315        if !response.status().is_success() {
316            return Err(handle_error_response(response, Some(&request.model)).await);
317        }
318
319        Ok(json_lines_stream(response))
320    }
321
322    /// List running models.
323    #[cfg(feature = "local")]
324    pub async fn list_running_models(&self) -> Result<Vec<RunningModel>> {
325        let url = self
326            .base_url
327            .join("api/ps")
328            .map_err(OllamaError::UrlError)?;
329        let response = self
330            .client
331            .get(url)
332            .send()
333            .await
334            .map_err(OllamaError::RequestError)?;
335
336        let models: ListRunningModelsResponse = self.handle_response(response, None).await?;
337        Ok(models.models)
338    }
339}