From cba6524926f99785f47086217611c947ed3b0720 Mon Sep 17 00:00:00 2001
From: clousky
Date: Thu, 16 Apr 2026 11:17:18 +0800
Subject: [PATCH] feat(combos): add new routing strategies and i18n support
- Add new routing strategies: fill-first, auto, lkgp, and context-optimized
- Add comprehensive strategy guidance and help text for new routing modes
- Implement i18n support for mode pack labels in intelligent routing step
- Update English and Chinese translations for new combo builder features
- Extend strategy recommendations with new routing mode examples
This enhances the combo builder with more sophisticated routing options
and improves internationalization coverage.
---
.../combos/BuilderIntelligentStep.tsx | 6 +-
src/app/(dashboard)/dashboard/combos/page.tsx | 56 ++-
src/i18n/messages/en.json | 365 +++++++++++++++++-
src/i18n/messages/zh-CN.json | 91 +++++
4 files changed, 500 insertions(+), 18 deletions(-)
diff --git a/src/app/(dashboard)/dashboard/combos/BuilderIntelligentStep.tsx b/src/app/(dashboard)/dashboard/combos/BuilderIntelligentStep.tsx
index a0e58bc0d2..3dabb89695 100644
--- a/src/app/(dashboard)/dashboard/combos/BuilderIntelligentStep.tsx
+++ b/src/app/(dashboard)/dashboard/combos/BuilderIntelligentStep.tsx
@@ -164,7 +164,11 @@ export default function BuilderIntelligentStep({
>
{MODE_PACK_OPTIONS.map((modePack) => (
))}
diff --git a/src/app/(dashboard)/dashboard/combos/page.tsx b/src/app/(dashboard)/dashboard/combos/page.tsx
index 5f07cf6610..696ffb1194 100644
--- a/src/app/(dashboard)/dashboard/combos/page.tsx
+++ b/src/app/(dashboard)/dashboard/combos/page.tsx
@@ -120,6 +120,21 @@ const STRATEGY_GUIDANCE_FALLBACK = {
avoid: "Avoid when models have different quality or latency and order matters.",
example: "Example: Multiple accounts of the same model to distribute usage evenly.",
},
+ 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.",
+ },
};
const ADVANCED_FIELD_HELP_FALLBACK = {
@@ -224,6 +239,33 @@ const STRATEGY_RECOMMENDATIONS_FALLBACK = {
"Guarantees no model is skipped or repeated within a cycle.",
],
},
+ auto: {
+ title: "Fail-safe baseline",
+ description: "Use one primary model and keep fallback chain short and reliable.",
+ tips: [
+ "Put your most reliable model first.",
+ "Keep 1-2 backup models with similar quality.",
+ "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.",
+ tips: [
+ "Put your most reliable model first.",
+ "Keep 1-2 backup models with similar quality.",
+ "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.",
+ tips: [
+ "Put your most reliable model first.",
+ "Keep 1-2 backup models with similar quality.",
+ "Use safe retries to absorb transient provider failures.",
+ ],
+ },
};
const COMBO_USAGE_GUIDE_STORAGE_KEY = "omniroute:combos:hide-usage-guide";
@@ -2467,7 +2509,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
{getI18nOrFallback(
t,
- "builderFlowDescription",
+ "builderStagesDescription",
"Move through the stages in order to define the combo, build the steps, choose the routing strategy and review the result."
)}
{getI18nOrFallback(
t,
- "builderDescription",
+ "builderStepsDescription",
"Build each combo step in sequence: provider, model, then account. This allows repeating the same provider and model with different accounts."
)}
@@ -2797,7 +2839,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
@@ -2820,7 +2862,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
? formatModelDisplay(builderCandidateStep)
: getI18nOrFallback(
t,
- "builderPreviewEmpty",
+ "previewNextStep",
"Choose provider and model to preview the next step."
)}
@@ -2858,7 +2900,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
@@ -3113,7 +3155,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
className="w-full mt-2 py-2 border border-dashed border-black/10 dark:border-white/10 rounded-lg text-xs text-text-muted hover:text-primary hover:border-primary/30 transition-colors flex items-center justify-center gap-1"
>
travel_explore
- {getI18nOrFallback(t, "builderOpenLegacyCatalog", "Browse legacy model catalog")}
+ {getI18nOrFallback(t, "browseLegacyCatalog", "Browse legacy model catalog")}
)}
@@ -3668,7 +3710,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
)
: getI18nOrFallback(
t,
- "builderNeedSteps",
+ "addStepBeforeContinue",
"Add at least one step before continuing to the next stage."
)}
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 0297a15393..a6269686d6 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -904,6 +904,36 @@
"when": "Use when you want perfectly even spread — each model used once before repeating.",
"avoid": "Avoid when models have different quality or latency and order matters.",
"example": "Example: Multiple accounts of the same model to distribute usage evenly."
+ },
+ "p2c": {
+ "when": "Use when you want low-latency selection using Power-of-Two-Choices algorithm.",
+ "avoid": "Avoid for small combos with 2 or fewer models — no benefit over round-robin.",
+ "example": "Example: High-throughput inference across 4+ equivalent model endpoints."
+ },
+ "context-relay": {
+ "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": {
@@ -983,6 +1013,25 @@
"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)",
@@ -1042,12 +1091,54 @@
"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)",
@@ -1073,6 +1164,16 @@
"reorderHandle": "Drag to reorder",
"failedReorder": "Failed to reorder models",
"builderFlowTitle": "Combo Builder Flow",
+ "builderStage": {
+ "basics": { "label": "Basics", "description": "Name and starting template" },
+ "steps": { "label": "Steps", "description": "Provider, model, and account selection" },
+ "strategy": { "label": "Strategy", "description": "Routing behavior and advanced settings" },
+ "intelligent": {
+ "label": "Smart Routing",
+ "description": "Auto-routing candidate pool, presets, and scoring"
+ },
+ "review": { "label": "Review", "description": "Final validation before saving" }
+ },
"builderStageVisited": "Stage completed",
"builderStageCurrent": "Current stage",
"builderStagePending": "Pending",
@@ -1102,16 +1203,7 @@
"reviewAdvanced": "Advanced Settings",
"reviewAgentFlags": "Agent Flags",
"reviewSequence": "Model Sequence",
- "reviewNoSteps": "No steps configured",
- "agentFeaturesTitle": "Agent Features",
- "agentFeaturesDescription": "— optional, for agent/tool workflows",
- "agentFeaturesSystemMessageOverride": "System Message Override",
- "agentFeaturesSystemMessagePlaceholder": "Override the system prompt for all requests routed through this combo…",
- "agentFeaturesSystemMessageHint": "Replaces any system message sent by the client. Leave empty to pass through client system messages.",
- "agentFeaturesToolFilterRegex": "Tool Filter Regex",
- "agentFeaturesToolFilterHint": "Only tools whose name matches this regex are forwarded to the provider. Leave empty to forward all tools.",
- "agentFeaturesContextCacheProtection": "Context Cache Protection",
- "agentFeaturesContextCacheHint": "Pins the provider/model across turns to preserve cache sessions. Internal tags are stripped before forwarding to the provider."
+ "reviewNoSteps": "No steps configured"
},
"costs": {
"title": "Costs",
@@ -3307,5 +3399,258 @@
"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 7d7ea2d20f..1e440c26e6 100644
--- a/src/i18n/messages/zh-CN.json
+++ b/src/i18n/messages/zh-CN.json
@@ -903,6 +903,36 @@
"when": "适用于希望实现绝对均匀分配的场景,每个模型在重复前都会恰好使用一次。",
"avoid": "如果模型之间的质量或延迟差异较大,且调用顺序很重要,则不建议使用。",
"example": "例如:同一模型挂载多个账户,用于更均匀地分摊使用量。"
+ },
+ "p2c": {
+ "when": "当您希望使用 Power-of-Two-Choices 算法进行低延迟选择时使用。",
+ "avoid": "对于少于等于 2 个模型的小型组合避免使用 — 与轮询相比没有优势。",
+ "example": "示例:在 4 个或更多等效模型端点之间进行高吞吐量推理。"
+ },
+ "context-relay": {
+ "when": "当长会话必须在账户轮换时保持工作上下文不丢失时使用。",
+ "avoid": "当账户切换很少发生或您不希望额外的摘要请求时避免使用。",
+ "example": "示例:在接近配额耗尽时轮换多个账户的 Codex 会话。"
+ },
+ "fill-first": {
+ "when": "当您希望在转移到下一个提供商之前完全耗尽一个提供商的配额时使用。",
+ "avoid": "当您需要在提供商之间进行请求级负载均衡时避免使用。",
+ "example": "示例:在使用完所有 200 美元的 Deepgram 额度后再回退到 Groq。"
+ },
+ "auto": {
+ "when": "当您有一个首选模型且只希望在失败时才回退到备用模型时使用。",
+ "avoid": "当您需要在多个模型之间平衡负载时避免使用。",
+ "example": "示例:主力编码模型搭配更便宜的备用模型以应对故障。"
+ },
+ "lkgp": {
+ "when": "当您有一个首选模型且只希望在失败时才回退到备用模型时使用。",
+ "avoid": "当您需要在多个模型之间平衡负载时避免使用。",
+ "example": "示例:主力编码模型搭配更便宜的备用模型以应对故障。"
+ },
+ "context-optimized": {
+ "when": "当您有一个首选模型且只希望在失败时才回退到备用模型时使用。",
+ "avoid": "当您需要在多个模型之间平衡负载时避免使用。",
+ "example": "示例:主力编码模型搭配更便宜的备用模型以应对故障。"
}
},
"advancedHelp": {
@@ -982,6 +1012,25 @@
"candidatePoolHint": "选择引擎应评估的供应商。留空则使用所有活跃供应商。",
"candidatePoolEmpty": "暂无可用活跃供应商。",
"candidatePoolAllProviders": "所有供应商",
+ "builderStagesDescription": "按顺序完成各个阶段以定义组合、构建步骤、选择路由策略并审查结果。",
+ "builderStepsDescription": "按顺序构建每个组合步骤:提供商、模型,然后是账户。这允许在不同账户上重复使用相同的提供商和模型。",
+ "selectProvider": "选择提供商",
+ "selectProviderPlaceholder": "选择提供商",
+ "selectModel": "选择模型",
+ "selectModelPlaceholder": "请先选择提供商",
+ "selectAccount": "选择账户",
+ "autoSelectAccount": "运行时自动选择账户",
+ "previewNextStep": "选择提供商和模型以预览下一步。",
+ "addStep": "添加步骤",
+ "comboReference": "组合引用",
+ "selectComboToReference": "选择要引用的现有组合",
+ "addComboReference": "添加组合引用",
+ "browseLegacyCatalog": "浏览旧版模型目录",
+ "addStepBeforeContinue": "在继续到下一阶段之前,请至少添加一个步骤。",
+ "modePackPerformance": "性能优先",
+ "modePackBudget": "预算优先",
+ "modePackBalanced": "均衡",
+ "modePackCustom": "自定义",
"modePackLabel": "模式包",
"routerStrategyLabel": "路由策略",
"strategyRules": "规则(6 因子评分)",
@@ -1041,12 +1090,54 @@
"tip2": "为高难度提示保留一个质量更高的回退模型。",
"tip3": "适合批处理或后台任务等成本是主要指标的场景。"
},
+ "fill-first": {
+ "title": "配额耗尽策略",
+ "description": "在切换到链中的下一个提供商之前,先耗尽一个提供商的配额。",
+ "tip1": "按免费配额大小排列模型 — 最大的放在第一位。",
+ "tip2": "启用健康检查以跳过已耗尽的提供商。",
+ "tip3": "非常适合免费层级堆叠(Deepgram → Groq → NIM)。"
+ },
+ "p2c": {
+ "title": "双选负载均衡",
+ "description": "每次请求从随机候选中选出负载较轻的一个 — 低延迟,高扩展性。",
+ "tip1": "配合 4 个或更多模型使用效果最佳。",
+ "tip2": "需要在设置中启用延迟遥测。",
+ "tip3": "是高吞吐量组合中轮询的绝佳替代方案。"
+ },
+ "context-relay": {
+ "title": "会话连续性优先",
+ "description": "当预期账户轮换且下一个账户必须继承简化的任务摘要时效果最佳。",
+ "tip1": "与为同一模型系列轮换账户的提供商配合使用。",
+ "tip2": "将交接阈值设置在硬配额截止值以下,以便有时间生成摘要。",
+ "tip3": "仅在主模型太贵或不稳定时,才设置专用摘要模型。"
+ },
"strict-random": {
"title": "洗牌池分配",
"description": "每轮中每个模型都会被恰好使用一次,然后再重新洗牌。",
"tip1": "至少使用 2 个模型,才能体现分配效果。",
"tip2": "最适合性能相近的模型。",
"tip3": "非常适合在多个 API 账户之间做负载均衡。"
+ },
+ "auto": {
+ "title": "稳妥基线",
+ "description": "使用一个主模型,并保持回退链路简短且可靠。",
+ "tip1": "把最可靠的模型放在第一位。",
+ "tip2": "保留 1 到 2 个质量相近的备用模型。",
+ "tip3": "开启安全重试,吸收临时性的提供商故障。"
+ },
+ "lkgp": {
+ "title": "稳妥基线",
+ "description": "使用一个主模型,并保持回退链路简短且可靠。",
+ "tip1": "把最可靠的模型放在第一位。",
+ "tip2": "保留 1 到 2 个质量相近的备用模型。",
+ "tip3": "开启安全重试,吸收临时性的提供商故障。"
+ },
+ "context-optimized": {
+ "title": "稳妥基线",
+ "description": "使用一个主模型,并保持回退链路简短且可靠。",
+ "tip1": "把最可靠的模型放在第一位。",
+ "tip2": "保留 1 到 2 个质量相近的备用模型。",
+ "tip3": "开启安全重试,吸收临时性的提供商故障。"
}
},
"templateFreeStack": "免费栈($0)",