llmrust 0.1.1

Unified Rust interface for 7 LLM providers (OpenAI, Anthropic, DeepSeek, Google Gemini, Ollama, Moonshot, OpenRouter) with HTTP proxy
Documentation
# Capabilities Matrix


Which features each provider supports, and how they map across providers.

> **Note on capabilities**: "Support" means llmrust maps the field or protocol. Actual upstream model support may vary by model. For example, llmrust sends `response_format` to DeepSeek and Moonshot, but some of their models may ignore or reject it. A ✅ in this matrix does not guarantee every model under that provider supports the feature.

## Provider support matrix


| Capability | OpenAI | DeepSeek | Moonshot | OpenRouter | Anthropic | Gemini | Ollama |
|------------|--------|----------|----------|------------|-----------|--------|--------|
| **chat** ||||||||
| **stream** ||||||||
| **tool calling (chat)** ||||||||
| **tool calling (stream)** ||||||||
| **JSON mode** ||||| ➖ (n/a) |||
| **JSON schema** ||||| ➖ (n/a) |||
| **logprobs** ||||| ➖ (n/a) |||
| **image input** |||||| ✅ (data: URL only) ||
| **system message** ||||||||
| **multi-turn** ||||||||
| **custom base URL** ||||||||
| **embeddings** ||||||||

> ✅ for embeddings means llmrust implements an embeddings transport for that provider. OpenAI, DeepSeek, Moonshot, and OpenRouter use OpenAI-compatible `/embeddings`; Ollama uses native `/api/embed`. Actual upstream/local model support may vary.

## Sampling parameter support


| Parameter | OpenAI-compat | Anthropic | Gemini | Ollama |
|-----------|---------------|-----------|--------|--------|
| `temperature` |||||
| `max_tokens` |||||
| `top_p` |||||
| `stop` | ✅ (array) | ✅ (sequences) |||
| `seed` |||||
| `presence_penalty` |||||
| `frequency_penalty` |||||
| `logprobs` |||||
| `top_logprobs` |||||
| `response_format` |||||
| `parallel_tool_calls` |||||
| `service_tier` |||||
| `store` |||||
| `metadata` |||||
| `user` |||||

## Error normalization


llmrust normalizes errors into `LlmError`:

| Source | `LlmError` variant |
|--------|-------------------|
| Network failure, TLS error, timeout | `LlmError::Http(reqwest::Error)` |
| Upstream 4xx/5xx with JSON body | `LlmError::Api { status, message }` |
| Malformed stream data | `LlmError::Stream(String)` |
| JSON parse failure | `LlmError::Parse(String)` |
| Unregistered provider name | `LlmError::UnknownProvider(String)` |
| Unsupported provider feature | `LlmError::Unsupported { feature, message }` |

### Proxy error mapping


The proxy maps `LlmError` to HTTP status codes and OpenAI-style error bodies:

| `LlmError` | HTTP status | Error type |
|------------|-------------|------------|
| `UnknownProvider` | 404 | `invalid_request_error` |
| `Parse` (invalid JSON) | 400 | `invalid_request_error` |
| `Api { status: 401 }` | 401 | `authentication_error` |
| `Api { status: 429 }` | 429 | `rate_limit_error` |
| Other `Api` / `Http` | 502 | `api_error` |
| `Stream` error | 502 | `api_error` |

## Streaming behavior


For all providers, streaming follows these rules:

1. Stream emits incremental `StreamChunk` items with `delta` text.
2. The terminal chunk has `done: true` and carries `finish_reason`, `usage`, and `tool_calls` (if applicable).
3. Parse errors in the stream are surfaced as `Err(LlmError::Parse(...))` chunks — never silently dropped.
4. Upstream API errors mid-stream are surfaced as `Err(LlmError::Api{...})` or `Err(LlmError::Stream(...))`.

### Provider-specific stream formats


| Provider | Wire format | SSE event structure |
|----------|-------------|---------------------|
| OpenAI-compat | SSE (`text/event-stream`) | `data: {"id":"...","object":"chat.completion.chunk","choices":[...]}` + `data: [DONE]` |
| Anthropic | SSE | `event: content_block_start/delta/ping`, `event: message_start/delta/stop` |
| Gemini | SSE (Gemini format) | `data: {"candidates":[...]}` with `text` deltas and `functionCall` parts |
| Ollama | NDJSON | `{"model":"...","message":{"content":"..."},"done":false}` per line |

## Things each provider does NOT support


This section exists to help AI agents avoid wasting time on impossible features.

### Ollama

- No tool calling (Ollama models vary widely in function-calling support; not worth the abstraction overhead).
- No JSON mode (same reason).
- No `top_p`, `stop`, `seed`, penalties, `n`, or request metadata fields.
- Image content is flattened to text (local models may not support vision).

### Anthropic

- No `seed`, `presence_penalty`, `frequency_penalty`, `logprobs`, `n`, `service_tier`, `store`, `metadata`, `user`.
- Image inputs are accepted via base64 `data:` URLs only in the Anthropic content-block format.
- `response_format` / JSON mode is not a first-class concept in the Messages API.

### Gemini

- Remote `http(s)` image URLs are skipped with a warning. Only `data:` URLs work.
- No `seed`, `presence_penalty`, `frequency_penalty`, `parallel_tool_calls`, `service_tier`, `store`, `metadata`.