- Add endpointAutoSelect field to ProviderMeta for persistence
- Lift autoSelect state from EndpointSpeedTest to ProviderForm
- Save auto-select preference when provider is saved
- Restore preference when editing existing provider
Fixes https://github.com/farion1231/cc-switch/issues/589
* feat(proxy): extract model name from API response for accurate usage tracking
- Add model field extraction in TokenUsage parsing for Claude, OpenAI, and Codex
- Prioritize response model over request model in usage logging
- Update model extractors to use parsed usage.model first
- Add tests for model extraction in stream and non-stream responses
* feat(proxy): implement streaming timeout control with validation
- Add first byte timeout (0 or 1-180s) for streaming requests
- Add idle timeout (0 or 60-600s) for streaming data gaps
- Add non-streaming timeout (0 or 60-1800s) for total request
- Implement timeout logic in response processor
- Add 1800s global timeout fallback when disabled
- Add database schema migration for timeout fields
- Add i18n translations for timeout settings
* feat(proxy): add model mapping module for provider-based model substitution
- Add model_mapper.rs with ModelMapping struct to extract model configs from Provider
- Support ANTHROPIC_MODEL, ANTHROPIC_REASONING_MODEL, and default models for haiku/sonnet/opus
- Implement thinking mode detection for reasoning model priority
- Include comprehensive unit tests for all mapping scenarios
* fix(proxy): bypass circuit breaker for single provider scenario
When failover is disabled (single provider), circuit breaker open state
would block all requests causing poor UX. Now bypasses circuit breaker
check in this scenario. Also integrates model mapping into request flow.
* feat(ui): add reasoning model field to Claude provider form
Add ANTHROPIC_REASONING_MODEL configuration field for Claude providers,
allowing users to specify a dedicated model for thinking/reasoning tasks.
* feat(proxy): add openrouter_compat_mode for optional format conversion
Add configurable OpenRouter compatibility mode that enables Anthropic to
OpenAI format conversion. When enabled, rewrites endpoint to /v1/chat/completions
and transforms request/response formats. Defaults to enabled for OpenRouter.
* feat(ui): add OpenRouter compatibility mode toggle
Add UI toggle for OpenRouter providers to enable/disable compatibility
mode which uses OpenAI Chat Completions format with SSE conversion.
* feat(stream-check): use provider-configured model for health checks
Extract model from provider's settings_config (ANTHROPIC_MODEL, GEMINI_MODEL,
or Codex config.toml) instead of always using default test models.
* refactor(ui): remove timeout settings from AutoFailoverConfigPanel
Remove streaming/non-streaming timeout configuration from failover panel
as these settings have been moved to a dedicated location.
* refactor(database): migrate proxy_config to per-app three-row structure
Replace singleton proxy_config table with app_type primary key structure,
allowing independent proxy settings for Claude, Codex, and Gemini.
Add GlobalProxyConfig queries and per-app config management in DAO layer.
* feat(proxy): add GlobalProxyConfig and AppProxyConfig types
Add new type definitions for the refactored proxy configuration:
- GlobalProxyConfig: shared settings (enabled, address, port, logging)
- AppProxyConfig: per-app settings (failover, timeouts, circuit breaker)
* refactor(proxy): update service layer for per-app config structure
Adapt proxy service, handler context, and provider router to use
the new per-app configuration model. Read enabled/timeout settings
from proxy_config table instead of settings table.
* feat(commands): add global and per-app proxy config commands
Add new Tauri commands for the refactored proxy configuration:
- get_global_proxy_config / update_global_proxy_config
- get_proxy_config_for_app / update_proxy_config_for_app
Update startup restore logic to read from proxy_config table.
* feat(api): add frontend API and Query hooks for proxy config
Add TypeScript wrappers and TanStack Query hooks for:
- Global proxy config (address, port, logging)
- Per-app proxy config (failover, timeouts, circuit breaker)
- Proxy takeover status management
* refactor(ui): redesign proxy panel with inline config controls
Replace ProxySettingsDialog with inline controls in ProxyPanel.
Add per-app takeover switches and global address/port settings.
Simplify AutoFailoverConfigPanel by removing timeout settings.
* feat(i18n): add proxy takeover translations and update types
Add i18n strings for proxy takeover status in zh/en/ja.
Update TypeScript types for GlobalProxyConfig and AppProxyConfig.
* refactor(proxy): load circuit breaker config per-app instead of globally
Extract app_type from router key and read circuit breaker settings
from the corresponding proxy_config row for each application.
Optimize custom endpoint management logic to distinguish between edit and create modes:
- Edit mode: endpoints are read/written directly to backend via API
- Create mode: use draftCustomEndpoints to stage, save on submit
- Remove duplicate endpoint loading in useSpeedTestEndpoints
- Add isSaving state and initialCustomUrls tracking
Clear placeholder values for model input fields to avoid suggesting specific model names that may not be applicable to all providers. This prevents user confusion when configuring different Claude providers.
- Add isPartner and partnerPromotionKey fields to Provider and ProviderPreset types
- Display gold star badge on partner presets in selector
- Show promotional message in API Key section for partners
- Configure Zhipu GLM as official partner with 10% discount promotion
- Support both Claude and Codex provider presets
- Add i18n support for partner promotion messages (zh/en)
Implement Solution A (complete deferred submission) for custom endpoint
management, replacing the dual-mode system with unified local staging.
Changes:
- Remove immediate backend saves from EndpointSpeedTest
* handleAddEndpoint: local state update only
* handleRemoveEndpoint: local state update only
* handleSelect: remove lastUsed timestamp update
- Add explicit clear detection in ProviderForm
* Distinguish "user cleared endpoints" from "user didn't modify"
* Pass empty object {} as clear signal vs null for no-change
- Fix mergeProviderMeta to handle three distinct cases:
* null/undefined: don't modify endpoints (no meta sent)
* empty object {}: explicitly clear endpoints (send empty meta)
* with data: add/update endpoints (overwrite)
Fixed Critical Bug:
When users deleted all custom endpoints, changes were not saved because:
- draftCustomEndpoints=[] resulted in customEndpointsToSave=null
- mergeProviderMeta(meta, null) returned undefined
- Backend interpreted missing meta as "don't modify", preserving old values
Solution:
Detect when user had endpoints and cleared them (hadEndpoints && length===0),
then pass empty object to mergeProviderMeta as explicit clear signal.
Architecture Improvements:
- Transaction atomicity: all fields submitted together on form save
- UX consistency: add/edit modes behave identically
- Cancel button: true rollback with no immediate saves
- Code simplification: removed ~40 lines of immediate save error handling
Testing:
- TypeScript type check: passed
- Rust backend tests: 10/10 passed
- Build: successful
- Add dedicated Haiku model placeholder "GLM-4.5-Air"
- Unify API key hints for all non-official providers
- Fix AiHubMix category from cn_official to aggregator
- Standardize GLM model name format (glm-4.6)
- Rename src/config/providerPresets.ts to claudeProviderPresets.ts
- Update all import statements across 11 files to reflect the new name
- Establish symmetrical naming convention with codexProviderPresets.ts
- Improve code clarity and maintainability
Remove KimiModelSelector component and useKimiModelSelector hook to
eliminate code duplication and unify model configuration across all
Claude-compatible providers.
**Problem Statement:**
Previously, we maintained two separate implementations for the same
functionality:
- KimiModelSelector: API-driven dropdown with 211 lines of code
- ClaudeFormFields: Simple text inputs for model configuration
After removing API fetching logic from KimiModelSelector, both components
became functionally identical (4 text inputs), violating DRY principle
and creating unnecessary maintenance burden.
**Changes:**
Backend (Rust):
- No changes (model normalization logic already in place)
Frontend (React):
- Delete KimiModelSelector.tsx (-211 lines)
- Delete useKimiModelSelector.ts (-142 lines)
- Update ClaudeFormFields.tsx: remove Kimi-specific props (-35 lines)
- Update ProviderForm.tsx: unify display logic (-31 lines)
- Clean up hooks/index.ts: remove useKimiModelSelector export (-1 line)
Configuration:
- Update Kimi preset: kimi-k2-turbo-preview → kimi-k2-0905-preview
* Uses official September 2025 release
* 256K context window (vs 128K in older version)
Internationalization:
- Remove kimiSelector.* i18n keys (15 keys × 2 languages = -36 lines)
- Remove providerForm.kimiApiKeyHint
**Unified Architecture:**
Before (complex branching):
ProviderForm
├─ if Kimi → useKimiModelSelector → KimiModelSelector (4 inputs)
└─ else → useModelState → ClaudeFormFields inline (4 inputs)
After (single path):
ProviderForm
└─ useModelState → ClaudeFormFields (4 inputs for all providers)
Display logic simplified:
- Old: shouldShowModelSelector = category !== "official" && !shouldShowKimiSelector
- New: shouldShowModelSelector = category !== "official"
**Impact:**
Code Quality:
- Remove 457 lines of redundant code (-98.5%)
- Eliminate dual-track maintenance
- Improve code consistency
- Pass TypeScript type checking with zero errors
- Zero remaining references to deleted code
User Experience:
- Consistent UI across all providers (including Kimi)
- Same model configuration workflow for everyone
- No functional changes from user perspective
Architecture:
- Single source of truth for model configuration
- Easier to extend for future providers
- Reduced bundle size (removed lucide-react icons dependency)
**Testing:**
- ✅ TypeScript compilation passes
- ✅ No dangling references
- ✅ All model configuration fields functional
- ✅ Display logic works for official/cn_official/aggregator categories
**Migration Notes:**
- Existing Kimi users: configurations automatically upgraded to new model name
- No manual intervention required
- Backend normalization ensures backward compatibility
Upgrade Claude model configuration from dual-key to quad-key system for
better model tier differentiation.
**Breaking Changes:**
- Replace `ANTHROPIC_SMALL_FAST_MODEL` with three granular keys:
- `ANTHROPIC_DEFAULT_HAIKU_MODEL`
- `ANTHROPIC_DEFAULT_SONNET_MODEL`
- `ANTHROPIC_DEFAULT_OPUS_MODEL`
**Backend (Rust):**
- Add `normalize_claude_models_in_value()` for automatic migration
- Implement fallback chain: `DEFAULT_* || SMALL_FAST || MODEL`
- Auto-cleanup: remove legacy `SMALL_FAST` key after normalization
- Apply normalization across 6 critical paths:
- Add/update provider
- Read from live config
- Write to live config
- Refresh config snapshot
**Frontend (React):**
- Expand UI from 2 to 4 model input fields
- Implement smart fallback in `useModelState` hook
- Update `useKimiModelSelector` for Kimi model picker
- Add i18n keys for Haiku/Sonnet/Opus labels (zh/en)
**Configuration:**
- Update all 7 provider presets to new format
- DeepSeek/Qwen/Moonshot: use same model for all tiers
- Zhipu: preserve tier differentiation (glm-4.5-air for Haiku)
**Backward Compatibility:**
- Old configs auto-upgrade on first read/write
- Fallback chain ensures graceful degradation
- No manual migration required
Closes #[issue-number]
Rename `AppType` to `AppId` across the entire frontend codebase to better
reflect its purpose as an application identifier rather than a type category.
This aligns frontend naming with backend command parameter conventions.
Changes:
- Rename type `AppType` to `AppId` in src/lib/api/types.ts
- Remove `AppType` export from src/lib/api/index.ts
- Update all component props from `appType` to `appId` (43 files)
- Update all variable names from `appType` to `appId`
- Synchronize documentation (CHANGELOG, refactoring plans)
- Update test files and MSW mocks
BREAKING CHANGE: `AppType` type is no longer exported. Use `AppId` instead.
All component props have been renamed from `appType` to `appId`.
- Migrate all form components from ProviderForm/ to providers/forms/
- Create shared components to eliminate code duplication:
* ApiKeySection: unified API key input with "Get API Key" link
* EndpointField: unified endpoint URL input with manage button
- Refactor ClaudeFormFields (-31% lines) and CodexFormFields (-33% lines)
- Update all import paths to use new locations
- Reduce code duplication from ~12% to ~7%
This change improves maintainability and makes the codebase more DRY.
- Create useSpeedTestEndpoints hook that collects endpoints from:
1. Current baseUrl/codexBaseUrl
2. Initial data URL (edit mode)
3. Preset's endpointCandidates array
- Pass speedTestEndpoints to ClaudeFormFields and CodexFormFields
- Update EndpointSpeedTest to use collected endpoints instead of just current URL
- Fix PackyCode preset endpoints not appearing in speed test modal
- Created ProviderPresetSelector component (80 lines)
- Created BasicFormFields component (60 lines)
- Created ClaudeFormFields component (272 lines)
- Created CodexFormFields component (131 lines)
- Reduced ProviderForm from 866 to 544 lines (37% reduction)
Each component now has a clear single responsibility:
- ProviderPresetSelector: Handles preset selection UI
- BasicFormFields: Name and website URL inputs
- ClaudeFormFields: All Claude-specific form fields
- CodexFormFields: All Codex-specific form fields
- ProviderForm: Orchestrates hooks and component composition
Benefits:
- Better code organization and maintainability
- Easier to test individual components
- Clearer separation of concerns
- More reusable components