Commit Graph
102 Commits
Author SHA1 Message Date
whit3rabbitandClaude Sonnet 4.6 c19a7c137c feat(proxy): add Gemini native HTTP client for generateContent endpoints
- New GeminiNativeClient with generate_content and generate_content_stream methods
- GeminiNative variant added to BackendClient enum and BackendError
- All existing match arms updated to handle the new variant
- 10 unit tests for URL construction, model mapping, error display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 09:49:28 -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
whit3rabbitandClaude Opus 4.6 320b44b6c0 fix: resolve clippy warnings from parallel subagent work
- Remove needless borrows in openai_batch_client.rs
- Replace redundant closure with function reference in CSRF middleware
- Narrow handler visibility to pub(crate) for anthropic_batch routes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 08:38:43 -05:00
whit3rabbit 09968ffca1 feat(proxy): register /v1/messages/batches routes, remove legacy stub 2026-03-28 00:04:06 -05:00
whit3rabbit 78def09d43 feat(admin-ui): send CSRF token in all state-mutating requests
- fetchCsrfToken(): fetches from GET /admin/csrf-token on load, stores in _csrfToken
- mutatingHeaders(): returns authHeaders merged with X-CSRF-Token header
- All POST/PUT/DELETE fetch calls now use mutatingHeaders() instead of authHeaders
- fetchCsrfToken() called before loadDashboard() so token is ready at startup
2026-03-27 23:49:51 -05:00
whit3rabbit 1d167e9863 feat(proxy): add Anthropic batch route handlers 2026-03-27 23:47:00 -05:00
whit3rabbit c9e9221caf ci: add cargo-audit step to catch crates with known CVEs
Runs after cargo test in the test job. Uses --locked for reproducibility.
2026-03-27 23:45:36 -05:00
whit3rabbit 6cd37068e3 feat(admin): add CSRF token middleware and /admin/csrf-token endpoint
- validate_csrf middleware: rejects POST/PUT/DELETE without matching X-CSRF-Token header + csrf_token cookie
- GET /admin/csrf-token: public endpoint returning JSON + Set-Cookie (not httpOnly, SameSite=Strict)
- CSRF middleware layered inside validate_admin_token (auth checked first)
- 7 new unit tests in routes.rs for CSRF behavior
- Updated virtual_keys.rs integration tests to include CSRF headers on all POST/DELETE admin calls
2026-03-27 23:45:03 -05:00
whit3rabbit 21bf301b24 feat(proxy): add anthropic_batch_map table for id mapping 2026-03-27 23:44:26 -05:00
whit3rabbit aabe7fe982 feat(proxy): add OpenAI batch API HTTP client 2026-03-27 23:41:47 -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 d81f0c32f4 feat(admin): add CSRF token generation and validation helpers
- generate_csrf_token: uses two UUID v4 values for 244 bits of entropy
- extract_csrf_cookie: parses csrf_token from Cookie header string
- validate_csrf_tokens: constant-time comparison, rejects empty tokens
- 7 unit tests covering all cases
2026-03-27 23:37:36 -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
whit3rabbit 3bd1db1e5c fix: batch JSONL error messages report absolute line number not non-empty-line count 2026-03-27 23:00:58 -05:00
whit3rabbit 00242ca653 fix: batch JSONL validation uses BufRead to avoid redundant Vec<u8> copy 2026-03-27 22:57:46 -05:00
whit3rabbit f7e4334bee fix: apply Gemini tool schema sanitizer to OpenAI-format chat_completions path 2026-03-27 22:52:36 -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 52c6ffa3dd feat: compute requests_per_second from SQLite in admin metrics snapshot 2026-03-27 22:38:45 -05:00
whit3rabbit cad85745f3 fix: replace brittle Bedrock event-type substring search with serde_json parse 2026-03-27 22:34:05 -05:00
whit3rabbit 08a03001c3 feat: wire Responses API streaming usage into request log and cost tracking 2026-03-27 22:27:08 -05:00
whit3rabbit c9ce57ebb1 fix: langfuse startTime uses ms precision for sub-second requests 2026-03-27 22:21:09 -05:00
whit3rabbit dbd63437b0 fix: remove stale is_streaming doc comment about metrics tracking 2026-03-27 22:16:33 -05:00
whit3rabbitandClaude Sonnet 4.6 c78e15e48a docs: mark Langfuse integration complete in COMPARISON_LITELLM.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:34:03 -05:00
whit3rabbitandClaude Sonnet 4.6 0408e669f3 feat: wire Langfuse named callback into CallbackConfig and startup
- Add NamedIntegration support to CallbackConfig (with_named constructor,
  named_count(), notify() dispatches to named integrations)
