diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index a6269686d6..1dcf49ab91 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -914,26 +914,6 @@
"when": "Use when long sessions must survive account rotation without losing the working context.",
"avoid": "Avoid when account switching is rare or when you do not want extra summarization requests.",
"example": "Example: Codex sessions that rotate across multiple accounts near quota exhaustion."
- },
- "fill-first": {
- "when": "Use when you want to fully exhaust one provider's quota before moving to the next.",
- "avoid": "Avoid when you need request-level load balancing between providers.",
- "example": "Example: Use up all $200 of Deepgram credits before falling back to Groq."
- },
- "auto": {
- "when": "Use when you have one preferred model and only want fallback on failure.",
- "avoid": "Avoid when you need balanced load between models.",
- "example": "Example: Primary coding model with cheaper backup for outages."
- },
- "lkgp": {
- "when": "Use when you have one preferred model and only want fallback on failure.",
- "avoid": "Avoid when you need balanced load between models.",
- "example": "Example: Primary coding model with cheaper backup for outages."
- },
- "context-optimized": {
- "when": "Use when you have one preferred model and only want fallback on failure.",
- "avoid": "Avoid when you need balanced load between models.",
- "example": "Example: Primary coding model with cheaper backup for outages."
}
},
"advancedHelp": {
@@ -1013,25 +993,6 @@
"candidatePoolHint": "Select which providers this engine should evaluate. Leave empty to use all active providers.",
"candidatePoolEmpty": "No active providers available yet.",
"candidatePoolAllProviders": "All providers",
- "builderStagesDescription": "Move through the stages in order to define the combo, build the steps, choose the routing strategy and review the result.",
- "builderStepsDescription": "Build each combo step in sequence: provider, model, then account. This allows repeating the same provider and model with different accounts.",
- "selectProvider": "Select Provider",
- "selectProviderPlaceholder": "Select provider",
- "selectModel": "Select Model",
- "selectModelPlaceholder": "Select provider first",
- "selectAccount": "Select Account",
- "autoSelectAccount": "Auto-select account at runtime",
- "previewNextStep": "Choose provider and model to preview the next step.",
- "addStep": "Add Step",
- "comboReference": "Combo Reference",
- "selectComboToReference": "Select an existing combo to reference",
- "addComboReference": "Add Combo Reference",
- "browseLegacyCatalog": "Browse legacy model catalog",
- "addStepBeforeContinue": "Add at least one step before continuing to the next stage.",
- "modePackPerformance": "Performance",
- "modePackBudget": "Budget",
- "modePackBalanced": "Balanced",
- "modePackCustom": "Custom",
"modePackLabel": "Mode Pack",
"routerStrategyLabel": "Router Strategy",
"strategyRules": "Rules (6-Factor Scoring)",
@@ -1091,54 +1052,12 @@
"tip2": "Keep a quality fallback for hard prompts.",
"tip3": "Use for batch/background jobs where cost is the main KPI."
},
- "fill-first": {
- "title": "Quota drain strategy",
- "description": "Exhausts one provider's quota before moving to the next in chain.",
- "tip1": "Order models by free quota size — biggest first.",
- "tip2": "Enable health checks to skip drained providers.",
- "tip3": "Ideal for free-tier stacking (Deepgram → Groq → NIM)."
- },
- "p2c": {
- "title": "Power-of-Two-Choices",
- "description": "Picks the less-loaded of two random candidates per request — low latency at scale.",
- "tip1": "Use with 4+ models for best effect.",
- "tip2": "Requires latency telemetry enabled in Settings.",
- "tip3": "Great replacement for round-robin in high-throughput combos."
- },
- "context-relay": {
- "title": "Session continuity first",
- "description": "Best when account rotation is expected and the next account must inherit a condensed task summary.",
- "tip1": "Use with providers that rotate accounts for the same model family.",
- "tip2": "Keep the handoff threshold below the hard quota cutoff to give the summary time to generate.",
- "tip3": "Set a dedicated summary model only when the primary model is too expensive or unstable."
- },
"strict-random": {
"title": "Shuffle deck distribution",
"description": "Each model is used exactly once per cycle before reshuffling.",
"tip1": "Use at least 2 models for meaningful distribution.",
"tip2": "Works best with equivalent-performance models.",
"tip3": "Ideal for load balancing across multiple API accounts."
- },
- "auto": {
- "title": "Fail-safe baseline",
- "description": "Use one primary model and keep fallback chain short and reliable.",
- "tip1": "Put your most reliable model first.",
- "tip2": "Keep 1-2 backup models with similar quality.",
- "tip3": "Use safe retries to absorb transient provider failures."
- },
- "lkgp": {
- "title": "Fail-safe baseline",
- "description": "Use one primary model and keep fallback chain short and reliable.",
- "tip1": "Put your most reliable model first.",
- "tip2": "Keep 1-2 backup models with similar quality.",
- "tip3": "Use safe retries to absorb transient provider failures."
- },
- "context-optimized": {
- "title": "Fail-safe baseline",
- "description": "Use one primary model and keep fallback chain short and reliable.",
- "tip1": "Put your most reliable model first.",
- "tip2": "Keep 1-2 backup models with similar quality.",
- "tip3": "Use safe retries to absorb transient provider failures."
}
},
"templateFreeStack": "Free Stack ($0)",
@@ -3399,258 +3318,5 @@
"deduplicatedRequests": "Deduplicated Requests",
"savedCalls": "Saved API Calls",
"totalProcessed": "Total Requests Processed"
- },
- "proxyConfigModal": {
- "levelGlobal": "Global",
- "levelProvider": "Provider",
- "levelCombo": "Combo",
- "levelKey": "Key",
- "levelDirect": "Direct (none)",
- "titleGlobal": "Global Proxy Configuration",
- "titleLevel": "{level} Proxy — {label}",
- "loading": "Loading proxy configuration...",
- "inheritingFrom": "Inheriting from",
- "source": "Source",
- "savedProxy": "Saved Proxy",
- "custom": "Custom",
- "selectSavedProxyPlaceholder": "Select saved proxy...",
- "proxyType": "Proxy Type",
- "host": "Host",
- "hostPlaceholder": "1.2.3.4 or proxy.example.com",
- "port": "Port",
- "authOptional": "Authentication (optional)",
- "username": "Username",
- "usernamePlaceholder": "Username",
- "password": "Password",
- "passwordPlaceholder": "Password",
- "connected": "Connected",
- "ip": "IP:",
- "connectionFailed": "Connection failed",
- "testConnection": "Test Connection",
- "clear": "Clear",
- "cancel": "Cancel",
- "save": "Save",
- "errorSelectSavedProxy": "Select a saved proxy before saving.",
- "errorSelectProxyFirst": "Select a saved proxy first.",
- "errorProxyNotFound": "Selected proxy not found.",
- "errorClearSavedProxy": "Failed to clear saved proxy",
- "errorSaveProxy": "Failed to save proxy configuration",
- "errorClearProxy": "Failed to clear proxy configuration",
- "errorSocks5Hidden": "SOCKS5 is configured but hidden because NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=false."
- },
- "oauthModal": {
- "title": "Connect {providerName}",
- "waiting": "Waiting for Authorization",
- "completeAuthInPopup": "Complete the authorization in the popup window.",
- "popupClosedHint": "If the popup closes without redirecting back (e.g. Qoder), this dialog will automatically switch to manual URL input mode.",
- "popupBlocked": "Popup blocked? Enter URL manually",
- "deviceCodeVisitUrl": "Visit the URL below and enter the code:",
- "deviceCodeVerificationUrl": "Verification URL",
- "deviceCodeYourCode": "Your Code",
- "deviceCodeWaiting": "Waiting for authorization...",
- "googleOAuthWarning": "Remote access + Google OAuth: The default credentials only accept redirects to localhost. After authorizing, your browser will try to open localhost — copy that full URL and paste it below. For fully remote use without this manual step, configure your own OAuth credentials.",
- "remoteAccessInfo": "Remote access: Since you're accessing OmniRoute remotely, after authorizing you'll see an error page (localhost not found). That's expected — just copy the full URL from your browser's address bar and paste it below.",
- "step1OpenUrl": "Step 1: Open this URL in your browser",
- "copy": "Copy",
- "step2PasteCallback": "Step 2: Paste the callback URL or auth code here",
- "step2Hint": "After authorization, paste the full callback URL. For Claude Code and Cline, you can also paste the Authentication Code directly, for example code#state.",
- "connect": "Connect",
- "cancel": "Cancel",
- "success": "Connected Successfully!",
- "successMessage": "Your {providerName} account has been connected.",
- "done": "Done",
- "error": "Connection Failed",
- "tryAgain": "Try Again"
- },
- "cursorAuthModal": {
- "title": "Connect Cursor IDE",
- "autoDetecting": "Auto-detecting tokens...",
- "readingFromCursor": "Reading from Cursor IDE or cursor-agent",
- "tokensAutoDetected": "Tokens auto-detected from Cursor IDE successfully!",
- "cursorNotDetected": "Cursor IDE not detected. Please paste your tokens manually.",
- "accessToken": "Access Token",
- "required": "*",
- "accessTokenPlaceholder": "Access token will be auto-filled...",
- "machineId": "Machine ID",
- "optional": "(optional)",
- "machineIdPlaceholder": "Machine ID will be auto-filled...",
- "importing": "Importing...",
- "importToken": "Import Token",
- "cancel": "Cancel",
- "errorAutoDetect": "Could not auto-detect tokens",
- "errorAutoDetectFailed": "Failed to auto-detect tokens",
- "errorEnterToken": "Please enter an access token",
- "errorImportFailed": "Import failed"
- },
- "pricingModal": {
- "title": "Pricing Configuration",
- "loading": "Loading pricing data...",
- "pricingRatesFormat": "Pricing Rates Format",
- "ratesDescription": "All rates are in dollars per million tokens ($/1M tokens). Example: Input rate of 2.50 means $2.50 per 1,000,000 input tokens.",
- "model": "Model",
- "input": "Input",
- "output": "Output",
- "cached": "Cached",
- "reasoning": "Reasoning",
- "cacheCreation": "Cache Creation",
- "noPricingData": "No pricing data available",
- "resetToDefaults": "Reset to Defaults",
- "cancel": "Cancel",
- "saving": "Saving...",
- "saveChanges": "Save Changes",
- "resetConfirm": "Reset all pricing to defaults? This cannot be undone.",
- "errorSaveFailed": "Failed to save pricing",
- "errorResetFailed": "Failed to reset pricing"
- },
- "proxyRegistry": {
- "title": "Proxy Registry",
- "description": "Store reusable proxies and track assignments.",
- "importLegacy": "Import Legacy",
- "bulkAssign": "Bulk Assign",
- "addProxy": "Add Proxy",
- "loading": "Loading proxies...",
- "noProxies": "No saved proxies yet.",
- "tableName": "Name",
- "tableEndpoint": "Endpoint",
- "tableStatus": "Status",
- "tableHealth": "Health (24h)",
- "tableUsage": "Usage",
- "tableActions": "Actions",
- "test": "Test",
- "edit": "Edit",
- "delete": "Delete",
- "modalCreateTitle": "Create Proxy",
- "modalEditTitle": "Edit Proxy",
- "labelName": "Name",
- "labelType": "Type",
- "labelHost": "Host",
- "labelPort": "Port",
- "labelUsername": "Username",
- "labelPassword": "Password",
- "labelRegion": "Region",
- "labelStatus": "Status",
- "labelNotes": "Notes",
- "usernamePlaceholderEdit": "Leave blank to keep current username",
- "passwordPlaceholderEdit": "Leave blank to keep current password",
- "statusActive": "active",
- "statusInactive": "inactive",
- "cancel": "Cancel",
- "save": "Save",
- "bulkModalTitle": "Bulk Proxy Assignment",
- "bulkLabelScope": "Scope",
- "bulkLabelProxy": "Proxy",
- "bulkClearAssignment": "(clear assignment)",
- "bulkLabelScopeIds": "Scope IDs (comma or newline)",
- "bulkScopeIdsPlaceholder": "provider-openai,provider-anthropic",
- "bulkApply": "Apply",
- "errorLoadFailed": "Failed to load proxy registry",
- "errorNameHostRequired": "Name and host are required",
- "errorSaveFailed": "Failed to save proxy",
- "errorDeleteFailed": "Failed to delete proxy",
- "errorForceDeleteConfirm": "This proxy is still assigned. Force delete and remove all assignments?",
- "errorMigrateFailed": "Failed to migrate legacy proxy config",
- "errorBulkFailed": "Failed to run bulk assignment",
- "success": "✓",
- "failure": "✗",
- "failed": "failed",
- "successRate": "{rate}% success",
- "avgLatency": "{latency} ms avg",
- "assignmentsCount": "{count} assignment(s)",
- "noData": "—",
- "testSuccess": "✓ {ip}",
- "testLatency": "{latency}ms",
- "testFailure": "✗ {error}"
- },
- "playground": {
- "title": "Model Playground",
- "description": "Test any model directly from the dashboard. Pick a provider, model, and endpoint type, then send a request to see the raw response.",
- "endpoint": "Endpoint",
- "provider": "Provider",
- "model": "Model",
- "accountKey": "Account / Key",
- "autoAccounts": "Auto ({count} accounts)",
- "noAccounts": "No accounts",
- "send": "Send",
- "cancel": "Cancel",
- "audioFile": "Audio File",
- "attachImages": "Attach Images (Vision)",
- "multipartFormData": "multipart/form-data",
- "upToImages": "up to 4 images",
- "selectAudioFile": "Select an audio file to transcribe (mp3, wav, m4a, ogg, flac…)",
- "clearAll": "Clear all",
- "request": "Request",
- "response": "Response",
- "transcription": "Transcription",
- "copy": "Copy",
- "resetToDefault": "Reset to default",
- "downloadAudio": "Download audio",
- "copyText": "Copy text",
- "transcriptionHint": "Transcription uses multipart/form-data. Upload the audio file above — JSON below controls extra params (model, language).",
- "imagesGenerated": "{count} image{count, plural, one {} other {s}} generated",
- "generatedImage": "Generated image {index}",
- "save": "Save",
- "endpointOptions": {
- "chat": "Chat Completions",
- "responses": "Responses",
- "images": "Image Generation",
- "embeddings": "Embeddings",
- "speech": "Text to Speech",
- "transcription": "Audio Transcription",
- "video": "Video Generation",
- "music": "Music Generation",
- "rerank": "Rerank",
- "search": "Web Search"
- }
- },
- "requestLogger": {
- "recording": "Recording",
- "paused": "Paused",
- "pipelineLogsOn": "Pipeline Logs On",
- "pipelineLogsOff": "Pipeline Logs Off",
- "updatingPipelineLogs": "Updating pipeline logs...",
- "searchPlaceholder": "Search model, provider, account, API key, combo...",
- "allProviders": "All Providers",
- "allModels": "All Models",
- "allAccounts": "All Accounts",
- "allApiKeys": "All API Keys",
- "total": "total",
- "ok": "OK",
- "err": "ERR",
- "combo": "combo",
- "keys": "keys",
- "shown": "shown",
- "sortNewest": "Newest",
- "sortOldest": "Oldest",
- "sortTokensDesc": "Tokens ↓",
- "sortTokensAsc": "Tokens ↑",
- "sortDurationDesc": "Duration ↓",
- "sortDurationAsc": "Duration ↑",
- "sortStatusDesc": "Status ↓",
- "sortStatusAsc": "Status ↑",
- "sortModelAsc": "Model A-Z",
- "sortModelDesc": "Model Z-A",
- "statusFilters": {
- "all": "All",
- "error": "Errors",
- "success": "Success",
- "combo": "Combo"
- },
- "columns": {
- "status": "Status",
- "model": "Model",
- "requested": "Requested",
- "provider": "Provider",
- "protocol": "Req Protocol",
- "account": "Account",
- "apiKey": "API Key",
- "combo": "Combo",
- "tokens": "Tokens",
- "duration": "Duration",
- "time": "Time"
- },
- "loadingLogs": "Loading logs...",
- "noLogs": "No logs recorded yet. Make some API calls to see them here.",
- "noMatchingLogs": "No logs match the current filters.",
- "callLogsInfo": "Call logs are also saved as JSON files to {dataDir} and rotated by {retentionDays} and {maxEntries}."
}
}
diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json
index f28f0c058f..171bd2152e 100644
--- a/src/i18n/messages/zh-CN.json
+++ b/src/i18n/messages/zh-CN.json
@@ -1163,6 +1163,28 @@
"reorderHandle": "拖拽排序",
"failedReorder": "模型重排序失败",
"builderFlowTitle": "组合构建流程",
+ "builderStage": {
+ "basics": {
+ "label": "基础",
+ "description": "名称和起始模板"
+ },
+ "steps": {
+ "label": "步骤",
+ "description": "提供商、模型和账户选择"
+ },
+ "strategy": {
+ "label": "策略",
+ "description": "路由行为和高级设置"
+ },
+ "intelligent": {
+ "label": "智能路由",
+ "description": "自动路由候选池、预设和评分"
+ },
+ "review": {
+ "label": "审查",
+ "description": "保存前的最终验证"
+ }
+ },
"builderStageVisited": "阶段已完成",
"builderStageCurrent": "当前阶段",
"builderStagePending": "待处理",