@xberg-io/opencode-liter-llm 2.2.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/ai-rulez-bundle.json +25 -16
- package/.opencode/ai-rulez-content.js +3 -3
- package/.opencode/commands/liter-llm-chat.md +14 -0
- package/.opencode/commands/liter-llm-stream.md +14 -0
- package/.opencode/plugins/liter-llm.js +9 -21
- package/.opencode/skills/liter-llm/SKILL.md +18 -24
- package/.opencode/skills/liter-llm/index.md +10 -0
- package/.opencode/skills/{calling-llms → liter-llm-calling-llms}/SKILL.md +13 -7
- package/.opencode/skills/liter-llm-calling-llms/index.md +10 -0
- package/.opencode/skills/{embeddings-and-search → liter-llm-embeddings-and-search}/SKILL.md +3 -3
- package/.opencode/skills/liter-llm-embeddings-and-search/index.md +10 -0
- package/.opencode/skills/{running-the-proxy → liter-llm-running-the-proxy}/SKILL.md +3 -3
- package/.opencode/skills/liter-llm-running-the-proxy/index.md +10 -0
- package/.opencode/skills/{streaming-responses → liter-llm-streaming-responses}/SKILL.md +3 -3
- package/.opencode/skills/liter-llm-streaming-responses/index.md +10 -0
- package/.opencode/skills/{tool-calling → liter-llm-tool-calling}/SKILL.md +3 -3
- package/.opencode/skills/liter-llm-tool-calling/index.md +10 -0
- package/.opencode/skills/liter-llm-using-the-mcp-server/SKILL.md +91 -0
- package/.opencode/skills/liter-llm-using-the-mcp-server/index.md +10 -0
- package/.pi/prompts/liter-llm-chat.md +14 -0
- package/.pi/prompts/liter-llm-stream.md +14 -0
- package/.pi/skills/liter-llm/SKILL.md +208 -0
- package/.pi/skills/liter-llm/index.md +10 -0
- package/.pi/skills/liter-llm-calling-llms/SKILL.md +75 -0
- package/.pi/skills/liter-llm-calling-llms/index.md +10 -0
- package/.pi/skills/liter-llm-embeddings-and-search/SKILL.md +81 -0
- package/.pi/skills/liter-llm-embeddings-and-search/index.md +10 -0
- package/.pi/skills/liter-llm-running-the-proxy/SKILL.md +74 -0
- package/.pi/skills/liter-llm-running-the-proxy/index.md +10 -0
- package/.pi/skills/liter-llm-streaming-responses/SKILL.md +63 -0
- package/.pi/skills/liter-llm-streaming-responses/index.md +10 -0
- package/.pi/skills/liter-llm-tool-calling/SKILL.md +77 -0
- package/.pi/skills/liter-llm-tool-calling/index.md +10 -0
- package/.pi/skills/liter-llm-using-the-mcp-server/SKILL.md +91 -0
- package/.pi/skills/liter-llm-using-the-mcp-server/index.md +10 -0
- package/README.md +151 -14
- package/package.json +76 -21
- package/scripts/mcp-launch.sh +6 -5
- package/.opencode/skills/using-the-mcp-server/SKILL.md +0 -63
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Universal LLM API client for 174 providers with native bindings for 14 languages. Use when writing code that calls LLM APIs via liter-llm in Python, TypeScript, Rust, Go, Java, C#, Ruby, PHP, Elixir, WASM, or C, when running the OpenAI-compatible proxy, or when calling LLMs through the MCP server. Covers chat, streaming, tool calling, embeddings, image generation, speech, transcription, moderation, web search, OCR, reranking, provider routing, middleware, and configuration.
|
|
3
|
+
license: MIT
|
|
4
|
+
metadata:
|
|
5
|
+
author: xberg-io
|
|
6
|
+
version: "0.1.0"
|
|
7
|
+
repository: https://github.com/xberg-io/liter-llm
|
|
8
|
+
name: liter-llm
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!--
|
|
12
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
13
|
+
Content-Hash: blake3:d6c306e983d2305d8037cb8f4e973ff21eeb0e3ed4a5ac342c4c7dbfae965836
|
|
14
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
15
|
+
Schema-Version: v1
|
|
16
|
+
-->
|
|
17
|
+
|
|
18
|
+
# Liter-LLM Universal LLM Client
|
|
19
|
+
|
|
20
|
+
Liter-LLM is a universal LLM API client with a Rust core and native bindings for
|
|
21
|
+
Python, TypeScript/Node.js, Go, Java, C#, Ruby, PHP, Elixir, WebAssembly, and C
|
|
22
|
+
(FFI). One unified interface reaches 174 providers (OpenAI, Anthropic, Google
|
|
23
|
+
Gemini, Groq, Mistral, Cohere, AWS Bedrock, Azure, and many more).
|
|
24
|
+
|
|
25
|
+
## Capability map
|
|
26
|
+
|
|
27
|
+
- **Modalities** — chat completions, streaming, tool/function calling,
|
|
28
|
+
structured outputs, embeddings, image generation, audio (speech,
|
|
29
|
+
transcription), moderation, web search, OCR, reranking, batch,
|
|
30
|
+
and file operations.
|
|
31
|
+
- **Provider routing** — `provider/model` prefix selects the backend
|
|
32
|
+
(`openai/gpt-4o`, `anthropic/claude-sonnet-4-20250514`,
|
|
33
|
+
`gemini/gemini-2.0-flash`). Set a full `provider/model` as `model_hint` to choose the client provider.
|
|
34
|
+
- **Middleware** — response caching, rate limiting (RPM/TPM), cost tracking,
|
|
35
|
+
budget enforcement (`hard` / `soft`), fallback chains, circuit-breaker
|
|
36
|
+
cooldown, and background health checks.
|
|
37
|
+
- **Search providers (12)** and **OCR providers (4)** — first-class `search`
|
|
38
|
+
and `ocr` methods routed by the same prefix convention.
|
|
39
|
+
- **Proxy server** — `liter-llm api` exposes an OpenAI-compatible gateway with
|
|
40
|
+
22 REST endpoints, virtual API keys, budgets, and SSE streaming.
|
|
41
|
+
- **MCP server** — `liter-llm mcp` exposes 22 tools mirroring the proxy
|
|
42
|
+
endpoints, for MCP-compatible clients (Claude Code, Claude Desktop).
|
|
43
|
+
- **14 language bindings** — same surface, language-native naming (snake_case
|
|
44
|
+
for Python/Rust/Ruby/Go/Elixir/PHP, camelCase for TS/Node/WASM/C#/Java).
|
|
45
|
+
|
|
46
|
+
## When to use the MCP server vs the SDK vs the proxy
|
|
47
|
+
|
|
48
|
+
| Use the … | When you want to … | Entry point |
|
|
49
|
+
|-----------|--------------------|-------------|
|
|
50
|
+
| **MCP server** | Let the agent call LLM APIs directly as tools, with no glue code | `liter-llm mcp --transport stdio` (this plugin auto-registers it) |
|
|
51
|
+
| **SDK / binding** | Write application code that calls LLMs in a specific language | `pip install liter-llm`, `cargo add liter-llm`, etc. |
|
|
52
|
+
| **Proxy** | Give many apps/teams a shared OpenAI-compatible endpoint with keys, budgets, and rate limits | `liter-llm api --config liter-llm-proxy.toml` |
|
|
53
|
+
|
|
54
|
+
Rule of thumb: reach for the MCP server inside an agent session; the SDK when
|
|
55
|
+
building software; the proxy when centralizing access for multiple consumers.
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
### CLI (proxy + MCP server)
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Homebrew (macOS / Linux)
|
|
63
|
+
brew install xberg-io/tap/liter-llm
|
|
64
|
+
|
|
65
|
+
# or run it without a persistent install (the CLI proxy package self-installs the binary)
|
|
66
|
+
pnpm dlx @xberg-io/liter-llm-cli@2.2.3 --help
|
|
67
|
+
|
|
68
|
+
# or download a prebuilt binary from the latest GitHub release:
|
|
69
|
+
# https://github.com/xberg-io/liter-llm/releases/latest
|
|
70
|
+
|
|
71
|
+
# or build from source
|
|
72
|
+
cargo install liter-llm-cli
|
|
73
|
+
|
|
74
|
+
# or Docker (35MB image)
|
|
75
|
+
docker pull ghcr.io/xberg-io/liter-llm
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Language bindings
|
|
79
|
+
|
|
80
|
+
| Language | Install |
|
|
81
|
+
|----------|---------|
|
|
82
|
+
| Python | `pip install liter-llm` |
|
|
83
|
+
| Node.js | `pnpm add @xberg-io/liter-llm` |
|
|
84
|
+
| Rust | `cargo add liter-llm` |
|
|
85
|
+
| Go | `go get github.com/xberg-io/liter-llm/packages/go/v2` |
|
|
86
|
+
| Ruby | `gem install liter_llm` |
|
|
87
|
+
| PHP | `composer require xberg-io/liter-llm` |
|
|
88
|
+
| C# | `dotnet add package XbergIo.LiterLlm` |
|
|
89
|
+
| WASM | `pnpm add @xberg-io/liter-llm-wasm` |
|
|
90
|
+
|
|
91
|
+
## Quick start (Python, async)
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import asyncio
|
|
95
|
+
import os
|
|
96
|
+
from liter_llm import create_client
|
|
97
|
+
from liter_llm._internal_bindings import ChatCompletionRequest
|
|
98
|
+
|
|
99
|
+
async def main() -> None:
|
|
100
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"])
|
|
101
|
+
request = ChatCompletionRequest.from_json(
|
|
102
|
+
'{"model":"openai/gpt-4o","messages":[{"role":"user","content":"Hello!"}]}'
|
|
103
|
+
)
|
|
104
|
+
response = await client.chat(request)
|
|
105
|
+
print(response.choices[0].message.content)
|
|
106
|
+
|
|
107
|
+
asyncio.run(main())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Provider routing
|
|
111
|
+
|
|
112
|
+
The prefix before `/` in the request's `model` selects the provider:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
ChatCompletionRequest.from_json('{"model":"openai/gpt-4o","messages":[...]}')
|
|
116
|
+
ChatCompletionRequest.from_json('{"model":"anthropic/claude-sonnet-4-20250514","messages":[...]}')
|
|
117
|
+
ChatCompletionRequest.from_json('{"model":"gemini/gemini-2.0-flash","messages":[...]}')
|
|
118
|
+
ChatCompletionRequest.from_json('{"model":"groq/llama3-70b","messages":[...]}')
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Client construction resolves credentials for its selected provider from environment variables: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`,
|
|
122
|
+
`GEMINI_API_KEY`, `GROQ_API_KEY`, `MISTRAL_API_KEY`, `COHERE_API_KEY`, and AWS
|
|
123
|
+
credentials for Bedrock. Create a separate client for each provider with matching
|
|
124
|
+
`model_hint` and credentials. Request prefixes change routing but reuse the client
|
|
125
|
+
API key; they do not select a new environment key per request.
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
Pass basic options to `create_client(...)` (`api_key`, `base_url`,
|
|
130
|
+
`timeout_secs`, `max_retries`, `model_hint`), or set the full surface in a
|
|
131
|
+
`liter-llm.toml` (SDK) / `liter-llm-proxy.toml` (proxy) — both auto-discover
|
|
132
|
+
from the cwd upward.
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
# liter-llm.toml
|
|
136
|
+
api_key = "${OPENAI_API_KEY}"
|
|
137
|
+
model_hint = "openai/gpt-4o"
|
|
138
|
+
timeout_secs = 120
|
|
139
|
+
max_retries = 5
|
|
140
|
+
|
|
141
|
+
[cache]
|
|
142
|
+
max_entries = 512
|
|
143
|
+
ttl_seconds = 600
|
|
144
|
+
|
|
145
|
+
[budget]
|
|
146
|
+
global_limit = 50.0
|
|
147
|
+
enforcement = "hard"
|
|
148
|
+
|
|
149
|
+
[rate_limit]
|
|
150
|
+
rpm = 60
|
|
151
|
+
tpm = 100000
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
| Option | Default | Description |
|
|
155
|
+
|--------|---------|-------------|
|
|
156
|
+
| `api_key` | required | Provider key, wrapped in `SecretString` (never logged). |
|
|
157
|
+
| `base_url` | from registry | Override the provider base URL. |
|
|
158
|
+
| `model_hint` | none | Full provider/model used to select the client provider and credentials. |
|
|
159
|
+
| `timeout_secs` | 60 | Request timeout in seconds. |
|
|
160
|
+
| `max_retries` | 3 | Retries on 429/5xx with exponential backoff. |
|
|
161
|
+
| `cache` | none | `max_entries`, `ttl_seconds`. |
|
|
162
|
+
| `budget` | none | `global_limit`, `model_limits`, `enforcement`. |
|
|
163
|
+
| `rate_limit` | none | `rpm`, `tpm`. |
|
|
164
|
+
| `cost_tracking` | false | Per-request cost tracking. |
|
|
165
|
+
| `tracing` | false | OpenTelemetry spans. |
|
|
166
|
+
|
|
167
|
+
## Proxy server
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
liter-llm api --config liter-llm-proxy.toml
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
22 OpenAI-compatible endpoints, model routing by prefix, virtual API keys,
|
|
174
|
+
per-key RPM/TPM limits, cost tracking, budget enforcement, response caching, SSE
|
|
175
|
+
streaming, and an OpenAPI 3.1 spec at `/openapi.json`.
|
|
176
|
+
|
|
177
|
+
## MCP server
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
liter-llm mcp --transport stdio # for Claude Code / Claude Desktop
|
|
181
|
+
liter-llm mcp --transport http --port 3001
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This plugin auto-registers the stdio server via `scripts/mcp-launch.sh`. The 22
|
|
185
|
+
tools mirror the proxy endpoints: `chat`, `embed`, `generate_image`, `speech`,
|
|
186
|
+
`transcribe`, `moderate`, `rerank`, `search`, `ocr`, `list_models`; file ops
|
|
187
|
+
(`create_file`, `list_files`, `retrieve_file`, `delete_file`, `file_content`);
|
|
188
|
+
batch ops (`create_batch`, `list_batches`, `retrieve_batch`, `cancel_batch`);
|
|
189
|
+
and Responses API (`create_response`, `retrieve_response`, `cancel_response`).
|
|
190
|
+
|
|
191
|
+
## Common pitfalls
|
|
192
|
+
|
|
193
|
+
1. **Provider routing** — use `"provider/model"` to select a request backend. Bare
|
|
194
|
+
model names fall back to the configured client provider (OpenAI by default).
|
|
195
|
+
2. **API keys are SecretString** — never logged or serialized. Read from env
|
|
196
|
+
vars; never hardcode.
|
|
197
|
+
3. **Python methods are async** — `await` inside an async context.
|
|
198
|
+
4. **Naming conventions differ** — camelCase for TS/Node/WASM/C#/Java,
|
|
199
|
+
snake_case elsewhere.
|
|
200
|
+
5. **Streaming chunks may have null content** — null-check
|
|
201
|
+
`chunk.choices[0].delta.content` before use.
|
|
202
|
+
6. **Budget modes** — `hard` rejects over-budget requests; `soft` logs and
|
|
203
|
+
allows.
|
|
204
|
+
|
|
205
|
+
## Additional resources
|
|
206
|
+
|
|
207
|
+
- Upstream docs: <https://docs.liter-llm.xberg.io>
|
|
208
|
+
- GitHub: <https://github.com/xberg-io/liter-llm>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:60957d0aa8465f2855ee8ec0ad10f311a31f8a64822760eb1bfbe7d1a559255b
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Liter Llm](SKILL.md) - Universal LLM API client for 174 providers with native bindings for 14 languages. Use when writing code that calls LLM APIs via liter-llm in Python, TypeScript, Rust, Go, Java, C#, Ruby, PHP, Elixir, WASM, or C, when running the OpenAI-compatible proxy, or when calling LLMs through the MCP server. Covers chat, streaming, tool calling, embeddings, image generation, speech, transcription, moderation, web search, OCR, reranking, provider routing, middleware, and configuration.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when sending chat completions through liter-llm and routing to a specific provider via the `provider/model` prefix. Covers the chat call shape, provider routing, model_hint, message roles, and error categories.
|
|
3
|
+
name: liter-llm-calling-llms
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
+
Content-Hash: blake3:d45c8ad7f731f0e0bc91646e677fd7c89b6af31e38944bff4302e7e242a12906
|
|
9
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
10
|
+
Schema-Version: v1
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Calling LLMs
|
|
14
|
+
|
|
15
|
+
Build a `ChatCompletionRequest` and send it with `client.chat(request)`. Create
|
|
16
|
+
the client with `create_client(...)`. The model string is `provider/model`; the
|
|
17
|
+
prefix selects the backend.
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import asyncio, json, os
|
|
21
|
+
from liter_llm import create_client
|
|
22
|
+
from liter_llm._internal_bindings import ChatCompletionRequest
|
|
23
|
+
|
|
24
|
+
async def main() -> None:
|
|
25
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"])
|
|
26
|
+
request = ChatCompletionRequest.from_json(json.dumps({
|
|
27
|
+
"model": "openai/gpt-4o",
|
|
28
|
+
"messages": [
|
|
29
|
+
{"role": "system", "content": "You are concise."},
|
|
30
|
+
{"role": "user", "content": "Name three Rust crates for HTTP."},
|
|
31
|
+
],
|
|
32
|
+
}))
|
|
33
|
+
response = await client.chat(request)
|
|
34
|
+
print(response.choices[0].message.content)
|
|
35
|
+
|
|
36
|
+
asyncio.run(main())
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Provider routing
|
|
40
|
+
|
|
41
|
+
The model string's prefix selects the provider; build a request per backend:
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
ChatCompletionRequest.from_json('{"model":"anthropic/claude-sonnet-4-20250514","messages":[...]}')
|
|
45
|
+
ChatCompletionRequest.from_json('{"model":"gemini/gemini-2.0-flash","messages":[...]}')
|
|
46
|
+
ChatCompletionRequest.from_json('{"model":"groq/llama3-70b","messages":[...]}')
|
|
47
|
+
ChatCompletionRequest.from_json('{"model":"mistral/mistral-large-latest","messages":[...]}')
|
|
48
|
+
ChatCompletionRequest.from_json('{"model":"bedrock/anthropic.claude-v2","messages":[...]}')
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Set a full `provider/model` as `model_hint` at construction to select the client
|
|
52
|
+
provider and credential source. Requests without a recognized prefix fall back to
|
|
53
|
+
that client provider:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"], model_hint="openai/gpt-4o")
|
|
57
|
+
# the request model can now omit the provider prefix:
|
|
58
|
+
request = ChatCompletionRequest.from_json('{"model":"gpt-4o","messages":[...]}')
|
|
59
|
+
await client.chat(request) # routes to OpenAI
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Notes
|
|
63
|
+
|
|
64
|
+
- Keys come from env vars (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, …); never
|
|
65
|
+
hardcode them.
|
|
66
|
+
- Without a recognized prefix, requests fall back to the client's configured provider;
|
|
67
|
+
a client without `model_hint` defaults to OpenAI.
|
|
68
|
+
- Environment credentials are selected when constructing the client, not independently
|
|
69
|
+
for every request. Use a separate client with matching `model_hint` and credentials
|
|
70
|
+
for each provider. A prefix changes routing but does not switch the client's API key.
|
|
71
|
+
- Python errors are typed exceptions exported from `liter_llm`:
|
|
72
|
+
`AuthenticationError`, `RateLimitedError`, `BadRequestError`,
|
|
73
|
+
`ContextWindowExceededError`, `ContentPolicyError`, `NotFoundError`,
|
|
74
|
+
`ServerError`, `ServiceUnavailableError`, `LiterLlmTimeoutError`,
|
|
75
|
+
`BudgetExceededError` — all subclasses of `LiterLlmError`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:9f87cef1b6478cba89e995bc6d58c203c63ad6668e495f5cda9aeca30caf20f0
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Calling Llms](SKILL.md) - Use when sending chat completions through liter-llm and routing to a specific provider via the `provider/model` prefix. Covers the chat call shape, provider routing, model_hint, message roles, and error categories.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when generating embeddings, calling the 12 web-search providers, or running OCR over documents with the 4 OCR providers through liter-llm. Covers embed, search, and ocr methods plus reranking.
|
|
3
|
+
name: liter-llm-embeddings-and-search
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
+
Content-Hash: blake3:b5eb4ea6ac25a0cf5bda7f68424924e9760e43055ad34b314f5030588a6c3fc8
|
|
9
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
10
|
+
Schema-Version: v1
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Embeddings and Search
|
|
14
|
+
|
|
15
|
+
liter-llm exposes embeddings, web search (12 providers), OCR (4 providers), and
|
|
16
|
+
reranking through the same `provider/model` routing convention.
|
|
17
|
+
|
|
18
|
+
## Embeddings
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
import asyncio, os
|
|
22
|
+
from liter_llm import create_client
|
|
23
|
+
from liter_llm._internal_bindings import EmbeddingRequest
|
|
24
|
+
|
|
25
|
+
async def main() -> None:
|
|
26
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"])
|
|
27
|
+
request = EmbeddingRequest.from_json(
|
|
28
|
+
'{"model":"openai/text-embedding-3-small","input":["first document","second document"]}'
|
|
29
|
+
)
|
|
30
|
+
response = await client.embed(request)
|
|
31
|
+
for item in response.data:
|
|
32
|
+
print(len(item.embedding))
|
|
33
|
+
|
|
34
|
+
asyncio.run(main())
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Many embedding models support dimension selection and base64 output; set
|
|
38
|
+
`dimensions` / `encoding_format` in the request where the provider allows it.
|
|
39
|
+
|
|
40
|
+
## Web search (12 providers)
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from liter_llm._internal_bindings import SearchRequest
|
|
44
|
+
|
|
45
|
+
client = create_client(api_key=os.environ["BRAVE_API_KEY"])
|
|
46
|
+
request = SearchRequest.from_json(
|
|
47
|
+
'{"model":"brave/web-search","query":"What is the Rust programming language?","max_results":5}'
|
|
48
|
+
)
|
|
49
|
+
response = await client.search(request)
|
|
50
|
+
for result in response.results:
|
|
51
|
+
print(result.title, result.url)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## OCR (4 providers)
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from liter_llm._internal_bindings import OcrRequest
|
|
58
|
+
|
|
59
|
+
client = create_client(api_key=os.environ["MISTRAL_API_KEY"])
|
|
60
|
+
request = OcrRequest.from_json(
|
|
61
|
+
'{"model":"mistral/mistral-ocr-latest",'
|
|
62
|
+
'"document":{"type":"document_url","url":"https://example.com/invoice.pdf"}}'
|
|
63
|
+
)
|
|
64
|
+
response = await client.ocr(request)
|
|
65
|
+
for page in response.pages:
|
|
66
|
+
print(page.index, page.markdown[:100])
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Reranking
|
|
70
|
+
|
|
71
|
+
Build a `RerankRequest` (model, query, documents) and call `client.rerank(request)`
|
|
72
|
+
to score and order candidate documents against a query for retrieval pipelines —
|
|
73
|
+
combine it with `embed` for hybrid retrieval. Each result carries `index` and
|
|
74
|
+
`relevance_score`. Routing follows the same `provider/model` convention.
|
|
75
|
+
|
|
76
|
+
## Notes
|
|
77
|
+
|
|
78
|
+
- Search and OCR providers each need their own API key (e.g. `BRAVE_API_KEY`,
|
|
79
|
+
`MISTRAL_API_KEY`); read them from env vars.
|
|
80
|
+
- See the upstream provider reference for the full list of the 12 search and 4
|
|
81
|
+
OCR backends and their model identifiers.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:06bd68d2dd91f5a85234a7fa75f4e8f90c5bed83ff6200cf601fc12814c38ddb
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Embeddings And Search](SKILL.md) - Use when generating embeddings, calling the 12 web-search providers, or running OCR over documents with the 4 OCR providers through liter-llm. Covers embed, search, and ocr methods plus reranking.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when running the `liter-llm api` OpenAI-compatible gateway — virtual keys, per-key rate limits, budgets, cost tracking, and model routing. Covers the TOML config and the 22 REST endpoints.
|
|
3
|
+
name: liter-llm-running-the-proxy
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
+
Content-Hash: blake3:b0a0178eae0e6882ae032983d8927142b6db16add6ab9cab391538db2f202eb4
|
|
9
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
10
|
+
Schema-Version: v1
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Running the Proxy
|
|
14
|
+
|
|
15
|
+
`liter-llm api` is a drop-in OpenAI-compatible gateway: 22 REST endpoints that
|
|
16
|
+
route to 174 providers, with multi-tenant virtual keys, rate limits, budgets,
|
|
17
|
+
and cost tracking.
|
|
18
|
+
|
|
19
|
+
## Start it
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
liter-llm api --config liter-llm-proxy.toml
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The proxy auto-discovers `liter-llm-proxy.toml` in the current directory.
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
```toml
|
|
30
|
+
[server]
|
|
31
|
+
host = "0.0.0.0"
|
|
32
|
+
port = 4000
|
|
33
|
+
|
|
34
|
+
[general]
|
|
35
|
+
master_key = "${LITER_LLM_MASTER_KEY}"
|
|
36
|
+
|
|
37
|
+
# Each [[models]] entry maps a routable name to a provider/model and its key.
|
|
38
|
+
[[models]]
|
|
39
|
+
name = "gpt-4o"
|
|
40
|
+
provider_model = "openai/gpt-4o"
|
|
41
|
+
api_key = "${OPENAI_API_KEY}"
|
|
42
|
+
|
|
43
|
+
[[models]]
|
|
44
|
+
name = "claude-sonnet"
|
|
45
|
+
provider_model = "anthropic/claude-sonnet-4-20250514"
|
|
46
|
+
api_key = "${ANTHROPIC_API_KEY}"
|
|
47
|
+
|
|
48
|
+
# Virtual keys scope which configured model names a caller may use.
|
|
49
|
+
[[keys]]
|
|
50
|
+
key = "sk-team-frontend"
|
|
51
|
+
models = ["gpt-4o", "claude-sonnet"]
|
|
52
|
+
rpm = 60
|
|
53
|
+
tpm = 100000
|
|
54
|
+
budget_limit = 50.0
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`${ENV_VAR}` interpolation keeps secrets out of the file.
|
|
58
|
+
|
|
59
|
+
## Call it like OpenAI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
curl http://localhost:4000/v1/chat/completions \
|
|
63
|
+
-H "Authorization: Bearer sk-team-frontend" \
|
|
64
|
+
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Notes
|
|
68
|
+
|
|
69
|
+
- Virtual keys scope which models a caller may use and carry their own RPM/TPM
|
|
70
|
+
and budget limits; the `master_key` administers them.
|
|
71
|
+
- The OpenAPI 3.1 spec is served at `/openapi.json`.
|
|
72
|
+
- Endpoints cover chat, embeddings, images, audio, moderations, files, batches,
|
|
73
|
+
responses, and model listing.
|
|
74
|
+
- Docker: `docker run -p 4000:4000 -e LITER_LLM_MASTER_KEY=sk-key ghcr.io/xberg-io/liter-llm`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:157fa991ddf536a4cd3c9413a6bf2f44b25762efcb64ad3f7732731e4cb2f14e
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Running The Proxy](SKILL.md) - Use when running the `liter-llm api` OpenAI-compatible gateway — virtual keys, per-key rate limits, budgets, cost tracking, and model routing. Covers the TOML config and the 22 REST endpoints.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when streaming tokens incrementally from an LLM via liter-llm over SSE or async iterators. Covers chat_stream, delta handling, and null-content chunks.
|
|
3
|
+
name: liter-llm-streaming-responses
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
+
Content-Hash: blake3:6de558b3f33f35e9e7ef76dc64d4ebd199a8a831dc28e306698f18fc16933b4e
|
|
9
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
10
|
+
Schema-Version: v1
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Streaming Responses
|
|
14
|
+
|
|
15
|
+
Use `chat_stream(...)` to receive tokens as they are produced instead of waiting
|
|
16
|
+
for the full completion. The proxy streams over SSE; bindings expose async
|
|
17
|
+
iterators.
|
|
18
|
+
|
|
19
|
+
## Python
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import asyncio, os
|
|
23
|
+
from liter_llm import create_client
|
|
24
|
+
from liter_llm._internal_bindings import ChatCompletionRequest
|
|
25
|
+
|
|
26
|
+
async def main() -> None:
|
|
27
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"])
|
|
28
|
+
request = ChatCompletionRequest.from_json(
|
|
29
|
+
'{"model":"openai/gpt-4o","messages":[{"role":"user","content":"Tell me a story"}],"stream":true}'
|
|
30
|
+
)
|
|
31
|
+
async for chunk in client.chat_stream(request):
|
|
32
|
+
if chunk.choices and chunk.choices[0].delta.content:
|
|
33
|
+
print(chunk.choices[0].delta.content, end="", flush=True)
|
|
34
|
+
print()
|
|
35
|
+
|
|
36
|
+
asyncio.run(main())
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## TypeScript
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { createClient } from "@xberg-io/liter-llm";
|
|
43
|
+
|
|
44
|
+
const client = createClient(process.env.OPENAI_API_KEY!);
|
|
45
|
+
const chunks = await client.chatStream({
|
|
46
|
+
model: "openai/gpt-4o",
|
|
47
|
+
messages: [{ role: "user", content: "Tell me a story" }],
|
|
48
|
+
});
|
|
49
|
+
for await (const chunk of chunks) {
|
|
50
|
+
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "");
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- The first and last chunks often carry null content. Always null-check
|
|
57
|
+
`chunk.choices[0].delta.content` (Python) or
|
|
58
|
+
`chunk.choices[0]?.delta?.content` (TypeScript) before using it.
|
|
59
|
+
- Tool-call deltas arrive in `delta.tool_calls` (Python) /
|
|
60
|
+
`delta.toolCalls` (TypeScript); accumulate `function.arguments` fragments
|
|
61
|
+
across chunks before parsing.
|
|
62
|
+
- Through the proxy, request streaming with `"stream": true` on
|
|
63
|
+
`/v1/chat/completions`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:614ded1eb401cb2a27434f2b901aa4cab62f6be4964c0882783e21592ebb347e
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Streaming Responses](SKILL.md) - Use when streaming tokens incrementally from an LLM via liter-llm over SSE or async iterators. Covers chat_stream, delta handling, and null-content chunks.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Use when defining functions/tools for an LLM to call through liter-llm, or requesting structured JSON outputs. Covers tool schemas, tool_calls handling, and response formats.
|
|
3
|
+
name: liter-llm-tool-calling
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!--
|
|
7
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
8
|
+
Content-Hash: blake3:f26049693a8fa3229f675eefc4097cfa0aeb669a5744967a7ab3614009a80310
|
|
9
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
10
|
+
Schema-Version: v1
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Tool Calling
|
|
14
|
+
|
|
15
|
+
Pass a `tools` array of function definitions; the model may respond with
|
|
16
|
+
`tool_calls` instead of (or alongside) text. Execute the named function and feed
|
|
17
|
+
the result back as a `tool` message.
|
|
18
|
+
|
|
19
|
+
## Python
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import asyncio, json, os
|
|
23
|
+
from liter_llm import create_client
|
|
24
|
+
from liter_llm._internal_bindings import ChatCompletionRequest
|
|
25
|
+
|
|
26
|
+
payload = {
|
|
27
|
+
"model": "openai/gpt-4o",
|
|
28
|
+
"messages": [{"role": "user", "content": "What is the weather in Berlin?"}],
|
|
29
|
+
"tools": [
|
|
30
|
+
{
|
|
31
|
+
"type": "function",
|
|
32
|
+
"function": {
|
|
33
|
+
"name": "get_weather",
|
|
34
|
+
"description": "Get the current weather for a location",
|
|
35
|
+
"parameters": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"properties": {
|
|
38
|
+
"location": {"type": "string", "description": "City name"},
|
|
39
|
+
},
|
|
40
|
+
"required": ["location"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"tool_choice": "auto",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async def main() -> None:
|
|
49
|
+
client = create_client(api_key=os.environ["OPENAI_API_KEY"])
|
|
50
|
+
request = ChatCompletionRequest.from_json(json.dumps(payload))
|
|
51
|
+
response = await client.chat(request)
|
|
52
|
+
for call in response.choices[0].message.tool_calls or []:
|
|
53
|
+
print(call.function.name, call.function.arguments) # arguments is a JSON string
|
|
54
|
+
|
|
55
|
+
asyncio.run(main())
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Structured outputs
|
|
59
|
+
|
|
60
|
+
Request strict JSON with `response_format`:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
request = ChatCompletionRequest.from_json(json.dumps({
|
|
64
|
+
"model": "openai/gpt-4o",
|
|
65
|
+
"messages": [{"role": "user", "content": "Extract name and age as JSON."}],
|
|
66
|
+
"response_format": {"type": "json_object"},
|
|
67
|
+
}))
|
|
68
|
+
response = await client.chat(request)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Notes
|
|
72
|
+
|
|
73
|
+
- `function.arguments` is a JSON **string** — parse it before use.
|
|
74
|
+
- Append each tool result as a message with `role="tool"` and the matching
|
|
75
|
+
`tool_call_id`, then call `chat` again to let the model continue.
|
|
76
|
+
- Tool support and JSON-mode availability vary by provider; check the provider
|
|
77
|
+
reference if a model ignores `tools`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AI-RULEZ :: GENERATED FILE — DO NOT EDIT
|
|
3
|
+
Content-Hash: blake3:27b7d413268994b2de57be9cce2eba3277c235244cf4dd3d8018746f2b98eeed
|
|
4
|
+
Source-Hash: blake3:88c87867f5079f6c8fcb582aee913cca4f6feefe541c5361d2307a9b45233f97
|
|
5
|
+
Schema-Version: v1
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Concepts
|
|
9
|
+
|
|
10
|
+
* [Tool Calling](SKILL.md) - Use when defining functions/tools for an LLM to call through liter-llm, or requesting structured JSON outputs. Covers tool schemas, tool_calls handling, and response formats.
|