- Derive Clone on NamedIntegration (required since CallbackConfig derives Clone)
- Parse "langfuse" from litellm_settings.callbacks into langfuse_requested
  flag; filter it out of callback_urls
- Init LangfuseClient from env when langfuse_requested in LiteLLM config
- Env-var-only path in main.rs also activates Langfuse if keys are set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:31:53 -05:00
whit3rabbitandClaude Sonnet 4.6 4424816c3b fix: correct days_from_civil formula and fix env-var test race
- Add missing +2 rounding bias to days_from_civil doy formula
  (Hinnant algorithm); without it Feb/Apr/Jun/Sep/Nov timestamps
  are off by one day
- Add February date test to catch this class of bug
- Comment integer truncation in latency_ms/1000 computation
- Remove env-var mutation from mod.rs test (races with langfuse tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:25:44 -05:00
whit3rabbitandClaude Sonnet 4.6 28cd98f58c feat: implement LangfuseClient with base64 and ISO 8601 helpers
Sends generation-create events to Langfuse's batch ingestion API.
No new dependencies: base64 and ISO 8601 parsing are std-only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:14:41 -05:00
whit3rabbitandClaude Sonnet 4.6 89dd165b6a feat: add integrations module skeleton with NamedIntegration enum
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:07:28 -05:00
whit3rabbitandClaude Sonnet 4.6 521b528452 feat: add cost-based routing strategy and update comparison docs
Adds CostBased routing strategy to ModelRouter: selects the deployment
with the lowest combined input+output cost per token using the bundled
model_pricing.json. Falls back to round-robin when no deployment has
known pricing. Parsed from router_settings.routing_strategy: cost-based
in LiteLLM config files.

Also updates COMPARISON_LITELLM.md to reflect already-shipped features:
spend alerts, LITELLM_IP_ALLOWLIST alias, and routing strategy parity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:05:10 -05:00
whit3rabbitandClaude Sonnet 4.6 50b70c4460 fix: use SQL comments in SQL string, move apply_env_aliases to cfg(test)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 16:00:50 -05:00
whit3rabbit f230085872 docs: document mutex poisoning recovery and batch storage tradeoffs 2026-03-27 15:56:40 -05:00
whit3rabbit 373d55d36e security: warn at startup when serving API keys over plaintext HTTP
Add warning logs when the proxy listener is bound to a non-loopback
address and either PROXY_API_KEYS is configured or virtual keys are
loaded from the database. Warns operators to place a TLS-terminating
reverse proxy in front of the service to protect credentials.

Does not block startup; purely informational.
2026-03-27 15:52:40 -05:00
whit3rabbit e441d2aaff security: add 1MB body size limit to admin API 2026-03-27 15:52:05 -05:00
whit3rabbit 2b915c0149 security: harden admin UI CSP header (add self to script/style-src, ws: to connect-src) 2026-03-27 15:51:08 -05:00
whit3rabbitandClaude Sonnet 4.6 88177a6350 security: audit log config changes and validate model names
Add structured tracing::info! log with key, old_value, new_value for
every config change via PUT /admin/api/config. Add is_safe_model_name
validation rejecting path traversal (..), query params (?#), and
non-alphanumeric characters outside the allowed set (-_./: @).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:49:47 -05:00
whit3rabbitandClaude Sonnet 4.6 f3f5179a64 security: add rate limiting to admin API endpoints (10 RPM per IP)
Defense-in-depth against token brute-force on the admin API. Uses a
DashMap-based sliding window (60s) per client IP, applied as the
outermost middleware layer on protected admin routes. Admin server now
uses into_make_service_with_connect_info to expose client IP. Limit
is configurable at runtime via set_admin_rpm for test flexibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:45:37 -05:00
whit3rabbitandClaude Sonnet 4.6 d9d0d9f0ec security: zeroize Bedrock credentials on drop
Store secret_access_key and session_token as Zeroizing<String> so they
are wiped from heap memory when BedrockClient is dropped. Credentials
are reconstructed on each SigV4 signing call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:34:24 -05:00
whit3rabbit 79975e41da Merge branch 'worktree-admin-ui-overhaul' 2026-03-27 15:32:54 -05:00
whit3rabbitandClaude Sonnet 4.6 845cc567d5 fix: use nextElementSibling for detail toggle, fix models double-render
- nextSibling could return text nodes causing duplicate detail panels
- loadModels showed data.note twice when no router active

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:30:45 -05:00
whit3rabbitandClaude Sonnet 4.6 591fdee72b security: configurable rate limit fail policy via RATE_LIMIT_FAIL_POLICY
Add "deny" as alias for "closed" policy. Change fail-closed retry-after
from 1s to 60s to give Redis time to recover. Add from_env default test.
Document env var in CLAUDE.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:30:12 -05:00
whit3rabbitandClaude Sonnet 4.6 3b43c8cae2 security: typed StatusFilter enum to prevent SQL injection by construction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:26:56 -05:00
whit3rabbitandClaude Sonnet 4.6 12217186ec feat: robust metrics rendering and add missing env groups to admin UI and backend
- loadDashboard: apply error_rate coloring (red >5%, green otherwise) matching the
  WebSocket handler; derive requests_per_second into RPM stat on initial load
- Backend cards now show error count alongside request count
- ENV_GROUPS: add Azure OpenAI, AWS Bedrock, Network groups; add PROXY_OPEN_RELAY to
  Auth & TLS; add AZURE_OPENAI_API_KEY, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN to
  ENV_SECRET_KEYS
- get_env endpoint: expose all Azure OpenAI, AWS Bedrock, PROXY_OPEN_RELAY, and Network
  (IP_ALLOWLIST, TRUST_PROXY_HEADERS, WEBHOOK_URLS) env vars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:25:09 -05:00
whit3rabbitandClaude Sonnet 4.6 adfccb238a security: add AUTH_MODE to control OIDC/key auth fallthrough
Rename AuthMode variants from Jwt/Keys terminology to OidcOnly/KeysOnly/Both.
Add allows_oidc() and allows_key_auth() helpers used in validate_auth.
Accept both new names (oidc, oidc-only, keys, keys-only, both) and legacy
names (jwt_only, keys_only, jwt_or_keys) for backward compatibility.
Default remains Both (try JWT first, fall through to keys).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:23:44 -05:00
whit3rabbitandClaude Sonnet 4.6 85423b115d feat: split token columns and add feed pause/filter controls
Split the single Tokens column into In/Out columns (7-column grid).
Add pause button and status filter (2xx/4xx/5xx) to the live feed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:22:35 -05:00
whit3rabbitandClaude Sonnet 4.6 21c3793131 security: warn on plaintext HTTP webhook URLs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:20:12 -05:00
whit3rabbitandClaude Sonnet 4.6 4bf6b65bb8 security: configurable admin token path via ADMIN_TOKEN_PATH
Extract resolve_admin_token_path() function to read ADMIN_TOKEN_PATH
env var (falling back to .admin_token). Replaces the previous inline
ADMIN_TOKEN_FILE env var. Updates non-Unix warning to reference the
new env var name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:17:53 -05:00
whit3rabbitandClaude Sonnet 4.6 915a703a5a security: HMAC-SHA256 keyed hashing for virtual keys with dual-mode migration
New virtual keys are hashed with HMAC-SHA256 using a per-installation
secret (auto-generated and stored in SQLite settings table). Auth
middleware tries HMAC hash first, falls back to legacy SHA-256 for
pre-existing keys. This binds key hashes to the installation, so a
stolen database cannot be used to brute-force keys elsewhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:14:34 -05:00