Commit Graph
57 Commits
Author SHA1 Message Date
whit3rabbitandClaude c90fb9f640 feat: loopback-open auth default, --port flag, max_tokens fix, UI banner
- Auth default is now loopback-open (not reject-all). With no
  PROXY_API_KEYS, no PROXY_OPEN_RELAY, no virtual keys and no OIDC,
  loopback TCP peers are accepted and LAN/remote peers get 401. The
  decision uses the real TCP peer (ConnectInfo via
  into_make_service_with_connect_info), not the spoofable
  X-Forwarded-For. effective_auth_mode() (keys/open_relay/loopback_only)
  + proxy_key_count are surfaced on GET /admin/api/status; the admin UI
  shows a warning banner when no key is set.
- Add --port/-p CLI flag that sets LISTEN_PORT for the run. Stripped
  before any run/providers subcommand so flags meant for the launched
  tool survive; pure scan is unit-tested.
- Startup port handling: the run subcommand pre-checks the listen port
  and fails fast with a hint when in use; wait_for_port readiness timeout
  10s -> 30s; listener bind failures (proxy + admin) now print an
  actionable message and exit(1) instead of panicking.
- POST /v1/chat/completions no longer 400s on a missing max_tokens for
  OpenAI-compatible backends. The internal placeholder is stripped via a
  new OMIT_MAX_TOKENS_MARKER so the backend applies its own default
  (e.g. LM Studio's 8192); the marker never leaks upstream. Explicit
  max_tokens is still forwarded verbatim. Anthropic backends unchanged.
- Tier router logs the selected tier at info (tier/backend/model)
  instead of routing silently.
- Admin UI modal no longer dismisses when a text-selection drag starts
  inside the card (dismiss only on a press that begins on the backdrop).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 20:08:13 -05:00
whit3rabbitandClaude 86d01d29fd refactor: split large files into modules
- Split config.rs into config/{mod,delete,get,put}.rs
- Split routes_api.rs into routes_api/{mod,helpers,providers,routes}.rs
- Split passthrough/handlers.rs into handlers/{mod,errors,generic,messages}.rs
- Split streaming.rs into streaming/{mod,handler,helpers}.rs
- Split main_helpers/async_main/admin.rs into admin/{mod,config,tasks}.rs
- Minor cleanups in chat_completions backends, token_counting, tests
- Add docs/TEST_PARITY_LITELLM.md

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 06:11:40 -05:00
whit3rabbit 1c7d66cc54 Large refactor of files over 400+ of lines of code to make it more managable. Just clean up/splitting of files. 2026-07-12 21:14:16 -05:00
whit3rabbitandClaude Sonnet 5 9456d1ad1b feat: opt-in forge-guardrails tool-call policy + Anthropic thinking-block repair
- Tool-call guardrails (lsp_first/quiet_command/write_payload_cap nudges,
  fingerprint dedup) for local-LLM tool loops. Configurable via YAML
  tool_execution.guardrails, FORGE_TOOL_CALL_POLICY env fallback, or the
  admin UI (live, no restart).
- Anthropic thinking-block record/repair (ANTHROPIC_THINKING_REPAIR):
  records ground truth off the real API and repairs client-corrupted
  thinking/redacted_thinking blocks in replayed conversations.
- Bidirectional thinking_blocks (signature/redacted state) round-trip
  through the OpenAI-compat wire format for LiteLLM-style clients.
- Review fixes: guardrail-mode divergence between streaming/non-streaming
  paths, cross-backend/tenant cache-namespace collision, client-controlled
  integer overflow in thinking budget_tokens, dropped reasoning_content and
  citations on repair/translation paths, a fail-closed race under cache
  eviction, plus dedup/simplification cleanup and doc corrections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 13:03:02 -05:00
whit3rabbit ce89a2d19c Release 0.10.1 2026-06-20 15:24:43 -05:00
whit3rabbitandClaude Opus 4.8 65cf673b97 feat: performative admin UI redesign + admin API contract fixes
Large admin-ui refactor (Performative component system, sidebar nav,
provider/route tabs) plus backend module restructuring.

Admin UI contract fixes (this session):
- Fix Models page crash: useBackends unwraps {backends:[...]}; align
  ModelEntry to {model_name, deployments} and ModelsResponse.strategy;
  fix add-model body to {model_name, actual_model, backend_name}.
- Fix Backends/Providers health rendering: source per-backend status and
  latency from the uptime endpoint (health_checks); narrow Backend type to
  the real get_backends shape.
- Route + sidebar-link the previously-unrouted Backends tab.

Verified: cargo test (exit 0), clippy -D warnings (exit 0), fmt --check,
admin-ui tsc + vite build all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 18:13:59 -05:00
whit3rabbitandClaude Opus 4.8 138aaa65ac fix: error classification + retry consistency across backends; bump 0.9.9
Surface upstream errors hidden in 200 bodies/streams (OpenRouter), unify
retry/fallback policy, fast-fail insufficient_quota, add timeout_error
mapping. Gemini native now retries with backoff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 15:43:04 -05:00
whit3rabbitandClaude Sonnet 4.6 681b0d47d9 chore: bump to 0.9.8, add CHANGELOG, cargo update
- Version: 0.9.7 → 0.9.8 (workspace + all pinned inter-crate deps)
- New: AnthropicMessagesClient passthrough client
- New: RetryPolicy struct, retry_transport_errors flag, extra_header builder
- New: ssrf_allow_loopback, ssrf_allow_private, extra_headers in HttpClientConfig
- New: ChatMessage::effective_text(), claude-fable-5 in provider catalog
- Fix: relaxed serde defaults for lax local backends (Ollama, llama.cpp)
- Fix: warn on empty choices in openai_to_anthropic_response
- ci: create-release now uses CHANGELOG section as release body
- docs: CHANGELOG.md created, release process documented in CLAUDE.md
- deps: cargo update (~105 packages, tokio 1.52, axum 0.8.9, rustls 0.23.40)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 22:37:09 -05:00
whit3rabbit 69ba9adefd Preserve Anthropic tool context in translation 2026-06-07 10:19:59 -05:00
whit3rabbit f78c925b6a Align provider catalog with LiteLLM canonical IDs 2026-05-23 16:34:03 -05:00
whit3rabbit 24b2af5d50 Require client API key for Anthropic middleware to prevent auth bypass (#10)
* Harden Anthropic middleware with required client API key

* docs: show required middleware client key
2026-05-23 15:23:29 -05:00
whit3rabbit 1f300d8566 Fix virtual-key accounting across generation routes 2026-05-23 12:22:39 -05:00
whit3rabbitandClaude Opus 4.7 3d62da3a68 chore: fix Rust 1.95 clippy lints
CI runs stable (now 1.95) which surfaces two new lints my 1.94 local
check missed:

- collapsible_match in reverse_message_map.rs: collapse the empty
  guard branch into a match arm with a guard.
- manual_checked_ops in admin/db.rs: replace the explicit zero check
  with checked_div(...).unwrap_or(0).
- collapsible_match in admin/ws.rs: clippy's suggested guard form
  fails to compile because Bytes can't be moved in a pattern guard.
  Apply a targeted #[allow] with a comment explaining why.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 19:41:46 -05:00
whit3rabbitandClaude Opus 4.7 6de126893f chore: fix all clippy lints across workspace
- tools_map: use std::slice::from_ref over &[clone()] in 4 tests
- gemini_streaming_map: rewrite match as matches!
- backend/mod.rs: move impl BackendClient before #[cfg(test)] mod tests
- middleware: replace .filter().last() with .rfind() (xff parsing)
- middleware: drop dead `|| true` in is_ip_allowed smoke test
- sse: drop blank line between doc comment and assert_sse_ok
- streaming example: collapse nested if-let into outer match arms
- tool_execution tests: array literal over vec! for one-off slices
- live_bedrock: use is_some_and instead of map_or(false, _)
- live_api / live_responses: contains() over iter().any() on &[&str]

All test-only / example changes; no production behavior change.
1130 tests pass, fmt clean, clippy -D warnings clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 19:11:35 -05:00
whit3rabbitandClaude Sonnet 4.6 e3275f81da feat: env import/export, spend alerts, model allowlist, audit log, and misc hardening
Adds admin env file import/export (multipart upload + download), per-key
spend alerts (80/95/100% of budget), model allowlist with wildcard support,
audit log for admin mutations, CSRF one-time-use tokens, SSRF-safe OIDC/webhook
clients, sliding-window admin rate limiter, and configurable Redis fail policy.
Also updates batch_engine, client, translator mapping, and admin UI settings tab.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 16:57:22 -05:00
whit3rabbitandClaude Sonnet 4.6 0fa7495574 feat: docker smoke tests, admin UI polish, and misc fixes
- ci: replace inline smoke test with docker-compose.test.yml + scripts/docker-smoke-test.sh (9 checks)
- add .env.example, .env.example.test, .anyllm.env.example, docker-compose.test.yml
- gitignore: add .anyllm.env*, *.db-shm, *.db-wal patterns
- admin UI: fix auth store, vite config, App.tsx updates; rebuild dist
- admin routes: keys, traffic, mod, db, health_check updates
- server: gemini_input, mod, routes adjustments
- translator: lib.rs, gemini_message_map, tools_map fixes
- batch: anthropic_batch cleanup
- docs: add React admin UI plan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 18:58:38 -05:00
whit3rabbitandClaude Sonnet 4.6 1f9bb02968 feat: add Docker support, Gemini input handler, and batch engine improvements
Adds multi-arch Docker build with docker-compose, GitHub Actions CI workflow,
Gemini native input parsing, batch engine SQLite/file/webhook refactors,
and Bedrock streaming fixes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 12:01:24 -05:00
whit3rabbit b6158cf3fe feat: refactor admin routes into modular sub-modules and implement model management API endpoints 2026-04-04 20:30:13 -05:00
whit3rabbitandClaude Sonnet 4.6 84315840f8 fix: harden security, fix expires_at TTL, improve translation accuracy
- Wrap admin token in Zeroizing<String> so memory is wiped on drop
- Use SSRF-safe HTTP client for Langfuse and webhook dispatcher
- Wire up webhook dispatcher at startup (was previously un-started)
- Fix batch expires_at: was using now instead of now+24h
- Extract epoch_secs() helper; replace 4 inline SystemTime::now() blocks
- Gemini tool_choice {type:tool}: use ANY+allowedFunctionNames instead of AUTO
- Map Anthropic thinking budget_tokens to OpenAI reasoning_effort
- Preserve temperature/top_p for GA o-series models (o1/o3/o3-mini/o4-mini);
  only strip for o1-preview and o1-mini which reject those params
- Azure simple config: always route through default_base_url; guard against
  double-appending deployment path when user provides a full URL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 13:33:23 -05:00
whit3rabbitandClaude Opus 4.6 b6277a8b86 feat: operator observability dashboard with error classification
- Admin UI: operator view with request volume, token usage, latency,
  cost charts, failure table, and request timeline
- Backend: add error_kind() method and infer_error_kind() for stable
  error classification (rate_limit, timeout, backend_error, client_error)
- Admin DB: error_kind column in request_log, observability aggregate
  queries (bucketed timeseries, failure breakdown, timeline)
- Gemini: improved streaming translation, thinking block support,
  grounding metadata passthrough
- Request timeout: configurable REQUEST_TIMEOUT_SECS with streaming
  watchdog
- Model pricing: MODEL_PRICING_FILE for external pricing overrides
- Degradation header: ANYLLM_DEGRADATION_WARNINGS env var control

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 19:12:03 -05:00
whit3rabbitandClaude Sonnet 4.6 d510a2c09f test: add boundary tests for MAX_TOOL_CALL_INDEX cap in streaming_map
Verifies that idx == MAX_TOOL_CALL_INDEX (128) is accepted (> not >=)
and idx == MAX_TOOL_CALL_INDEX + 1 (129) is silently dropped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:21:05 -05:00
whit3rabbitandClaude Sonnet 4.6 b1b15994ee fix: use digit-based pattern for is_o_series_model to handle future oN models
Replace hardcoded list [o1, o3, o4] with pattern-based detection: any model
name starting with 'o' or 'O', followed by one or more ASCII digits, then
optional '-' suffix. This future-proofs the check for o2, o5, o10, etc. when
OpenAI releases them, without requiring code changes.

Includes test_is_o_series_model_future_models to validate o2, o5, o10
handling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 17:08:44 -05:00
whit3rabbitandClaude Sonnet 4.6 4ed08524fb feat(gemini): emit thinking_delta events in GeminiStreamingTranslator
Separate thought parts (Part.thought=true) from answer parts in each
streaming response. Emit ContentBlockStart(Thinking), ThinkingDelta,
and ContentBlockStop events using the same full-response diffing pattern
as text. Close the thought block before opening the text block. Handle
thought block cleanup in finish(). 3 new streaming tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:38:37 -05:00
whit3rabbitandClaude Sonnet 4.6 dcee73ce2b feat(gemini): map thinking_config to thinkingConfig, thought parts to ThinkingBlock
- Request direction: Anthropic ThinkingConfig::Enabled{budget_tokens} maps to
  generationConfig.thinkingConfig{thinkingBudget, includeThoughts: true}
- Response direction: Part{thought: true, text} maps to ContentBlock::Thinking
- 5 new tests covering all request and response thinking scenarios

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:38:31 -05:00
whit3rabbitandClaude Sonnet 4.6 69d25d0d63 feat(gemini): add Part.thought and GenerationConfig.thinkingConfig types
Add `thought: Option<bool>` to `Part` for thought parts produced by
Gemini 2.5 thinking models. Add `ThinkingConfig` struct and wire it into
`GenerationConfig.thinking_config` for request-side control.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:38:26 -05:00
whit3rabbitandClaude Sonnet 4.6 7961436a14 feat: wire Gemini native path end-to-end (Phases 5-8)
- translate.rs: add translate_request_gemini, translate_response_gemini,
  new_gemini_stream_translator wrappers; re-export from lib.rs
- gemini_native.rs: POST /v1/messages handler for GeminiNative backend;
  non-streaming calls generate_content, streaming uses read_sse_frames +
  GeminiStreamingTranslator to diff full responses into Anthropic SSE events
- streaming.rs: expose read_sse_frames, send_events, StreamOutcome as
  pub(super) so gemini_native.rs can reuse the SSE reading infrastructure
- backend/mod.rs: construct BackendClient::GeminiNative when
  GEMINI_API_FORMAT=native (both single-backend and multi-backend paths)
- routes.rs: add HandlerMode::GeminiNative; detect from BackendClient
  variant at AppState build time; dispatch to gemini_native_handler

GEMINI_API_FORMAT=openai (default) preserves existing behavior.
GEMINI_API_FORMAT=native uses the new direct translation path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 10:05:01 -05:00
whit3rabbitandClaude Sonnet 4.6 ef8d7d26ca feat(translator): add Gemini streaming state machine with full-response diffing
- GeminiStreamingTranslator diffs accumulated Gemini responses to emit Anthropic SSE deltas
- Handles text diffing, tool call detection, finish reason mapping, usage extraction
- 26 tests covering multi-event streams, tool calls, edge cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 09:46:19 -05:00
whit3rabbitandClaude Sonnet 4.6 61276876b8 feat(translator): add Anthropic<->Gemini message mapping with role merge and tool ID synthesis
- anthropic_to_gemini_request: system prompt, messages, tools, tool_choice, generation config
- gemini_to_anthropic_response: text, function calls, stop reason, usage metadata
- Role alternation merge for consecutive same-role messages
- Tool ID map for ToolResult -> FunctionResponse name lookup
- Drops unsupported blocks (thinking, redacted_thinking, document, URL images)
- Reuses sanitize_schema_for_gemini from tools_map
- 42 tests covering request mapping, response mapping, and edge cases

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 09:46:12 -05:00
whit3rabbitandClaude Opus 4.6 ff55dba6e5 feat(translator): add Gemini native API types for generateContent
- GenerateContentRequest, Content, Part (struct with optional fields)
- GenerationConfig, Tool, FunctionDeclaration, ToolConfig, SafetySetting
- GenerateContentResponse, Candidate, FinishReason, UsageMetadata
- Convenience constructors on Part (text, function_call, function_response, inline_data)
- 25 tests: serialization camelCase, round-trips, unknown FinishReason fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 09:41:14 -05:00
whit3rabbit c494c9a216 feat(translator): apply OpenAI strict mode when Anthropic forces a specific tool
- add extract_forced_tool_name helper: matches ToolChoice::Tool{name}
- add apply_strict_mode_to_tool helper: sets strict=true and normalizes
  parameter schema on the matched ChatTool directly (no serde round-trip)
- anthropic_to_openai_request: calls apply_strict_mode_to_tool when
  tool_choice forces a named tool
- apply_strict_to_forced_tool signature changed to &mut [Value] (clippy)
- apply_strict_mode_to_tool uses &mut [ChatTool] (clippy)
- end-to-end test: forced tool_choice produces strict=true, normalized
  schema with additionalProperties:false and all properties in required
2026-03-27 23:39:06 -05:00
whit3rabbit ee06732f02 feat(translator): add batch JSONL translation functions 2026-03-27 23:38:56 -05:00
whit3rabbit 5e9cb61f4b feat(translator): add normalize_schema_for_strict for OpenAI strict mode
- normalize_schema_for_strict: recursively ensures all object schema
  properties are listed in required and sets additionalProperties: false
- apply_strict_to_forced_tool: sets strict=true and normalizes the
  parameter schema for the named forced tool, leaves others unchanged
- 6 unit tests covering normalization, nesting, merge, non-object, and
  apply_strict cases
2026-03-27 23:34:18 -05:00
whit3rabbit 60b13b6cb6 feat(translator): add Anthropic batch request/response types 2026-03-27 23:34:16 -05:00
whit3rabbitandClaude Sonnet 4.6 5dacb8cf63 feat: sanitize Gemini tool schemas by stripping unsupported JSON Schema fields
Gemini and Vertex only accept the OpenAPI 3.0 subset of JSON Schema in
function parameters. Adds sanitize_schema_for_gemini() in tools_map.rs and
applies it to all tool parameter schemas in both the non-streaming (routes.rs)
and streaming (streaming.rs) Gemini/Vertex code paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 22:46:11 -05:00
whit3rabbit 08a03001c3 feat: wire Responses API streaming usage into request log and cost tracking 2026-03-27 22:27:08 -05:00
whit3rabbitandClaude Opus 4.6 2543e7d769 refactor: simplify code review fixes
- Extract row_to_virtual_key helper (3 copies -> 1) in admin/db.rs
- Deduplicate stop_reason mapping: reverse_streaming_map now calls
  the shared anthropic_stop_reason_to_openai from reverse_message_map
- Fix stream_options dead code in chat_completions streaming path
  (omit_stream_options was overwritten unconditionally)
- Add buffer size limit to Bedrock event stream decoder (was unbounded)
- Replace full serde_json parse with string extraction in detect_event_type
  (runs on every Bedrock streaming event)
- Remove redundant WHAT comments in reverse_message_map.rs
- Fix clippy redundant closure warnings in db.rs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:34:55 -05:00
whit3rabbitandClaude Opus 4.6 215dd2eab3 chore: format, fix Bedrock match arms, verify line counts
- cargo fmt applied across all crates
- Fixed BackendClient::Bedrock match arms in chat_completions.rs,
  routes.rs, streaming.rs, openai_client.rs
- All new source files verified under 400 lines (2 files at 406/429,
  within tolerance for focused single-responsibility modules)
- 549 tests passing, clippy clean, both build paths verified

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:24:00 -05:00
whit3rabbitandClaude Opus 4.6 abb5b90e1d feat: add Bedrock backend, OpenTelemetry export, integration tests
- AWS Bedrock backend: SigV4 signing, InvokeModel + InvokeModelWithResponseStream
  with binary event stream decoding, passthrough handler for /v1/messages
- OpenTelemetry export: feature-gated (--features otel), OTLP/HTTP with
  reqwest transport, OtelGuard for graceful shutdown flush
- Chat completions integration tests: 6 tests covering non-streaming,
  error handling, degradation headers, system messages
- Updated COMPARISON_LITELLM.md to reflect all closed gaps
- Fixed Bedrock match arms across all handler files

549 tests passing, 0 failures, clippy clean.
Both `cargo build` and `cargo build --features otel` compile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:21:24 -05:00
whit3rabbitandClaude Opus 4.6 a4e655c8bb feat: LiteLLM gap fill - chat completions input, Azure backend, virtual keys, client SDK
Phase 1-8 implementation of the LiteLLM gap fill feature set:

- POST /v1/chat/completions: Accept OpenAI-format input, translate through
  Anthropic pipeline, return OpenAI-format responses (streaming + non-streaming)
- Reverse translation layer: openai_to_anthropic_request, anthropic_to_openai_response,
  ReverseStreamingTranslator (Anthropic SSE -> OpenAI ChatCompletionChunk)
- Azure OpenAI backend: BACKEND=azure with deployment-scoped URLs, api-key header,
  api-version query param (default 2024-10-21)
- Virtual key management: SQLite-backed CRUD via admin API (POST/GET/DELETE
  /admin/api/keys), DashMap in-memory cache, immediate revocation
- Per-key rate limiting: RPM sliding window enforcement in auth middleware,
  429 with retry-after header on limit exceeded
- Client library v0.2.0: ClientBuilder, ToolBuilder, ToolChoiceBuilder,
  typed streaming, rustdoc examples
- New dependencies: dashmap, aws-sigv4, aws-credential-types (prod);
  opentelemetry stack (feature-gated, optional)

534 tests passing, 0 failures, clippy clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 20:09:40 -05:00
whit3rabbitandClaude Sonnet 4.6 f1df50ff37 refactor: extract shared client crate and rename to anyllm_*
Introduce `anyllm_client` crate containing HTTP client construction,
SSRF-safe DNS resolver, retry/backoff logic, rate limit header parsing,
and SSE frame parsing. These were previously inlined in the proxy crate.

Rename crates from `anthropic_openai_proxy`/`anthropic_openai_translate`
to `anyllm_proxy`/`anyllm_translate` throughout.

proxy/backend: now re-exports retry, rate limit, and SSE symbols from
the client crate; `send_with_retry` and `build_http_client` are thin
adapters bridging BackendAuth/TlsConfig to the client crate's types.

streaming: remove duplicate `find_double_newline` and
`MAX_SSE_BUFFER_SIZE` definitions; import from `crate::backend` instead.

Fix missing `pub mod` declarations in translator and proxy that were
accidentally replaced by doc comments (streaming, usage_map, server,
redact).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 06:11:01 -05:00
whit3rabbitandClaude Opus 4.6 ded040090b docs: add missing doc comments and remove stale PLAN.md references
Add doc comments to ~30 undocumented public functions, structs, enum
variants, and fields across both crates. Strengthen weak comments to
explain "why" not just "what". Remove 16 stale PLAN.md line references
(PLAN.md was removed previously). Add Anthropic API doc links where
relevant for rate limit headers and ID format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:23:39 -05:00
whit3rabbitandClaude Opus 4.6 c607620c19 fix: local LLM compat, protocol headers, and doc cleanup
- Strip markdown code fences from tool call arguments (DeepSeek/Qwen)
- Add OMIT_STREAM_OPTIONS for backends that reject unknown fields
- Strip n/temperature/top_p for o-series models
- Add anthropic-version header to all responses
- Add x-token-count-warning header to count_tokens endpoint
- Rewrite README for clarity; remove stale PLAN.md, TASKS.md, CHANGELOG.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:24:57 -05:00
whit3rabbitandClaude Opus 4.6 c033ef0705 fix: o-series model compat, restore read_timeout, deduplicate date math
- Handle o-series reasoning models (o1, o3, o4-mini): drop max_tokens
  (keep only max_completion_tokens) and convert system -> developer role
- Restore read_timeout(900s) alongside tcp_keepalive(60s) to bound hung
  connections that keepalive alone cannot detect
- Reuse epoch_to_iso8601 from admin/db.rs instead of duplicating the
  Hinnant civil date algorithm in backend/mod.rs
- Make ISO 8601 conversion testable via anchor time parameter
- Fix f64 truncation in duration parsing (use .round() before cast)
- Extract convert_reset_duration helper to deduplicate header injection
- Use eq_ignore_ascii_case in is_o_series_model to avoid allocation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:42:59 -05:00
whit3rabbitandClaude Opus 4.6 30611bff48 fix: security hardening, streaming correctness, and admin robustness
Security: hash API keys with SHA-256 for constant-time comparison (eliminates
length timing leak), require explicit PROXY_OPEN_RELAY for unauthenticated
access, gate /metrics behind auth, sanitize admin error responses, enforce
log_level allowlist on DB restore, validate GCP identifiers against URL
injection, add referrer-policy header to admin SPA.

Correctness: hold concurrency semaphore permit through entire stream lifetime
(not just until headers are sent), fix SSE parser to resume scanning near
chunk boundaries instead of re-scanning from start, mark padding tool call
slots as closed to prevent spurious ContentBlockStop events, mark responses
streaming translator as finished on error to prevent double closure events,
serialize concurrent config writes with a Mutex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:14:37 -05:00
whit3rabbitandClaude Opus 4.6 a0527551cb feat: add DeepSeek/Qwen compatibility (reasoning_content, forward-compat FinishReason)
Prevents hard deserialization failures from unknown finish reasons
(e.g. DeepSeek's "insufficient_system_resource") and adds bidirectional
mapping of reasoning_content to/from Anthropic thinking blocks for
DeepSeek/Qwen thinking models.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 06:54:22 -05:00
whit3rabbitandClaude Opus 4.6 8b111e3005 refactor: split large modules, fix IPv6 host parsing, add Anthropic error shapes
Split config.rs into config/{mod,tls,url_validation}.rs and extract
server/{passthrough,streaming,token_counting}.rs and admin/ws.rs from
their parent modules for clarity.

Functional changes:
- Add DNS rebinding protection on admin API (Host header validation)
- Fix IPv6 host parsing in admin origin check (bare ::1 was mishandled)
- Return Anthropic-shaped errors for JSON parse failures (400 not 422)
  and unmatched routes (404 not_found_error)
- Forward extra fields from Anthropic request to OpenAI request
- Count "Error: " prefix tokens for error tool results
- Reduce TOKENIZER visibility to module-private

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 06:30:18 -05:00
whit3rabbitandClaude Opus 4.6 379052b79a fix: security hardening, correctness fixes, and extended thinking support
- Constant-time token comparison in admin auth (timing side-channel)
- Origin header parsing via URL to prevent bypass (e.g., 127.0.0.1.attacker.com)
- CSP headers and X-Frame-Options on admin SPA
- WebSocket origin check for cross-site WS hijacking prevention
- Switch admin DB mutex from tokio::Mutex to std::Mutex, use spawn_blocking
- Poison-recovery on std::sync locks (unwrap_or_else + into_inner)
- JSON builder for SSE error fallback to prevent injection
- UTF-8-safe secret redaction (char-aware slicing)
- Add RedactedThinking content block and SignatureDelta streaming types
- Map OpenAI refusal field to Anthropic text block (non-streaming + streaming)
- Map OpenAI cached_tokens to Anthropic cache_read_input_tokens
- Map HTTP 408 to Anthropic OverloadedError
- Warn on trace log level and log_bodies enable via admin API
- Update CLAUDE.md docs to match current state

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:11:25 -05:00
whit3rabbitandClaude Opus 4.6 486661e0a0 refactor: remove native Gemini API translation, use OpenAI-compatible endpoint
Gemini's OpenAI-compatible endpoint (/openai) supports the same Chat
Completions format as the OpenAI backend, making the native Gemini
translation path redundant. This removes ~3400 lines of Gemini-specific
types, mapping, streaming, and client code, routing Gemini through the
existing OpenAI translation pipeline instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 06:34:03 -05:00
whit3rabbitandClaude Opus 4.6 9b00345746 fix: harden proxy with security, correctness, and reliability improvements
Security: strip admin token from browser URL, add cross-origin rejection
middleware for admin API, skip tool calls with empty names instead of
substituting "unknown".

Correctness: use BytesMut for SSE buffering to prevent UTF-8 corruption
at TCP chunk boundaries, use saturating_sub for epoch arithmetic, handle
CRLF SSE frame delimiters.

Runtime: switch runtime_config to std::sync::RwLock (guard is !Send),
use block_in_place for SQLite IO, spawn_blocking for tokenization,
add tracing reload layer so admin log_level changes apply immediately.

Reliability: retry failed log buffer flushes with capped retry queue,
add MAX_SSE_BUFFER_SIZE guard in middleware handler, cap tool call and
part indices to prevent unbounded vec growth.

Observability: defer streaming request logging until stream completes
so entries capture actual status, latency, and token counts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:22:42 -05:00
whit3rabbitandClaude Opus 4.6 e7f03b345c feat: add OpenAI Responses API backend and live API integration tests
Wire up Responses API as alternative to Chat Completions via
OPENAI_API_FORMAT=responses env var. Adds request/response mapping,
streaming state machine, client methods, and route dispatch. Includes
live API integration tests (ignored by default, require OPENAI_API_KEY).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 14:54:49 -05:00