Resolves conflicts: take HEAD (security audit) for mcp.rs imports,
register_server_blocking error handling, and maybe_execute_tools loop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Proxy batch handlers now use BatchEngine for job lifecycle, file storage,
and webhook delivery instead of direct SQLite calls. Old batch/db.rs
stripped to Anthropic-specific mapping only. Cancel endpoint at
POST /v1/batches/{id}/cancel. BatchEngine initialized in main.rs startup
with second SQLite connection. Cancel integration test added.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSRF tokens are now one-time use. Tests updated to:
- Pre-insert TEST_CSRF_TOKEN in test_admin_router() for unit tests
- Call reinsert_csrf() before each additional mutation in multi-step tests
- Add fetch_csrf() helper for real-server tests
- update create_key_via_admin() to fetch a fresh token per call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace batch/mod.rs types with re-exports from anyllm_batch_engine
- Strip batch/db.rs to Anthropic->OpenAI ID mapping only (batch_file/batch_job owned by engine)
- Rewrite batch/routes.rs to use BatchEngine for upload, create, get, list, cancel
- Remove batch_file/batch_job table creation from admin/db.rs init_db
- Add batch_engine parameter to app_multi_with_shared (5th arg, Option<Arc<BatchEngine>>)
- Initialize BatchEngine in main.rs with its own SQLite connection (admin-enabled path)
- Update batch_api.rs tests to use make_test_batch_engine() helper
- Fix anthropic_batch.rs to call init_anthropic_batch_map_table instead of removed init_batch_tables
- Add cancel_queued_batch integration test
partition_tool_calls now returns three buckets (auto_execute, pass_through,
denied). Denied tools generate ToolResult entries with is_error:true and a
"denied by policy" message, which are fed back to the LLM in a follow-up
turn so it can see the rejection. Updated all callers (maybe_execute_tools,
streaming handler in chat_completions.rs) and added unit + integration tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ToolEngineState struct (registry, policy, loop_config, mcp_manager)
and tool_engine field to AppState. Update app_multi_with_shared signature
to accept the new parameter; all callers pass None until config-driven
wiring is implemented in a future task.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wrap all four inject_degradation_header calls in chat_completions.rs with
state.expose_degradation_warnings guard, matching the pattern already applied
in routes.rs. Update existing degradation test to use expose_degradation_warnings:
true; add suppressed-when-disabled test for the false case.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add ConnectInfo<SocketAddr> extractor to all seven admin mutation
handlers (put_config, delete_config_override, create_key, update_key,
revoke_key, add_model, remove_model). Pass addr.ip().to_string() as
source_ip in each emit_audit call.
Update integration tests to use into_make_service_with_connect_info
and MockConnectInfo so the ConnectInfo extractor is satisfied in test
servers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
Accept LiteLLM config.yaml directly via PROXY_CONFIG=config.yaml. Parses
model_list with provider/model format, supports multiple deployments per
model name with round-robin + RPM-aware load balancing, cross-backend
dispatch, and os.environ/VAR env var syntax. Adds env var aliases for
LITELLM_MASTER_KEY, AZURE_API_KEY, AZURE_API_BASE, LITELLM_CONFIG.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Update CLAUDE.md and README.md to document new backends (Azure, Bedrock),
virtual key management, rate limiting, OTEL, and client v0.2.0. Add CI
workflow for the new integration test suite. Final task-list completions
and virtual key integration tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
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>
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>
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>
Collapse retry + final-attempt into single inclusive loop, drain response
body before retry to return connections to pool, and replace panic on
invalid x-request-id with UUID fallback. Update CLAUDE.md/TASKS.md for
Phase 22 status. Apply cargo fmt across touched files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 19: Replace count_tokens stub with tiktoken-rs implementation using
o200k_base encoder. Extracts text from system prompt, messages, tool
definitions, and thinking blocks for approximate token counting.
Phase 20b: Extract BackendClient enum in backend/mod.rs with OpenAI/Vertex
variants and dispatch methods, preparing for future native Gemini backend.
Pure refactor with no behavior change.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parameterize OpenAIClient with BackendAuth enum to support both OpenAI
and Vertex AI backends. BACKEND=vertex enables Vertex AI with
VERTEX_PROJECT, VERTEX_REGION, and VERTEX_API_KEY/GOOGLE_ACCESS_TOKEN
env vars. Model defaults change to gemini-2.5-pro/gemini-2.5-flash
for Vertex. No new client type; reuses OpenAIClient with different
URL construction and auth header dispatch.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phases 12-18: release infrastructure (LICENSE, README, Dockerfile, CI,
CHANGELOG), transparent proxy with anthropic-version/anthropic-beta header
passthrough and lossy translation warnings, BIG_MODEL/SMALL_MODEL env-based
model mapping, mTLS client cert support (P12/PEM), max_completion_tokens
and reasoning_effort passthrough via serde flatten, extended thinking type
support (thinking blocks stripped in translation), top_k typed field.
Phase 20: Gemini backend research with docs/gemini-api-diffs.md covering
native API format, tool calling, streaming, auth, schema restrictions,
and Vertex AI OpenAI-compatible endpoint. Task roadmap through Phase 22.
Test count: 169 -> expanded with new fixture and unit tests for all phases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rust workspace with two crates:
- translator: pure, IO-free mapping between Anthropic Messages API and OpenAI Chat Completions
- proxy: axum HTTP server with auth, streaming SSE, retry/backoff, concurrency limits
169 tests passing (unit, golden fixture, integration).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>