use serde::{Deserialize, Serialize};
use crate::{error::OllamaError, Ollama};
#[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
#[cfg(feature = "stream")]
pub type PullModelStatusStream = std::pin::Pin<
Box<dyn tokio_stream::Stream<Item = crate::error::Result<PullModelStatus>> + Send>,
>;
impl Ollama {
#[cfg_attr(docsrs, doc(cfg(feature = "stream")))]
#[cfg(feature = "stream")]
pub async fn pull_model_stream(
&self,
model_name: String,
allow_insecure: bool,
) -> crate::error::Result<PullModelStatusStream> {
use crate::error::{InternalOllamaError, OllamaError};
use tokio_stream::StreamExt;
let request = PullModelRequest {
model_name,
allow_insecure,
stream: true,
};
let url = format!("{}api/pull", self.url_str());
let builder = self.reqwest_client.post(url);
#[cfg(feature = "headers")]
let builder = builder.headers(self.request_headers.clone());
let res = builder.json(&request).send().await?;
if !res.status().is_success() {
return Err(OllamaError::Other(res.text().await?));
}
let mut stream = res.bytes_stream();
let stream = async_stream::try_stream! {
let mut buffer = Vec::new();
while let Some(chunk) = stream.next().await {
let bytes = chunk.map_err(|e| OllamaError::Other(e.to_string()))?;
buffer.extend_from_slice(&bytes);
while let Some(i) = buffer.iter().position(|&b| b == b'\n') {
let line_bytes: Vec<u8> = buffer.drain(..=i).collect();
let line_slice = &line_bytes[..line_bytes.len() - 1];
if line_slice.is_empty() {
continue;
}
let res = serde_json::from_slice::<PullModelStatus>(line_slice);
match res {
Ok(res) => yield res,
Err(e) => {
let err = serde_json::from_slice::<InternalOllamaError>(line_slice);
match err {
Ok(err) => Err(OllamaError::InternalError(err))?,
Err(_) => Err::<(), OllamaError>(e.into())?,
}
}
}
}
}
};
Ok(std::pin::Pin::from(Box::new(stream)))
}
pub async fn pull_model(
&self,
model_name: String,
allow_insecure: bool,
) -> crate::error::Result<PullModelStatus> {
let request = PullModelRequest {
model_name,
allow_insecure,
stream: false,
};
let url = format!("{}api/pull", self.url_str());
let builder = self.reqwest_client.post(url);
#[cfg(feature = "headers")]
let builder = builder.headers(self.request_headers.clone());
let res = builder.json(&request).send().await?;
if !res.status().is_success() {
return Err(OllamaError::Other(res.text().await?));
}
let res = res.bytes().await?;
let res = serde_json::from_slice::<PullModelStatus>(&res)?;
Ok(res)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct PullModelRequest {
#[serde(rename = "name")]
model_name: String,
#[serde(rename = "insecure")]
allow_insecure: bool,
stream: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PullModelStatus {
#[serde(rename = "status")]
pub message: String,
pub digest: Option<String>,
pub total: Option<u64>,
pub completed: Option<u64>,
}