diff --git a/src/app/(dashboard)/dashboard/combos/page.tsx b/src/app/(dashboard)/dashboard/combos/page.tsx index 696ffb1194..5684b12118 100644 --- a/src/app/(dashboard)/dashboard/combos/page.tsx +++ b/src/app/(dashboard)/dashboard/combos/page.tsx @@ -121,19 +121,19 @@ const STRATEGY_GUIDANCE_FALLBACK = { 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.", + when: "Use when you want multi-factor scoring based on cost, latency, and quality.", + avoid: "Avoid when you need strict priority ordering or historical persistence.", + example: "Example: Balance requests between models with different strengths.", }, 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.", + when: "Use when you want routing based on historical success rates and performance.", + avoid: "Avoid when historical data is limited or unreliable.", + example: "Example: Route to models with proven track records for specific tasks.", }, "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.", + when: "Use when you need to optimize for context window usage across models.", + avoid: "Avoid when models have similar context lengths or simple tasks.", + example: "Example: Distribute long conversations across models with large context windows.", }, }; @@ -240,30 +240,30 @@ const STRATEGY_RECOMMENDATIONS_FALLBACK = { ], }, auto: { - title: "Fail-safe baseline", - description: "Use one primary model and keep fallback chain short and reliable.", + title: "Multi-factor optimization", + description: "Routes based on real-time scoring of cost, latency, quality, and health.", tips: [ - "Put your most reliable model first.", - "Keep 1-2 backup models with similar quality.", - "Use safe retries to absorb transient provider failures.", + "Let the engine balance across multiple factors automatically.", + "Monitor which factors drive routing decisions in the logs.", + "Use for complex workloads where no single factor dominates.", ], }, lkgp: { - title: "Fail-safe baseline", - description: "Use one primary model and keep fallback chain short and reliable.", + title: "History-based routing", + description: "Routes based on historical success rates and persistent performance data.", tips: [ - "Put your most reliable model first.", - "Keep 1-2 backup models with similar quality.", - "Use safe retries to absorb transient provider failures.", + "Let success history accumulate before relying on this strategy.", + "Models with better track records get preference over time.", + "Ideal for stable workloads with consistent model availability.", ], }, "context-optimized": { - title: "Fail-safe baseline", - description: "Use one primary model and keep fallback chain short and reliable.", + title: "Context-aware distribution", + description: "Routes to optimize context window usage and conversation continuity.", tips: [ - "Put your most reliable model first.", - "Keep 1-2 backup models with similar quality.", - "Use safe retries to absorb transient provider failures.", + "Best for long conversations that span multiple requests.", + "Selects models with appropriate context capacity automatically.", + "Use when context limits are a bottleneck for your workload.", ], }, }; diff --git a/src/app/(dashboard)/dashboard/playground/page.tsx b/src/app/(dashboard)/dashboard/playground/page.tsx index 91906d705a..3b36cf8e8e 100644 --- a/src/app/(dashboard)/dashboard/playground/page.tsx +++ b/src/app/(dashboard)/dashboard/playground/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, useCallback, useRef } from "react"; +import { useState, useEffect, useCallback, useRef, useMemo } from "react"; import { useTranslations } from "next-intl"; import { Card, Button, Select, Badge } from "@/shared/components"; import { ALIAS_TO_ID } from "@/shared/constants/providers"; @@ -185,18 +185,21 @@ export default function PlaygroundPage() { const t = useTranslations("playground"); // Get translated endpoint options - const getEndpointOptions = () => [ - { value: "chat", label: t("endpointOptions.chat") }, - { value: "responses", label: t("endpointOptions.responses") }, - { value: "images", label: t("endpointOptions.images") }, - { value: "embeddings", label: t("endpointOptions.embeddings") }, - { value: "speech", label: t("endpointOptions.speech") }, - { value: "transcription", label: t("endpointOptions.transcription") }, - { value: "video", label: t("endpointOptions.video") }, - { value: "music", label: t("endpointOptions.music") }, - { value: "rerank", label: t("endpointOptions.rerank") }, - { value: "search", label: t("endpointOptions.search") }, - ]; + const endpointOptions = useMemo( + () => [ + { value: "chat", label: t("endpointOptions.chat") }, + { value: "responses", label: t("endpointOptions.responses") }, + { value: "images", label: t("endpointOptions.images") }, + { value: "embeddings", label: t("endpointOptions.embeddings") }, + { value: "speech", label: t("endpointOptions.speech") }, + { value: "transcription", label: t("endpointOptions.transcription") }, + { value: "video", label: t("endpointOptions.video") }, + { value: "music", label: t("endpointOptions.music") }, + { value: "rerank", label: t("endpointOptions.rerank") }, + { value: "search", label: t("endpointOptions.search") }, + ], + [t] + ); const [models, setModels] = useState([]); const [providers, setProviders] = useState([]); @@ -495,7 +498,7 @@ export default function PlaygroundPage() {

{t("pricingRatesFormat")}

- ", "") - .replace("", ""), - }} - /> + {t.rich("ratesDescription", { + strong: (chunks) => {chunks}, + })}

diff --git a/src/shared/components/ProxyConfigModal.tsx b/src/shared/components/ProxyConfigModal.tsx index 00a2351d30..54fd53630d 100644 --- a/src/shared/components/ProxyConfigModal.tsx +++ b/src/shared/components/ProxyConfigModal.tsx @@ -354,7 +354,10 @@ export default function ProxyConfigModal({ const title = level === "global" ? t("titleGlobal") - : `${t(`level${level.charAt(0).toUpperCase() + level.slice(1)}` as any)} Proxy — ${levelLabel || levelId || ""}`; + : t("titleLevel", { + level: t(`level${level.charAt(0).toUpperCase() + level.slice(1)}` as any), + label: levelLabel || levelId || "", + }); return ( diff --git a/src/shared/components/RequestLoggerV2.tsx b/src/shared/components/RequestLoggerV2.tsx index 1899b5f772..48f0c7e7e4 100644 --- a/src/shared/components/RequestLoggerV2.tsx +++ b/src/shared/components/RequestLoggerV2.tsx @@ -116,27 +116,35 @@ export default function RequestLoggerV2() { const t = useTranslations("requestLogger"); // Get translated status filters - const getStatusFilters = () => [ - { key: "all", label: t("statusFilters.all"), icon: "" }, - { key: "error", label: t("statusFilters.error"), icon: "error" }, - { key: "ok", label: t("statusFilters.success"), icon: "check_circle" }, - { key: "combo", label: t("statusFilters.combo"), icon: "hub" }, - ]; + const statusFilters = useMemo( + () => [ + { key: "all", label: t("statusFilters.all"), icon: "" }, + { key: "error", label: t("statusFilters.error"), icon: "error" }, + { key: "ok", label: t("statusFilters.success"), icon: "check_circle" }, + { key: "combo", label: t("statusFilters.combo"), icon: "hub" }, + ], + [t] + ); // Get translated columns - const getColumns = () => [ - { key: "status", label: t("columns.status") }, - { key: "model", label: t("columns.model") }, - { key: "requestedModel", label: t("columns.requested") }, - { key: "provider", label: t("columns.provider") }, - { key: "protocol", label: t("columns.protocol") }, - { key: "account", label: t("columns.account") }, - { key: "apiKey", label: t("columns.apiKey") }, - { key: "combo", label: t("columns.combo") }, - { key: "tokens", label: t("columns.tokens") }, - { key: "duration", label: t("columns.duration") }, - { key: "time", label: t("columns.time") }, - ]; + const columns = useMemo( + () => [ + { key: "status", label: t("columns.status") }, + { key: "cacheSource", label: t("columns.cacheSource") }, + { key: "model", label: t("columns.model") }, + { key: "requestedModel", label: t("columns.requested") }, + { key: "provider", label: t("columns.provider") }, + { key: "protocol", label: t("columns.protocol") }, + { key: "account", label: t("columns.account") }, + { key: "apiKey", label: t("columns.apiKey") }, + { key: "combo", label: t("columns.combo") }, + { key: "tokens", label: t("columns.tokens") }, + { key: "tps", label: t("columns.tps") }, + { key: "duration", label: t("columns.duration") }, + { key: "time", label: t("columns.time") }, + ], + [t] + ); const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); @@ -159,7 +167,7 @@ export default function RequestLoggerV2() { // Column visibility with localStorage persistence const [visibleColumns, setVisibleColumns] = useState(() => { - const defaultVisible = Object.fromEntries(getColumns().map((c) => [c.key, true])); + const defaultVisible = Object.fromEntries(columns.map((c) => [c.key, true])); if (typeof window === "undefined") return defaultVisible; try { const saved = localStorage.getItem("loggerVisibleColumns"); @@ -525,7 +533,7 @@ export default function RequestLoggerV2() { {/* Quick Filters */}
{/* Status Filters */} - {getStatusFilters().map((f) => ( + {statusFilters.map((f) => (