llmshim 0.3.1__tar.gz → 0.3.3__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.3.1 → llmshim-0.3.3}/CLAUDE.md +2 -2
- {llmshim-0.3.1 → llmshim-0.3.3}/Cargo.lock +1 -1
- {llmshim-0.3.1 → llmshim-0.3.3}/Cargo.toml +1 -1
- {llmshim-0.3.1 → llmshim-0.3.3}/PKG-INFO +3 -3
- {llmshim-0.3.1 → llmshim-0.3.3}/README.md +2 -2
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/reasoning.md +5 -5
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/models.md +5 -3
- {llmshim-0.3.1 → llmshim-0.3.3}/src/main.rs +4 -2
- {llmshim-0.3.1 → llmshim-0.3.3}/src/models.rs +28 -10
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/gemini.rs +8 -4
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/xai.rs +7 -4
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_gemini.rs +42 -0
- llmshim-0.3.3/tests/integration_xai.rs +72 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_gemini.rs +28 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_models.rs +2 -1
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_xai.rs +23 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.github/workflows/pages.yml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.github/workflows/release.yml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/.gitignore +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/CODE_OF_CONDUCT.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/CONTRIBUTING.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/LICENSE-APACHE +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/LICENSE-MIT +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/NOTICE +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/SECURITY.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/benchmarks/bench.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/benchmarks/bench_python.py +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/benchmarks/loadtest.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/.gitignore +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/book.toml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/mermaid-init.js +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/mermaid.min.js +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/SUMMARY.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/concepts/contracts.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/concepts/conversations.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/concepts/portability.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/concepts/routing.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/concepts/translation-flow.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/fallbacks.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/images.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/native-controls.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/streaming.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/guides/tools.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/introduction.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/proxy/deployment.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/proxy/http-api.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/proxy/scaling.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/api.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/cli.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/configuration.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/errors.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/providers.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/request-fields.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/reference/surfaces.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/choose.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/cli.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/clients.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/configure.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/proxy.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/docs/src/start/rust.md +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/examples/chat.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/examples/stream.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/llmshim/__init__.py +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/llmshim/_client.py +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/llmshim/_server.py +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/llmshim/types.py +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/pyproject.toml +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/client.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/config.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/env.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/error.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/fallback.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/lib.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/log.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/provider.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/anthropic.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/mod.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/openai.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/openai_compat.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/providers/openrouter.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/convert.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/error.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/handlers.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/mod.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/ratelimit.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/proxy/types.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/router.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/src/vision.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_fallback.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_gemini_tools.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_long_context.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_multimodel.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_openrouter.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_proxy.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_sglang.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_thinking.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_tool_roundtrip.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/integration_vision.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_anthropic.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_fallback.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_fast_mode.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_log.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_multimodel.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_openai.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_openai_compat.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_openrouter.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_proxy.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_proxy_convert.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_router.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_sse.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_tools.rs +0 -0
- {llmshim-0.3.1 → llmshim-0.3.3}/tests/unit_vision.rs +0 -0
|
@@ -14,8 +14,8 @@ This is a public crate on crates.io. Do NOT make breaking changes to `pub` items
|
|
|
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-5`, `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.
|
|
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.7-flash`, `gemini-3.6-flash`, `gemini-3.5-flash`, `gemini-3.5-flash-lite`
|
|
18
|
+
- **xAI:** `grok-4.6`, `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
|
- **OpenRouter:** not enumerated (huge/dynamic catalog) — any `openrouter/<vendor>/<model>` slug routes through, e.g. `openrouter/anthropic/claude-sonnet-4.5`.
|
|
20
20
|
- **vLLM / SGLang:** not enumerated (self-hosted) — any `vllm/<served-model>` or `sglang/<served-model>` routes through to the configured server, e.g. `sglang/Qwen/Qwen3.6-35B-A3B-FP8`.
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: llmshim
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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-5`, `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.
|
|
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` |
|
|
225
|
+
| Gemini | `gemini-3.7-flash`, `gemini-3.6-flash`, `gemini-3.5-flash`, `gemini-3.5-flash-lite` |
|
|
226
|
+
| xAI | `grok-4.6`, `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
|
|
|
@@ -364,8 +364,8 @@ Standard library only. Full docs: [`clients/ruby/README.md`](clients/ruby/README
|
|
|
364
364
|
|----------|--------|-------------------|
|
|
365
365
|
| **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) |
|
|
366
366
|
| **Anthropic** | `claude-opus-5`, `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) |
|
|
367
|
-
| **Google Gemini** | `gemini-3.
|
|
368
|
-
| **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) |
|
|
367
|
+
| **Google Gemini** | `gemini-3.7-flash`, `gemini-3.6-flash`, `gemini-3.5-flash`, `gemini-3.5-flash-lite` | Yes (thought summaries) |
|
|
368
|
+
| **xAI** | `grok-4.6`, `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) |
|
|
369
369
|
|
|
370
370
|
Use a bare model name (auto-detected by prefix) or an explicit `provider/model` string.
|
|
371
371
|
|
|
@@ -117,7 +117,7 @@ token budget scaled from `max_tokens` and floored at 1024:
|
|
|
117
117
|
Gemini uses the four-rung
|
|
118
118
|
`generationConfig.thinkingConfig.thinkingLevel` enum:
|
|
119
119
|
|
|
120
|
-
| unified | gemini-3.5-flash / 3-flash
|
|
120
|
+
| unified | gemini-3.5-flash / 3.6-flash / 3.5-flash-lite | gemini-3.7-flash (and gemini-3.1-pro) |
|
|
121
121
|
|---|---|---|
|
|
122
122
|
| `none` | `minimal` (zero thinking tokens) | **`low`** (this model cannot disable thinking) |
|
|
123
123
|
| `low` | `low` | `low` |
|
|
@@ -126,15 +126,15 @@ Gemini uses the four-rung
|
|
|
126
126
|
| `xhigh` | **`high`** | **`high`** |
|
|
127
127
|
| `max` | **`high`** | **`high`** |
|
|
128
128
|
|
|
129
|
-
Gemini 3.1 Pro
|
|
130
|
-
to
|
|
129
|
+
Gemini 3.7 Flash and Gemini 3.1 Pro reject both `minimal` and `thinkingBudget: 0`, so `none`
|
|
130
|
+
clamps to their `low` floor (verified live). Other flash models accept `minimal` and can disable thinking. The legacy integer `thinkingBudget` remains available only
|
|
131
131
|
through `x-gemini.thinkingConfig`.
|
|
132
132
|
|
|
133
133
|
### xAI Responses API
|
|
134
134
|
|
|
135
135
|
xAI receives the nested native shape `reasoning: {effort}`:
|
|
136
136
|
|
|
137
|
-
| unified | grok-4.3 | grok-4.5 | grok-4.20-\*-reasoning / -non-reasoning |
|
|
137
|
+
| unified | grok-4.3 | grok-4.5 / grok-4.6 | grok-4.20-\*-reasoning / -non-reasoning |
|
|
138
138
|
|---|---|---|---|
|
|
139
139
|
| `none` | `none` | **`low`** | omitted |
|
|
140
140
|
| `low` | `low` | `low` | omitted |
|
|
@@ -143,7 +143,7 @@ xAI receives the nested native shape `reasoning: {effort}`:
|
|
|
143
143
|
| `xhigh` | `xhigh` | `xhigh` | omitted |
|
|
144
144
|
| `max` | **`xhigh`** | **`xhigh`** | omitted |
|
|
145
145
|
|
|
146
|
-
grok-4.5 cannot disable reasoning, so `none` clamps to `low`. grok-4.20 models
|
|
146
|
+
grok-4.5 and grok-4.6 cannot disable reasoning, so `none` clamps to `low`. grok-4.20 models
|
|
147
147
|
are name-locked: reasoning on or off is encoded in the model name, and the API
|
|
148
148
|
rejects any reasoning parameter. llmshim therefore omits it for that family.
|
|
149
149
|
|
|
@@ -14,7 +14,7 @@ the ID and display label.
|
|
|
14
14
|
|
|
15
15
|
## Registered catalog
|
|
16
16
|
|
|
17
|
-
The current registry contains
|
|
17
|
+
The current registry contains 26 entries, newest first within each provider.
|
|
18
18
|
This page mirrors `src/models.rs`; use runtime discovery rather than parsing
|
|
19
19
|
this table in applications.
|
|
20
20
|
|
|
@@ -48,14 +48,16 @@ this table in applications.
|
|
|
48
48
|
|
|
49
49
|
| ID | Display name |
|
|
50
50
|
|---|---|
|
|
51
|
+
| `gemini/gemini-3.7-flash` | Gemini 3.7 Flash |
|
|
52
|
+
| `gemini/gemini-3.6-flash` | Gemini 3.6 Flash |
|
|
51
53
|
| `gemini/gemini-3.5-flash` | Gemini 3.5 Flash |
|
|
52
|
-
| `gemini/gemini-3.
|
|
53
|
-
| `gemini/gemini-3-flash-preview` | Gemini 3 Flash |
|
|
54
|
+
| `gemini/gemini-3.5-flash-lite` | Gemini 3.5 Flash Lite |
|
|
54
55
|
|
|
55
56
|
### xAI
|
|
56
57
|
|
|
57
58
|
| ID | Display name |
|
|
58
59
|
|---|---|
|
|
60
|
+
| `xai/grok-4.6` | Grok 4.6 |
|
|
59
61
|
| `xai/grok-4.5` | Grok 4.5 |
|
|
60
62
|
| `xai/grok-4.3` | Grok 4.3 |
|
|
61
63
|
| `xai/grok-4.20-multi-agent-beta-0309` | Grok 4.20 Multi-Agent |
|
|
@@ -22,9 +22,11 @@ const MODELS: &[(&str, &str)] = &[
|
|
|
22
22
|
("anthropic/claude-opus-4-6", "Claude Opus 4.6"),
|
|
23
23
|
("anthropic/claude-sonnet-4-6", "Claude Sonnet 4.6"),
|
|
24
24
|
("anthropic/claude-haiku-4-5-20251001", "Claude Haiku 4.5"),
|
|
25
|
+
("gemini/gemini-3.7-flash", "Gemini 3.7 Flash"),
|
|
26
|
+
("gemini/gemini-3.6-flash", "Gemini 3.6 Flash"),
|
|
25
27
|
("gemini/gemini-3.5-flash", "Gemini 3.5 Flash"),
|
|
26
|
-
("gemini/gemini-3.
|
|
27
|
-
("
|
|
28
|
+
("gemini/gemini-3.5-flash-lite", "Gemini 3.5 Flash Lite"),
|
|
29
|
+
("xai/grok-4.6", "Grok 4.6"),
|
|
28
30
|
("xai/grok-4.5", "Grok 4.5"),
|
|
29
31
|
("xai/grok-4.3", "Grok 4.3"),
|
|
30
32
|
(
|
|
@@ -326,32 +326,50 @@ pub const MODELS: &[ModelInfo] = &[
|
|
|
326
326
|
capabilities: CAPS_FULL,
|
|
327
327
|
},
|
|
328
328
|
ModelInfo {
|
|
329
|
-
id: "gemini/gemini-3.
|
|
329
|
+
id: "gemini/gemini-3.7-flash",
|
|
330
330
|
provider: "gemini",
|
|
331
|
-
name: "gemini-3.
|
|
332
|
-
label: "Gemini 3.
|
|
331
|
+
name: "gemini-3.7-flash",
|
|
332
|
+
label: "Gemini 3.7 Flash",
|
|
333
333
|
context_window_tokens: Some(1_048_576),
|
|
334
334
|
max_output_tokens: Some(65_536),
|
|
335
|
-
capabilities:
|
|
335
|
+
capabilities: CAPS_STD,
|
|
336
336
|
},
|
|
337
337
|
ModelInfo {
|
|
338
|
-
id: "gemini/gemini-3.
|
|
338
|
+
id: "gemini/gemini-3.6-flash",
|
|
339
339
|
provider: "gemini",
|
|
340
|
-
name: "gemini-3.
|
|
341
|
-
label: "Gemini 3.
|
|
340
|
+
name: "gemini-3.6-flash",
|
|
341
|
+
label: "Gemini 3.6 Flash",
|
|
342
342
|
context_window_tokens: Some(1_048_576),
|
|
343
343
|
max_output_tokens: Some(65_536),
|
|
344
344
|
capabilities: CAPS_STD,
|
|
345
345
|
},
|
|
346
346
|
ModelInfo {
|
|
347
|
-
id: "gemini/gemini-3-flash
|
|
347
|
+
id: "gemini/gemini-3.5-flash",
|
|
348
|
+
provider: "gemini",
|
|
349
|
+
name: "gemini-3.5-flash",
|
|
350
|
+
label: "Gemini 3.5 Flash",
|
|
351
|
+
context_window_tokens: Some(1_048_576),
|
|
352
|
+
max_output_tokens: Some(65_536),
|
|
353
|
+
capabilities: CAPS_FULL,
|
|
354
|
+
},
|
|
355
|
+
ModelInfo {
|
|
356
|
+
id: "gemini/gemini-3.5-flash-lite",
|
|
348
357
|
provider: "gemini",
|
|
349
|
-
name: "gemini-3-flash-
|
|
350
|
-
label: "Gemini 3 Flash",
|
|
358
|
+
name: "gemini-3.5-flash-lite",
|
|
359
|
+
label: "Gemini 3.5 Flash Lite",
|
|
351
360
|
context_window_tokens: Some(1_048_576),
|
|
352
361
|
max_output_tokens: Some(65_536),
|
|
353
362
|
capabilities: CAPS_STD,
|
|
354
363
|
},
|
|
364
|
+
ModelInfo {
|
|
365
|
+
id: "xai/grok-4.6",
|
|
366
|
+
provider: "xai",
|
|
367
|
+
name: "grok-4.6",
|
|
368
|
+
label: "Grok 4.6",
|
|
369
|
+
context_window_tokens: Some(500_000),
|
|
370
|
+
max_output_tokens: None,
|
|
371
|
+
capabilities: CAPS_XAI,
|
|
372
|
+
},
|
|
355
373
|
ModelInfo {
|
|
356
374
|
id: "xai/grok-4.5",
|
|
357
375
|
provider: "xai",
|
|
@@ -581,11 +581,15 @@ fn transform_response_to_openai(model: &str, resp: &Value) -> Result<Value> {
|
|
|
581
581
|
}))
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
-
/// Models that cannot turn thinking off: gemini-3.1-pro
|
|
585
|
-
/// thinkingLevel "minimal" and thinkingBudget 0
|
|
586
|
-
///
|
|
584
|
+
/// Models that cannot turn thinking off: gemini-3.1-pro and gemini-3.7-flash
|
|
585
|
+
/// reject thinkingLevel "minimal" (and thinkingBudget 0) — verified live.
|
|
586
|
+
/// Unified effort "none" clamps to "low" for them.
|
|
587
587
|
fn cannot_disable_thinking(model: &str) -> bool {
|
|
588
|
-
model.to_lowercase()
|
|
588
|
+
let m = model.to_lowercase();
|
|
589
|
+
// gemini-3.1-pro and gemini-3.7-flash reject thinkingLevel "minimal" (and
|
|
590
|
+
// thinkingBudget 0), verified live — unified "none" clamps to "low". Other
|
|
591
|
+
// flash models (3.5/3.6/3.5-lite) accept "minimal" and can disable thinking.
|
|
592
|
+
m.contains("3.1-pro") || m.contains("3.7-flash")
|
|
589
593
|
}
|
|
590
594
|
|
|
591
595
|
impl Provider for Gemini {
|
|
@@ -189,11 +189,14 @@ fn is_reasoning_name_locked(model: &str) -> bool {
|
|
|
189
189
|
model.to_lowercase().contains("4.20")
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
/// grok-4.5 cannot disable reasoning: `effort: "none"` -> 400 ("does
|
|
193
|
-
/// support `reasoning_effort` value `none`", verified live). Unified effort
|
|
194
|
-
/// "none" clamps to "low". grok-4.3
|
|
192
|
+
/// grok-4.5 / grok-4.6 cannot disable reasoning: `effort: "none"` -> 400 ("does
|
|
193
|
+
/// not support `reasoning_effort` value `none`", verified live). Unified effort
|
|
194
|
+
/// "none" clamps to "low". grok-4.3 DOES accept "none".
|
|
195
195
|
fn reasoning_cannot_disable(model: &str) -> bool {
|
|
196
|
-
model.to_lowercase()
|
|
196
|
+
let m = model.to_lowercase();
|
|
197
|
+
// grok-4.5 and grok-4.6 both 400 on `reasoning_effort: "none"` (verified
|
|
198
|
+
// live); unified "none" clamps to "low" for them. grok-4.3 accepts "none".
|
|
199
|
+
m.contains("4.5") || m.contains("4.6")
|
|
197
200
|
}
|
|
198
201
|
|
|
199
202
|
impl Provider for Xai {
|
|
@@ -436,3 +436,45 @@ async fn all_three_providers_same_shape() {
|
|
|
436
436
|
);
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
+
|
|
440
|
+
#[tokio::test]
|
|
441
|
+
#[ignore]
|
|
442
|
+
async fn gemini_3_7_flash_completion() {
|
|
443
|
+
if std::env::var("GEMINI_API_KEY").is_err() {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
let router = router();
|
|
447
|
+
let req = json!({
|
|
448
|
+
"model": "gemini/gemini-3.7-flash",
|
|
449
|
+
"messages": [{"role": "user", "content": "In one short sentence, what is Rust?"}],
|
|
450
|
+
"max_tokens": 2000,
|
|
451
|
+
});
|
|
452
|
+
let resp = llmshim::completion(&router, &req).await.unwrap();
|
|
453
|
+
let content = resp["choices"][0]["message"]["content"]
|
|
454
|
+
.as_str()
|
|
455
|
+
.unwrap_or("");
|
|
456
|
+
assert!(!content.is_empty(), "expected a response, got: {resp}");
|
|
457
|
+
println!("gemini-3.7-flash said: {content}");
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#[tokio::test]
|
|
461
|
+
#[ignore]
|
|
462
|
+
async fn gemini_3_7_flash_none_clamps_to_low() {
|
|
463
|
+
// gemini-3.7-flash 400s on thinkingLevel "minimal"; llmshim must clamp
|
|
464
|
+
// reasoning_effort "none" to "low". Asserts success, not a 400.
|
|
465
|
+
if std::env::var("GEMINI_API_KEY").is_err() {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
let router = router();
|
|
469
|
+
let req = json!({
|
|
470
|
+
"model": "gemini/gemini-3.7-flash",
|
|
471
|
+
"messages": [{"role": "user", "content": "Say hi."}],
|
|
472
|
+
"max_tokens": 2000,
|
|
473
|
+
"reasoning_effort": "none",
|
|
474
|
+
});
|
|
475
|
+
let resp = llmshim::completion(&router, &req)
|
|
476
|
+
.await
|
|
477
|
+
.expect("none must clamp to low for gemini-3.7-flash, not 400");
|
|
478
|
+
assert_eq!(resp["object"], "chat.completion");
|
|
479
|
+
println!("gemini-3.7-flash none->low clamp OK");
|
|
480
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/// Integration tests for xAI (Grok) hitting the real API.
|
|
2
|
+
/// Run with: XAI_API_KEY=... cargo test --test integration_xai -- --ignored --nocapture
|
|
3
|
+
use serde_json::json;
|
|
4
|
+
|
|
5
|
+
fn router() -> llmshim::router::Router {
|
|
6
|
+
llmshim::router::Router::from_env()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[tokio::test]
|
|
10
|
+
#[ignore]
|
|
11
|
+
async fn grok_4_6_completion() {
|
|
12
|
+
if std::env::var("XAI_API_KEY").is_err() {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
let router = router();
|
|
16
|
+
let req = json!({
|
|
17
|
+
"model": "xai/grok-4.6",
|
|
18
|
+
"messages": [{"role": "user", "content": "In one short sentence, what is Rust?"}],
|
|
19
|
+
"max_tokens": 2000,
|
|
20
|
+
});
|
|
21
|
+
let resp = llmshim::completion(&router, &req).await.unwrap();
|
|
22
|
+
assert_eq!(resp["object"], "chat.completion");
|
|
23
|
+
let content = resp["choices"][0]["message"]["content"]
|
|
24
|
+
.as_str()
|
|
25
|
+
.unwrap_or("");
|
|
26
|
+
assert!(!content.is_empty(), "expected a response, got: {resp}");
|
|
27
|
+
println!("grok-4.6 said: {content}");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
#[tokio::test]
|
|
31
|
+
#[ignore]
|
|
32
|
+
async fn grok_4_6_reasoning_none_clamps_to_low() {
|
|
33
|
+
// grok-4.6 400s on reasoning_effort "none"; llmshim must clamp it to "low".
|
|
34
|
+
// This asserts the request SUCCEEDS (not a 400), proving the clamp.
|
|
35
|
+
if std::env::var("XAI_API_KEY").is_err() {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let router = router();
|
|
39
|
+
let req = json!({
|
|
40
|
+
"model": "xai/grok-4.6",
|
|
41
|
+
"messages": [{"role": "user", "content": "Say hi."}],
|
|
42
|
+
"max_tokens": 2000,
|
|
43
|
+
"reasoning_effort": "none",
|
|
44
|
+
});
|
|
45
|
+
let resp = llmshim::completion(&router, &req)
|
|
46
|
+
.await
|
|
47
|
+
.expect("reasoning_effort=none must be clamped to low, not 400");
|
|
48
|
+
assert_eq!(resp["object"], "chat.completion");
|
|
49
|
+
println!("grok-4.6 none->low clamp OK");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[tokio::test]
|
|
53
|
+
#[ignore]
|
|
54
|
+
async fn grok_4_6_reasoning_high() {
|
|
55
|
+
if std::env::var("XAI_API_KEY").is_err() {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
let router = router();
|
|
59
|
+
let req = json!({
|
|
60
|
+
"model": "xai/grok-4.6",
|
|
61
|
+
"messages": [{"role": "user", "content": "What is 17 * 24? Reason it through."}],
|
|
62
|
+
"max_tokens": 3000,
|
|
63
|
+
"reasoning_effort": "high",
|
|
64
|
+
});
|
|
65
|
+
let resp = llmshim::completion(&router, &req).await.unwrap();
|
|
66
|
+
let content = resp["choices"][0]["message"]["content"]
|
|
67
|
+
.as_str()
|
|
68
|
+
.unwrap_or("");
|
|
69
|
+
assert!(!content.is_empty(), "expected an answer, got: {resp}");
|
|
70
|
+
assert!(content.contains("408"), "expected 408 in answer: {content}");
|
|
71
|
+
println!("grok-4.6 reasoning OK: {content}");
|
|
72
|
+
}
|
|
@@ -1529,3 +1529,31 @@ fn request_strips_foreign_reasoning_signature() {
|
|
|
1529
1529
|
assert!(!body.contains("redacted-should-not-leak"));
|
|
1530
1530
|
assert!(!body.contains("reasoning_signature"));
|
|
1531
1531
|
}
|
|
1532
|
+
|
|
1533
|
+
#[test]
|
|
1534
|
+
fn reasoning_none_clamps_per_model_disable_capability() {
|
|
1535
|
+
// gemini-3.7-flash rejects thinkingLevel "minimal" (verified live) -> "none"
|
|
1536
|
+
// clamps to "low". Other flash models accept "minimal" -> "none" stays "minimal".
|
|
1537
|
+
let p = provider();
|
|
1538
|
+
let req = json!({
|
|
1539
|
+
"model": "x",
|
|
1540
|
+
"messages": [{"role": "user", "content": "hi"}],
|
|
1541
|
+
"reasoning_effort": "none",
|
|
1542
|
+
});
|
|
1543
|
+
let r = p.transform_request("gemini-3.7-flash", &req).unwrap();
|
|
1544
|
+
assert_eq!(
|
|
1545
|
+
r.body["generationConfig"]["thinkingConfig"]["thinkingLevel"], "low",
|
|
1546
|
+
"gemini-3.7-flash cannot disable thinking -> none clamps to low"
|
|
1547
|
+
);
|
|
1548
|
+
for m in [
|
|
1549
|
+
"gemini-3.6-flash",
|
|
1550
|
+
"gemini-3.5-flash",
|
|
1551
|
+
"gemini-3.5-flash-lite",
|
|
1552
|
+
] {
|
|
1553
|
+
let r = p.transform_request(m, &req).unwrap();
|
|
1554
|
+
assert_eq!(
|
|
1555
|
+
r.body["generationConfig"]["thinkingConfig"]["thinkingLevel"], "minimal",
|
|
1556
|
+
"{m} can disable thinking -> none maps to minimal"
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
@@ -11,7 +11,7 @@ fn models_registry_has_all_providers() {
|
|
|
11
11
|
|
|
12
12
|
#[test]
|
|
13
13
|
fn models_registry_has_expected_count() {
|
|
14
|
-
assert_eq!(MODELS.len(),
|
|
14
|
+
assert_eq!(MODELS.len(), 26);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
#[test]
|
|
@@ -160,6 +160,7 @@ fn reasoning_support_matches_provider_behavior() {
|
|
|
160
160
|
"openai/gpt-5.6-sol",
|
|
161
161
|
"anthropic/claude-opus-5",
|
|
162
162
|
"anthropic/claude-opus-4-8",
|
|
163
|
+
"xai/grok-4.6",
|
|
163
164
|
"xai/grok-4.5",
|
|
164
165
|
] {
|
|
165
166
|
assert_eq!(
|
|
@@ -666,6 +666,29 @@ fn name_locked_grok_4_20_omits_reasoning_entirely() {
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
+
#[test]
|
|
670
|
+
fn reasoning_none_clamps_to_low_for_grok_4_5_and_4_6() {
|
|
671
|
+
// grok-4.5 and grok-4.6 400 on effort "none" (verified live) — clamp to "low".
|
|
672
|
+
let p = provider();
|
|
673
|
+
for model in ["grok-4.5", "grok-4.6"] {
|
|
674
|
+
let req = json!({
|
|
675
|
+
"model": "x",
|
|
676
|
+
"messages": [{"role": "user", "content": "hi"}],
|
|
677
|
+
"reasoning_effort": "none",
|
|
678
|
+
});
|
|
679
|
+
let r = p.transform_request(model, &req).unwrap();
|
|
680
|
+
assert_eq!(r.body["reasoning"]["effort"], "low", "{model} none->low");
|
|
681
|
+
}
|
|
682
|
+
// grok-4.3 CAN disable — "none" stays "none".
|
|
683
|
+
let req = json!({
|
|
684
|
+
"model": "x",
|
|
685
|
+
"messages": [{"role": "user", "content": "hi"}],
|
|
686
|
+
"reasoning_effort": "none",
|
|
687
|
+
});
|
|
688
|
+
let r = p.transform_request("grok-4.3", &req).unwrap();
|
|
689
|
+
assert_eq!(r.body["reasoning"]["effort"], "none");
|
|
690
|
+
}
|
|
691
|
+
|
|
669
692
|
#[test]
|
|
670
693
|
fn mode_pro_bumps_effort() {
|
|
671
694
|
let p = provider();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|