llmshim 0.1.26__tar.gz → 0.2.1__tar.gz
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.
- llmshim-0.2.1/.github/workflows/pages.yml +50 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/CLAUDE.md +5 -5
- {llmshim-0.1.26 → llmshim-0.2.1}/Cargo.lock +1 -1
- {llmshim-0.1.26 → llmshim-0.2.1}/Cargo.toml +1 -1
- {llmshim-0.1.26 → llmshim-0.2.1}/PKG-INFO +3 -3
- {llmshim-0.1.26 → llmshim-0.2.1}/README.md +18 -18
- llmshim-0.2.1/docs/.gitignore +1 -0
- llmshim-0.2.1/docs/book.toml +22 -0
- llmshim-0.2.1/docs/mermaid-init.js +39 -0
- llmshim-0.2.1/docs/mermaid.min.js +2609 -0
- llmshim-0.2.1/docs/src/SUMMARY.md +47 -0
- llmshim-0.2.1/docs/src/concepts/contracts.md +83 -0
- llmshim-0.2.1/docs/src/concepts/conversations.md +56 -0
- llmshim-0.2.1/docs/src/concepts/portability.md +81 -0
- llmshim-0.2.1/docs/src/concepts/routing.md +87 -0
- llmshim-0.2.1/docs/src/concepts/translation-flow.md +74 -0
- llmshim-0.2.1/docs/src/guides/fallbacks.md +107 -0
- llmshim-0.2.1/docs/src/guides/images.md +82 -0
- llmshim-0.2.1/docs/src/guides/native-controls.md +133 -0
- llmshim-0.2.1/docs/src/guides/reasoning.md +181 -0
- llmshim-0.2.1/docs/src/guides/streaming.md +118 -0
- llmshim-0.2.1/docs/src/guides/tools.md +164 -0
- llmshim-0.2.1/docs/src/introduction.md +73 -0
- llmshim-0.2.1/docs/src/proxy/deployment.md +81 -0
- llmshim-0.2.1/docs/src/proxy/http-api.md +175 -0
- llmshim-0.2.1/docs/src/proxy/scaling.md +100 -0
- llmshim-0.2.1/docs/src/reference/api.md +23 -0
- llmshim-0.2.1/docs/src/reference/cli.md +76 -0
- llmshim-0.2.1/docs/src/reference/configuration.md +102 -0
- llmshim-0.2.1/docs/src/reference/errors.md +90 -0
- llmshim-0.2.1/docs/src/reference/models.md +123 -0
- llmshim-0.2.1/docs/src/reference/providers.md +54 -0
- llmshim-0.2.1/docs/src/reference/request-fields.md +112 -0
- llmshim-0.2.1/docs/src/reference/surfaces.md +56 -0
- llmshim-0.2.1/docs/src/start/choose.md +35 -0
- llmshim-0.2.1/docs/src/start/cli.md +77 -0
- llmshim-0.2.1/docs/src/start/clients.md +129 -0
- llmshim-0.2.1/docs/src/start/configure.md +75 -0
- llmshim-0.2.1/docs/src/start/proxy.md +106 -0
- llmshim-0.2.1/docs/src/start/rust.md +115 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/main.rs +0 -6
- llmshim-0.2.1/src/models.rs +408 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/anthropic.rs +107 -3
- {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/openai.rs +4 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/xai.rs +4 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/types.rs +1 -1
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_gemini.rs +1 -1
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_thinking.rs +73 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_tool_roundtrip.rs +2 -2
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_vision.rs +1 -1
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_anthropic.rs +179 -2
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_fast_mode.rs +2 -4
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_gemini.rs +30 -8
- llmshim-0.2.1/tests/unit_models.rs +182 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_openai.rs +22 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_router.rs +2 -2
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_tools.rs +6 -12
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_xai.rs +50 -76
- llmshim-0.1.26/docs/reasoning.md +0 -143
- llmshim-0.1.26/src/models.rs +0 -174
- llmshim-0.1.26/tests/unit_models.rs +0 -69
- {llmshim-0.1.26 → llmshim-0.2.1}/.github/workflows/release.yml +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/.gitignore +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/LICENSE +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/bench.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/bench_python.py +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/loadtest.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/examples/chat.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/examples/stream.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/__init__.py +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/_client.py +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/_server.py +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/types.py +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/pyproject.toml +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/client.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/config.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/env.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/error.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/fallback.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/lib.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/log.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/provider.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/gemini.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/mod.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/convert.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/error.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/handlers.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/mod.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/ratelimit.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/router.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/src/vision.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_fallback.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_gemini_tools.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_long_context.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_multimodel.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_proxy.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_fallback.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_log.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_multimodel.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_proxy.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_proxy_convert.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_sse.rs +0 -0
- {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_vision.rs +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Deploy docs to GitHub Pages
|
|
2
|
+
|
|
3
|
+
# Gated to main: this only runs after the docs branch is merged. It never
|
|
4
|
+
# fires on feature branches or pull requests.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
paths:
|
|
9
|
+
- 'docs/**'
|
|
10
|
+
- '.github/workflows/pages.yml'
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pages: write
|
|
16
|
+
id-token: write
|
|
17
|
+
|
|
18
|
+
# Allow one concurrent deployment; let an in-progress deploy finish.
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: false
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- name: Install mdBook + mermaid preprocessor
|
|
29
|
+
uses: taiki-e/install-action@v2
|
|
30
|
+
with:
|
|
31
|
+
# Pin both: mdbook-mermaid must match mdbook's preprocessor protocol
|
|
32
|
+
# (an unpinned mismatch fails with "Unable to parse the input").
|
|
33
|
+
tool: mdbook@0.4.52,mdbook-mermaid@0.16.2
|
|
34
|
+
- name: Build book
|
|
35
|
+
run: mdbook build docs
|
|
36
|
+
- name: Upload Pages artifact
|
|
37
|
+
uses: actions/upload-pages-artifact@v3
|
|
38
|
+
with:
|
|
39
|
+
path: docs/book
|
|
40
|
+
|
|
41
|
+
deploy:
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment:
|
|
45
|
+
name: github-pages
|
|
46
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
47
|
+
steps:
|
|
48
|
+
- name: Deploy to GitHub Pages
|
|
49
|
+
id: deployment
|
|
50
|
+
uses: actions/deploy-pages@v4
|
|
@@ -8,14 +8,14 @@ A pure Rust LLM API translation layer. Takes OpenAI-format JSON requests, transl
|
|
|
8
8
|
|
|
9
9
|
**Published on crates.io as `llmshim`** — https://crates.io/crates/llmshim
|
|
10
10
|
|
|
11
|
-
This is a public crate. Do NOT make breaking changes to `pub` items in `src/lib.rs`, `src/router.rs`, `src/provider.rs`, `src/error.rs`, `src/fallback.rs`, `src/log.rs`, `src/config.rs`, `src/models.rs`, or `src/vision.rs` without a semver bump.
|
|
11
|
+
This is a public crate on crates.io. Do NOT make breaking changes to `pub` items in `src/lib.rs`, `src/router.rs`, `src/provider.rs`, `src/error.rs`, `src/fallback.rs`, `src/log.rs`, `src/config.rs`, `src/models.rs`, or `src/vision.rs` without a semver bump.
|
|
12
12
|
|
|
13
13
|
## Supported models
|
|
14
14
|
|
|
15
15
|
- **OpenAI:** `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.5-pro`, `gpt-5.4`, `gpt-5.4-pro`, `gpt-5.4-mini`, `gpt-5.4-nano`
|
|
16
16
|
- **Anthropic:** `claude-opus-4-8`, `claude-sonnet-5`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001`
|
|
17
|
-
- **Gemini:** `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3
|
|
18
|
-
- **xAI:** `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning
|
|
17
|
+
- **Gemini:** `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-flash-preview`
|
|
18
|
+
- **xAI:** `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning`
|
|
19
19
|
|
|
20
20
|
## Build & Test
|
|
21
21
|
|
|
@@ -70,7 +70,7 @@ Image content blocks are translated between providers automatically. Users can s
|
|
|
70
70
|
|
|
71
71
|
### Multi-model conversations
|
|
72
72
|
|
|
73
|
-
Each provider sanitizes messages from other providers in `transform_request`. OpenAI's `annotations`/`refusal` stripped for Anthropic/Gemini. `reasoning_content` stripped
|
|
73
|
+
Each provider sanitizes messages from other providers in `transform_request`. OpenAI's `annotations`/`refusal` stripped for Anthropic/Gemini. `reasoning_content` is stripped by other providers, but **Anthropic reconstructs a native `thinking` block** from `reasoning_content` + `reasoning_signature` (and `redacted_thinking` from `redacted_reasoning_content`) as the first block of the assistant turn, so extended-thinking + tool-use round-trips losslessly (surfaced on responses incl. streaming; opaque signatures are stripped by other providers so they never leak cross-provider; no signature → still stripped). Symmetric to the tool-call `thought_signature` round-trip. Tool calls normalized to OpenAI format in responses, translated back per-provider on input.
|
|
74
74
|
|
|
75
75
|
### Provider extension namespaces (`x-anthropic`, `x-gemini`)
|
|
76
76
|
|
|
@@ -81,7 +81,7 @@ Callers pass provider-specific controls under these keys. Each provider copies w
|
|
|
81
81
|
|
|
82
82
|
### Unified reasoning controls
|
|
83
83
|
|
|
84
|
-
Two knobs work across every provider: `reasoning_effort` (`none|low|medium|high|xhigh|max`) and `reasoning_mode` (`standard|pro`). Each provider transform maps them to its native dialect, **clamping to the nearest tier the target model accepts** (all boundaries verified live — e.g. `max` is native only on OpenAI gpt-5.6; Anthropic 4.6 rejects `xhigh` but has `max`; Gemini's enum tops out at `high`; xAI grok-4.20 models reject any reasoning param). `mode: "pro"` is native on OpenAI gpt-5.6/-pro models (`reasoning.mode`), emulated as a one-tier effort bump elsewhere; explicit `none` always wins. Native passthrough (`x-openai.reasoning`, `x-anthropic.thinking`, `x-gemini.thinkingConfig`) bypasses the mapping entirely and always takes precedence. **Full per-provider mapping tables: `docs/reasoning.md`** — update it and the pinning tests in `tests/unit_*.rs` together whenever a mapping changes.
|
|
84
|
+
Two knobs work across every provider: `reasoning_effort` (`none|low|medium|high|xhigh|max`) and `reasoning_mode` (`standard|pro`). Each provider transform maps them to its native dialect, **clamping to the nearest tier the target model accepts** (all boundaries verified live — e.g. `max` is native only on OpenAI gpt-5.6; Anthropic 4.6 rejects `xhigh` but has `max`; Gemini's enum tops out at `high`; xAI grok-4.20 models reject any reasoning param). `mode: "pro"` is native on OpenAI gpt-5.6/-pro models (`reasoning.mode`), emulated as a one-tier effort bump elsewhere; explicit `none` always wins. Native passthrough (`x-openai.reasoning`, `x-anthropic.thinking`, `x-gemini.thinkingConfig`) bypasses the mapping entirely and always takes precedence. **Full per-provider mapping tables: `docs/src/guides/reasoning.md`** — update it and the pinning tests in `tests/unit_*.rs` together whenever a mapping changes.
|
|
85
85
|
|
|
86
86
|
### Tool format translation
|
|
87
87
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llmshim
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -222,8 +222,8 @@ billed provider calls; run it only when you deliberately want to hit real APIs.
|
|
|
222
222
|
|----------|--------|
|
|
223
223
|
| OpenAI | `gpt-5.5`, `gpt-5.5-pro`, `gpt-5.4`, `gpt-5.4-pro`, `gpt-5.4-mini`, `gpt-5.4-nano` |
|
|
224
224
|
| Anthropic | `claude-opus-4-8`, `claude-sonnet-5`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001` |
|
|
225
|
-
| Gemini | `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3
|
|
226
|
-
| xAI | `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning
|
|
225
|
+
| Gemini | `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-flash-preview` |
|
|
226
|
+
| xAI | `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning` |
|
|
227
227
|
|
|
228
228
|
Call `llmshim.models()` for the live list filtered to your configured providers.
|
|
229
229
|
|
|
@@ -72,7 +72,7 @@ async fn main() {
|
|
|
72
72
|
let router = llmshim::router::Router::from_env();
|
|
73
73
|
|
|
74
74
|
let request = json!({
|
|
75
|
-
"model": "claude-sonnet-
|
|
75
|
+
"model": "claude-sonnet-5",
|
|
76
76
|
"messages": [{"role": "user", "content": "What is Rust?"}],
|
|
77
77
|
"max_tokens": 500,
|
|
78
78
|
});
|
|
@@ -93,7 +93,7 @@ use serde_json::json;
|
|
|
93
93
|
|
|
94
94
|
let router = llmshim::router::Router::from_env();
|
|
95
95
|
let request = json!({
|
|
96
|
-
"model": "gpt-5.
|
|
96
|
+
"model": "gpt-5.6-sol",
|
|
97
97
|
"messages": [{"role": "user", "content": "Write a haiku about Rust."}],
|
|
98
98
|
"max_tokens": 128,
|
|
99
99
|
});
|
|
@@ -142,7 +142,7 @@ llmshim proxy
|
|
|
142
142
|
```bash
|
|
143
143
|
curl http://localhost:3000/v1/chat \
|
|
144
144
|
-H "Content-Type: application/json" \
|
|
145
|
-
-d '{"model":"claude-sonnet-
|
|
145
|
+
-d '{"model":"claude-sonnet-5","messages":[{"role":"user","content":"Hi"}],"config":{"max_tokens":100}}'
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
| Method | Path | Description |
|
|
@@ -206,14 +206,14 @@ import llmshim
|
|
|
206
206
|
# Keys can also come from env vars or `llmshim configure`.
|
|
207
207
|
llmshim.configure(anthropic="sk-ant-...", openai="sk-...")
|
|
208
208
|
|
|
209
|
-
resp = llmshim.chat("claude-sonnet-
|
|
209
|
+
resp = llmshim.chat("claude-sonnet-5", "Hello!", max_tokens=500)
|
|
210
210
|
print(resp["message"]["content"])
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
**Streaming:**
|
|
214
214
|
|
|
215
215
|
```python
|
|
216
|
-
for event in llmshim.stream("claude-sonnet-
|
|
216
|
+
for event in llmshim.stream("claude-sonnet-5", "Write a poem"):
|
|
217
217
|
if event["type"] == "content":
|
|
218
218
|
print(event["text"], end="", flush=True)
|
|
219
219
|
elif event["type"] == "usage":
|
|
@@ -225,13 +225,13 @@ for event in llmshim.stream("claude-sonnet-4-6", "Write a poem"):
|
|
|
225
225
|
```python
|
|
226
226
|
messages = [{"role": "user", "content": "What is a closure?"}]
|
|
227
227
|
|
|
228
|
-
r1 = llmshim.chat("claude-sonnet-
|
|
228
|
+
r1 = llmshim.chat("claude-sonnet-5", messages, max_tokens=500)
|
|
229
229
|
print(f"Claude: {r1['message']['content']}")
|
|
230
230
|
|
|
231
231
|
messages.append({"role": "assistant", "content": r1["message"]["content"]})
|
|
232
232
|
messages.append({"role": "user", "content": "Now explain it differently."})
|
|
233
233
|
|
|
234
|
-
r2 = llmshim.chat("gpt-5.
|
|
234
|
+
r2 = llmshim.chat("gpt-5.6-sol", messages, max_tokens=500)
|
|
235
235
|
print(f"GPT: {r2['message']['content']}")
|
|
236
236
|
```
|
|
237
237
|
|
|
@@ -251,7 +251,7 @@ tools = [{
|
|
|
251
251
|
},
|
|
252
252
|
}]
|
|
253
253
|
|
|
254
|
-
resp = llmshim.chat("claude-sonnet-
|
|
254
|
+
resp = llmshim.chat("claude-sonnet-5", "Weather in Tokyo?", max_tokens=500, tools=tools)
|
|
255
255
|
for tc in resp["message"].get("tool_calls", []):
|
|
256
256
|
print(f"{tc['function']['name']}({tc['function']['arguments']})")
|
|
257
257
|
```
|
|
@@ -260,7 +260,7 @@ for tc in resp["message"].get("tool_calls", []):
|
|
|
260
260
|
|
|
261
261
|
```python
|
|
262
262
|
resp = llmshim.chat(
|
|
263
|
-
"claude-sonnet-
|
|
263
|
+
"claude-sonnet-5",
|
|
264
264
|
"Solve: x^2 - 5x + 6 = 0",
|
|
265
265
|
max_tokens=4000,
|
|
266
266
|
reasoning_effort="high", # none | low | medium | high | xhigh | max
|
|
@@ -270,16 +270,16 @@ print(resp["reasoning"]) # thinking content
|
|
|
270
270
|
print(resp["message"]["content"]) # answer
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
llmshim maps these to each provider's native control (OpenAI `reasoning.effort`/`mode`, Anthropic adaptive thinking, Gemini `thinkingLevel`, xAI `reasoning.effort`), clamping to the nearest tier the target model actually supports — so `reasoning_effort="max"` works everywhere even though only some models have a native `max`. Full verified mapping tables: [
|
|
273
|
+
llmshim maps these to each provider's native control (OpenAI `reasoning.effort`/`mode`, Anthropic adaptive thinking, Gemini `thinkingLevel`, xAI `reasoning.effort`), clamping to the nearest tier the target model actually supports — so `reasoning_effort="max"` works everywhere even though only some models have a native `max`. Full verified mapping tables: [the reasoning guide](https://sanjay920.github.io/llmshim/guides/reasoning.html). Prefer a provider's exact native dialect? Pass it via `provider_config` (`x-openai.reasoning`, `x-anthropic.thinking`, `x-gemini.thinkingConfig`) and llmshim won't touch it.
|
|
274
274
|
|
|
275
275
|
**Fallback chains** — automatic failover across providers:
|
|
276
276
|
|
|
277
277
|
```python
|
|
278
278
|
resp = llmshim.chat(
|
|
279
|
-
"anthropic/claude-sonnet-
|
|
279
|
+
"anthropic/claude-sonnet-5",
|
|
280
280
|
"Hello",
|
|
281
281
|
max_tokens=100,
|
|
282
|
-
fallback=["openai/gpt-5.
|
|
282
|
+
fallback=["openai/gpt-5.6-sol", "gemini/gemini-3.5-flash"],
|
|
283
283
|
)
|
|
284
284
|
```
|
|
285
285
|
|
|
@@ -298,7 +298,7 @@ import { Client } from "llmshim";
|
|
|
298
298
|
|
|
299
299
|
const client = new Client(); // no baseUrl -> auto-starts the bundled proxy
|
|
300
300
|
const res = await client.chat({
|
|
301
|
-
model: "anthropic/claude-sonnet-
|
|
301
|
+
model: "anthropic/claude-sonnet-5",
|
|
302
302
|
messages: [{ role: "user", content: "Hello!" }],
|
|
303
303
|
});
|
|
304
304
|
console.log(res.message.content);
|
|
@@ -315,7 +315,7 @@ go get github.com/sanjay920/llmshim/clients/go
|
|
|
315
315
|
```go
|
|
316
316
|
client := llmshim.New() // defaults to http://localhost:3000
|
|
317
317
|
resp, err := client.Chat(ctx, llmshim.ChatRequest{
|
|
318
|
-
Model: "anthropic/claude-sonnet-
|
|
318
|
+
Model: "anthropic/claude-sonnet-5",
|
|
319
319
|
Messages: []llmshim.Message{{Role: "user", Content: "Hello!"}},
|
|
320
320
|
})
|
|
321
321
|
```
|
|
@@ -331,7 +331,7 @@ gem install llmshim
|
|
|
331
331
|
```ruby
|
|
332
332
|
require "llmshim"
|
|
333
333
|
|
|
334
|
-
resp = Llmshim.chat(model: "anthropic/claude-sonnet-
|
|
334
|
+
resp = Llmshim.chat(model: "anthropic/claude-sonnet-5", messages: [{role: "user", content: "Hello!"}])
|
|
335
335
|
puts resp.message.content
|
|
336
336
|
```
|
|
337
337
|
|
|
@@ -345,8 +345,8 @@ Standard library only. Full docs: [`clients/ruby/README.md`](clients/ruby/README
|
|
|
345
345
|
|----------|--------|-------------------|
|
|
346
346
|
| **OpenAI** | `gpt-5.6-sol`, `gpt-5.6-terra`, `gpt-5.6-luna`, `gpt-5.5`, `gpt-5.5-pro`, `gpt-5.4`, `gpt-5.4-pro`, `gpt-5.4-mini`, `gpt-5.4-nano` | Yes (summaries) |
|
|
347
347
|
| **Anthropic** | `claude-opus-4-8`, `claude-sonnet-5`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-haiku-4-5-20251001` | Yes (full thinking) |
|
|
348
|
-
| **Google Gemini** | `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3
|
|
349
|
-
| **xAI** | `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning
|
|
348
|
+
| **Google Gemini** | `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-flash-preview` | Yes (thought summaries) |
|
|
349
|
+
| **xAI** | `grok-4.5`, `grok-4.3`, `grok-4.20-multi-agent-beta-0309`, `grok-4.20-beta-0309-reasoning`, `grok-4.20-beta-0309-non-reasoning` | No (hidden) |
|
|
350
350
|
|
|
351
351
|
Use a bare model name (auto-detected by prefix) or an explicit `provider/model` string.
|
|
352
352
|
|
|
@@ -366,7 +366,7 @@ No canonical struct. Requests flow as `serde_json::Value` — each provider maps
|
|
|
366
366
|
|
|
367
367
|
```
|
|
368
368
|
llmshim::completion(router, request)
|
|
369
|
-
→ router.resolve("anthropic/claude-sonnet-
|
|
369
|
+
→ router.resolve("anthropic/claude-sonnet-5")
|
|
370
370
|
→ provider.transform_request(model, &value)
|
|
371
371
|
→ HTTP
|
|
372
372
|
→ provider.transform_response(model, body)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
book/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[book]
|
|
2
|
+
title = "llmshim"
|
|
3
|
+
authors = ["llmshim contributors"]
|
|
4
|
+
language = "en"
|
|
5
|
+
multilingual = false
|
|
6
|
+
src = "src"
|
|
7
|
+
|
|
8
|
+
[build]
|
|
9
|
+
build-dir = "book"
|
|
10
|
+
create-missing = false
|
|
11
|
+
|
|
12
|
+
[preprocessor.mermaid]
|
|
13
|
+
command = "mdbook-mermaid"
|
|
14
|
+
|
|
15
|
+
[output.html]
|
|
16
|
+
default-theme = "light"
|
|
17
|
+
preferred-dark-theme = "navy"
|
|
18
|
+
git-repository-url = "https://github.com/sanjay920/llmshim"
|
|
19
|
+
edit-url-template = "https://github.com/sanjay920/llmshim/edit/docs/concepts-site/docs/{path}"
|
|
20
|
+
site-url = "/llmshim/"
|
|
21
|
+
additional-js = ["mermaid.min.js", "mermaid-init.js"]
|
|
22
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
|
|
5
|
+
(() => {
|
|
6
|
+
const darkThemes = ['ayu', 'navy', 'coal'];
|
|
7
|
+
const lightThemes = ['light', 'rust'];
|
|
8
|
+
|
|
9
|
+
const classList = document.getElementsByTagName('html')[0].classList;
|
|
10
|
+
|
|
11
|
+
let lastThemeWasLight = true;
|
|
12
|
+
for (const cssClass of classList) {
|
|
13
|
+
if (darkThemes.includes(cssClass)) {
|
|
14
|
+
lastThemeWasLight = false;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const theme = lastThemeWasLight ? 'default' : 'dark';
|
|
20
|
+
mermaid.initialize({ startOnLoad: true, theme });
|
|
21
|
+
|
|
22
|
+
// Simplest way to make mermaid re-render the diagrams in the new theme is via refreshing the page
|
|
23
|
+
|
|
24
|
+
for (const darkTheme of darkThemes) {
|
|
25
|
+
document.getElementById(darkTheme).addEventListener('click', () => {
|
|
26
|
+
if (lastThemeWasLight) {
|
|
27
|
+
window.location.reload();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const lightTheme of lightThemes) {
|
|
33
|
+
document.getElementById(lightTheme).addEventListener('click', () => {
|
|
34
|
+
if (!lastThemeWasLight) {
|
|
35
|
+
window.location.reload();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
})();
|