- Settings: "?" help tooltips on every option; router key auto-probes the connection right after saving (no more hanging "unavailable") - Local models card reworked into 3 clear steps (install cortiq → add model → run locally); removed the hand-entered .cmf path / pool-id fields — models are added via the Import tab and the managed server registers them - Fresh install ships an EMPTY model pool; the local .cmf is auto-registered by the managed CMF server, so it needs no static [[models]] entry - New: port-availability check — GET /admin/api/cmf/port + "Check port" button with next-free-port suggestion. bind()+connect() probe so Docker-proxied / dual-stack ports (e.g. 8081) are correctly detected as occupied - /admin/api/cmf now reports the serving model + port; card shows "▶ serving …" - On-demand runtime install: POST /admin/api/cmf/install
378 lines
17 KiB
JavaScript
Vendored
378 lines
17 KiB
JavaScript
Vendored
// English — source of truth. Other languages override per key; missing keys fall
|
||
// back to English. Keep keys identical across all language files.
|
||
export default {
|
||
// brand / common
|
||
"brand.sub": "Gateway",
|
||
"common.loading": "Loading…",
|
||
"common.save": "Save",
|
||
"common.saving": "Saving…",
|
||
"common.cancel": "Cancel",
|
||
"common.add": "Add",
|
||
"common.edit": "Edit",
|
||
"common.delete": "Delete",
|
||
"common.close": "Close",
|
||
"common.test": "Test",
|
||
"common.copy": "Copy",
|
||
"common.copied": "Copied",
|
||
"common.confirm": "Confirm",
|
||
"common.none": "none",
|
||
"common.saved": "Saved",
|
||
"common.error": "Error",
|
||
"common.ok": "OK",
|
||
"common.refresh": "Refresh",
|
||
"common.actions": "Actions",
|
||
"common.optional": "optional",
|
||
"common.search": "Search",
|
||
"common.all": "all",
|
||
|
||
// nav
|
||
"nav.dashboard": "Dashboard",
|
||
"nav.models": "Models",
|
||
"nav.import": "Import model",
|
||
"nav.routing": "Routing",
|
||
"nav.protocols": "Protocols",
|
||
"nav.keys": "API keys",
|
||
"nav.playground": "Playground",
|
||
"nav.settings": "Settings",
|
||
"nav.foot": "Gateway · v{v}",
|
||
"nav.billing": "Router key & billing",
|
||
|
||
// topbar / status
|
||
"status.router": "Router",
|
||
"status.gateway": "Gateway",
|
||
"status.online": "online",
|
||
"status.offline": "offline",
|
||
"status.checking": "checking…",
|
||
"status.noKey": "no key",
|
||
"theme.toggle": "Theme",
|
||
"logout": "Log out",
|
||
|
||
// login
|
||
"login.title": "allaigate Gateway",
|
||
"login.subtitle": "Enter the admin token to manage this gateway.",
|
||
"login.token": "Admin token",
|
||
"login.tokenPlaceholder": "paste admin token",
|
||
"login.submit": "Open console",
|
||
"login.hint": "The token is printed in the gateway log on startup, or set via [admin].token_env.",
|
||
"login.failed": "Invalid admin token.",
|
||
|
||
// dashboard
|
||
"dash.title": "Dashboard",
|
||
"dash.subtitle": "Live traffic, cost and routing analytics for your model pool.",
|
||
"dash.range": "Range",
|
||
"dash.range.1h": "1h",
|
||
"dash.range.24h": "24h",
|
||
"dash.range.7d": "7d",
|
||
"dash.kpi.requests": "Requests",
|
||
"dash.kpi.tokens": "Tokens",
|
||
"dash.kpi.cost": "Cost",
|
||
"dash.kpi.latency": "Avg latency",
|
||
"dash.kpi.success": "Success rate",
|
||
"dash.kpi.failovers": "Failovers",
|
||
"dash.series.title": "Requests over time",
|
||
"dash.series.cost": "Cost over time",
|
||
"dash.series.empty": "No traffic yet in this range.",
|
||
"dash.breakdown.title": "Breakdown",
|
||
"dash.groupby.model": "by model",
|
||
"dash.groupby.tier": "by tier",
|
||
"dash.groupby.label": "by task",
|
||
"dash.groupby.account": "by account",
|
||
"dash.recent.title": "Recent requests",
|
||
"dash.recent.empty": "No requests recorded yet. Try the Playground.",
|
||
"dash.col.time": "Time",
|
||
"dash.col.model": "Model",
|
||
"dash.col.tier": "Tier",
|
||
"dash.col.task": "Task",
|
||
"dash.col.tokens": "Tokens",
|
||
"dash.col.cost": "Cost",
|
||
"dash.col.latency": "Latency",
|
||
"dash.col.outcome": "Outcome",
|
||
"dash.col.requests": "Requests",
|
||
"dash.col.errors": "Errors",
|
||
"dash.health.title": "Health",
|
||
"dash.health.reachable": "reachable",
|
||
"dash.health.unreachable": "unreachable",
|
||
"dash.health.getKey": "Get a router key",
|
||
|
||
// models
|
||
"models.title": "Models",
|
||
"models.subtitle": "Your model pool — mix cheap local models with cloud APIs.",
|
||
"models.add": "Add model",
|
||
"models.empty": "No models yet. Add your first model to get started.",
|
||
"models.col.id": "ID",
|
||
"models.col.provider": "Provider",
|
||
"models.col.model": "Model",
|
||
"models.col.tier": "Cost tier",
|
||
"models.col.key": "Key",
|
||
"models.col.caps": "Capabilities",
|
||
"models.col.price": "Price (in/out)",
|
||
"models.probe": "Probe",
|
||
"models.probe.ok": "Reachable ({ms} ms)",
|
||
"models.probe.fail": "Failed: {err}",
|
||
"models.form.addTitle": "Add model",
|
||
"models.form.editTitle": "Edit model",
|
||
"models.form.id": "ID",
|
||
"models.form.idHint": "Unique id used in routing, e.g. local-qwen",
|
||
"models.form.provider": "Provider",
|
||
"models.form.baseUrl": "Base URL",
|
||
"models.form.model": "Model name",
|
||
"models.form.kind": "Kind",
|
||
"models.form.costTier": "Cost tier",
|
||
"models.form.priceIn": "Price in ($/1M)",
|
||
"models.form.priceOut": "Price out ($/1M)",
|
||
"models.form.apiKeyEnv": "API key env name",
|
||
"models.form.apiKeyEnvHint": "Name of the env var / stored secret holding this provider's key.",
|
||
"models.form.caps": "Capabilities",
|
||
"models.form.secret": "Secret value",
|
||
"models.form.secretStored": "stored",
|
||
"models.form.secretEnv": "from env",
|
||
"models.form.secretMissing": "not set",
|
||
"models.form.secretPlaceholder": "set or replace key (write-only)",
|
||
"models.deleteConfirm": "Delete model \"{id}\"?",
|
||
|
||
// routing
|
||
"routing.title": "Routing",
|
||
"routing.subtitle": "Map complexity tiers to an ordered list of models (priority = fallback).",
|
||
"routing.default": "Default model",
|
||
"routing.defaultHint": "Used when the router is unreachable.",
|
||
"routing.tiers": "Tiers",
|
||
"routing.tierHint": "Drag chips to reorder; first is preferred, the rest are fallbacks.",
|
||
"routing.addModel": "+ add",
|
||
"routing.addTier": "Add tier",
|
||
"routing.tierName": "Tier name",
|
||
"routing.policy": "Policy",
|
||
"routing.policy.mode": "Mode",
|
||
"routing.policy.fixed": "Fixed table",
|
||
"routing.policy.costAware": "Cost-aware",
|
||
"routing.policy.maxCost": "Max cost per request ($)",
|
||
"routing.policy.minClass": "Minimum class per tier",
|
||
"routing.empty": "No models available — add models first.",
|
||
|
||
// protocols
|
||
"protocols.title": "Protocols",
|
||
"protocols.subtitle": "Enable only the inbound API surfaces you need. Toggles apply live.",
|
||
"protocols.implemented": "implemented",
|
||
"protocols.planned": "planned",
|
||
"protocols.openai_chat": "OpenAI Chat Completions",
|
||
"protocols.openai_chat.d": "POST /v1/chat/completions — works with OpenAI SDK, LangChain, crewAI.",
|
||
"protocols.openai_completions": "OpenAI Completions (legacy)",
|
||
"protocols.openai_completions.d": "POST /v1/completions — legacy integrations.",
|
||
"protocols.openai_embeddings": "OpenAI Embeddings",
|
||
"protocols.openai_embeddings.d": "POST /v1/embeddings — RAG pipelines.",
|
||
"protocols.openai_models": "OpenAI Models",
|
||
"protocols.openai_models.d": "GET /v1/models — client auto-discovery.",
|
||
"protocols.anthropic_messages": "Anthropic Messages",
|
||
"protocols.anthropic_messages.d": "POST /v1/messages — Claude-native agents.",
|
||
"protocols.mcp": "MCP (Model Context Protocol)",
|
||
"protocols.mcp.d": "JSON-RPC / SSE — MCP-native orchestrators.",
|
||
"protocols.native_passthrough": "Native passthrough",
|
||
"protocols.native_passthrough.d": "POST /route — direct access to the routing decision.",
|
||
|
||
// keys
|
||
"keys.title": "API keys",
|
||
"keys.subtitle": "Virtual gateway keys for your agents. Provider keys never leave the gateway.",
|
||
"keys.add": "New key",
|
||
"keys.empty": "No keys configured — the gateway is in open mode (no auth).",
|
||
"keys.openMode": "Open mode: any request is accepted (no key required).",
|
||
"keys.col.key": "Key",
|
||
"keys.col.account": "Account",
|
||
"keys.col.rate": "Rate / min",
|
||
"keys.col.models": "Allowed models",
|
||
"keys.form.title": "New API key",
|
||
"keys.form.account": "Account",
|
||
"keys.form.rate": "Rate limit / min",
|
||
"keys.form.allow": "Allowed models",
|
||
"keys.form.allowHint": "Leave empty to allow all models.",
|
||
"keys.form.custom": "Custom key (optional)",
|
||
"keys.form.customHint": "Leave empty to auto-generate sk-gw-…",
|
||
"keys.created": "Key created — copy it now, it won't be shown again:",
|
||
"keys.revoke": "Revoke",
|
||
"keys.revokeConfirm": "Revoke this key?",
|
||
"keys.allowAll": "all",
|
||
|
||
// playground
|
||
"pg.title": "Playground",
|
||
"pg.subtitle": "Send a prompt through the live pipeline and inspect the routing decision.",
|
||
"pg.system": "System prompt",
|
||
"pg.systemPlaceholder": "optional system message",
|
||
"pg.prompt": "Prompt",
|
||
"pg.promptPlaceholder": "Ask something…",
|
||
"pg.mode": "Mode",
|
||
"pg.mode.auto": "Auto (route)",
|
||
"pg.mode.pinned": "Pinned model",
|
||
"pg.profile": "Profile",
|
||
"pg.model": "Model",
|
||
"pg.temp": "Temperature",
|
||
"pg.maxTokens": "Max tokens",
|
||
"pg.send": "Send",
|
||
"pg.stream": "Stream",
|
||
"pg.sending": "Sending…",
|
||
"pg.answer": "Answer",
|
||
"pg.empty": "The answer and routing decision will appear here.",
|
||
"pg.decision": "Routing decision",
|
||
"pg.task": "Task",
|
||
"pg.tier": "Tier",
|
||
"pg.score": "Complexity",
|
||
"pg.selected": "Selected model",
|
||
"pg.source": "Route source",
|
||
"pg.cost": "Cost",
|
||
"pg.latency": "Latency",
|
||
"pg.tokens": "Tokens (in/out)",
|
||
"pg.failover": "Failover",
|
||
"pg.error": "Request failed",
|
||
|
||
// settings
|
||
"settings.title": "Settings",
|
||
"settings.subtitle": "Gateway connection, routing input, reliability and observability.",
|
||
"settings.router": "Router connection",
|
||
"settings.router.url": "Router URL",
|
||
"settings.router.keyEnv": "Router key env name",
|
||
"settings.router.timeout": "Timeout (ms)",
|
||
"settings.router.verifyTls": "Verify TLS certificate",
|
||
"settings.router.enabled": "Use the router (off = local models only)",
|
||
"settings.cmf": "Local models (CMF)",
|
||
"settings.cmf.localOnly": "Use only local models",
|
||
"settings.cmf.manage": "Run a local model server",
|
||
"settings.cmf.autoInstall": "Auto-install cortiq (crates.io)",
|
||
"settings.cmf.autoUpdate": "Auto-update cortiq",
|
||
"settings.cmf.model": "Local .cmf model path",
|
||
"settings.cmf.port": "Local server port",
|
||
"settings.cmf.modelId": "Model id in the pool",
|
||
"settings.cmf.note": "Serves a local .cmf via cortiq (github.com/infosave2007/cmf). Works even when the router is unavailable.",
|
||
"settings.cmf.threads": "Worker threads",
|
||
"settings.cmf.gpu": "Use GPU (Metal / wgpu)",
|
||
"settings.cmf.speedNote": "Threads parallelise the decode matvecs (~3x faster; 0 = runtime default). GPU offload mainly helps prefill of long prompts — decode is memory-bandwidth-bound, so on Apple Silicon it is roughly neutral.",
|
||
"settings.cmf.checkPort": "Check port",
|
||
"settings.cmf.portFree": "port is free",
|
||
"settings.cmf.portBusy": "port is in use",
|
||
"settings.cmf.useSuggested": "use {port}",
|
||
"settings.cmf.serving": "serving {model} on :{port}",
|
||
"settings.cmf.intro": "Run models locally with the cortiq engine — works even offline or when the router is down.",
|
||
"settings.cmf.runtime": "cortiq runtime",
|
||
"settings.cmf.runtimeHelp": "The `cortiq` command-line tool that loads and runs .cmf models. Install it once before starting a local server.",
|
||
"settings.cmf.install": "Install cortiq",
|
||
"settings.cmf.reinstall": "Reinstall / update",
|
||
"settings.cmf.installing": "installing… (compiles, a few minutes)",
|
||
"settings.cmf.installedOk": "cortiq installed",
|
||
"settings.cmf.notInstalled": "not installed",
|
||
"settings.cmf.addModel": "Add a model",
|
||
"settings.cmf.addModelHelp": "Models are downloaded and converted in the Import tab (HuggingFace → .cmf). Then point the local server at the .cmf file below.",
|
||
"settings.cmf.importNote": "No model yet? Import one:",
|
||
"settings.cmf.importLink": "Open Import",
|
||
"settings.cmf.modelHelp": "Path to the .cmf file to serve (e.g. models/qwen.cmf), produced by the Import tab. Running several models for different tasks? Import them and switch this path.",
|
||
"settings.cmf.portHelp": "TCP port the local model server listens on (default 8081). Change only if it clashes with another service.",
|
||
"settings.cmf.threadsHelp": "How many CPU cores to use for generation. More = faster (~3× vs 1 core); 0 lets the runtime decide.",
|
||
"settings.cmf.gpuHelp": "Offload matrix math to the GPU (Metal/wgpu). On Apple Silicon this mostly speeds up long prompts — decode is memory-bandwidth-bound.",
|
||
"settings.cmf.localOnlyHelp": "Route every request to the local model, ignoring the router and any cloud providers.",
|
||
"settings.cmf.manageHelp": "The gateway starts `cortiq serve` for the .cmf below at launch and routes to it. Needs cortiq installed (step 1). Applies on restart.",
|
||
"settings.cmf.autoInstallHelp": "If cortiq is missing at startup, install it automatically from crates.io.",
|
||
"settings.cmf.autoUpdateHelp": "On startup, upgrade cortiq to the newest release when a newer one exists.",
|
||
"settings.router.enabledHelp": "Use the cortiq-router to classify each request (task type + complexity) and pick a model. Off = use the local routing table only.",
|
||
"settings.router.verifyTlsHelp": "Verify the router's TLS certificate. Turn off only for a self-signed / IP-bound certificate.",
|
||
"settings.telemetry.metricsHelp": "Expose Prometheus metrics at GET /metrics.",
|
||
"settings.cortiq.echoHelp": "Add a `cortiq` field (task, model, cost) to each response body. Response headers are always sent.",
|
||
"settings.stats.enabledHelp": "Record recent requests to power the dashboard and charts.",
|
||
"settings.cache.enabledHelp": "Semantic cache: return a saved answer when a new prompt is very similar to a past one, skipping the model call. Needs an embedding model in the pool.",
|
||
"settings.log.bodiesHelp": "Log full request/response message bodies. Off by default for privacy.",
|
||
"settings.router.taxonomy": "Taxonomy id",
|
||
"settings.router.getKey": "Get or top up a router key at",
|
||
"settings.router.secret": "Router key (value)",
|
||
"settings.router.saveKey": "Save key",
|
||
"settings.router.keySaved": "Router key saved",
|
||
"settings.router.keyEmpty": "Paste the key first",
|
||
"settings.router.secretHelp": "Paste the key from your api.allaigate.com email, then Save key. This is NOT a “Keys” value — those are for clients calling your gateway.",
|
||
"settings.router.keyStoredMask": "•••••••••••• — key saved (paste again to replace)",
|
||
"settings.router.test": "Test router",
|
||
"router.probe.ok": "Router OK ({ms} ms)",
|
||
"router.probe.noKey": "Router key is not set",
|
||
"router.probe.auth": "Key rejected — invalid or revoked",
|
||
"router.probe.payment": "Payment required — plan expired or quota exhausted",
|
||
"router.probe.quota": "Rate limit exceeded",
|
||
"router.probe.timeout": "Router timed out",
|
||
"router.probe.unreachable": "Router unreachable",
|
||
"router.probe.error": "Router error",
|
||
"router.usage": "Usage",
|
||
"settings.route": "Routing input",
|
||
"settings.route.strategy": "Text strategy",
|
||
"settings.route.maxChars": "Max chars",
|
||
"settings.route.cacheTtl": "Cache TTL",
|
||
"settings.route.profile": "Default profile",
|
||
"settings.breaker": "Circuit breaker",
|
||
"settings.breaker.threshold": "Error threshold",
|
||
"settings.breaker.cooldown": "Cooldown",
|
||
"settings.log": "Logging",
|
||
"settings.log.level": "Log level",
|
||
"settings.log.bodies": "Log message bodies (privacy off)",
|
||
"settings.telemetry": "Telemetry",
|
||
"settings.telemetry.metrics": "Expose Prometheus /metrics",
|
||
"settings.cortiq": "Response metadata",
|
||
"settings.cortiq.echo": "Echo cortiq routing block in responses",
|
||
"settings.stats": "Statistics",
|
||
"settings.stats.enabled": "Record request statistics",
|
||
"settings.stats.file": "Stats file (JSONL)",
|
||
"settings.stats.retention": "Retention",
|
||
"settings.listen": "Listen address",
|
||
"settings.export": "Export config",
|
||
"settings.import": "Import config",
|
||
"settings.needsRestart": "Saved. Some changes (listen address) require a restart.",
|
||
|
||
// toasts / misc
|
||
"toast.saved": "Changes saved",
|
||
"toast.deleted": "Deleted",
|
||
"toast.probing": "Probing…",
|
||
"toast.imported": "Config imported",
|
||
"dash.cache": "Semantic cache",
|
||
"dash.cache.hitRate": "Hit rate",
|
||
"dash.cache.hits": "Hits",
|
||
"dash.cache.saved": "Saved",
|
||
"dash.cache.disabled": "Semantic cache is disabled — enable it in Settings.",
|
||
"settings.cache": "Semantic cache",
|
||
"settings.cache.enabled": "Enable semantic cache",
|
||
"settings.cache.threshold": "Similarity threshold",
|
||
"settings.cache.ttl": "Entry TTL",
|
||
"settings.cache.embed": "Embedding model",
|
||
"settings.cache.note": "Cache settings take effect after a restart.",
|
||
|
||
// import (HuggingFace → CMF)
|
||
"import.subtitle": "Search HuggingFace, convert to a local .cmf, and serve it.",
|
||
"import.searchPlaceholder": "Search HuggingFace models (e.g. qwen3, llama)…",
|
||
"import.searching": "Searching…",
|
||
"import.noResults": "No models found.",
|
||
"import.configure": "Configure conversion",
|
||
"import.outName": "Output name",
|
||
"import.outNameHint": "The .cmf file name (letters, digits, - and _).",
|
||
"import.quant": "Quantization",
|
||
"import.convert": "Convert & load",
|
||
"import.converting": "Converting",
|
||
"import.started": "Conversion started",
|
||
"import.failed": "Conversion failed — see the log.",
|
||
"import.register": "Register as local model",
|
||
"import.registered": "Registered as '{id}'. Run cortiq-server to serve it.",
|
||
"import.jobs": "Conversions",
|
||
"import.cancel": "Cancel",
|
||
"import.cancelling": "Cancelling — cleaning up partial output…",
|
||
"import.delete": "Delete",
|
||
"import.deleteConfirm": "Delete this model and its converted .cmf file from disk?",
|
||
"import.deleted": "Deleted with its files",
|
||
"import.state.running": "running",
|
||
"import.state.done": "done",
|
||
"import.state.error": "error",
|
||
"import.state.cancelled": "cancelled",
|
||
"import.q.q8_2f": "8-bit two-field (𝒲×θ) — recommended: best quality/size.",
|
||
"import.q.q8_row": "8-bit per-row — simple, robust.",
|
||
"import.q.q4": "4-bit block — smallest, lower quality.",
|
||
"import.q.vbit": "Variable 3–8 bit — per-expert bit budgets.",
|
||
"import.q.f16": "16-bit — no quantization.",
|
||
"import.q.f32": "32-bit — full precision (large file).",
|
||
"import.adv.title": "Advanced settings",
|
||
"import.adv.auto": "auto",
|
||
"import.adv.linearCore": "Linear core",
|
||
"import.adv.linearHint": "How linear-attention layers are carried/folded.",
|
||
"import.adv.nphase": "vmf_phase phases/head",
|
||
"import.adv.vbitShape": "VBIT budget shape",
|
||
"import.adv.meanBits": "VBIT mean bits",
|
||
"import.adv.shard": "Shard max (GB)",
|
||
"import.adv.noShard": "no sharding",
|
||
"import.adv.skipMtp": "skip MTP head",
|
||
};
|