diff --git a/examples/delete.rs b/examples/delete.rs new file mode 100644 index 0000000..e88a8b1 --- /dev/null +++ b/examples/delete.rs @@ -0,0 +1,16 @@ +use std::{env, error::Error}; + +use ollama_rs::OllamaClient; +use ollama_rs::types::delete::DeleteRequest; + +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt().init(); + let _ = dotenvy::dotenv(); + let server_address = env::var("OLLAMA_SERVER")?; + let model = env::args().nth(1).expect("usage: delete "); + let ollama_client = OllamaClient::new(server_address); + ollama_client.delete(DeleteRequest::new(&model)).await?; + println!("Model '{}' deleted successfully", model); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 83d940f..349aad9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ use crate::{ error::{OllamaError, OllamaResult}, types::{ chat::{ChatRequest, ChatResponse}, + delete::DeleteRequest, generate::{GenerateRequest, GenerateResponse}, ps::PsResponse, pull::{PullRequest, PullResponse}, @@ -247,6 +248,40 @@ impl OllamaClient { .await?) } + /// Deletes a model from the Ollama server. + /// + /// Calls `DELETE /api/delete`. + /// + /// # Errors + /// + /// Returns [`OllamaError::NetworkError`] if the server is unreachable, the model + /// does not exist, or the server returns a non-success status code. + /// + /// # Examples + /// + /// ```no_run + /// # use ollama_rs::OllamaClient; + /// # use ollama_rs::types::delete::DeleteRequest; + /// # async fn run() -> ollama_rs::error::OllamaResult<()> { + /// let client = OllamaClient::default(); + /// let request = DeleteRequest::new("gemma3"); + /// client.delete(request).await?; + /// println!("Model deleted successfully"); + /// # Ok(()) + /// # } + /// ``` + pub async fn delete(&self, request: DeleteRequest) -> OllamaResult<()> { + let request_address = format!("{}/api/delete", self.server_address); + info!("Delete model: {}", request.model); + self.client + .delete(request_address) + .json(&request) + .send() + .await? + .error_for_status()?; + Ok(()) + } + fn stream_response( &self, endpoint: String, diff --git a/src/types/delete.rs b/src/types/delete.rs new file mode 100644 index 0000000..6b2e3f7 --- /dev/null +++ b/src/types/delete.rs @@ -0,0 +1,77 @@ +//! Types for the model delete endpoint (`DELETE /api/delete`). +//! +//! Use [`DeleteRequest::new()`] to construct a request and pass it to +//! [`OllamaClient::delete()`](crate::OllamaClient::delete). +//! +//! # Examples +//! +//! ```no_run +//! # use ollama_rs::OllamaClient; +//! # use ollama_rs::types::delete::DeleteRequest; +//! # async fn run() -> ollama_rs::error::OllamaResult<()> { +//! let client = OllamaClient::default(); +//! let request = DeleteRequest::new("gemma3"); +//! client.delete(request).await?; +//! println!("Model deleted successfully"); +//! # Ok(()) +//! # } +//! ``` + +use serde::{Deserialize, Serialize}; + +/// A request to delete a model from the Ollama server (`DELETE /api/delete`). +/// +/// # Examples +/// +/// ``` +/// use ollama_rs::types::delete::DeleteRequest; +/// +/// let request = DeleteRequest::new("gemma3"); +/// assert_eq!(request.model, "gemma3"); +/// ``` +#[derive(Debug, Serialize, Deserialize)] +pub struct DeleteRequest { + /// The name of the model to delete (e.g., `"gemma3"`, `"llama3:latest"`). + pub model: String, +} + +impl DeleteRequest { + /// Creates a new [`DeleteRequest`] for the given model name. + pub fn new>(model: M) -> Self { + Self { + model: model.into(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn new_creates_request() { + let request = DeleteRequest::new("gemma3"); + assert_eq!(request.model, "gemma3"); + } + + #[test] + fn new_accepts_string() { + let request = DeleteRequest::new(String::from("llama3:latest")); + assert_eq!(request.model, "llama3:latest"); + } + + #[test] + fn request_serializes_correctly() { + let request = DeleteRequest::new("gemma3"); + let json = serde_json::to_value(&request).unwrap(); + assert_eq!(json, json!({"model": "gemma3"})); + } + + #[test] + fn request_deserializes_correctly() { + let json = json!({"model": "gemma3"}); + let request: DeleteRequest = serde_json::from_value(json).unwrap(); + assert_eq!(request.model, "gemma3"); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 734355c..5b42851 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,14 +2,15 @@ //! //! Each submodule corresponds to an API endpoint: //! -//! | Module | Endpoint | Description | -//! |--------------|-------------------|------------------------------------------| -//! | [`chat`] | `POST /api/chat` | Multi-turn chat conversations | -//! | [`generate`] | `POST /api/generate` | Single-prompt text generation | -//! | [`pull`] | `POST /api/pull` | Download models from the registry | -//! | [`tags`] | `GET /api/tags` | List available models | -//! | [`ps`] | `GET /api/ps` | List currently loaded/running models | -//! | [`version`] | `GET /api/version` | Query the server version | +//! | Module | Endpoint | Description | +//! |--------------|-----------------------|------------------------------------------| +//! | [`chat`] | `POST /api/chat` | Multi-turn chat conversations | +//! | [`delete`] | `DELETE /api/delete` | Delete a model from the server | +//! | [`generate`] | `POST /api/generate` | Single-prompt text generation | +//! | [`pull`] | `POST /api/pull` | Download models from the registry | +//! | [`tags`] | `GET /api/tags` | List available models | +//! | [`ps`] | `GET /api/ps` | List currently loaded/running models | +//! | [`version`] | `GET /api/version` | Query the server version | //! //! The [`common`] module contains types shared across multiple endpoints, such as //! [`Options`](common::Options) for generation parameters, [`Think`](common::Think) @@ -17,6 +18,7 @@ pub mod chat; pub mod common; +pub mod delete; pub mod generate; pub mod ps; pub mod pull;