* feat(failover): add auto-failover master switch with proxy integration
- Add persistent auto_failover_enabled setting in database
- Add get/set_auto_failover_enabled commands
- Provider router respects master switch state
- Proxy shutdown automatically disables failover
- Enabling failover auto-starts proxy server
- Optimistic updates for failover queue toggle
* feat(proxy): persist proxy takeover state across app restarts
- Add proxy_takeover_{app_type} settings for per-app state tracking
- Restore proxy takeover state automatically on app startup
- Preserve state on normal exit, clear on manual stop
- Add stop_with_restore_keep_state method for graceful shutdown
* fix(proxy): set takeover state for all apps in start_with_takeover
- Add support for ANTHROPIC_API_KEY in Claude auth extraction
- Only update existing token fields during sync, avoid adding fields
that weren't originally configured by the user
- Add tests for both scenarios
- Detect takeover residue in Live configs even when proxy is not running
- Implement 3-tier fallback: backup → SSOT → cleanup placeholders
- Only delete backup after successful restore to prevent data loss
- Fix EditProviderDialog to check current app's takeover status only
- Remove global proxy auto-start flag from config and UI.
- Simplify per-app takeover start/stop and stop server when the last takeover is disabled.
- Restore live takeover detection used for crash recovery.
- Keep proxy_config.enabled column but always write 0 for compatibility.
- Tests: not run (not requested).
Replace global live takeover with granular per-app control:
- Add start_proxy_server command (start without takeover)
- Add get_proxy_takeover_status to query each app's state
- Add set_proxy_takeover_for_app for individual app control
- Use live backup existence as SSOT for takeover state
- Refactor sync_live_to_provider to eliminate code duplication
- Update ProxyToggle to show status per active app
- Update Codex `model_providers.<model_provider>.base_url` to the proxy origin with `/v1`
- Add route fallbacks for `/responses` and `/chat/completions` (plus double-`/v1` safeguard)
- Add unit tests for the TOML base_url takeover logic
- Set takeover flag before writing proxy config to fix race condition
where crash during takeover left Live configs corrupted but flag unset
- Add fallback detection by checking for placeholder tokens in Live
configs when backups exist but flag is false (handles legacy/edge cases)
- Improve error handling with proper rollback at each stage of startup
- Clean up stale backups when Live configs are not in takeover state
to avoid long-term storage of sensitive tokens
- Skip live writes when takeover is active and proxy is running
- Refresh live backups from provider edits during takeover
- Sync live tokens to DB without clobbering real keys with placeholders
- Avoid injecting extra placeholder keys into Claude live env
- Reapply takeover after proxy listen address/port changes
- In takeover mode, edit dialog uses DB config and keeps API key state in sync
* feat(proxy): add error mapper for HTTP status code mapping
- Add error_mapper.rs module to map ProxyError to HTTP status codes
- Implement map_proxy_error_to_status() for error classification
- Implement get_error_message() for user-friendly error messages
- Support all error types: upstream, timeout, connection, provider failures
- Include comprehensive unit tests for all mappings
* feat(proxy): enhance error logging with context support
- Add log_error_with_context() method for detailed error recording
- Support streaming flag, session_id, and provider_type fields
- Remove dead_code warning from log_error() method
- Enable comprehensive error request tracking in database
* feat(proxy): implement error capture and logging in all handlers
- Capture and log all failed requests in handle_messages (Claude)
- Capture and log all failed requests in handle_gemini (Gemini)
- Capture and log all failed requests in handle_responses (Codex)
- Capture and log all failed requests in handle_chat_completions (Codex)
- Record error status codes, messages, and latency for all failures
- Generate unique session_id for each request
- Support both streaming and non-streaming error scenarios
* style: fix clippy warnings and typescript errors
- Add allow(dead_code) for CircuitBreaker::get_state (reserved for future)
- Fix all uninlined format string warnings (27 instances)
- Use inline format syntax for better readability
- Fix unused import and parameter warnings in ProviderActions.tsx
- Achieve zero warnings in both Rust and TypeScript
* style: apply code formatting
- Remove trailing whitespace in misc.rs
- Add trailing comma in App.tsx
- Format multi-line className in ProviderCard.tsx
* feat(proxy): add settings button to proxy panel
Add configuration buttons in both running and stopped states to
provide easy access to proxy settings dialog.
* fix(speedtest): skip client build for invalid inputs
* chore(clippy): fix uninlined format args
* Merge branch 'main' into feature/error-request-logging
- Remove `is_proxy_target` field from Provider struct (Rust & TypeScript)
- Remove related DAO methods: get_proxy_target_provider, set_proxy_target
- Remove deprecated Tauri commands: get_proxy_targets, set_proxy_target
- Add `is_available()` method to CircuitBreaker for availability checks
without consuming HalfOpen probe permits (used in select_providers)
- Keep `allow_request()` for actual request gating with permit tracking
- Update stream_check to use failover_queue instead of is_proxy_target
- Clean up commented-out reset circuit breaker button in ProviderActions
- Remove unused useProxyTargets and useSetProxyTarget hooks
Clear all provider_health records when stopping the proxy server,
ensuring health badges reset to "healthy" state. This fixes the
inconsistency where circuit breakers (in memory) would reset on
stop but health badges (in database) would retain stale state.
Add FailoverSwitchManager to handle provider switching after successful
failover. This ensures the UI reflects the actual provider in use:
- Create failover_switch.rs with deduplication and async switching logic
- Pass AppHandle through ProxyService -> ProxyServer -> RequestForwarder
- Update is_current in database when failover succeeds
- Emit provider-switched event for frontend refresh
- Update tray menu and live backup synchronously
The switching runs asynchronously via tokio::spawn to avoid blocking
API responses while still providing immediate UI feedback.
- Fix HalfOpen counter underflow: increment half_open_requests when
transitioning from Open to HalfOpen to prevent underflow in
record_success/record_failure
- Fix Gemini config field names: unify to GEMINI_API_KEY and
GOOGLE_GEMINI_BASE_URL (removed GOOGLE_API_KEY and GEMINI_API_BASE)
- Fix Codex proxy takeover: write base_url to config.toml instead of
OPENAI_BASE_URL in auth.json (Codex CLI reads from config.toml)
This commit addresses several critical issues in the failover system:
**Circuit breaker state persistence (previous fix)**
- Promote ProviderRouter to ProxyState for cross-request state sharing
- Remove redundant router.rs module
- Fix 429 errors to be retryable (rate limiting should try other providers)
**Hot-update circuit breaker config**
- Add update_circuit_breaker_configs() to ProxyServer and ProxyService
- Connect update_circuit_breaker_config command to running circuit breakers
- Add reset_provider_circuit_breaker() for manual breaker reset
**Fix HalfOpen deadlock bug**
- Change half_open_requests from cumulative count to in-flight count
- Release quota in record_success()/record_failure() when in HalfOpen state
- Prevents permanent deadlock when success_threshold > 1
**Fix duplicate select_providers() call**
- Store providers list in RequestContext, pass to forward_with_retry()
- Avoid consuming HalfOpen quota twice per request
- Single call to select_providers() per request lifecycle
**Add per-provider retry with exponential backoff**
- Implement forward_with_provider_retry() with configurable max_retries
- Backoff delays: 100ms, 200ms, 400ms, etc.
When the app crashes or is force-killed while proxy mode is active,
the live config files remain pointing to the dead proxy server with
placeholder tokens, causing CLI tools to fail.
This change adds startup detection:
- Check `live_takeover_active` flag on app launch
- If flag is true but proxy is not running → abnormal exit detected
- Automatically restore live configs from database backup
- Clear takeover flag and delete backups
The recovery runs before auto-start, ensuring correct sequence even
when proxy auto-start is enabled.
When proxy is active, switching providers only updated the database flags
but not the live backup. This caused the wrong provider config to be
restored when stopping the proxy.
Added `update_live_backup_from_provider()` method to ProxyService that
generates backup from provider's settings_config instead of reading from
live files (which are already taken over by proxy).
Previously, when proxy was started, the enabled flag was set to true and
persisted to database. However, stopping the proxy didn't reset this flag,
causing the proxy to auto-start on every subsequent app launch.
Now the enabled flag is set to false when proxy stops, ensuring the proxy
remains off after restart unless explicitly started by the user.
When proxy takeover is activated, tokens are replaced with placeholders
in live config files. However, the proxy reads tokens from the database,
not from live files. If the user's token only exists in the live config
(e.g., manually added), the database won't have it, causing auth failures.
Changes:
- Add sync_live_to_providers() to sync tokens from live configs to DB
- Add update_provider_settings_config() DAO method for partial updates
- Use "PROXY_MANAGED" placeholder instead of empty string to avoid
"missing API key" warnings in Claude Code status bar
- Integrate sync step into start_with_takeover() flow before clearing tokens
The new takeover flow:
1. setup_proxy_targets()
2. backup_live_configs()
3. sync_live_to_providers() <- NEW
4. takeover_live_configs()
5. Start proxy server
Issues fixed:
1. Route prefix mismatch - Live config was set to use /claude, /codex,
/gemini prefixes but server only had routes without prefixes
2. Proxy targets not auto-configured - start_with_takeover only modified
Live config but didn't set is_proxy_target=true for current providers
Changes:
- Add prefixed routes (/claude/v1/messages, /codex/v1/*, /gemini/v1beta/*)
to server.rs for backward compatibility
- Remove URL prefixes from takeover_live_configs() - server can route
by API endpoint path alone (/v1/messages=Claude, /v1/chat/completions=Codex)
- Add setup_proxy_targets() method that automatically sets current providers
as proxy targets when starting proxy with takeover
- Unify proxy toggle in SettingsPage to use takeover mode (same as main UI)
- Fix error message extraction in useProxyStatus hooks
- Fix provider switch logic to check both takeover flag AND proxy running state
* feat(proxy): implement local HTTP proxy server with multi-provider failover
Add a complete HTTP proxy server implementation built on Axum framework,
enabling local API request forwarding with automatic provider failover
and load balancing capabilities.
Backend Implementation (Rust):
- Add proxy server module with 7 core components:
* server.rs: Axum HTTP server lifecycle management (start/stop/status)
* router.rs: API routing configuration for Claude/OpenAI/Gemini endpoints
* handlers.rs: Request/response handling and transformation
* forwarder.rs: Upstream forwarding logic with retry mechanism (652 lines)
* error.rs: Comprehensive error handling and HTTP status mapping
* types.rs: Shared types (ProxyConfig, ProxyStatus, ProxyServerInfo)
* health.rs: Provider health check infrastructure
Service Layer:
- Add ProxyService (services/proxy.rs, 157 lines):
* Manage proxy server lifecycle
* Handle configuration updates
* Track runtime status and metrics
Database Layer:
- Add proxy configuration DAO (dao/proxy.rs, 242 lines):
* Persist proxy settings (listen address, port, timeout)
* Store provider priority and availability flags
- Update schema with proxy_config table (schema.rs):
* Support runtime configuration persistence
Tauri Commands:
- Add 6 command endpoints (commands/proxy.rs):
* start_proxy_server: Launch proxy server
* stop_proxy_server: Gracefully shutdown server
* get_proxy_status: Query runtime status
* get_proxy_config: Retrieve current configuration
* update_proxy_config: Modify settings without restart
* is_proxy_running: Check server state
Frontend Implementation (React + TypeScript):
- Add ProxyPanel component (222 lines):
* Real-time server status display
* Start/stop controls
* Provider availability monitoring
- Add ProxySettingsDialog component (420 lines):
* Configuration editor (address, port, timeout)
* Provider priority management
* Settings validation
- Add React hooks:
* useProxyConfig: Manage proxy configuration state
* useProxyStatus: Poll and display server status
- Add TypeScript types (types/proxy.ts):
* Define ProxyConfig, ProxyStatus interfaces
Provider Integration:
- Extend Provider model with availability field (providers.rs):
* Track provider health for failover logic
- Update ProviderCard UI to display proxy status
- Integrate proxy controls in Settings page
Dependencies:
- Add Axum 0.7 (async web framework)
- Add Tower 0.4 (middleware and service abstractions)
- Add Tower-HTTP (CORS layer)
- Add Tokio sync primitives (oneshot, RwLock)
Technical Details:
- Graceful shutdown via oneshot channel
- Shared state with Arc<RwLock<T>> for thread-safe config updates
- CORS enabled for cross-origin frontend access
- Request/response streaming support
- Automatic retry with exponential backoff (forwarder)
- API key extraction from multiple config formats (Claude/Codex/Gemini)
File Statistics:
- 41 files changed
- 3491 insertions(+), 41 deletions(-)
- Core modules: 1393 lines (server + forwarder + handlers)
- Frontend UI: 642 lines (ProxyPanel + ProxySettingsDialog)
- Database/DAO: 326 lines
This implementation provides the foundation for advanced features like:
- Multi-provider load balancing
- Automatic failover on provider errors
- Request logging and analytics
- Usage tracking and cost monitoring
* fix(proxy): resolve UI/UX issues and database constraint error
Simplify proxy control interface and fix database persistence issues:
Backend Fixes:
- Fix NOT NULL constraint error in proxy_config.created_at field
* Use COALESCE to preserve created_at on updates
* Ensure proper INSERT OR REPLACE behavior
- Remove redundant enabled field validation on startup
* Auto-enable when user clicks start button
* Persist enabled state after successful start
- Preserve enabled state during config updates
* Prevent accidental service shutdown on config save
Frontend Improvements:
- Remove duplicate proxy enable switch from settings dialog
* Keep only runtime toggle in ProxyPanel
* Simplify user experience with single control point
- Hide proxy target button when proxy service is stopped
* Add isProxyRunning prop to ProviderCard
* Conditionally render proxy controls based on service status
- Update form schema to omit enabled field
* Managed automatically by backend
Files: 5 changed, 81 insertions(+), 94 deletions(-)
* fix(proxy): improve URL building and Gemini request handling
- Refactor URL construction with version path deduplication (/v1, /v1beta)
- Preserve query parameters for Gemini API requests
- Support GOOGLE_GEMINI_API_KEY field name (with fallback)
- Change default proxy port from 5000 to 15721
- Fix test: use Option type for is_proxy_target field
* refactor(proxy): remove unused request handlers and routes
- Remove unused GET/DELETE request forwarding methods
- Remove count_tokens, get/delete response handlers
- Simplify router by removing unused endpoints
- Keep only essential routes: /v1/messages, /v1/responses, /v1beta/*
* Merge branch 'main' into feat/proxy-server
* fix(proxy): resolve clippy warnings for dead code and uninlined format args
- Add #[allow(dead_code)] to unused ProviderUnhealthy variant
- Inline format string arguments in handlers.rs and codex.rs log macros
- Refactor error response handling to properly pass through upstream errors
- Add URL deduplication logic for /v1/v1 paths in CodexAdapter
* feat(proxy): implement provider adapter pattern with OpenRouter support
This major refactoring introduces a modular provider adapter architecture
to support format transformation between different AI API formats.
New features:
- Add ProviderAdapter trait for unified provider abstraction
- Implement Claude, Codex, and Gemini adapters with specific logic
- Add Anthropic ↔ OpenAI format transformation for OpenRouter compatibility
- Support model mapping from provider configuration (ANTHROPIC_MODEL, etc.)
- Add OpenRouter preset to Claude provider presets
Refactoring:
- Extract authentication logic into auth.rs with AuthInfo and AuthStrategy
- Move URL building and request transformation to individual adapters
- Simplify ProviderRouter to only use proxy target providers
- Refactor RequestForwarder to use adapter-based request/response handling
- Use whitelist mode for header forwarding (only pass necessary headers)
Architecture:
- providers/adapter.rs: ProviderAdapter trait definition
- providers/auth.rs: AuthInfo, AuthStrategy types
- providers/claude.rs: Claude adapter with OpenRouter detection
- providers/codex.rs: Codex (OpenAI) adapter
- providers/gemini.rs: Gemini (Google) adapter
- providers/models/: Anthropic and OpenAI API data models
- providers/transform.rs: Bidirectional format transformation
* feat(proxy): add streaming SSE transform and thinking parameter support
New features:
- Add OpenAI → Anthropic SSE streaming response transformation
- Support thinking parameter detection for reasoning model selection
- Add ANTHROPIC_REASONING_MODEL config option for extended thinking
Changes:
- streaming.rs: Implement SSE event parsing and Anthropic format conversion
- transform.rs: Add thinking detection logic and reasoning model mapping
- handlers.rs: Integrate streaming transform for OpenRouter compatibility
- Cargo.toml: Add async-stream and bytes dependencies
* feat(db): add usage tracking schema and types
Add database tables for proxy request logs and model pricing.
Extend Provider and error types to support usage statistics.
* feat(proxy): implement usage tracking subsystem
Add request logger with automatic cost calculation.
Implement token parser for Claude/OpenAI/Gemini responses.
Add cost calculator based on model pricing configuration.
* feat(proxy): integrate usage logging into request handlers
Add usage logging to forwarder and streaming handlers.
Track token usage and costs for each proxy request.
* feat(commands): add usage statistics Tauri commands
Register usage commands for summary, trends, logs, and pricing.
Expose usage stats service through Tauri command layer.
* feat(api): add frontend usage API and query hooks
Add TypeScript types for usage statistics.
Implement usage API with Tauri invoke calls.
Add TanStack Query hooks for usage data fetching.
* feat(ui): add usage dashboard components
Add UsageDashboard with summary cards, trend chart, and data tables.
Implement model pricing configuration panel.
Add request log viewer with filtering and detail panel.
* fix(ui): integrate usage dashboard and fix type errors
Add usage dashboard tab to settings page.
Fix UsageScriptModal TypeScript type annotations.
* deps: add recharts for charts and rust_decimal/uuid for usage tracking
- recharts: Chart visualization for usage trends
- rust_decimal: Precise cost calculations
- uuid: Request ID generation
* feat(proxy): add ProviderType enum for fine-grained provider detection
Introduce ProviderType enum to distinguish between different provider
implementations (Claude, ClaudeAuth, Codex, Gemini, GeminiCli, OpenRouter).
This enables proper authentication handling and request transformation
based on the actual provider type rather than just AppType.
- Add ProviderType enum with detection logic from config
- Enhance Claude adapter with OpenRouter detection
- Enhance Gemini adapter with CLI mode detection
- Add helper methods for provider type inference
* feat(database): extend schema with streaming and timing fields
Add new columns to proxy_request_logs table for enhanced usage tracking:
- first_token_ms and duration_ms for performance metrics
- provider_type and is_streaming for request classification
- cost_multiplier for flexible pricing
Update model pricing with accurate rates for Claude/GPT/Gemini models.
Add ensure_model_pricing_seeded() call on database initialization.
Add test for model pricing auto-seeding verification.
* feat(proxy/usage): enhance token parser and logger for multi-format support
Parser enhancements:
- Add OpenAI Chat Completions format parsing (prompt_tokens/completion_tokens)
- Add model field to TokenUsage for actual model name extraction
- Add from_codex_response_adjusted() for proper cache token handling
- Add debug logging for better stream event tracing
Logger enhancements:
- Add first_token_ms, provider_type, is_streaming, cost_multiplier fields
- Extend RequestLog struct with full metadata tracking
- Update log_with_calculation() signature for new fields
Calculator: Update tests with model field in TokenUsage.
* feat(proxy): enhance proxy server with session tracking and OpenAI route
Error handling:
- Add StreamIdleTimeout and AuthError variants for better error classification
Module exports:
- Export ResponseType, StreamHandler, NonStreamHandler from response_handler
- Export ProxySession, ClientFormat from session module
Server routing:
- Add /v1/chat/completions route for OpenAI Chat Completions API
Handlers:
- Add log_usage_with_session() for enhanced usage tracking with session context
- Add first_token_ms timing measurement for streaming responses
- Use SseUsageCollector with start_time for accurate latency calculation
- Track is_streaming flag in usage logs
* feat(services): add pagination and enhanced filtering for request logs
Usage stats service:
- Change get_request_logs() from limit/offset to page/page_size pagination
- Return PaginatedLogs with total count, page, and page_size
- Add appType and providerName filters with LIKE search
- Add is_streaming, first_token_ms, duration_ms to RequestLogDetail
- Join with providers table for provider name lookup
Commands:
- Update get_request_logs command signature for pagination params
Module exports:
- Export PaginatedLogs struct
* feat(frontend): update usage types and API for pagination support
Types (usage.ts):
- Add isStreaming, firstTokenMs, durationMs to RequestLog
- Add PaginatedLogs interface with data, total, page, pageSize
- Change LogFilters: providerId -> appType + providerName
API (usage.ts):
- Change getRequestLogs params from limit/offset to page/pageSize
- Return PaginatedLogs instead of RequestLog[]
- Pass filters object directly to backend
Query (usage.ts):
- Update usageKeys.logs key generation for pagination
- Update useRequestLogs hook signature
* refactor(ui): enhance RequestLogTable with filtering and pagination
UI improvements:
- Add filter bar with app type, provider name, model, status selectors
- Add date range picker (startDate/endDate)
- Add search/reset/refresh buttons
Pagination:
- Implement proper page-based pagination with page info display
- Show total count and current page range
- Add prev/next navigation buttons
Features:
- Default to last 24 hours filter
- Streamlined table columns layout
- Query invalidation on refresh
* style(config): format mcpPresets code style
Apply consistent formatting to createNpxCommand function and
sequential-thinking server configuration.
* fix(ui): update SettingsPage tab styles for improved appearance (#342)
* feat(model-test): add provider model availability testing
Implement standalone model testing feature to verify provider API connectivity:
- Add ModelTestService for Claude/Codex/Gemini endpoint testing
- Create model_test_logs table for test result persistence
- Add test button to ProviderCard with loading state
- Include ModelTestConfigPanel for customizing test parameters
* fix(proxy): resolve token parsing for OpenRouter streaming responses
Problem:
- OpenRouter and similar third-party services return streaming responses
where input_tokens appear in message_delta instead of message_start
- The previous implementation only extracted input_tokens from message_start,
causing input_tokens to be recorded as 0 for these providers
Changes:
- streaming.rs: Add prompt_tokens field to Usage struct and include
input_tokens in the transformed message_delta event when converting
OpenAI format to Anthropic format
- parser.rs: Update from_claude_stream_events() to handle input_tokens
from both message_start (native Claude API) and message_delta (OpenRouter)
- Use if-let pattern instead of direct unwrap for safer parsing
- Only update input_tokens from message_delta if not already set
- logger.rs: Adjust test parameters to match updated function signature
Tests:
- Add test_openrouter_stream_parsing() for OpenRouter format validation
- Add test_native_claude_stream_parsing() for native Claude API validation
* fix(pricing): standardize model ID format for pricing lookup
Normalize model IDs by removing vendor prefixes and converting dots to hyphens to ensure consistent pricing lookups across different API response formats.
Changes:
- Update seed data to use hyphen format (e.g., gpt-5-1, gemini-2-5-pro)
- Add normalize_model_id() function to strip vendor prefixes (anthropic/, openai/)
- Convert dots to hyphens in model IDs (claude-haiku-4.5 → claude-haiku-4-5)
- Try both original and normalized IDs for exact matching
- Use normalized ID for suffix-based fallback matching
- Add comprehensive test cases for prefix and dot handling
- Add warning log when no pricing found
This ensures pricing lookups work correctly for:
- Models with vendor prefixes: anthropic/claude-haiku-4.5
- Models with dots in version: claude-sonnet-4.5
- Models with date suffixes: claude-haiku-4-5-20240229
* style(rust): apply clippy formatting suggestions
Apply automatic clippy fixes for uninlined_format_args warnings across Rust codebase. Replace format string placeholders with inline variable syntax for improved readability.
Changes:
- Convert format!("{}", var) to format!("{var}")
- Apply to model_test.rs, parser.rs, and usage_stats.rs
- Fix line length issues by breaking long function calls
- Improve code formatting consistency
All changes are automatic formatting with no functional impact.
* fix(ui): restore card borders in usage statistics panels
Restore proper card styling for ModelTestConfigPanel and PricingConfigPanel by adding back border and rounded-lg classes. The transparent background styling was causing visual inconsistency.
Changes:
- Replace border-none bg-transparent shadow-none with border rounded-lg
- Apply to both loading and error states for consistency
- Format TypeScript code for better readability
- Break long function signatures across multiple lines
This ensures the usage statistics panels have consistent visual appearance with proper borders and rounded corners.
* feat(pricing): add GPT-5 Codex model pricing presets
Add pricing configuration for GPT-5 Codex variants to support cost tracking for Codex-specific models.
Changes:
- Add gpt-5-codex model with standard GPT-5 pricing
- Add gpt-5-1-codex model with standard GPT-5.1 pricing
- Input: $1.25/M tokens, Output: $10/M tokens
- Cache read: $0.125/M tokens, Cache creation: $0
This ensures accurate cost calculation for Codex API requests using GPT-5 Codex models.