Commit Graph

106 Commits

Author SHA1 Message Date
Alishahryar1 29e7714337 feat(logging): structured TRACE events and end-to-end request correlation
Add core/trace.py with trace_event, traced_async_stream, and payload snapshots.
Merge TRACE fields into JSON logs; promote claude_session_id, http path/method.
Instrument API, messaging/CLI, and OpenAI-compat/native provider paths.
Harden log sink with enqueue and stdlib intercept re-entrancy guard.
Document behavior in .env.example and README; extend tests.
2026-05-10 18:24:48 -07:00
Alishahryar1 f9fc614563 Allow unauthenticated root probes 2026-05-10 16:03:19 -07:00
Alishahryar1 e386a3c8aa Improve admin UI setup flow 2026-05-10 15:57:56 -07:00
Alishahryar1 b9bad70771 log urls at startup 2026-05-10 01:45:04 -07:00
Alishahryar1 8ee72968ed Initial admin impl 2026-05-10 01:21:16 -07:00
alex 8687fb3cbb fix: accept betas body field from Claude Code clients (#360)
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.
2026-05-05 21:45:15 -07:00
Leung Ka Chun 19e08f2da1 fix(deepseek): accept document blocks and normalize tool_result content (#358)
## 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>
2026-05-05 21:36:38 -07:00
Alishahryar1 d3a3b37e9e Filter OpenRouter model variants by thinking support 2026-04-30 22:01:36 -07:00
Alishahryar1 db3c9521b1 Add no-thinking model picker variants 2026-04-30 21:27:23 -07:00
Alishahryar1 72b34ad57c Added claude-code native model picker 2026-04-30 20:34:35 -07:00
Alishahryar1 d9040ce901 Report startup validation failures without tracebacks 2026-04-30 00:43:43 -07:00
Alishahryar1 85232a3ccb Log startup model validation failures clearly 2026-04-30 00:37:32 -07:00
Alishahryar1 eb5516e53b Validate configured models at startup 2026-04-30 00:33:45 -07:00
@aaronjmars b8f850813c fix(security): constant-time comparison for ANTHROPIC_AUTH_TOKEN (#262) 2026-04-29 10:13:21 -07:00
Brandy Good 51112a4cf3 fix: only strip valid env assignments in command parsing (#229)
## 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>
2026-04-27 18:34:48 -07:00
Brandy Good 19ce65647e fix: handle env-prefixed commands in filepath extraction (#207)
## 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>
2026-04-26 15:02:12 -07:00
Alishahryar1 6297b48f81 feat(deepseek): use native Anthropic Messages transport
- 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
2026-04-26 12:03:21 -07:00
Alishahryar1 f3a7528d49 Major refactor: API, providers, messaging, and Anthropic protocol
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.
2026-04-26 03:01:14 -07:00
Alishahryar1 f29e693dc5 Add per-model thinking toggles 2026-04-25 20:51:07 -07:00
Alishahryar1 40951c145a refactor: drop legacy title-generation detection copy
Remove new-conversation-topic heuristic; keep sentence-case and JSON session
title patterns. Update unit and smoke E2E payloads accordingly.
2026-04-25 00:45:22 -07:00
Alishahryar1 080ebefc7b fix: detect Claude Code 2.1+ session title requests for optimization skip
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.
2026-04-25 00:44:25 -07:00
Alishahryar1 b926f60f64 feat: Anthropic web server tools, provider metadata, messaging hardening
- Add local web_search/web_fetch SSE handling and optional tool schemas
- Extend HeuristicToolParser for JSON-style WebFetch/WebSearch text
- Consolidate provider defaults, ids, and exception typing; stream contracts
- Messaging: typed options, voice config injection, platform contract cleanup
- Tests for web server tools, converters, parsers, contracts; ignore debug-*.log
2026-04-24 23:01:14 -07:00
Alishahryar1 0e3b2c24b4 refactor: remove OpenRouter rollback, shims, and redundant layers
- OpenRouter: native Anthropic only; remove chat_request and OPENROUTER_TRANSPORT
- Drop OpenAICompatibleProvider alias, api.request_utils, voice_pipeline facade
- Simplify OpenRouter SSE, generic reasoning in conversion, messaging dispatch
- Shared markdown table helpers; API optimization response helper; contract guards
- Restore PLAN.md; update docs and tests
2026-04-24 21:08:38 -07:00
Alishahryar1 26b8a29537 Architecture refactor: core anthropic, runtime, smoke tiers, remove providers.common 2026-04-24 20:03:14 -07:00
Alishahryar1 66ef23072c Refactor provider routing and smoke coverage 2026-04-24 19:34:34 -07:00
Alishahryar1 48b085950a Warn on inherited auth token 2026-04-24 00:42:33 -07:00
Alishahryar1 6f3d762a4f Revert "Add per-model thinking toggles"
This reverts commit 1f12a33dd7.
2026-04-24 00:26:15 -07:00
Alishahryar1 1f12a33dd7 Add per-model thinking toggles 2026-04-24 00:14:49 -07:00
arssing 2fe15bd2cd feat: add proxy support for httpx clients (#125)
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>
2026-04-22 17:06:16 -07:00
Pavel Yurchenko e719e4aed2 feat: deepseek api support (#118)
## Summary

* add native DeepSeek provider support via the shared OpenAI-compatible
provider base
* allow `deepseek/...` model prefixes in config validation
* add `DEEPSEEK_API_KEY` and `DEEPSEEK_BASE_URL` settings
* add DeepSeek entries to `.env.example` and `config/env.example`
* implement `DeepSeekProvider` and register it in provider dependencies
* add a DeepSeek request builder with DeepSeek-specific thinking payload
handling
* preserve Anthropic thinking blocks as `reasoning_content` for
DeepSeek-compatible continuation flows
* update `claude-pick` to discover DeepSeek models from the DeepSeek API
* document DeepSeek usage in `README.md`
* add tests for config validation, provider dependency wiring, request
building, and streaming behavior

## Motivation

DeepSeek exposes an OpenAI-compatible API and can be used directly
without routing through OpenRouter. This lets users spend their existing
DeepSeek balance through the proxy while keeping the same Claude Code
workflow and per-model provider mapping.

## Example

```dotenv
DEEPSEEK_API_KEY="sk-..."
DEEPSEEK_BASE_URL="https://api.deepseek.com"

MODEL_OPUS="deepseek/deepseek-reasoner"
MODEL_SONNET="deepseek/deepseek-chat"
MODEL_HAIKU="deepseek/deepseek-chat"
MODEL="deepseek/deepseek-chat"

---------

Co-authored-by: Alishahryar1 <alishahryar2@gmail.com>
2026-04-22 17:06:01 -07:00
Alishahryar1 835d0454e8 Fixes for issue 113 and 116 2026-04-18 16:32:31 -07:00
th-ch f703a0e403 Implement optional authentication (Anthropic style) (#80) 2026-03-27 11:11:47 -07:00
Alishahryar1 4a59f1f708 Added log line for missed optimization 2026-03-11 07:27:16 -07:00
Alishahryar1 55945df1d2 removed logging utils 2026-03-11 07:24:50 -07:00
Alishahryar1 5a36a32836 feat: add llama.cpp provider for local anthropic messages API 2026-03-08 10:38:25 -07:00
Alishahryar1 a7d88d5cbd Updated README with per-model mapping, fixed test .env isolation 2026-03-01 21:52:35 -08:00
Ali Khokhar 0b324e0421 Per claude model mapping (#66) 2026-03-01 21:32:23 -08:00
Alishahryar1 35a2760f6e Fixed encapsulation violations 2026-03-01 04:28:22 -08:00
Alishahryar1 34757511a0 Improve deterministic error surfacing across stream and API 2026-03-01 01:32:52 -08:00
Alishahryar1 7f2612d2df Added optimization logging 2026-03-01 01:02:59 -08:00
Ali Khokhar aee9f0ad93 Add code review fix plan covering 11 issues across modularity, encapsulation, performance, and dead code (#62) 2026-03-01 00:45:33 -08:00
Alishahryar1 c54c57a742 style 2026-02-28 09:13:25 -08:00
Alishahryar1 744eec2772 Major cleanup with GLM-5 2026-02-28 09:10:21 -08:00
Alishahryar1 a74ec74271 Major refactor done with minimax m2.5 2026-02-28 04:36:29 -08:00
Alishahryar1 79a1ae0c54 minor refactor using minimax m2.5 2026-02-27 20:44:39 -08:00
Ali Khokhar c4d8681000 Backup/before cleanup 20260222 230402 (#58) 2026-02-27 19:50:21 -08:00
Alishahryar1 d6a0e1a401 Provider inferred from model name using prefix 2026-02-19 20:53:02 -08:00
Alishahryar1 0c8d59e33e Removed deprecated modules and updated imports 2026-02-19 20:38:11 -08:00
Alishahryar1 2b0495dd08 moved text.py to common utils for providers 2026-02-19 20:32:45 -08:00
Claude 99f99fce90 Remove max_cli_sessions — CLI session pool is now unbounded
The max_sessions cap in CLISessionManager was the only thing enforcing
a limit on concurrent CLI processes. Now that provider concurrency is
controlled at the streaming layer (PROVIDER_MAX_CONCURRENCY semaphore),
the CLI session pool cap is redundant and removed entirely.

Changes:
- cli/manager.py: remove max_sessions param, cap check, _cleanup_idle_sessions_unlocked, max_sessions from get_stats()
- config/settings.py: remove max_cli_sessions field
- api/app.py: remove max_sessions=settings.max_cli_sessions from CLISessionManager constructor
- messaging/handler.py: remove "Waiting for slot" status check; stats display no longer shows Max CLI
- .env.example: remove MAX_CLI_SESSIONS line
- tests/cli/test_cli.py: remove max_sessions args and assertion from manager tests
- tests/cli/test_cli_manager_edge_cases.py: remove two tests for cap/cleanup behavior
- tests/api/test_app_lifespan_and_errors.py: remove max_cli_sessions from all SimpleNamespace settings
- tests/config/test_config.py: remove max_cli_sessions isinstance assertion
- tests/conftest.py: remove max_sessions from mock stats
- tests/messaging/test_handler.py: merge slot/capacity tests into single new-conversation test; remove Max CLI assertion from stats test
- tests/messaging/test_handler_markdown_and_status_edges.py: remove "Waiting for slot" assertion; drop max_sessions from all stats mocks

https://claude.ai/code/session_014mrF1WMNgmNjtPBuoQHsbg
2026-02-19 14:31:47 +00:00