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.
Files changed (104) hide show
  1. llmshim-0.2.1/.github/workflows/pages.yml +50 -0
  2. {llmshim-0.1.26 → llmshim-0.2.1}/CLAUDE.md +5 -5
  3. {llmshim-0.1.26 → llmshim-0.2.1}/Cargo.lock +1 -1
  4. {llmshim-0.1.26 → llmshim-0.2.1}/Cargo.toml +1 -1
  5. {llmshim-0.1.26 → llmshim-0.2.1}/PKG-INFO +3 -3
  6. {llmshim-0.1.26 → llmshim-0.2.1}/README.md +18 -18
  7. llmshim-0.2.1/docs/.gitignore +1 -0
  8. llmshim-0.2.1/docs/book.toml +22 -0
  9. llmshim-0.2.1/docs/mermaid-init.js +39 -0
  10. llmshim-0.2.1/docs/mermaid.min.js +2609 -0
  11. llmshim-0.2.1/docs/src/SUMMARY.md +47 -0
  12. llmshim-0.2.1/docs/src/concepts/contracts.md +83 -0
  13. llmshim-0.2.1/docs/src/concepts/conversations.md +56 -0
  14. llmshim-0.2.1/docs/src/concepts/portability.md +81 -0
  15. llmshim-0.2.1/docs/src/concepts/routing.md +87 -0
  16. llmshim-0.2.1/docs/src/concepts/translation-flow.md +74 -0
  17. llmshim-0.2.1/docs/src/guides/fallbacks.md +107 -0
  18. llmshim-0.2.1/docs/src/guides/images.md +82 -0
  19. llmshim-0.2.1/docs/src/guides/native-controls.md +133 -0
  20. llmshim-0.2.1/docs/src/guides/reasoning.md +181 -0
  21. llmshim-0.2.1/docs/src/guides/streaming.md +118 -0
  22. llmshim-0.2.1/docs/src/guides/tools.md +164 -0
  23. llmshim-0.2.1/docs/src/introduction.md +73 -0
  24. llmshim-0.2.1/docs/src/proxy/deployment.md +81 -0
  25. llmshim-0.2.1/docs/src/proxy/http-api.md +175 -0
  26. llmshim-0.2.1/docs/src/proxy/scaling.md +100 -0
  27. llmshim-0.2.1/docs/src/reference/api.md +23 -0
  28. llmshim-0.2.1/docs/src/reference/cli.md +76 -0
  29. llmshim-0.2.1/docs/src/reference/configuration.md +102 -0
  30. llmshim-0.2.1/docs/src/reference/errors.md +90 -0
  31. llmshim-0.2.1/docs/src/reference/models.md +123 -0
  32. llmshim-0.2.1/docs/src/reference/providers.md +54 -0
  33. llmshim-0.2.1/docs/src/reference/request-fields.md +112 -0
  34. llmshim-0.2.1/docs/src/reference/surfaces.md +56 -0
  35. llmshim-0.2.1/docs/src/start/choose.md +35 -0
  36. llmshim-0.2.1/docs/src/start/cli.md +77 -0
  37. llmshim-0.2.1/docs/src/start/clients.md +129 -0
  38. llmshim-0.2.1/docs/src/start/configure.md +75 -0
  39. llmshim-0.2.1/docs/src/start/proxy.md +106 -0
  40. llmshim-0.2.1/docs/src/start/rust.md +115 -0
  41. {llmshim-0.1.26 → llmshim-0.2.1}/src/main.rs +0 -6
  42. llmshim-0.2.1/src/models.rs +408 -0
  43. {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/anthropic.rs +107 -3
  44. {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/openai.rs +4 -0
  45. {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/xai.rs +4 -0
  46. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/types.rs +1 -1
  47. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_gemini.rs +1 -1
  48. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_thinking.rs +73 -0
  49. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_tool_roundtrip.rs +2 -2
  50. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_vision.rs +1 -1
  51. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_anthropic.rs +179 -2
  52. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_fast_mode.rs +2 -4
  53. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_gemini.rs +30 -8
  54. llmshim-0.2.1/tests/unit_models.rs +182 -0
  55. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_openai.rs +22 -0
  56. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_router.rs +2 -2
  57. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_tools.rs +6 -12
  58. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_xai.rs +50 -76
  59. llmshim-0.1.26/docs/reasoning.md +0 -143
  60. llmshim-0.1.26/src/models.rs +0 -174
  61. llmshim-0.1.26/tests/unit_models.rs +0 -69
  62. {llmshim-0.1.26 → llmshim-0.2.1}/.github/workflows/release.yml +0 -0
  63. {llmshim-0.1.26 → llmshim-0.2.1}/.gitignore +0 -0
  64. {llmshim-0.1.26 → llmshim-0.2.1}/LICENSE +0 -0
  65. {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/bench.rs +0 -0
  66. {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/bench_python.py +0 -0
  67. {llmshim-0.1.26 → llmshim-0.2.1}/benchmarks/loadtest.rs +0 -0
  68. {llmshim-0.1.26 → llmshim-0.2.1}/examples/chat.rs +0 -0
  69. {llmshim-0.1.26 → llmshim-0.2.1}/examples/stream.rs +0 -0
  70. {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/__init__.py +0 -0
  71. {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/_client.py +0 -0
  72. {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/_server.py +0 -0
  73. {llmshim-0.1.26 → llmshim-0.2.1}/llmshim/types.py +0 -0
  74. {llmshim-0.1.26 → llmshim-0.2.1}/pyproject.toml +0 -0
  75. {llmshim-0.1.26 → llmshim-0.2.1}/src/client.rs +0 -0
  76. {llmshim-0.1.26 → llmshim-0.2.1}/src/config.rs +0 -0
  77. {llmshim-0.1.26 → llmshim-0.2.1}/src/env.rs +0 -0
  78. {llmshim-0.1.26 → llmshim-0.2.1}/src/error.rs +0 -0
  79. {llmshim-0.1.26 → llmshim-0.2.1}/src/fallback.rs +0 -0
  80. {llmshim-0.1.26 → llmshim-0.2.1}/src/lib.rs +0 -0
  81. {llmshim-0.1.26 → llmshim-0.2.1}/src/log.rs +0 -0
  82. {llmshim-0.1.26 → llmshim-0.2.1}/src/provider.rs +0 -0
  83. {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/gemini.rs +0 -0
  84. {llmshim-0.1.26 → llmshim-0.2.1}/src/providers/mod.rs +0 -0
  85. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/convert.rs +0 -0
  86. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/error.rs +0 -0
  87. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/handlers.rs +0 -0
  88. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/mod.rs +0 -0
  89. {llmshim-0.1.26 → llmshim-0.2.1}/src/proxy/ratelimit.rs +0 -0
  90. {llmshim-0.1.26 → llmshim-0.2.1}/src/router.rs +0 -0
  91. {llmshim-0.1.26 → llmshim-0.2.1}/src/vision.rs +0 -0
  92. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration.rs +0 -0
  93. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_fallback.rs +0 -0
  94. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_gemini_tools.rs +0 -0
  95. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_long_context.rs +0 -0
  96. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_multimodel.rs +0 -0
  97. {llmshim-0.1.26 → llmshim-0.2.1}/tests/integration_proxy.rs +0 -0
  98. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_fallback.rs +0 -0
  99. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_log.rs +0 -0
  100. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_multimodel.rs +0 -0
  101. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_proxy.rs +0 -0
  102. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_proxy_convert.rs +0 -0
  103. {llmshim-0.1.26 → llmshim-0.2.1}/tests/unit_sse.rs +0 -0
  104. {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. The `ragents` crate (https://github.com/sanjay920/ragents) depends on this.
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.1-flash-lite-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`, `grok-4-1-fast-reasoning`, `grok-4-1-fast-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 for all. Tool calls normalized to OpenAI format in responses, translated back per-provider on input.
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
 
@@ -958,7 +958,7 @@ checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
958
958
 
959
959
  [[package]]
960
960
  name = "llmshim"
961
- version = "0.1.26"
961
+ version = "0.2.1"
962
962
  dependencies = [
963
963
  "async-stream",
964
964
  "async-trait",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "llmshim"
3
- version = "0.1.26"
3
+ version = "0.2.1"
4
4
  edition = "2021"
5
5
  description = "Blazing fast LLM API translation layer in pure Rust"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llmshim
3
- Version: 0.1.26
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.1-flash-lite-preview`, `gemini-3-flash-preview` |
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`, `grok-4-1-fast-reasoning`, `grok-4-1-fast-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-4-6",
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.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-4-6","messages":[{"role":"user","content":"Hi"}],"config":{"max_tokens":100}}'
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-4-6", "Hello!", max_tokens=500)
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-4-6", "Write a poem"):
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-4-6", messages, max_tokens=500)
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.5", messages, max_tokens=500)
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-4-6", "Weather in Tokyo?", max_tokens=500, tools=tools)
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-4-6",
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: [`docs/reasoning.md`](docs/reasoning.md). 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.
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-4-6",
279
+ "anthropic/claude-sonnet-5",
280
280
  "Hello",
281
281
  max_tokens=100,
282
- fallback=["openai/gpt-5.5", "gemini/gemini-3.5-flash"],
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-4-6",
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-4-6",
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-4-6", messages: [{role: "user", content: "Hello!"}])
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.1-flash-lite-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`, `grok-4-1-fast-reasoning`, `grok-4-1-fast-non-reasoning` | No (hidden) |
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-4-6")
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
+ })();