Compare commits
12 Commits
f15a2c53d6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bfe4d9f94d | |||
| 1b37c8dafe | |||
| 19fd7ad712 | |||
| 8fc1268e81 | |||
| de2efc51d5 | |||
| 5bad832353 | |||
| 6f14e5e908 | |||
| 0f796f1a2f | |||
| b885ca3c1c | |||
| fe74d78645 | |||
| c5abf87d9d | |||
| 63c08c484c |
127
Cargo.lock
generated
127
Cargo.lock
generated
@@ -11,6 +11,56 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell_polyfill",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -115,6 +165,46 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.55"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
@@ -124,6 +214,12 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -385,6 +481,12 @@ version = "0.16.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -616,6 +718,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
@@ -755,6 +863,7 @@ name = "ollama-rs"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
|
"clap",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@@ -773,6 +882,12 @@ version = "1.21.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1278,6 +1393,12 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -1622,6 +1743,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ futures-util = "0.3"
|
|||||||
async-stream = "0.3"
|
async-stream = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
clap = { version = "4.5", features = ["derive", "env"] }
|
||||||
dialoguer = "0.12.0"
|
dialoguer = "0.12.0"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
tokio = { version = "1.49.0", features = ["full"] }
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -8,7 +8,8 @@ An async Rust client library for the [Ollama](https://ollama.com/) API. Provides
|
|||||||
- Text generation and multi-turn chat conversations
|
- Text generation and multi-turn chat conversations
|
||||||
- Structured JSON output with schema validation
|
- Structured JSON output with schema validation
|
||||||
- Tool calling / function calling support
|
- Tool calling / function calling support
|
||||||
- Model management (list, pull, inspect running models)
|
- Model management (list, pull, delete, inspect running models)
|
||||||
|
- Text embeddings generation
|
||||||
- Builder pattern for constructing requests
|
- Builder pattern for constructing requests
|
||||||
- Configurable generation parameters (temperature, top-k, top-p, and more)
|
- Configurable generation parameters (temperature, top-k, top-p, and more)
|
||||||
- Thinking / reasoning mode support
|
- Thinking / reasoning mode support
|
||||||
@@ -160,6 +161,9 @@ When the model decides to call a tool, the response `message.tool_calls` field w
|
|||||||
| `generate(request)` | Generate text (streaming) |
|
| `generate(request)` | Generate text (streaming) |
|
||||||
| `chat(request)` | Chat conversation (streaming) |
|
| `chat(request)` | Chat conversation (streaming) |
|
||||||
| `pull(request)` | Pull/download a model (streaming) |
|
| `pull(request)` | Pull/download a model (streaming) |
|
||||||
|
| `delete(request)` | Delete a model from the server |
|
||||||
|
| `show_model(request)` | Show information about a model |
|
||||||
|
| `embed(request)` | Generate vector embeddings |
|
||||||
|
|
||||||
**`OllamaClient::builder(server_address)`** -- `.connection_timeout(Duration)`, `.build()`
|
**`OllamaClient::builder(server_address)`** -- `.connection_timeout(Duration)`, `.build()`
|
||||||
|
|
||||||
@@ -180,6 +184,10 @@ let client = OllamaClient::builder("http://localhost:11434")
|
|||||||
|
|
||||||
**`PullRequest::builder(model)`** -- `.stream()`
|
**`PullRequest::builder(model)`** -- `.stream()`
|
||||||
|
|
||||||
|
**`EmbedRequest::builder(model)`** -- `.input()`, `.inputs()`, `.truncate()`, `.dimensions()`, `.keep_alive()`, `.options()`
|
||||||
|
|
||||||
|
**`ShowModelRequest::new(model)`** -- `.verbose()`
|
||||||
|
|
||||||
### Generation Options
|
### Generation Options
|
||||||
|
|
||||||
Configure sampling parameters via `Options::builder()`:
|
Configure sampling parameters via `Options::builder()`:
|
||||||
@@ -206,6 +214,9 @@ The `examples/` directory contains runnable programs:
|
|||||||
| `structured_output` | JSON structured output with schema |
|
| `structured_output` | JSON structured output with schema |
|
||||||
| `tool_call` | Function calling / tool use |
|
| `tool_call` | Function calling / tool use |
|
||||||
| `pull` | Download a model |
|
| `pull` | Download a model |
|
||||||
|
| `delete` | Delete a model |
|
||||||
|
| `show_model` | Show model information |
|
||||||
|
| `embed` | Generate text embeddings |
|
||||||
| `tags` | List available models |
|
| `tags` | List available models |
|
||||||
| `ps` | List running models |
|
| `ps` | List running models |
|
||||||
| `version` | Query server version |
|
| `version` | Query server version |
|
||||||
|
|||||||
16
examples/delete.rs
Normal file
16
examples/delete.rs
Normal file
@@ -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<dyn Error>> {
|
||||||
|
tracing_subscriber::fmt().init();
|
||||||
|
let _ = dotenvy::dotenv();
|
||||||
|
let server_address = env::var("OLLAMA_SERVER")?;
|
||||||
|
let model = env::args().nth(1).expect("usage: delete <model>");
|
||||||
|
let ollama_client = OllamaClient::new(server_address);
|
||||||
|
ollama_client.delete(DeleteRequest::new(&model)).await?;
|
||||||
|
println!("Model '{}' deleted successfully", model);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
24
examples/embed.rs
Normal file
24
examples/embed.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use std::{env, error::Error};
|
||||||
|
|
||||||
|
use ollama_rs::OllamaClient;
|
||||||
|
use ollama_rs::types::embed::EmbedRequest;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
tracing_subscriber::fmt().init();
|
||||||
|
let _ = dotenvy::dotenv();
|
||||||
|
let server_address = env::var("OLLAMA_SERVER")?;
|
||||||
|
let ollama_client = OllamaClient::new(server_address);
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.input("Generate embeddings for this text")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let response = ollama_client.embed(request).await?;
|
||||||
|
for (i, embedding) in response.embeddings.iter().enumerate() {
|
||||||
|
println!("Embedding {}: {} dimensions", i, embedding.len());
|
||||||
|
if embedding.len() >= 3 {
|
||||||
|
println!(" First 3 values: {:?}", &embedding[..3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,21 +1,37 @@
|
|||||||
use std::{env, error::Error, io::Write};
|
use std::{error::Error, io::Write};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use ollama_rs::{OllamaClient, types::pull::PullRequest};
|
use ollama_rs::{OllamaClient, types::pull::PullRequest};
|
||||||
|
|
||||||
const MODEL: &str = "functiongemma";
|
#[derive(Parser, Debug)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
|
/// The model to pull
|
||||||
|
#[arg(short, long)]
|
||||||
|
model: String,
|
||||||
|
|
||||||
|
/// The Ollama server address
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
env = "OLLAMA_SERVER",
|
||||||
|
default_value = "http://localhost:11434"
|
||||||
|
)]
|
||||||
|
server_address: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Box<dyn Error>> {
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let _ = dotenvy::dotenv();
|
let _ = dotenvy::dotenv();
|
||||||
let server_address = env::var("OLLAMA_SERVER")?;
|
let args = Args::parse();
|
||||||
let ollama_client = OllamaClient::new(server_address);
|
let ollama_client = OllamaClient::new(args.server_address);
|
||||||
|
|
||||||
let request = PullRequest::builder(MODEL).stream(true).build();
|
let request = PullRequest::builder(&args.model).stream(true).build();
|
||||||
let mut stream = ollama_client.pull(request);
|
let mut stream = ollama_client.pull(request);
|
||||||
while let Some(response) = stream.next().await {
|
while let Some(response) = stream.next().await {
|
||||||
let response = response?;
|
let response = response?;
|
||||||
println!("{:?}", response);
|
println!("{}", response.status);
|
||||||
std::io::stdout().flush()?;
|
std::io::stdout().flush()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
37
examples/show_model.rs
Normal file
37
examples/show_model.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use ollama_rs::OllamaClient;
|
||||||
|
use ollama_rs::types::show::ShowModelRequest;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let client = OllamaClient::default();
|
||||||
|
let model = "qwen3:8b";
|
||||||
|
|
||||||
|
println!("Requesting info for model: {}", model);
|
||||||
|
let request = ShowModelRequest::new(model.to_string());
|
||||||
|
let response = client.show_model(request).await?;
|
||||||
|
|
||||||
|
println!("Model Info:");
|
||||||
|
println!(
|
||||||
|
" License: {}",
|
||||||
|
response
|
||||||
|
.license
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("N/A")
|
||||||
|
.lines()
|
||||||
|
.next()
|
||||||
|
.unwrap_or("")
|
||||||
|
); // First line only
|
||||||
|
println!(
|
||||||
|
" Modelfile: {} bytes",
|
||||||
|
response.modelfile.as_ref().map(|s| s.len()).unwrap_or(0)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" Parameters: {}",
|
||||||
|
response.parameters.as_ref().map(|s| s.len()).unwrap_or(0)
|
||||||
|
);
|
||||||
|
println!(" Template: {:?}", response.template.is_some());
|
||||||
|
println!(" Details: {:?}", response.details);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
115
src/lib.rs
115
src/lib.rs
@@ -93,9 +93,12 @@ use crate::{
|
|||||||
error::{OllamaError, OllamaResult},
|
error::{OllamaError, OllamaResult},
|
||||||
types::{
|
types::{
|
||||||
chat::{ChatRequest, ChatResponse},
|
chat::{ChatRequest, ChatResponse},
|
||||||
|
delete::DeleteRequest,
|
||||||
|
embed::{EmbedRequest, EmbedResponse},
|
||||||
generate::{GenerateRequest, GenerateResponse},
|
generate::{GenerateRequest, GenerateResponse},
|
||||||
ps::PsResponse,
|
ps::PsResponse,
|
||||||
pull::{PullRequest, PullResponse},
|
pull::{PullRequest, PullResponse},
|
||||||
|
show::{ShowModelRequest, ShowModelResponse},
|
||||||
tags::TagsResponse,
|
tags::TagsResponse,
|
||||||
version::VersionResponse,
|
version::VersionResponse,
|
||||||
},
|
},
|
||||||
@@ -247,6 +250,81 @@ impl OllamaClient {
|
|||||||
.await?)
|
.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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates vector embeddings for the given input text(s).
|
||||||
|
///
|
||||||
|
/// Calls `POST /api/embed`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`OllamaError::NetworkError`] if the server is unreachable or returns
|
||||||
|
/// a non-success status code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use ollama_rs::OllamaClient;
|
||||||
|
/// # use ollama_rs::types::embed::EmbedRequest;
|
||||||
|
/// # async fn run() -> ollama_rs::error::OllamaResult<()> {
|
||||||
|
/// let client = OllamaClient::default();
|
||||||
|
/// let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
/// .input("Generate embeddings for this text")
|
||||||
|
/// .build();
|
||||||
|
///
|
||||||
|
/// let response = client.embed(request).await?;
|
||||||
|
/// for embedding in &response.embeddings {
|
||||||
|
/// println!("Dimension count: {}", embedding.len());
|
||||||
|
/// }
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn embed(&self, request: EmbedRequest) -> OllamaResult<EmbedResponse> {
|
||||||
|
let request_address = format!("{}/api/embed", self.server_address);
|
||||||
|
info!("Generate embeddings: {}", request.model);
|
||||||
|
Ok(self
|
||||||
|
.client
|
||||||
|
.post(request_address)
|
||||||
|
.json(&request)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.error_for_status()?
|
||||||
|
.json()
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
fn stream_response<R: Serialize, T: DeserializeOwned>(
|
fn stream_response<R: Serialize, T: DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
endpoint: String,
|
endpoint: String,
|
||||||
@@ -379,6 +457,43 @@ impl OllamaClient {
|
|||||||
let request_address = format!("{}/api/pull", self.server_address);
|
let request_address = format!("{}/api/pull", self.server_address);
|
||||||
self.stream_response(request_address, request)
|
self.stream_response(request_address, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shows information about a model.
|
||||||
|
///
|
||||||
|
/// Calls `POST /api/show`.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns [`OllamaError::NetworkError`] if the server is unreachable or returns
|
||||||
|
/// a non-success status code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # use ollama_rs::OllamaClient;
|
||||||
|
/// # use ollama_rs::types::show::ShowModelRequest;
|
||||||
|
/// # async fn run() -> ollama_rs::error::OllamaResult<()> {
|
||||||
|
/// let client = OllamaClient::default();
|
||||||
|
/// let request = ShowModelRequest::new("llama3".to_string());
|
||||||
|
///
|
||||||
|
/// let response = client.show_model(request).await?;
|
||||||
|
/// println!("Model info: {:?}", response);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub async fn show_model(&self, request: ShowModelRequest) -> OllamaResult<ShowModelResponse> {
|
||||||
|
let request_address = format!("{}/api/show", self.server_address);
|
||||||
|
info!("Show model: {}", request.name);
|
||||||
|
Ok(self
|
||||||
|
.client
|
||||||
|
.post(request_address)
|
||||||
|
.json(&request)
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.error_for_status()?
|
||||||
|
.json()
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A builder for constructing an [`OllamaClient`] with custom configuration.
|
/// A builder for constructing an [`OllamaClient`] with custom configuration.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Detailed metadata about a model, returned by the tags and ps endpoints.
|
/// Detailed metadata about a model, returned by the tags, ps, and show endpoints.
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ModelDetails {
|
pub struct ModelDetails {
|
||||||
/// The model file format (e.g., `"gguf"`).
|
/// The model file format (e.g., `"gguf"`).
|
||||||
@@ -84,7 +84,7 @@ pub enum ThinkLevel {
|
|||||||
/// .stop(Stop::Single("END".to_string()))
|
/// .stop(Stop::Single("END".to_string()))
|
||||||
/// .build();
|
/// .build();
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
pub struct Options {
|
pub struct Options {
|
||||||
/// Random seed for reproducible outputs.
|
/// Random seed for reproducible outputs.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -220,7 +220,7 @@ impl OptionsBuilder {
|
|||||||
/// let single = Stop::Single("END".to_string());
|
/// let single = Stop::Single("END".to_string());
|
||||||
/// let multiple = Stop::Multiple(vec!["END".to_string(), "STOP".to_string()]);
|
/// let multiple = Stop::Multiple(vec!["END".to_string(), "STOP".to_string()]);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Stop {
|
pub enum Stop {
|
||||||
/// A single stop sequence.
|
/// A single stop sequence.
|
||||||
|
|||||||
77
src/types/delete.rs
Normal file
77
src/types/delete.rs
Normal file
@@ -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<M: Into<String>>(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");
|
||||||
|
}
|
||||||
|
}
|
||||||
306
src/types/embed.rs
Normal file
306
src/types/embed.rs
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
//! Types for the embedding endpoint (`POST /api/embed`).
|
||||||
|
//!
|
||||||
|
//! Use [`EmbedRequest::builder()`] to construct a request and pass it to
|
||||||
|
//! [`OllamaClient::embed()`](crate::OllamaClient::embed).
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! # use ollama_rs::OllamaClient;
|
||||||
|
//! # use ollama_rs::types::embed::EmbedRequest;
|
||||||
|
//! # async fn run() -> ollama_rs::error::OllamaResult<()> {
|
||||||
|
//! let client = OllamaClient::default();
|
||||||
|
//! let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
//! .input("Generate embeddings for this text")
|
||||||
|
//! .build();
|
||||||
|
//!
|
||||||
|
//! let response = client.embed(request).await?;
|
||||||
|
//! println!("Embeddings: {:?}", response.embeddings);
|
||||||
|
//! # Ok(())
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::types::common::Options;
|
||||||
|
|
||||||
|
/// The input text(s) to generate embeddings for.
|
||||||
|
///
|
||||||
|
/// Accepts either a single string or an array of strings. Serialized as an
|
||||||
|
/// untagged enum so both `"hello"` and `["hello", "world"]` are valid JSON
|
||||||
|
/// representations.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ollama_rs::types::embed::EmbedInput;
|
||||||
|
///
|
||||||
|
/// let single = EmbedInput::Single("hello".to_string());
|
||||||
|
/// let multiple = EmbedInput::Multiple(vec!["hello".to_string(), "world".to_string()]);
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum EmbedInput {
|
||||||
|
/// A single text string.
|
||||||
|
Single(String),
|
||||||
|
/// Multiple text strings.
|
||||||
|
Multiple(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A request to generate embeddings (`POST /api/embed`).
|
||||||
|
///
|
||||||
|
/// Construct via [`EmbedRequest::builder()`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ollama_rs::types::embed::EmbedRequest;
|
||||||
|
///
|
||||||
|
/// let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
/// .input("Generate embeddings for this text")
|
||||||
|
/// .build();
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct EmbedRequest {
|
||||||
|
/// The model name to use for generating embeddings.
|
||||||
|
pub model: String,
|
||||||
|
|
||||||
|
/// The text or array of texts to generate embeddings for.
|
||||||
|
pub input: EmbedInput,
|
||||||
|
|
||||||
|
/// If `true`, truncate inputs that exceed the context window. If `false`,
|
||||||
|
/// returns an error. Defaults to `true` on the server.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub truncate: Option<bool>,
|
||||||
|
|
||||||
|
/// Number of dimensions to generate embeddings for.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub dimensions: Option<u32>,
|
||||||
|
|
||||||
|
/// How long the model stays loaded in memory (e.g., `"5m"`, `"1h"`).
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub keep_alive: Option<String>,
|
||||||
|
|
||||||
|
/// Runtime options for the embedding model.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub options: Option<Options>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmbedRequest {
|
||||||
|
/// Returns an [`EmbedRequestBuilder`] for the given model name.
|
||||||
|
pub fn builder<M: Into<String>>(model: M) -> EmbedRequestBuilder {
|
||||||
|
EmbedRequestBuilder {
|
||||||
|
embed_request: EmbedRequest {
|
||||||
|
model: model.into(),
|
||||||
|
input: EmbedInput::Single(String::new()),
|
||||||
|
truncate: None,
|
||||||
|
dimensions: None,
|
||||||
|
keep_alive: None,
|
||||||
|
options: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A builder for constructing an [`EmbedRequest`].
|
||||||
|
///
|
||||||
|
/// Obtain a builder via [`EmbedRequest::builder()`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use ollama_rs::types::embed::EmbedRequest;
|
||||||
|
///
|
||||||
|
/// let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
/// .input("hello world")
|
||||||
|
/// .truncate(true)
|
||||||
|
/// .build();
|
||||||
|
/// ```
|
||||||
|
pub struct EmbedRequestBuilder {
|
||||||
|
embed_request: EmbedRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmbedRequestBuilder {
|
||||||
|
/// Sets a single text string as the input.
|
||||||
|
pub fn input<S: Into<String>>(mut self, input: S) -> Self {
|
||||||
|
self.embed_request.input = EmbedInput::Single(input.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets multiple text strings as the input.
|
||||||
|
pub fn inputs<S: Into<String>>(mut self, inputs: Vec<S>) -> Self {
|
||||||
|
self.embed_request.input =
|
||||||
|
EmbedInput::Multiple(inputs.into_iter().map(|s| s.into()).collect());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether to truncate inputs that exceed the context window.
|
||||||
|
pub fn truncate(mut self, truncate: bool) -> Self {
|
||||||
|
self.embed_request.truncate = Some(truncate);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the number of dimensions for the embeddings.
|
||||||
|
pub fn dimensions(mut self, dimensions: u32) -> Self {
|
||||||
|
self.embed_request.dimensions = Some(dimensions);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets how long the model stays loaded in memory (e.g., `"5m"`).
|
||||||
|
pub fn keep_alive<S: Into<String>>(mut self, keep_alive: S) -> Self {
|
||||||
|
self.embed_request.keep_alive = Some(keep_alive.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets runtime options for the embedding model.
|
||||||
|
pub fn options(mut self, options: Options) -> Self {
|
||||||
|
self.embed_request.options = Some(options);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the builder and returns the configured [`EmbedRequest`].
|
||||||
|
pub fn build(self) -> EmbedRequest {
|
||||||
|
self.embed_request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The response from the embedding endpoint.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct EmbedResponse {
|
||||||
|
/// The model that produced the embeddings.
|
||||||
|
pub model: String,
|
||||||
|
|
||||||
|
/// The generated vector embeddings. Each inner `Vec<f64>` corresponds to
|
||||||
|
/// one input text, in the same order as the request.
|
||||||
|
pub embeddings: Vec<Vec<f64>>,
|
||||||
|
|
||||||
|
/// Total time spent generating embeddings, in nanoseconds.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub total_duration: Option<u64>,
|
||||||
|
|
||||||
|
/// Time spent loading the model, in nanoseconds.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub load_duration: Option<u64>,
|
||||||
|
|
||||||
|
/// Number of input tokens processed to generate the embeddings.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub prompt_eval_count: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builder_minimal() {
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.input("hello")
|
||||||
|
.build();
|
||||||
|
assert_eq!(request.model, "embeddinggemma");
|
||||||
|
assert!(matches!(request.input, EmbedInput::Single(ref s) if s == "hello"));
|
||||||
|
assert!(request.truncate.is_none());
|
||||||
|
assert!(request.dimensions.is_none());
|
||||||
|
assert!(request.keep_alive.is_none());
|
||||||
|
assert!(request.options.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builder_with_all_fields() {
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.inputs(vec!["hello", "world"])
|
||||||
|
.truncate(false)
|
||||||
|
.dimensions(256)
|
||||||
|
.keep_alive("10m")
|
||||||
|
.options(Options::builder().seed(42).build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert!(matches!(request.input, EmbedInput::Multiple(ref v) if v.len() == 2));
|
||||||
|
assert_eq!(request.truncate, Some(false));
|
||||||
|
assert_eq!(request.dimensions, Some(256));
|
||||||
|
assert_eq!(request.keep_alive, Some("10m".to_string()));
|
||||||
|
assert!(request.options.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_skips_none_fields() {
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.input("hello")
|
||||||
|
.build();
|
||||||
|
let json = serde_json::to_value(&request).unwrap();
|
||||||
|
let obj = json.as_object().unwrap();
|
||||||
|
assert!(obj.contains_key("model"));
|
||||||
|
assert!(obj.contains_key("input"));
|
||||||
|
assert!(!obj.contains_key("truncate"));
|
||||||
|
assert!(!obj.contains_key("dimensions"));
|
||||||
|
assert!(!obj.contains_key("keep_alive"));
|
||||||
|
assert!(!obj.contains_key("options"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_serializes_single_input() {
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.input("hello")
|
||||||
|
.build();
|
||||||
|
let json = serde_json::to_value(&request).unwrap();
|
||||||
|
assert_eq!(json["input"], json!("hello"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_serializes_multiple_inputs() {
|
||||||
|
let request = EmbedRequest::builder("embeddinggemma")
|
||||||
|
.inputs(vec!["hello", "world"])
|
||||||
|
.build();
|
||||||
|
let json = serde_json::to_value(&request).unwrap();
|
||||||
|
assert_eq!(json["input"], json!(["hello", "world"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn embed_input_single_round_trip() {
|
||||||
|
let input = EmbedInput::Single("test".to_string());
|
||||||
|
let json = serde_json::to_value(&input).unwrap();
|
||||||
|
assert_eq!(json, json!("test"));
|
||||||
|
let deserialized: EmbedInput = serde_json::from_value(json).unwrap();
|
||||||
|
assert!(matches!(deserialized, EmbedInput::Single(s) if s == "test"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn embed_input_multiple_round_trip() {
|
||||||
|
let input = EmbedInput::Multiple(vec!["a".to_string(), "b".to_string()]);
|
||||||
|
let json = serde_json::to_value(&input).unwrap();
|
||||||
|
assert_eq!(json, json!(["a", "b"]));
|
||||||
|
let deserialized: EmbedInput = serde_json::from_value(json).unwrap();
|
||||||
|
assert!(matches!(deserialized, EmbedInput::Multiple(v) if v == vec!["a", "b"]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_deserialize() {
|
||||||
|
let json = json!({
|
||||||
|
"model": "embeddinggemma",
|
||||||
|
"embeddings": [[0.010071029, -0.0017594862, 0.05007221]],
|
||||||
|
"total_duration": 14143917,
|
||||||
|
"load_duration": 1019500,
|
||||||
|
"prompt_eval_count": 8
|
||||||
|
});
|
||||||
|
let response: EmbedResponse = serde_json::from_value(json).unwrap();
|
||||||
|
assert_eq!(response.model, "embeddinggemma");
|
||||||
|
assert_eq!(response.embeddings.len(), 1);
|
||||||
|
assert_eq!(response.embeddings[0].len(), 3);
|
||||||
|
assert_eq!(response.total_duration, Some(14143917));
|
||||||
|
assert_eq!(response.load_duration, Some(1019500));
|
||||||
|
assert_eq!(response.prompt_eval_count, Some(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_deserialize_minimal() {
|
||||||
|
let json = json!({
|
||||||
|
"model": "embeddinggemma",
|
||||||
|
"embeddings": [[1.0, 2.0], [3.0, 4.0]]
|
||||||
|
});
|
||||||
|
let response: EmbedResponse = serde_json::from_value(json).unwrap();
|
||||||
|
assert_eq!(response.embeddings.len(), 2);
|
||||||
|
assert!(response.total_duration.is_none());
|
||||||
|
assert!(response.load_duration.is_none());
|
||||||
|
assert!(response.prompt_eval_count.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,17 @@
|
|||||||
//!
|
//!
|
||||||
//! Each submodule corresponds to an API endpoint:
|
//! Each submodule corresponds to an API endpoint:
|
||||||
//!
|
//!
|
||||||
//! | Module | Endpoint | Description |
|
//! | Module | Endpoint | Description |
|
||||||
//! |--------------|-------------------|------------------------------------------|
|
//! |--------------|-----------------------|------------------------------------------|
|
||||||
//! | [`chat`] | `POST /api/chat` | Multi-turn chat conversations |
|
//! | [`chat`] | `POST /api/chat` | Multi-turn chat conversations |
|
||||||
//! | [`generate`] | `POST /api/generate` | Single-prompt text generation |
|
//! | [`delete`] | `DELETE /api/delete` | Delete a model from the server |
|
||||||
//! | [`pull`] | `POST /api/pull` | Download models from the registry |
|
//! | [`embed`] | `POST /api/embed` | Generate vector embeddings |
|
||||||
//! | [`tags`] | `GET /api/tags` | List available models |
|
//! | [`generate`] | `POST /api/generate` | Single-prompt text generation |
|
||||||
//! | [`ps`] | `GET /api/ps` | List currently loaded/running models |
|
//! | [`pull`] | `POST /api/pull` | Download models from the registry |
|
||||||
//! | [`version`] | `GET /api/version` | Query the server version |
|
//! | [`show`] | `POST /api/show` | Show model details |
|
||||||
|
//! | [`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
|
//! The [`common`] module contains types shared across multiple endpoints, such as
|
||||||
//! [`Options`](common::Options) for generation parameters, [`Think`](common::Think)
|
//! [`Options`](common::Options) for generation parameters, [`Think`](common::Think)
|
||||||
@@ -17,8 +20,11 @@
|
|||||||
|
|
||||||
pub mod chat;
|
pub mod chat;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
pub mod delete;
|
||||||
|
pub mod embed;
|
||||||
pub mod generate;
|
pub mod generate;
|
||||||
pub mod ps;
|
pub mod ps;
|
||||||
pub mod pull;
|
pub mod pull;
|
||||||
|
pub mod show;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod version;
|
pub mod version;
|
||||||
|
|||||||
101
src/types/show.rs
Normal file
101
src/types/show.rs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
//! Types for the show model information endpoint (`POST /api/show`).
|
||||||
|
//!
|
||||||
|
//! Use [`ShowModelRequest`] to construct a request and pass it to
|
||||||
|
//! [`OllamaClient::show_model`](crate::OllamaClient::show_model).
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::common::ModelDetails;
|
||||||
|
|
||||||
|
/// A request to the show model endpoint (`POST /api/show`).
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct ShowModelRequest {
|
||||||
|
/// The name of the model to retrieve information for.
|
||||||
|
pub name: String,
|
||||||
|
/// Whether to return full model details (e.g. system prompt, template).
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub verbose: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShowModelRequest {
|
||||||
|
/// Creates a new request for the given model name.
|
||||||
|
pub fn new(name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
verbose: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables verbose output, returning full model details.
|
||||||
|
pub fn verbose(mut self) -> Self {
|
||||||
|
self.verbose = Some(true);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A response from the show model endpoint.
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct ShowModelResponse {
|
||||||
|
/// The license of the model.
|
||||||
|
pub license: Option<String>,
|
||||||
|
/// The modelfile content.
|
||||||
|
pub modelfile: Option<String>,
|
||||||
|
/// The model parameters (e.g., stop tokens, temperature defaults).
|
||||||
|
pub parameters: Option<String>,
|
||||||
|
/// The prompt template.
|
||||||
|
pub template: Option<String>,
|
||||||
|
/// The system prompt.
|
||||||
|
pub system: Option<String>,
|
||||||
|
/// Detailed model metadata.
|
||||||
|
pub details: Option<ModelDetails>,
|
||||||
|
/// Additional model information.
|
||||||
|
pub model_info: Option<serde_json::Map<String, serde_json::Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_new_serialized() {
|
||||||
|
let request = ShowModelRequest::new("llama3".to_string());
|
||||||
|
let json = serde_json::to_value(&request).unwrap();
|
||||||
|
assert_eq!(json, json!({"name": "llama3"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_verbose_serialized() {
|
||||||
|
let request = ShowModelRequest::new("llama3".to_string()).verbose();
|
||||||
|
let json = serde_json::to_value(&request).unwrap();
|
||||||
|
assert_eq!(json, json!({"name": "llama3", "verbose": true}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn response_deserialization() {
|
||||||
|
let json = json!({
|
||||||
|
"license": "MIT",
|
||||||
|
"modelfile": "FROM llama3",
|
||||||
|
"parameters": "temperature 0.7",
|
||||||
|
"template": "Please answer:",
|
||||||
|
"system": "You are helpful",
|
||||||
|
"details": {
|
||||||
|
"format": "gguf",
|
||||||
|
"family": "llama",
|
||||||
|
"families": ["llama"],
|
||||||
|
"parameter_size": "8B",
|
||||||
|
"quantization_level": "Q4_0"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let response: ShowModelResponse = serde_json::from_value(json).unwrap();
|
||||||
|
assert_eq!(response.license, Some("MIT".to_string()));
|
||||||
|
assert_eq!(response.modelfile, Some("FROM llama3".to_string()));
|
||||||
|
assert_eq!(response.parameters, Some("temperature 0.7".to_string()));
|
||||||
|
assert_eq!(response.template, Some("Please answer:".to_string()));
|
||||||
|
assert_eq!(response.system, Some("You are helpful".to_string()));
|
||||||
|
|
||||||
|
let details = response.details.unwrap();
|
||||||
|
assert_eq!(details.format, "gguf");
|
||||||
|
assert_eq!(details.family, "llama");
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user