Feat/provider individual config (#663)

* refactor(ui): simplify UpdateBadge to minimal dot indicator

* feat(provider): add individual test and proxy config for providers

Add support for provider-specific model test and proxy configurations:

- Add ProviderTestConfig and ProviderProxyConfig types in Rust and TypeScript
- Create ProviderAdvancedConfig component with collapsible panels
- Update stream_check service to merge provider config with global config
- Proxy config UI follows global proxy style (single URL input)

Provider-level configs stored in meta field, no database schema changes needed.

* feat(ui): add failover toggle and improve proxy controls

- Add FailoverToggle component with slide animation
- Simplify ProxyToggle style to match FailoverToggle
- Add usage statistics button when proxy is active
- Fix i18n parameter passing for failover messages
- Add missing failover translation keys (inQueue, addQueue, priority)
- Replace AboutSection icon with app logo

* fix(proxy): support system proxy fallback and provider-level proxy config

- Remove no_proxy() calls in http_client.rs to allow system proxy fallback
- Add get_for_provider() to build HTTP client with provider-specific proxy
- Update forwarder.rs and stream_check.rs to use provider proxy config
- Fix EditProviderDialog.tsx to include provider.meta in useMemo deps
- Add useEffect in ProviderAdvancedConfig.tsx to sync expand state

Fixes #636
Fixes #583

* fix(ui): sync toast theme with app setting

* feat(settings): add log config management

Fixes #612
Fixes #514

* fix(proxy): increase request body size limit to 200MB

Fixes #666

* docs(proxy): update timeout config descriptions and defaults

Fixes #612

* fix(proxy): filter x-goog-api-key header to prevent duplication

* fix(proxy): prevent proxy recursion when system proxy points to localhost

Detect if HTTP_PROXY, HTTPS_PROXY, or ALL_PROXY environment variables
point to loopback addresses (localhost, 127.0.0.1), and bypass system
proxy in such cases to avoid infinite request loops.

* fix(i18n): add providerAdvanced i18n keys and fix failover toast parameter

- Add providerAdvanced.* i18n keys to en.json, zh.json, and ja.json
- Fix failover toggleFailed toast to pass detail parameter
- Remove Chinese fallback text from UI for English/Japanese users

* fix(tray): restore tray-provider events and enable Auto failover properly

- Emit provider-switched event on tray provider click (backward compatibility)
- Auto button now: starts proxy, takes over live config, enables failover

* fix(log): enable dynamic log level and single file mode

- Initialize log at Trace level for dynamic adjustment
- Change rotation strategy to KeepSome(1) for single file
- Set max file size to 1GB
- Delete old log file on startup for clean start

* fix(tray): fix clippy uninlined format args warning

Use inline format arguments: {app_type_str} instead of {}

* fix(provider): allow typing :// in endpoint URL inputs

Change input type from "url" to "text" to prevent browser
URL validation from blocking :// input.

Closes #681

* fix(stream-check): use Gemini native streaming API format

- Change endpoint from OpenAI-compatible to native streamGenerateContent
- Add alt=sse parameter for SSE format response
- Use x-goog-api-key header instead of Bearer token
- Convert request body to Gemini contents/parts format

* feat(proxy): add request logging for debugging

Add debug logs for outgoing requests including URL and body content
with byte size, matching the existing response logging format.

* fix(log): prevent usize underflow in KeepSome rotation strategy

KeepSome(n) internally computes n-2, so n=1 causes underflow.
Use KeepSome(2) as the minimum safe value.
This commit is contained in:
Dex Miller
2026-01-20 21:02:44 +08:00
committed by GitHub
parent 7bb458eecb
commit e7badb1a24
46 changed files with 2008 additions and 331 deletions
+53
View File
@@ -208,6 +208,29 @@
"responseGroup": "Response Rectification",
"thinkingSignature": "Thinking Signature Rectification",
"thinkingSignatureDescription": "Automatically fix Claude API errors caused by thinking signature validation failures"
},
"logConfig": {
"title": "Log Management",
"description": "Control log output level",
"enabled": "Enable Logging",
"enabledDescription": "Master switch, all logging will be disabled when turned off",
"level": "Log Level",
"levelDescription": "Set the minimum log level to output",
"levels": {
"error": "Error",
"warn": "Warning",
"info": "Info",
"debug": "Debug",
"trace": "Trace"
},
"levelHint": "Log level descriptions:",
"levelDesc": {
"error": "Critical errors only",
"warn": "Errors + warnings",
"info": "General operation info (default)",
"debug": "Detailed info including SSE stream and request/response",
"trace": "All logs, most verbose"
}
}
},
"language": "Language",
@@ -427,6 +450,22 @@
"testFailed": "Speed test failed: {{error}}",
"status": "Status: {{code}}"
},
"providerAdvanced": {
"testConfig": "Model Test Config",
"useCustomConfig": "Use separate config",
"testConfigDesc": "Configure separate model testing parameters for this provider. Uses global settings when disabled.",
"testModel": "Test Model",
"testModelPlaceholder": "Leave empty to use global config",
"timeoutSecs": "Timeout (seconds)",
"testPrompt": "Test Prompt",
"degradedThreshold": "Degraded Threshold (ms)",
"maxRetries": "Max Retries",
"proxyConfig": "Proxy Config",
"useCustomProxy": "Use separate proxy",
"proxyConfigDesc": "Configure separate network proxy for this provider. Uses system proxy or global settings when disabled.",
"proxyUsername": "Username (optional)",
"proxyPassword": "Password (optional)"
},
"codexConfig": {
"authJson": "auth.json (JSON) *",
"authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}",
@@ -1088,6 +1127,20 @@
"circuitOpen": "Circuit Open",
"consecutiveFailures": "{{count}} consecutive failures"
},
"failover": {
"enabled": "{{app}} failover enabled",
"disabled": "{{app}} failover disabled",
"toggleFailed": "Operation failed: {{detail}}",
"inQueue": "In queue",
"addQueue": "Add",
"priority": {
"tooltip": "Failover priority {{priority}}"
},
"tooltip": {
"enabled": "{{app}} failover enabled\nAutomatically switch to next available provider",
"disabled": "Enable {{app}} failover\nAutomatically switch when current provider fails"
}
},
"proxy": {
"panel": {
"serviceAddress": "Service Address",
+53
View File
@@ -208,6 +208,29 @@
"responseGroup": "レスポンス整流",
"thinkingSignature": "Thinking 署名整流",
"thinkingSignatureDescription": "Claude API の thinking 署名検証エラーを自動修正"
},
"logConfig": {
"title": "ログ管理",
"description": "ログ出力レベルを制御",
"enabled": "ログを有効化",
"enabledDescription": "マスタースイッチ、オフにするとすべてのログが無効になります",
"level": "ログレベル",
"levelDescription": "出力する最小ログレベルを設定",
"levels": {
"error": "エラー",
"warn": "警告",
"info": "情報",
"debug": "デバッグ",
"trace": "トレース"
},
"levelHint": "ログレベルの説明:",
"levelDesc": {
"error": "重大なエラーのみ",
"warn": "エラー + 警告",
"info": "一般的な操作情報(デフォルト)",
"debug": "SSE ストリームとリクエスト/レスポンスを含む詳細情報",
"trace": "すべてのログ、最も詳細"
}
}
},
"language": "言語",
@@ -427,6 +450,22 @@
"testFailed": "速度テストに失敗しました: {{error}}",
"status": "ステータス: {{code}}"
},
"providerAdvanced": {
"testConfig": "モデルテスト設定",
"useCustomConfig": "個別設定を使用",
"testConfigDesc": "このプロバイダーに個別のモデルテストパラメータを設定します。無効の場合はグローバル設定を使用します。",
"testModel": "テストモデル",
"testModelPlaceholder": "空白の場合はグローバル設定を使用",
"timeoutSecs": "タイムアウト(秒)",
"testPrompt": "テストプロンプト",
"degradedThreshold": "低下閾値(ミリ秒)",
"maxRetries": "最大リトライ回数",
"proxyConfig": "プロキシ設定",
"useCustomProxy": "個別プロキシを使用",
"proxyConfigDesc": "このプロバイダーに個別のネットワークプロキシを設定します。無効の場合はシステムプロキシまたはグローバル設定を使用します。",
"proxyUsername": "ユーザー名(任意)",
"proxyPassword": "パスワード(任意)"
},
"codexConfig": {
"authJson": "auth.json (JSON) *",
"authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}",
@@ -1088,6 +1127,20 @@
"circuitOpen": "サーキットオープン",
"consecutiveFailures": "{{count}} 回連続失敗"
},
"failover": {
"enabled": "{{app}} フェイルオーバーが有効になりました",
"disabled": "{{app}} フェイルオーバーが無効になりました",
"toggleFailed": "操作に失敗しました: {{detail}}",
"inQueue": "キュー内",
"addQueue": "追加",
"priority": {
"tooltip": "フェイルオーバー優先度 {{priority}}"
},
"tooltip": {
"enabled": "{{app}} フェイルオーバーが有効\n次の利用可能なプロバイダーに自動切り替え",
"disabled": "{{app}} フェイルオーバーを有効にする\n現在のプロバイダーが失敗した場合に自動切り替え"
}
},
"proxy": {
"panel": {
"serviceAddress": "サービスアドレス",
+53
View File
@@ -208,6 +208,29 @@
"responseGroup": "响应整流",
"thinkingSignature": "Thinking 签名整流",
"thinkingSignatureDescription": "自动修复 Claude API 中因 thinking 签名校验失败导致的请求错误"
},
"logConfig": {
"title": "日志管理",
"description": "控制日志输出级别",
"enabled": "启用日志",
"enabledDescription": "总开关,关闭后所有日志将被禁用",
"level": "日志级别",
"levelDescription": "设置输出的最低日志级别",
"levels": {
"error": "错误",
"warn": "警告",
"info": "信息",
"debug": "调试",
"trace": "跟踪"
},
"levelHint": "日志级别说明:",
"levelDesc": {
"error": "仅严重错误",
"warn": "错误 + 警告信息",
"info": "一般操作信息(默认)",
"debug": "详细信息,包含 SSE 流和请求/响应详情",
"trace": "全部日志,最详细"
}
}
},
"language": "界面语言",
@@ -427,6 +450,22 @@
"testFailed": "测速失败: {{error}}",
"status": "状态码:{{code}}"
},
"providerAdvanced": {
"testConfig": "模型测试配置",
"useCustomConfig": "使用单独配置",
"testConfigDesc": "为此供应商配置单独的模型测试参数,不启用时使用全局配置。",
"testModel": "测试模型",
"testModelPlaceholder": "留空使用全局配置",
"timeoutSecs": "超时时间(秒)",
"testPrompt": "测试提示词",
"degradedThreshold": "降级阈值(毫秒)",
"maxRetries": "最大重试次数",
"proxyConfig": "代理配置",
"useCustomProxy": "使用单独代理",
"proxyConfigDesc": "为此供应商配置单独的网络代理,不启用时使用系统代理或全局设置。",
"proxyUsername": "用户名(可选)",
"proxyPassword": "密码(可选)"
},
"codexConfig": {
"authJson": "auth.json (JSON) *",
"authJsonPlaceholder": "{\n \"OPENAI_API_KEY\": \"sk-your-api-key-here\"\n}",
@@ -1088,6 +1127,20 @@
"circuitOpen": "熔断",
"consecutiveFailures": "连续失败 {{count}} 次"
},
"failover": {
"enabled": "{{app}} 故障转移已启用",
"disabled": "{{app}} 故障转移已关闭",
"toggleFailed": "操作失败: {{detail}}",
"inQueue": "已加入",
"addQueue": "加入",
"priority": {
"tooltip": "故障转移优先级 {{priority}}"
},
"tooltip": {
"enabled": "{{app}} 故障转移已启用\n自动切换到下一个可用供应商",
"disabled": "启用 {{app}} 故障转移\n当当前供应商失败时自动切换"
}
},
"proxy": {
"panel": {
"serviceAddress": "服务地址",