@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.
Files changed (39) hide show
  1. package/.opencode/ai-rulez-bundle.json +25 -16
  2. package/.opencode/ai-rulez-content.js +3 -3
  3. package/.opencode/commands/liter-llm-chat.md +14 -0
  4. package/.opencode/commands/liter-llm-stream.md +14 -0
  5. package/.opencode/plugins/liter-llm.js +9 -21
  6. package/.opencode/skills/liter-llm/SKILL.md +18 -24
  7. package/.opencode/skills/liter-llm/index.md +10 -0
  8. package/.opencode/skills/{calling-llms → liter-llm-calling-llms}/SKILL.md +13 -7
  9. package/.opencode/skills/liter-llm-calling-llms/index.md +10 -0
  10. package/.opencode/skills/{embeddings-and-search → liter-llm-embeddings-and-search}/SKILL.md +3 -3
  11. package/.opencode/skills/liter-llm-embeddings-and-search/index.md +10 -0
  12. package/.opencode/skills/{running-the-proxy → liter-llm-running-the-proxy}/SKILL.md +3 -3
  13. package/.opencode/skills/liter-llm-running-the-proxy/index.md +10 -0
  14. package/.opencode/skills/{streaming-responses → liter-llm-streaming-responses}/SKILL.md +3 -3
  15. package/.opencode/skills/liter-llm-streaming-responses/index.md +10 -0
  16. package/.opencode/skills/{tool-calling → liter-llm-tool-calling}/SKILL.md +3 -3
  17. package/.opencode/skills/liter-llm-tool-calling/index.md +10 -0
  18. package/.opencode/skills/liter-llm-using-the-mcp-server/SKILL.md +91 -0
  19. package/.opencode/skills/liter-llm-using-the-mcp-server/index.md +10 -0
  20. package/.pi/prompts/liter-llm-chat.md +14 -0
  21. package/.pi/prompts/liter-llm-stream.md +14 -0
  22. package/.pi/skills/liter-llm/SKILL.md +208 -0
  23. package/.pi/skills/liter-llm/index.md +10 -0
  24. package/.pi/skills/liter-llm-calling-llms/SKILL.md +75 -0
  25. package/.pi/skills/liter-llm-calling-llms/index.md +10 -0
  26. package/.pi/skills/liter-llm-embeddings-and-search/SKILL.md +81 -0
  27. package/.pi/skills/liter-llm-embeddings-and-search/index.md +10 -0
  28. package/.pi/skills/liter-llm-running-the-proxy/SKILL.md +74 -0
  29. package/.pi/skills/liter-llm-running-the-proxy/index.md +10 -0
  30. package/.pi/skills/liter-llm-streaming-responses/SKILL.md +63 -0
  31. package/.pi/skills/liter-llm-streaming-responses/index.md +10 -0
  32. package/.pi/skills/liter-llm-tool-calling/SKILL.md +77 -0
  33. package/.pi/skills/liter-llm-tool-calling/index.md +10 -0
  34. package/.pi/skills/liter-llm-using-the-mcp-server/SKILL.md +91 -0
  35. package/.pi/skills/liter-llm-using-the-mcp-server/index.md +10 -0
  36. package/README.md +151 -14
  37. package/package.json +76 -21
  38. package/scripts/mcp-launch.sh +6 -5
  39. 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.