Update live smoke model defaults for NIM, OpenRouter, and Gemini; normalize tool-call indexes; downgrade DeepSeek forced tool_choice; and add coverage for the provider smoke fixes.
Route these providers through POST /messages with vendor headers and bases (including Kimi model list on OpenAI /v1/models). Remove Z.ai from OpenAI-chat server-tool rejection; extend tests and README.
## Summary
Adds support for the **OpenCode Go** subscription gateway at
`opencode.ai/zen/go/v1`, as requested in #504.
OpenCode Go exposes the same OpenAI-compatible Chat Completions API as
OpenCode Zen, so the implementation reuses `OpenCodeProvider` with a
configurable `provider_name` parameter — avoiding code duplication.
### Changes
- **Provider**: `OpenCodeProvider` now accepts `provider_name` (defaults
to `"OPENCODE"` for backward compatibility)
- **Catalog**: New `opencode_go` descriptor with correct base URL,
credential, and capabilities
- **Registry**: `_create_opencode_go` factory that passes
`provider_name="OPENCODE_GO"`
- **Settings**: `opencode_go_api_key` and `opencode_go_proxy` fields
- **Admin UI**: OpenCode Go API key, proxy, and smoke model config
fields
- **API services**: `opencode_go` added to OpenAI Chat Completions
upstream IDs
- **Smoke config**: Default smoke model `opencode_go/gpt-5.3-codex`
- **Tests**: New test for base URL, provider name, and API key; existing
tests updated
## Test plan
- [x] `test_opencode_go_provider_config_uses_correct_base_url_and_name`
— passes
- [x] `test_create_provider_instantiates_each_builtin` — covers
opencode_go
- [x]
`test_provider_and_platform_registries_include_advertised_builtins` —
covers opencode_go
- [x] `uv run ruff format`, `ruff check`, `ty check`, `pytest` — pass
locally on Python 3.14
Closes#504.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Alishahryar1 <alishahryar2@gmail.com>
Registers FIREWORKS_API_KEY / FIREWORKS_PROXY in api/admin_config FIELDS so the UI can set credentials and provider status stays accurate.
Adds admin apply test and contract guards linking PROVIDER_CATALOG to FIELD_BY_KEY; updates .env.example.
## Summary
- Refactors the admin interface into focused views and simplifies the
header (removes noisy status labels; hides managed-source labels where
appropriate).
- Fixes Claude runtime settings handling, reduces Z.ai base URL leakage
in the admin UI, and streamlines API startup logging.
- Updates configuration and catalog behavior (including `.env.example` /
README), and expands automated tests around admin, app lifespan, and
config/registry behavior.
## Test plan
- [ ] `uv run ruff format`, `uv run ruff check`, `uv run ty check`, `uv
run pytest`
- [ ] Smoke the admin UI: navigation between views, settings save/load,
no sensitive URL leakage in the UI
- [ ] Confirm API startup logs are readable and not overly verbose in
normal operation
## Summary
- split the admin UI into Providers, Model Config, and Messaging views
- remove generated env, diagnostics, smoke, managed-label, and fixed
cloud/runtime settings from the visible admin UX
- make Z.ai base URL, Claude workspace, and Claude CLI binary fixed
app-level behavior instead of managed env fields
## Verification
- uv run ruff format
- uv run ruff check
- uv run ty check
- uv run pytest
- Default LOG_FILE to logs/server.log in settings and admin config
- Create log parent directories in configure_logging
- Gitignore /logs/, /server.*.log; keep server.log for root override
- Add test for nested log path mkdir
Claude Code newer versions send a `betas` list in the request body (e.g.
"interleaved-thinking-2025-05-14"). This landed in `__pydantic_extra__`
and triggered `_openai_reject_native_only_top_level_fields`, surfacing
as "Invalid request sent to provider." for all OpenAI Chat upstreams
(kimi, nvidia_nim).
Declare `betas` on both `MessagesRequest` and `TokenCountRequest` with
`exclude=True` so it is accepted from clients but never forwarded to any
provider body.
## What
Fixes the 422 error returned by the proxy when Claude Code v2.1.128+
sends PDF context as Anthropic `document` blocks, plus two related
DeepSeek-only issues uncovered while debugging.
Closes#357.
## Why
Claude Code now attaches PDFs/files as `document` content blocks. The
`Message.content` union didn't include that variant, so every follow-up
turn after a PDF read returned `422 Unprocessable Content` from the
proxy itself (before the request ever reached DeepSeek).
## Changes
- `api/models/anthropic.py`: new `ContentBlockDocument`, added to
`Message.content` union.
- `providers/deepseek/request.py`:
- `_strip_unsupported_attachment_blocks` — silently drops
`image`/`document` blocks for DeepSeek.
- `_serialize_tool_result_content` + `_normalize_tool_result_content` —
coerces `tool_result.content` to a string per DeepSeek's API contract.
- Hooked into `build_request_body` (strip before validation, normalize
before send).
- `tests/providers/test_deepseek.py`: +4 tests, 1 legacy test updated.
## Verification
- `uv run ruff format` ✅
- `uv run ruff check` ✅
- `uv run ty check` ✅
- `uv run pytest` ✅ (1194 passed)
- Manual: `claude-deepseek` against a PDF no longer 422s.
## Scope / non-goals
- No README change (per CONTRIBUTING).
- Image/document blocks for DeepSeek are stripped, not converted to text
— Claude Code already ships the extracted text in the paired
tool_result.
- No changes to other providers.
---------
Co-authored-by: Alishahryar1 <alishahryar2@gmail.com>
## What changed
This updates the command parsing helpers to recognize leading
environment assignments only when they match shell-style variable syntax
like `FOO=bar`. The previous implementation treated any leading token
containing `=` as an env assignment, which could misclassify real
commands or file paths that happen to include `=`. Both
`_strip_env_assignments()` and `extract_command_prefix()` now share the
same validation helper so their behavior stays consistent.
## Why it matters
These helpers are used by fast-path request optimizations, so incorrect
prefix detection can bypass normal handling with the wrong result.
Restricting env stripping to valid assignment tokens fixes false
positives without changing the public API or the response format.
## How it was tested
I validated the updated logic against representative inputs such as
`FOO=bar git status`, plain commands like `git status`, and malformed or
path-like tokens containing `=` that should not be treated as env
assignments. The change is isolated to parsing logic and does not
introduce new dependencies.
---------
Co-authored-by: genoshide <genoshide@users.noreply.github.com>
## What changed
This updates `extract_filepaths_from_command()` to skip leading
environment variable assignments before detecting the actual command.
The same parsing pattern was already used in `extract_command_prefix()`,
but filepath extraction still treated the first token as the command
even when it was an env assignment.
## Why it matters
Commands like `FOO=bar cat README.md` or `DEBUG=1 grep pattern file.txt`
were incorrectly classified because `FOO=bar` was treated as the
executable name. That caused the optimization path to return an empty
`<filepaths>` block instead of the file paths the command actually
reads.
## How it was tested
Tested the updated logic against env-prefixed read commands such as
`FOO=bar cat README.md` and `DEBUG=1 grep needle app.py`. Also verified
existing behavior remains unchanged for plain commands like `cat
file.txt`, `grep pattern file.txt`, and listing commands like `ls`.
Co-authored-by: genoshide <genoshide@users.noreply.github.com>
- Point DeepSeek at api.deepseek.com/anthropic with x-api-key headers
- Native request builder, DeepSeek-specific thinking/block sanitization
- Drop deepseek from OpenAI-chat server-tool preflight; update tests and docs
- Default smoke model deepseek-v4-pro; re-export dump_raw_messages_request
Consolidates the incremental refactor work into a single change set: modular web tools (api/web_tools), native Anthropic request building and SSE block policy, OpenAI conversion and error handling, provider transports and rate limiting, messaging handler and tree queue, safe logging, smoke tests, and broad test coverage.
Expand is_title_generation_request to match sentence-case/JSON title prompts
in addition to legacy new-conversation-topic copy. Add unit test for the
current session-title system text shape.
Add proxy support for providers based on
[doc](https://www.python-httpx.org/advanced/proxies/):
- Add per-provider proxy support (HTTP and SOCKS5) for all 4 providers:
nvidia_nim, open_router, lmstudio, llamacpp
- Each provider gets its own env var (NVIDIA_NIM_PROXY,
OPENROUTER_PROXY, LMSTUDIO_PROXY, LLAMACPP_PROXY) for independent proxy
configuration
---------
Co-authored-by: Alishahryar1 <alishahryar2@gmail.com>