oai_sdk/lib.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
15//! # Library Name Note
16//!
17//! This library is published as `ollama-api-rs` on crates.io.
18//! Users should write `use oai_sdk::{ModelClient, ChatRequest, Message};`
19//!
20//! # Features
21//!
22//! - **Async/await support** - Built on top of Tokio for efficient async operations
23//! - **Easy configuration** - Simple client setup with `ModelClient::builder()`
24//! - **Streaming responses** - Real-time streaming for both chat and generation
25//! - **Full Ollama API compatibility** - Complete coverage of all Ollama API endpoints
26//! - **Modular design** - Separate modules for chat, generate, embed, and model operations
27//! - **Comprehensive error handling** - Custom error types with detailed context
28//! - **Tool calling** - Support for function/tool calling in chat completions
29//! - **Structured outputs** - JSON schema validation support for responses
30//! - **Model lifecycle management** - Load/unload models programmatically
31//! - **Blob management** - Push and check model blobs
32//! - **Batch embeddings** - Efficient batch processing for embeddings
33//!
34//! # Examples
35//!
36//! ## Basic Chat Completion
37//!
38//! ```no_run
39//! use oai_sdk::{ModelClient, ChatRequest, Message};
40//!
41//! #[tokio::main]
42//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
43//! let client = ModelClient::builder()
44//! .base_url("http://localhost:11434")
45//! .build()?;
46//!
47//! let request = ChatRequest {
48//! model: "llama3.1:8b".to_string(),
49//! messages: vec![Message::user("Why is the sky blue?")],
50//! stream: false,
51//! ..Default::default()
52//! };
53//!
54//! let response = client.chat(request).await?;
55//! println!("{}", response.message.content);
56//!
57//! Ok(())
58//! }
59//! ```
60//!
61//! ## Streaming Chat
62//!
63//! ```no_run
64//! use oai_sdk::{ModelClient, ChatRequest, Message};
65//! use tokio_stream::StreamExt;
66//!
67//! #[tokio::main]
68//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
69//! let client = ModelClient::builder()
70//! .base_url("http://localhost:11434")
71//! .build()?;
72//!
73//! let request = ChatRequest {
74//! model: "llama3.1:8b".to_string(),
75//! messages: vec![Message::user("Write a story about Rust")],
76//! stream: true,
77//! ..Default::default()
78//! };
79//!
80//! let mut stream = client.chat_stream(request).await?;
81//! while let Some(result) = stream.next().await {
82//! match result {
83//! Ok(response) => print!("{}", response.message.content),
84//! Err(e) => eprintln!("Error: {}", e),
85//! }
86//! }
87//!
88//! Ok(())
89//! }
90//! ```
91//!
92//! ## Text Generation
93//!
94//! ```no_run
95//! use oai_sdk::{ModelClient, GenerateRequest};
96//!
97//! #[tokio::main]
98//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
99//! let client = ModelClient::builder()
100//! .base_url("http://localhost:11434")
101//! .build()?;
102//!
103//! let request = GenerateRequest {
104//! model: "llama3.1:8b".to_string(),
105//! prompt: "Why is the sky blue?".to_string(),
106//! ..Default::default()
107//! };
108//!
109//! let response = client.generate(request).await?;
110//! println!("{}", response.response);
111//!
112//! Ok(())
113//! }
114//! ```
115//!
116//! ## Embeddings
117//!
118//! ```no_run
119//! use oai_sdk::{ModelClient, EmbedRequest, EmbedInput};
120//!
121//! #[tokio::main]
122//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
123//! let client = ModelClient::builder()
124//! .base_url("http://localhost:11434")
125//! .build()?;
126//!
127//! let request = EmbedRequest {
128//! model: "llama3:8b".to_string(),
129//! input: EmbedInput::Single("Hello, world!".to_string()),
130//! truncate: Some(true),
131//! ..Default::default()
132//! };
133//!
134//! let response = client.embed(request).await?;
135//! println!("Embeddings: {:?}", response.embeddings);
136//!
137//! Ok(())
138//! }
139//! ```
140//!
141//! ## Tool Calling
142//!
143//! ```no_run
144//! use oai_sdk::{ModelClient, ChatRequest, Message, Tool, ToolFunction};
145//! use serde_json::json;
146//!
147//! #[tokio::main]
148//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
149//! let client = ModelClient::builder()
150//! .base_url("http://localhost:11434")
151//! .build()?;
152//!
153//! let tools = vec![
154//! Tool {
155//! tool_type: "function".to_string(),
156//! function: ToolFunction {
157//! name: "get_current_weather".to_string(),
158//! description: "Get the current weather for a location".to_string(),
159//! parameters: json!({
160//! "type": "object",
161//! "properties": {
162//! "location": {
163//! "type": "string",
164//! "description": "The location to get the weather for"
165//! },
166//! "format": {
167//! "type": "string",
168//! "enum": ["celsius", "fahrenheit"]
169//! }
170//! },
171//! "required": ["location", "format"]
172//! }),
173//! }
174//! }
175//! ];
176//!
177//! let request = ChatRequest {
178//! model: "llama3.1:8b".to_string(),
179//! messages: vec![Message::user("What is the weather in Tokyo?")],
180//! tools: Some(tools),
181//! ..Default::default()
182//! };
183//!
184//! let response = client.chat(request).await?;
185//! if let Some(tool_calls) = response.message.tool_calls {
186//! for tool_call in tool_calls {
187//! println!("Tool call: {}", tool_call.function.name);
188//! }
189//! }
190//!
191//! Ok(())
192//! }
193//! ```
194//!
195//! ## Model Management
196//!
197//! ```no_run
198//! use oai_sdk::{ModelClient, ShowModelRequest, CopyModelRequest, DeleteModelRequest};
199//!
200//! #[tokio::main]
201//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
202//! let client = ModelClient::builder()
203//! .base_url("http://localhost:11434")
204//! .build()?;
205//!
206//! let models = client.list_models().await?;
207//! for model in models {
208//! println!("Model: {}", model.name);
209//! }
210//!
211//! let request = ShowModelRequest {
212//! model: "llama3.1:8b".to_string(),
213//! verbose: Some(true),
214//! };
215//! let info = client.show_model(request).await?;
216//! println!("Model info: {:?}", info);
217//!
218//! let copy_req = CopyModelRequest {
219//! source: "llama3.1:8b".to_string(),
220//! destination: "llama3-backup".to_string(),
221//! };
222//! client.copy_model(copy_req).await?;
223//!
224//! let delete_req = DeleteModelRequest {
225//! model: "llama3-backup".to_string(),
226//! };
227//! client.delete_model(delete_req).await?;
228//!
229//! Ok(())
230//! }
231//! ```
232//!
233//! ## OpenAI-Compatible Endpoints
234//!
235//! ```no_run
236//! use oai_sdk::{ModelClient, ChatCompletionsRequest, ChatMessage};
237//!
238//! #[tokio::main]
239//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
240//! let client = ModelClient::builder()
241//! .base_url("http://localhost:11434")
242//! .build()?;
243//!
244//! let request = ChatCompletionsRequest {
245//! model: "llama3.1:8b".to_string(),
246//! messages: vec![ChatMessage::user("Why is the sky blue?")],
247//! stream: Some(false),
248//! ..Default::default()
249//! };
250//!
251//! let response = client.chat_completions(request).await?;
252//! println!("{}", response.choices[0].message.content);
253//!
254//! Ok(())
255//! }
256//! ```
257//!
258//! ## Model Lifecycle (requires `local` feature)
259//!
260//! ```no_run
261//! # #[cfg(feature = "local")]
262//! # {
263//! use oai_sdk::ModelClient;
264//!
265//! # #[tokio::main]
266//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
267//! let client = ModelClient::builder()
268//! .base_url("http://localhost:11434")
269//! .build()?;
270//!
271//! client.load_model("llama3.1:8b").await?;
272//! println!("Model loaded");
273//!
274//! client.unload_model("llama3.1:8b").await?;
275//! println!("Model unloaded");
276//!
277//! Ok(())
278//! # }
279//! # }
280//! ```
281//!
282//! ## API Modules
283//!
284//! - [`chat`](crate::chat) - Chat completion with streaming and tool support
285//! - [`generate`](crate::generate) - Text generation with streaming support
286//! - [`embed`](crate::embed) - Single and batch embeddings
287//! - [`model`](crate::model) - Model management (CRUD, pull, push, running models)
288//! - [`openai`](crate::openai) - OpenAI-compatible endpoints (chat, embeddings, responses)
289//! - [`client`](crate::client) - Core client, blob management, model lifecycle
290//! - [`error`](crate::error) - Error types and handling
291
292mod chat;
293mod client;
294mod embed;
295mod error;
296mod generate;
297mod model;
298mod openai;
299
300pub use chat::{
301 ChatRequest, ChatResponse, Format, Message, Tool, ToolCall, ToolCallFunction, ToolFunction,
302};
303pub use client::{ModelClient, ModelClientBuilder};
304pub use embed::{EmbedInput, EmbedRequest, EmbedResponse, EmbeddingsRequest, EmbeddingsResponse};
305pub use error::{OllamaError, Result};
306pub use generate::{GenerateRequest, GenerateResponse};
307pub use model::{
308 CopyModelRequest, CreateModelRequest, DeleteModelRequest, License, ListModelsResponse,
309 ListRunningModelsResponse, ModelDetails, ModelInfo, PullModelRequest, PushModelRequest,
310 RunningModel, ShowModelRequest, ShowModelResponse, StatusResponse, VersionResponse,
311};
312pub use openai::{
313 ChatCompletionsRequest, ChatCompletionsResponse, ChatMessage, OpenAIEmbedding,
314 OpenAIEmbeddingsInput, OpenAIEmbeddingsRequest, OpenAIEmbeddingsResponse, ResponsesRequest,
315 ResponsesResponse,
316};