Compare commits

..

22 Commits

Author SHA1 Message Date
diegosouzapw 0517dcf0b7 chore: bump version to 1.7.10 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 39s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 15:31:38 -03:00
diegosouzapw b7f0665ce9 fix: streaming tool calls missing id and wrong finish_reason in Responses→ChatCompletions translation (#180)
- Added tool_calls[].id and type:'function' to argument delta chunks
  so OpenAI-compatible clients can associate argument fragments with
  the correct tool call
- Changed finish_reason from hardcoded 'stop' to 'tool_calls' when
  tool calls were emitted (in flush, response.completed, and final chunk)
- Fixes state desync in agentic clients (OpenCode, Claude Code, etc.)
  where the assistant thinks tools already ran
2026-03-02 15:30:54 -03:00
diegosouzapw 144628755d chore: bump version to 1.7.9 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 29s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 14:55:56 -03:00
diegosouzapw 7c5bb2c6b6 Merge PR #179: docs: update Cline URL to OpenClaw 2026-03-02 14:55:13 -03:00
diegosouzapw afadb0fea1 Merge PR #178: fix: add JWT_SECRET env to electron release build step 2026-03-02 14:54:51 -03:00
MAINER4IK b6c9c8a822 Change Cline to OpenClaw :)) 2026-03-02 22:28:01 +05:00
benzntech 11f43ca65c fix: add JWT_SECRET env to electron release build step
The Next.js build in electron-release.yml fails because the secrets
validator detects missing JWT_SECRET and exits with code 1. This adds
the env var to the build step, matching the pattern used in ci.yml.
2026-03-02 22:50:33 +05:30
diegosouzapw 8dce812a4d chore: bump version to 1.7.8 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 35s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 12:14:58 -03:00
diegosouzapw 93047069b6 Merge PR #174: feat: add theme color settings and complete media/theme i18n 2026-03-02 12:13:53 -03:00
diegosouzapw c4f1990aff fix: address agent review issues for theme color settings (#174)
Code Quality Improvements:
- Export COLOR_THEMES from themeStore.ts for reuse (DRY)
- Add coral preset to color list (fixes default inconsistency)
- Sync local customThemeColor state reactively via Zustand subscribe
- Add hex color validation with visual feedback (red border + disabled button)
- Remove dead /themes route from Header.tsx (page doesn't exist)
- Add CSS color-mix() fallback for older browsers
- Add themeCoral i18n key to all 30 locale files
2026-03-02 12:12:52 -03:00
mainer4ik 6e2816f08b feat: add theme color settings and complete media/theme i18n 2026-03-02 18:37:27 +05:00
diegosouzapw 227268024d chore: bump version to 1.7.7 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 28s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 10:29:08 -03:00
diegosouzapw 8d5891a382 fix: sanitize tool schemas for Gemini provider (#173)
- Added cleanJSONSchemaForAntigravity() to openaiToGeminiBase() tool conversion
- Both OpenAI-format and Claude-format tool parameters are now sanitized
- Also sanitized response_format json_schema using the same function
- Removes unsupported JSON Schema keywords (additionalProperties, $schema, etc.)
- All Gemini paths (standard, CLI, Antigravity) now consistently sanitize schemas
2026-03-02 10:28:26 -03:00
diegosouzapw 7700fca501 chore: bump version to 1.7.6 and update CHANGELOG
Build Electron Desktop App / Validate version (push) Failing after 28s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
2026-03-02 10:19:43 -03:00
diegosouzapw 527c542d6d fix: cloud proxy endpoint shows undefined/v1 when env var not set (#171)
- syncAndVerify now returns cloudUrl in API response for frontend to use
- EndpointPageClient uses dynamic cloudBaseUrl state instead of relying on env var
- Falls back gracefully when NEXT_PUBLIC_CLOUD_URL is not set (Docker deployments)
- Fixed setInterval in accountFallback.ts global scope for Cloudflare Workers compat
2026-03-02 10:18:21 -03:00
diegosouzapw 8fbae5e467 feat(release): v1.7.5 — OAuth re-auth duplicate fix (#170)
Build Electron Desktop App / Validate version (push) Failing after 35s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
- Fixed OAuth re-auth creating duplicate connections instead of updating existing ones
- CHANGELOG.md updated with v1.7.5 section
- Version bumped to 1.7.5
2026-03-02 00:38:33 -03:00
diegosouzapw 4d2a5efd12 fix: OAuth re-auth now updates existing connection instead of creating duplicates (#170)
- Modified OAuth exchange route to use upsert logic at all 3 connection-save locations
- Before creating a new connection, checks for existing connections with same provider+email+authType
- If match found, calls updateProviderConnection() to refresh tokens instead of creating duplicate
- Falls back to createProviderConnection() for genuinely new connections
- Fixes: re-auth button creating new account entries instead of refreshing existing ones
2026-03-02 00:37:50 -03:00
diegosouzapw 5ffa14190a feat(release): v1.7.4 — OpenCode CLI integration, endpoint page restructure, i18n translations
Build Electron Desktop App / Validate version (push) Failing after 32s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
- OpenCode CLI integration guide added to README (#169)
- Endpoint page restructured with 3 categories + Responses API & Models endpoints
- 21 new i18n keys (settings + endpoint) translated across 30 locales
- 30 translated READMEs synced with v1.7.3 features
- 3 new workflow files: update-docs, generate-release, issue-triage
2026-03-01 23:03:54 -03:00
diegosouzapw 7820145cbe docs: add OpenCode CLI integration guide (#169)
- Added OpenCode section to README.md CLI Integration with step-by-step instructions
- Uses @ai-sdk/openai-compatible adapter with custom opencode.json config
- Includes example models and baseURL configuration
- Closes #169
2026-03-01 22:59:10 -03:00
diegosouzapw b7a6c563ac feat: add i18n translations for Model Aliases & Background Degradation + restructure Endpoint page
- Added 14 translated settings keys (modelAliasesTitle, backgroundDegradationTitle, enableDegradation, etc.) to all 30 locale files
- Added 7 translated endpoint keys (responsesDesc, listModelsDesc, categoryCore/Media/Utility) to all 30 locale files
- Restructured Endpoint page with 3 grouped categories: Core APIs, Media & Multi-Modal, Utility & Management
- Added Responses API (/v1/responses) and List Models (/v1/models) endpoint sections
- Fixed missing translation display issue where raw keys were shown instead of translated text
2026-03-01 22:50:07 -03:00
diegosouzapw 52221488d0 docs: sync all 30 language READMEs with v1.7.3 features + create workflow files
- Synced feature tables across all 28 translated READMEs (Model Aliases, Background Degradation, Rate Limit Persistence, Token Refresh Resilience)
- Updated 6 docs/i18n/*/FEATURES.md with new Settings description
- Created workflows: update-docs.md (with multi-language sync step), generate-release.md, issue-triage.md
2026-03-01 22:02:38 -03:00
diegosouzapw 4a1acb1446 feat(release): v1.7.3 — model deprecation, background degradation, rate limit persistence, thinking improvements, circuit breaker
Build Electron Desktop App / Validate version (push) Failing after 35s
Build Electron Desktop App / Build Electron (macos-arm64) (push) Has been skipped
Build Electron Desktop App / Build Electron (linux) (push) Has been skipped
Build Electron Desktop App / Build Electron (macos-intel) (push) Has been skipped
Build Electron Desktop App / Build Electron (windows) (push) Has been skipped
Build Electron Desktop App / Create Release (push) Has been skipped
Features:
- Model Deprecation Auto-Forward (10+ built-in aliases + custom via UI)
- Background Task Smart Degradation (19 patterns, degradation map)
- Rate Limit Persistence (SQLite, 60s debounce, 24h staleness)
- thinkingLevel string → budget conversion (high/medium/low/none)
- Claude -thinking model auto-injection
- Gemini 3.0/3.1 model registry distinction
- Token Refresh Circuit Breaker (5 failures → 30min cooldown)

Tests: 561 total (40+ new), 0 failures
2026-03-01 21:42:39 -03:00
99 changed files with 4307 additions and 452 deletions
+78
View File
@@ -0,0 +1,78 @@
---
description: Create a new release, bump version up to 1.x.10 threshold, update changelog, and manage Pull Requests
---
# Generate Release Workflow
Bump version, finalize CHANGELOG, commit, tag, push, publish to npm, and create GitHub release.
## Steps
### 1. Determine new version
Check current version in `package.json` and increment the patch number:
```bash
grep '"version"' package.json
```
Version format: `1.x.y` — increment `y` for patch, `x` for minor (threshold: y=10 triggers x+1).
### 2. Finalize CHANGELOG.md
Replace `[Unreleased]` header with the new version and date:
```markdown
## [1.x.y] — YYYY-MM-DD
```
### 3. Bump version in package.json
```bash
sed -i 's/"version": "OLD"/"version": "NEW"/' package.json
```
### 4. Stage, commit, and tag
// turbo-all
```bash
git add -A
git commit -m "feat(release): vX.Y.Z — summary of changes"
git tag -a vX.Y.Z -m "Release vX.Y.Z — summary"
```
### 5. Push to GitHub
```bash
git push origin main
git push origin vX.Y.Z
```
### 6. Publish to npm
```bash
npm publish
```
Wait for completion (prepublishOnly runs `npm run build:cli` automatically).
### 7. Create GitHub release
```bash
gh release create vX.Y.Z --title "Release vX.Y.Z" --notes-file /tmp/release_notes.md
```
### 8. Deploy to VPS (if requested)
See `/deploy-vps` workflow for Akamai VPS or use npm for local VPS:
```bash
ssh root@<VPS_IP> "npm install -g omniroute@X.Y.Z && pm2 restart omniroute"
```
## Notes
- Always run `/update-docs` BEFORE this workflow (ensures CHANGELOG and README are current)
- The `prepublishOnly` script runs `npm run build:cli` automatically during `npm publish`
- After npm publish, verify with `npm info omniroute version`
+50
View File
@@ -0,0 +1,50 @@
---
description: How to respond to GitHub issues with insufficient information
---
# Issue Triage Workflow
Respond to GitHub issues that need more information before they can be investigated.
## Steps
### 1. Identify issues needing triage
```bash
gh issue list --state open --limit 20
```
### 2. Evaluate each issue
Check if the issue has:
- Clear reproduction steps
- Environment details (OS, Node.js version, OmniRoute version)
- Error logs/screenshots
- Expected vs actual behavior
### 3. Respond with triage template
For issues missing information:
```markdown
Thank you for reporting this issue! To help us investigate, please provide:
1. **OmniRoute version**: (`omniroute --version`)
2. **Node.js version**: (`node --version`)
3. **Operating system**: (e.g., Ubuntu 24.04, macOS 15, Windows 11)
4. **Installation method**: (npm, Docker, source)
5. **Steps to reproduce**: (exact commands/actions that trigger the issue)
6. **Error logs**: (paste relevant logs from the console)
7. **Expected behavior**: (what should happen)
This will help us debug and resolve your issue faster. 🙏
```
### 4. Label the issue
Add appropriate labels: `needs-info`, `bug`, `enhancement`, `question`, etc.
```bash
gh issue edit <NUMBER> --add-label "needs-info"
```
+105
View File
@@ -0,0 +1,105 @@
---
description: How to automatically summarize recent changes and update README and CHANGELOG
---
# Update Documentation Workflow
Update CHANGELOG.md, README.md, docs/ files, and all multi-language translations whenever features are added or changed.
## Steps
### 1. Summarize recent changes
Review git log and identify new features, fixes, or changes since the last release tag:
```bash
git log $(git describe --tags --abbrev=0)..HEAD --oneline
```
### 2. Update English CHANGELOG.md
Add an `[Unreleased]` section (or version header if releasing) with:
- `### ✨ New Features` — each feature as a bullet point
- `### 🐛 Bug Fixes` — if applicable
- `### 🧪 Tests` — test count changes
- `### 📁 New Files` — table of new files with purpose
### 3. Update English README.md
Update the feature tables in these sections:
- **🧠 Routing & Intelligence** — for routing/model features
- **🛡️ Resilience & Security** — for security/resilience features
- **📊 Observability & Analytics** — for monitoring features
- **☁️ Deploy & Sync** — for deployment features
### 4. Update docs/ files
- `docs/FEATURES.md` — update the Settings section description
- `docs/API_REFERENCE.md` — add new API routes if any
- `docs/ARCHITECTURE.md` — update architecture if structural changes
### 5. 🌐 Sync Multi-Language Documentation (CRITICAL)
// turbo-all
**This step MUST be run after every README or docs update.**
The project has **30 language versions** of documentation:
**README files (root directory):**
```
README.md (English - source of truth)
README.pt-BR.md README.pt.md README.es.md README.fr.md README.it.md
README.de.md README.nl.md README.sv.md README.no.md README.da.md README.fi.md
README.ru.md README.uk-UA.md README.bg.md README.sk.md README.pl.md README.ro.md README.hu.md
README.ar.md README.he.md README.th.md README.in.md README.id.md README.ms.md README.vi.md
README.ja.md README.ko.md README.zh-CN.md README.phi.md
```
**docs/i18n/ directories (29 languages):**
```
docs/i18n/{ar,bg,da,de,es,fi,fr,he,hu,id,in,it,ja,ko,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv,th,uk-UA,vi,zh-CN}/
Each contains: API_REFERENCE.md, ARCHITECTURE.md, CODEBASE_DOCUMENTATION.md, FEATURES.md, TROUBLESHOOTING.md, USER_GUIDE.md
```
**Sync approach for feature table updates:**
a. Identify which feature table rows were added to English README.md
b. For each translated README, find the corresponding anchor lines:
- **Routing section:** Find the `💬` (System Prompt) table row — the line before it is always the last routing feature. Insert new routing features before System Prompt.
- **Resilience section:** Find the `📊` Rate Limits table row (the one in lines 590-600, NOT the quota tracking one in lines 560-570). Insert new resilience features after it.
c. The new feature entries can stay in English for technical features, matching the pattern used in the existing translations.
d. Use `sed` or similar tool to batch-insert across all 29 translated READMEs.
**Verification:**
```bash
# Verify all READMEs have the new features
grep -l "NEW_FEATURE_NAME" README.*.md | wc -l
# Should return 30 (all language versions)
```
**FEATURES.md sync:**
```bash
# Update Settings description in all docs/i18n/*/FEATURES.md
for dir in docs/i18n/*/; do
# Update the Settings section description to mention new features
# Check FEATURES.md in each directory
done
```
### 6. Verify documentation changes
```bash
# Check all modified files
git status --short
# Verify no broken markdown
# Optional: run markdownlint if available
```
+2
View File
@@ -89,6 +89,8 @@ jobs:
run: npm ci
- name: Build Next.js standalone
env:
JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation
run: npm run build
- name: Install Electron dependencies
+112
View File
@@ -7,6 +7,118 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---
## [1.7.10] — 2026-03-02
### 🐛 Bug Fixes
- **Streaming Tool Calls (Responses→ChatCompletions)** — Fixed two issues in the `openaiResponsesToOpenAIResponse` translator that broke tool call execution in agentic clients (OpenCode, Claude Code, Cursor, etc.): (1) Argument delta chunks now include `tool_calls[].id` and `type: "function"` so clients can associate argument fragments correctly. (2) `finish_reason` is now `"tool_calls"` instead of hardcoded `"stop"` when tool calls occurred. Fixes #180
## [1.7.9] — 2026-03-02
### 🐛 Bug Fixes
- **Electron CI Build** — Added `JWT_SECRET` environment variable to the Electron release workflow `Build Next.js standalone` step, fixing build failures in GitHub Actions. PR #178 by @benzntech
### 📝 Documentation
- **README** — Updated OpenClaw link from `cline/cline` to `openclaw/openclaw` to reflect the project rename. PR #179 by @MAINER4IK
## [1.7.8] — 2026-03-02
### ✨ New Features
- **Theme Color Customization** — Users can now select from 7 preset accent colors (Coral, Blue, Red, Green, Violet, Orange, Cyan) or define a custom color via color picker/hex input. The chosen color dynamically updates `--color-primary` and `--color-primary-hover` CSS variables across the entire UI. PR #174 by @mainer4ik
### 🌐 Multi-Language Sync
- **Theme & Media i18n** — Added `themeCoral`, `themeBlue`, `themeRed`, `themeGreen`, `themeViolet`, `themeOrange`, `themeCyan`, `themeAccent`, `themeAccentDesc`, `themeCustom`, `themeCreate`, and media section translations across all **30 language locales**
### 🔧 Code Quality (Review Improvements)
- Exported `COLOR_THEMES` constant from `themeStore.ts` for DRY reuse
- Added hex color validation with visual feedback (red border + disabled apply button)
- Synced local state via Zustand `subscribe` pattern for cross-tab consistency
- Removed dead `/themes` route from Header.tsx
- Added CSS `color-mix()` fallback for older browsers
## [1.7.7] — 2026-03-02
### 🐛 Bug Fixes
- **Gemini Tool Schema Sanitization** — The standard Gemini provider now sanitizes OpenAI tool schemas before forwarding to Gemini API, removing unsupported JSON Schema keywords (`additionalProperties`, `$schema`, `const`, `default`, `not`, etc.). Previously, sanitization only ran in the CLI executor path, causing Gemini to reject tool calls when schemas contained unsupported constraints. Also applied sanitization to `response_format.json_schema`. Fixes #173
## [1.7.6] — 2026-03-02
### 🐛 Bug Fixes
- **Cloud Proxy `undefined/v1` Fix** — When the `NEXT_PUBLIC_CLOUD_URL` environment variable is not set (common in Docker deployments), the endpoint page now correctly falls back instead of showing `undefined/v1`. The cloud sync API now returns `cloudUrl` in its response so the frontend can use it dynamically. Fixes #171
### ✨ New Features
- **Cloud Worker `/v1/models` Endpoint** — The Cloud Worker now supports the `/v1/models` endpoint for both URL formats (`/v1/models` and `/{machineId}/v1/models`), returning all available models synced from the local OmniRoute instance
### 🔧 Infrastructure
- **Cloudflare Workers Compatibility** — Fixed `setInterval` in global scope issue in `accountFallback.ts` that blocked Cloud Worker deployment. Lazy initialization pattern ensures compatibility with Cloudflare Workers runtime restrictions
## [1.7.5] — 2026-03-02
### 🐛 Bug Fixes
- **OAuth Re-Auth Duplicate Fix** — Re-authenticating an expired OAuth connection now updates the existing connection instead of creating a duplicate entry. When re-auth is triggered, the system matches by `provider` + `email` + `authType` and refreshes tokens in-place. Fixes #170
## [1.7.4] — 2026-03-01
### ✨ New Features
- **OpenCode CLI Integration** — Added full integration guide for [OpenCode](https://opencode.ai) AI CLI tool using `@ai-sdk/openai-compatible` adapter with custom `opencode.json` config. Resolves #169
- **Endpoint Page Restructured** — Reorganized the Endpoint dashboard page into 3 grouped categories (Core APIs, Media & Multi-Modal, Utility & Management) with visual dividers. Added 2 new endpoint sections: **Responses API** (`/v1/responses`) and **List Models** (`/v1/models`)
- **Model Aliases & Background Degradation i18n** — Added 14 translated settings keys and 7 translated endpoint keys across all **30 language locales**. Fixed missing translations showing raw keys like `settings.modelAliasesTitle` in the UI
### 🌐 Multi-Language Sync
- **30 README translations synced** — All 28 translated READMEs updated with v1.7.3 feature entries (Model Aliases, Background Degradation, Rate Limit Persistence, Token Refresh Resilience)
- **6 docs/i18n FEATURES.md updated** — Settings description expanded in da, it, nl, phi, pl, sv
### 📁 New Files
| File | Purpose |
| --------------------------------------- | ----------------------------------------------------------- |
| `.agents/workflows/update-docs.md` | Documentation update workflow with multi-language sync step |
| `.agents/workflows/generate-release.md` | Release generation workflow (version bump, npm, GitHub) |
| `.agents/workflows/issue-triage.md` | Issue triage workflow for issues with insufficient info |
## [1.7.3] — 2026-03-01
### ✨ New Features
- **Model Deprecation Auto-Forward** — New `modelDeprecation.ts` service with 10+ built-in aliases for legacy Gemini, Claude, and OpenAI models. Deprecated model IDs (e.g., `gemini-pro`, `claude-2`) are automatically forwarded to their current replacements. Custom aliases configurable via new Settings → Routing → Model Aliases UI tab with full CRUD API (`/api/settings/model-aliases`)
- **Background Task Smart Degradation** — New `backgroundTaskDetector.ts` service detects background/utility requests (title generation, summarization, etc.) via 19 system prompt patterns and `X-Request-Priority` header, and automatically reroutes them to cheaper models. Configurable degradation map and detection patterns via new Settings → Routing → Background Degradation UI tab. Disabled by default (opt-in)
- **Rate Limit Persistence** — Learned rate limits from API response headers are now persisted to SQLite with 60-second debouncing and restored on startup (24h staleness filter). Rate limits survive server restarts instead of being lost in memory
- **thinkingLevel String Conversion** — `applyThinkingBudget()` now handles string-based `thinkingLevel` inputs (`"high"`, `"medium"`, `"low"`, `"none"`) by converting them to numeric token budgets. Supports `thinkingLevel`, `thinking_level`, and Gemini's `generationConfig.thinkingConfig.thinkingLevel` fields
- **Claude -thinking Model Auto-Injection** — Models ending with `-thinking` suffix (e.g., `claude-opus-4-6-thinking`) automatically get thinking parameters injected to prevent API errors. `hasThinkingCapableModel()` updated to recognize these suffixes
- **Gemini 3.0/3.1 Model Registry** — Updated provider registry to explicitly distinguish Gemini 3.1 (Pro, Flash) from 3.0 Preview variants across `gemini`, `gemini-cli`, and `antigravity` providers with clear naming conventions
- **Token Refresh Circuit Breaker** — Per-provider circuit breaker in `refreshWithRetry()`: 5 consecutive failures trigger a 30-minute cooldown to prevent infinite retry loops. Added 30-second timeout wrapper per refresh attempt. Exported `isProviderBlocked()` and `getCircuitBreakerStatus()` for diagnostics
### 🧪 Tests
- **40+ new unit tests** across 3 files: `model-deprecation.test.mjs` (14 tests), `background-task-detector.test.mjs` (14 tests), extended `thinking-budget.test.mjs` (+13 tests). Total suite: **561 tests, 0 failures**
### 📁 New Files
| File | Purpose |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `open-sse/services/modelDeprecation.ts` | Model deprecation alias resolver with built-in + custom aliases |
| `open-sse/services/backgroundTaskDetector.ts` | Background task detection with pattern matching and model degradation |
| `src/app/api/settings/model-aliases/route.ts` | CRUD API for model alias management |
| `src/app/api/settings/background-degradation/route.ts` | API for background degradation config |
| `src/app/(dashboard)/settings/components/ModelAliasesTab.tsx` | Settings UI for model alias management |
| `src/app/(dashboard)/settings/components/BackgroundDegradationTab.tsx` | Settings UI for background degradation |
| `tests/unit/model-deprecation.test.mjs` | 14 unit tests for model deprecation |
| `tests/unit/background-task-detector.test.mjs` | 14 unit tests for background task detection |
---
## [1.7.2] — 2026-03-01
### ✨ New Features
+4
View File
@@ -567,6 +567,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **نماذج مخصصة** | أضف أي معرف نموذج إلى أي مزود |
| 🌐 **جهاز توجيه Wildcard** | قم بتوجيه أنماط `provider/*` إلى أي مزود ديناميكيًا |
| 🧠 **ميزانية التفكير** | أوضاع العبور والتلقائي والمخصص والتكيفي لنماذج الاستدلال |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **الحقن الفوري للنظام** | يتم تطبيق موجه النظام العالمي على كافة الطلبات |
| 📄 **Responses API** | دعم واجهة برمجة تطبيقات استجابات OpenAI الكاملة (`/v1/responses`) لـ Codex |
@@ -592,6 +594,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **انتحال بصمة الإصبع TLS** | تجاوز اكتشاف الروبوتات المستندة إلى TLS عبر wreq-js |
| 🌐 **تصفية IP** | القائمة المسموح بها/القائمة المحظورة للتحكم في الوصول إلى واجهة برمجة التطبيقات |
| 📊 **حدود المعدل القابلة للتحرير** | عدد الدورات في الدقيقة القابل للتكوين والفجوة الدنيا والحد الأقصى المتزامن على مستوى النظام |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **حماية نقطة نهاية واجهة برمجة التطبيقات** | بوابة المصادقة + حظر الموفر لنقطة النهاية `/models` |
| 🔒 **رؤية الوكيل** | شارات مرمزة بالألوان: 🟢 عالمية، 🟡 مزود، 🔵 لكل اتصال مع عرض IP |
| 🌐 ** تكوين الوكيل ذو 3 مستويات ** | قم بتكوين الوكلاء على المستوى العالمي أو لكل مزود أو لكل اتصال |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Персонализирани модели** | Добавете всеки модел ID към който и да е доставчик |
| 🌐 **Wildcard Router** | Насочвайте `provider/*` шаблони към всеки доставчик динамично |
| 🧠 **Мислен бюджет** | Преминаване, автоматичен, персонализиран и адаптивен режим за модели на разсъждение |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Системно бързо инжектиране** | Глобална системна подкана, приложена към всички заявки |
| 📄 **API за отговори** | Пълна поддръжка на OpenAI Responses API (`/v1/responses`) за Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | Заобикаляне на TLS-базирано откриване на бот чрез wreq-js |
| 🌐 **IP филтриране** | Списък с разрешени/блокирани за контрол на достъпа до API |
| 📊 **Редактируеми ограничения на скоростта** | Конфигурируеми обороти в минута, минимална разлика и максимална едновременност на системно ниво |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Удостоверяване + блокиране на доставчик за крайната точка `/models` |
| 🔒 **Прокси видимост** | Цветно кодирани значки: 🟢 глобален, 🟡 доставчик, 🔵 за връзка с IP дисплей |
| 🌐 **3-ниво на прокси конфигурация** | Конфигуриране на прокси сървъри на глобално ниво, на ниво доставчик или на ниво връзка |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Tilpassede modeller** | Tilføj ethvert model-id til enhver udbyder |
| 🌐 **Wildcard-router** | Rut `provider/*` mønstre til enhver udbyder dynamisk |
| 🧠 **Tænkende budget** | Passthrough, auto, brugerdefinerede og adaptive tilstande til ræsonnerende modeller |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Global systemprompt anvendt på tværs af alle anmodninger |
| 📄 **Responses API** | Fuld OpenAI Responses API (`/v1/responses`) understøttelse af Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | Omgå TLS-baseret botdetektion via wreq-js |
| 🌐 **IP-filtrering** | Tilladelsesliste/blokeringsliste til API-adgangskontrol |
| 📊 **Redigerbare satsgrænser** | Konfigurerbar RPM, min. gap og maks. samtidighed på systemniveau |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API-endepunktsbeskyttelse** | Auth gating + udbyderblokering for `/models` slutpunktet |
| 🔒 **Proxysynlighed** | Farvekodede badges: 🟢 global, 🟡 udbyder, 🔵 per forbindelse med IP-skærm |
| 🌐 **3-Level Proxy Config** | Konfigurer proxyer på globalt niveau, pr. udbyder eller pr. forbindelsesniveau |
+13 -9
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Benutzerdefinierte Modelle** | Jede Modell-ID zu jedem Anbieter hinzufügen |
| 🌐 **Wildcard-Router** | `provider/*` Muster dynamisch an jeden Anbieter routen |
| 🧠 **Reasoning-Budget** | Passthrough, auto, custom und adaptive Modi für Reasoning-Modelle |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Globaler System Prompt für alle Anfragen |
| 📄 **API Responses** | Volle Unterstützung der OpenAI Responses API (`/v1/responses`) für Codex |
@@ -584,15 +586,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ Resilienz & Sicherheit
| Funktion | Was es macht |
| ------------------------------- | -------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-Öffnung/-Schließung pro Anbieter mit konfigurierbaren Schwellen |
| 🛡️ **Anti-Thundering Herd** | Mutex + Semaphor Rate-Limit für API-Key-Anbieter |
| 🧠 **Semantischer Cache** | Zwei-Ebenen-Cache (Signatur + Semantik) senkt Kosten und Latenz |
| ⚡ **Anfrage-Idempotenz** | 5s Dedup-Fenster für doppelte Anfragen |
| 🔒 **TLS-Fingerprint-Spoofing** | Bot-Erkennung umgehen via wreq-js |
| 🌐 **IP-Filterung** | Allowlist/Blocklist für API-Zugriffskontrolle |
| 📊 **Editierbare Rate-Limits** | Konfigurierbare RPM, minimaler Abstand, max. Konkurrenz |
| Funktion | Was es macht |
| ------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-Öffnung/-Schließung pro Anbieter mit konfigurierbaren Schwellen |
| 🛡️ **Anti-Thundering Herd** | Mutex + Semaphor Rate-Limit für API-Key-Anbieter |
| 🧠 **Semantischer Cache** | Zwei-Ebenen-Cache (Signatur + Semantik) senkt Kosten und Latenz |
| ⚡ **Anfrage-Idempotenz** | 5s Dedup-Fenster für doppelte Anfragen |
| 🔒 **TLS-Fingerprint-Spoofing** | Bot-Erkennung umgehen via wreq-js |
| 🌐 **IP-Filterung** | Allowlist/Blocklist für API-Zugriffskontrolle |
| 📊 **Editierbare Rate-Limits** | Konfigurierbare RPM, minimaler Abstand, max. Konkurrenz |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 Observability & Analytics
+13 -9
View File
@@ -572,6 +572,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modelos Personalizados** | Agrega cualquier ID de modelo a cualquier proveedor |
| 🌐 **Enrutador Wildcard** | Enruta patrones `provider/*` a cualquier proveedor dinámicamente |
| 🧠 **Presupuesto de Razonamiento** | Modos passthrough, auto, custom y adaptativo para modelos de razonamiento |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Inyección de System Prompt** | System prompt global aplicado en todas las solicitudes |
| 📄 **API Responses** | Soporte completo de la API Responses de OpenAI (`/v1/responses`) para Codex |
@@ -588,15 +590,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ Resiliencia y Seguridad
| Característica | Qué Hace |
| ---------------------------------- | ---------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-apertura/cierre por proveedor con umbrales configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + semáforo rate-limit para proveedores con API key |
| 🧠 **Caché Semántico** | Caché de dos niveles (firma + semántico) reduce costo y latencia |
| ⚡ **Idempotencia de Solicitud** | Ventana de dedup de 5s para solicitudes duplicadas |
| 🔒 **Spoofing de Fingerprint TLS** | Bypass de detección de bot vía TLS con wreq-js |
| 🌐 **Filtrado de IP** | Allowlist/blocklist para control de acceso a la API |
| 📊 **Rate Limits Editables** | RPM, gap mínimo y concurrencia máxima configurables |
| Característica | Qué Hace |
| ---------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-apertura/cierre por proveedor con umbrales configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + semáforo rate-limit para proveedores con API key |
| 🧠 **Caché Semántico** | Caché de dos niveles (firma + semántico) reduce costo y latencia |
| ⚡ **Idempotencia de Solicitud** | Ventana de dedup de 5s para solicitudes duplicadas |
| 🔒 **Spoofing de Fingerprint TLS** | Bypass de detección de bot vía TLS con wreq-js |
| 🌐 **Filtrado de IP** | Allowlist/blocklist para control de acceso a la API |
| 📊 **Rate Limits Editables** | RPM, gap mínimo y concurrencia máxima configurables |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 Observabilidad y Analytics
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Räätälöidyt mallit** | Lisää mikä tahansa mallitunnus mille tahansa toimittajalle |
| 🌐 **Wildcard-reititin** | Reititä `provider/*` mallit mille tahansa palveluntarjoajalle dynaamisesti |
| 🧠 **Ajatteleva budjetti** | Läpivienti-, automaatti-, mukautetut ja mukautuvat tilat päättelymalleille |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Järjestelmän pikaruiskutus** | Maailmanlaajuinen järjestelmäkehote käytössä kaikissa pyynnöissä |
| 📄 **Responses API** | Täysi OpenAI Responses API (`/v1/responses`) tuki Codexille |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS-sormenjälkien huijaus** | Ohita TLS-pohjainen bot-tunnistus wreq-js:n avulla |
| 🌐 **IP-suodatus** | API-käyttöoikeuksien hallinnan sallittu-/estoluettelo |
| 📊 **Muokattavat hintarajat** | Konfiguroitava kierrosluku, minimiväli ja suurin samanaikainen järjestelmätasolla |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Todennusportin + tarjoajan esto `/models`-päätepisteelle |
| 🔒 **Välityspalvelimen näkyvyys** | Värikoodatut merkit: 🟢 maailmanlaajuinen, 🟡 tarjoaja, 🔵 yhteyskohtainen IP-näytöllä |
| 🌐 **3-tason välityspalvelimen määritys** | Määritä välityspalvelimet maailmanlaajuisesti, palveluntarjoajakohtaisesti tai yhteyskohtaisesti |
+13 -9
View File
@@ -570,6 +570,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modèles personnalisés** | Ajoutez n'importe quel ID de modèle à n'importe quel fournisseur |
| 🌐 **Routeur wildcard** | Routez les patterns `provider/*` vers n'importe quel fournisseur dynamiquement |
| 🧠 **Budget de raisonnement** | Modes passthrough, auto, custom et adaptive pour les modèles de raisonnement |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Injection System Prompt** | System prompt global appliqué à toutes les requêtes |
| 📄 **API Responses** | Support complet de l'API Responses d'OpenAI (`/v1/responses`) pour Codex |
@@ -586,15 +588,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ Résilience & Sécurité
| Fonctionnalité | Ce qu'elle fait |
| ------------------------------- | -------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Ouverture/fermeture auto par fournisseur avec seuils configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + sémaphore de rate-limit pour les fournisseurs avec clé API |
| 🧠 **Cache sémantique** | Cache à deux niveaux (signature + sémantique) réduit coût et latence |
| ⚡ **Idempotence des requêtes** | Fenêtre de dédup 5s pour les requêtes dupliquées |
| 🔒 **Spoofing TLS Fingerprint** | Contournement de détection de bot via wreq-js |
| 🌐 **Filtrage IP** | Allowlist/blocklist pour le contrôle d'accès API |
| 📊 **Rate limits éditables** | RPM configurable, intervalle minimum, concurrence max |
| Fonctionnalité | Ce qu'elle fait |
| ------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Ouverture/fermeture auto par fournisseur avec seuils configurables |
| 🛡️ **Anti-Thundering Herd** | Mutex + sémaphore de rate-limit pour les fournisseurs avec clé API |
| 🧠 **Cache sémantique** | Cache à deux niveaux (signature + sémantique) réduit coût et latence |
| ⚡ **Idempotence des requêtes** | Fenêtre de dédup 5s pour les requêtes dupliquées |
| 🔒 **Spoofing TLS Fingerprint** | Contournement de détection de bot via wreq-js |
| 🌐 **Filtrage IP** | Allowlist/blocklist pour le contrôle d'accès API |
| 📊 **Rate limits éditables** | RPM configurable, intervalle minimum, concurrence max |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 Observabilité & Analytique
+29 -25
View File
@@ -557,19 +557,21 @@ npm run electron:build:linux # Linux (.AppImage)
### 🧠 ניתוב ליבה ומודיעין
| תכונה | מה זה עושה |
| ---------------------------- | -------------------------------------------------------------------------------- |
| 🎯 **Smart 4-tier Fallback** | מסלול אוטומטי: מנוי → מפתח API → זול → חינם |
| 📊 **מעקב מכסות בזמן אמת** | ספירת אסימונים חיה + איפוס ספירה לאחור לכל ספק |
| 🔄 **תרגום פורמט** | OpenAI ↔ קלוד ↔ תאומים ↔ סמן ↔ Kiro חלקה + חיטוי תגובה |
| 👥 **תמיכה בריבוי חשבונות** | מספר חשבונות לכל ספק עם בחירה חכמה |
| 🔄 **רענון אסימון אוטומטי** | אסימוני OAuth מתרעננים אוטומטית עם ניסיון חוזר |
| 🎨 **שילובים מותאמים אישית** | 6 אסטרטגיות: מילוי ראשון, עגול רובין, p2c, אקראי, פחות בשימוש, אופטימיזציה לעלות |
| 🧩 **דגמים מותאמים אישית** | הוסף כל מזהה דגם לכל ספק |
| 🌐 **נתב תווים כלליים** | נתב דפוסי `provider/*` לכל ספק באופן דינמי |
| 🧠 **תקציב חשיבה** | מצבי מעבר, אוטומטי, מותאמים אישית והתאמה למודלים של חשיבה |
| 💬 **הזרקת מערכת מיידית** | הודעת מערכת גלובלית הוחלה בכל הבקשות |
| 📄 **Responses API** | תמיכה מלאה של OpenAI Responses API (`/v1/responses`) עבור Codex |
| תכונה | מה זה עושה |
| ----------------------------- | -------------------------------------------------------------------------------- |
| 🎯 **Smart 4-tier Fallback** | מסלול אוטומטי: מנוי → מפתח API → זול → חינם |
| 📊 **מעקב מכסות בזמן אמת** | ספירת אסימונים חיה + איפוס ספירה לאחור לכל ספק |
| 🔄 **תרגום פורמט** | OpenAI ↔ קלוד ↔ תאומים ↔ סמן ↔ Kiro חלקה + חיטוי תגובה |
| 👥 **תמיכה בריבוי חשבונות** | מספר חשבונות לכל ספק עם בחירה חכמה |
| 🔄 **רענון אסימון אוטומטי** | אסימוני OAuth מתרעננים אוטומטית עם ניסיון חוזר |
| 🎨 **שילובים מותאמים אישית** | 6 אסטרטגיות: מילוי ראשון, עגול רובין, p2c, אקראי, פחות בשימוש, אופטימיזציה לעלות |
| 🧩 **דגמים מותאמים אישית** | הוסף כל מזהה דגם לכל ספק |
| 🌐 **נתב תווים כלליים** | נתב דפוסי `provider/*` לכל ספק באופן דינמי |
| 🧠 **תקציב חשיבה** | מצבי מעבר, אוטומטי, מותאמים אישית והתאמה למודלים של חשיבה |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **הזרקת מערכת מיידית** | הודעת מערכת גלובלית הוחלה בכל הבקשות |
| 📄 **Responses API** | תמיכה מלאה של OpenAI Responses API (`/v1/responses`) עבור Codex |
### 🎵 ממשקי API רב-מודאליים
@@ -584,18 +586,20 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ חוסן וביטחון
| תכונה | מה זה עושה |
| ------------------------------------ | -------------------------------------------------------------- |
| 🔌 **מפסק מעגלים** | פתיחה/סגירה אוטומטית לכל ספק עם ערכי סף הניתנים להגדרה |
| 🛡️ **עדר נגד רעמים** | Mutex + מגבלת קצב סמפור עבור ספקי מפתח API |
| 🧠 **מטמון סמנטי** | מטמון דו-שכבתי (חתימה + סמנטי) מפחית את העלות והשהייה |
| ⚡ **בקש אימפוטנציה** | חלון ביטול 5s עבור בקשות כפולות |
| 🔒 **זיוף טביעות אצבע TLS** | עוקף זיהוי בוט מבוסס TLS באמצעות wreq-js |
| 🌐 **סינון IP** | רשימת הרשאות/רשימת חסימות עבור בקרת גישה ל-API |
| 📊 **מגבלות תעריפים הניתנות לעריכה** | RPM ניתן להגדרה, פער מינימלי ומקסימום במקביל ברמת המערכת |
| 🛡 **הגנת נקודת קצה API** | Auth gating + חסימת ספק עבור נקודת הקצה `/models` |
| 🔒 **נראות פרוקסי** | תגים מקודדים בצבע: 🟢 גלובלי, 🟡 ספק, 🔵 לכל חיבור עם תצוגת IP |
| 🌐 **תצורת פרוקסי בשלוש רמות** | הגדר פרוקסי ברמה גלובלית, לכל ספק או לכל חיבור |
| תכונה | מה זה עושה |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| 🔌 **מפסק מעגלים** | פתיחה/סגירה אוטומטית לכל ספק עם ערכי סף הניתנים להגדרה |
| 🛡️ **עדר נגד רעמים** | Mutex + מגבלת קצב סמפור עבור ספקי מפתח API |
| 🧠 **מטמון סמנטי** | מטמון דו-שכבתי (חתימה + סמנטי) מפחית את העלות והשהייה |
| ⚡ **בקש אימפוטנציה** | חלון ביטול 5s עבור בקשות כפולות |
| 🔒 **זיוף טביעות אצבע TLS** | עוקף זיהוי בוט מבוסס TLS באמצעות wreq-js |
| 🌐 **סינון IP** | רשימת הרשאות/רשימת חסימות עבור בקרת גישה ל-API |
| 📊 **מגבלות תעריפים הניתנות לעריכה** | RPM ניתן להגדרה, פער מינימלי ומקסימום במקביל ברמת המערכת |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **הגנת נקודת קצה API** | Auth gating + חסימת ספק עבור נקודת הקצה `/models` |
| 🔒 **נראות פרוקסי** | תגים מקודדים בצבע: 🟢 גלובלי, 🟡 ספק, 🔵 לכל חיבור עם תצוגת IP |
| 🌐 **תצורת פרוקסי בשלוש רמות** | הגדר פרוקסי ברמה גלובלית, לכל ספק או לכל חיבור |
### 📊 יכולת תצפית וניתוח
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Egyedi modellek** | Adjon hozzá bármilyen modellazonosítót bármely szolgáltatóhoz |
| 🌐 **Wildcard Router** | `provider/*` minták továbbítása bármely szolgáltatóhoz dinamikusan |
| 🧠 **Átgondolt költségvetés** | Átjárási, automatikus, egyéni és adaptív módok érvelési modellekhez |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Rendszer azonnali befecskendezés** | Globális rendszerkérdés minden kérelemre érvényes |
| 📄 **Responses API** | Teljes OpenAI Responses API (`/v1/responses`) támogatás a Codexhez |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS ujjlenyomat-hamisítás** | A TLS-alapú botészlelés megkerülése a wreq-js segítségével |
| 🌐 **IP-szűrés** | Allowlist/blokkolista API hozzáférés-vezérléshez |
| 📊 **Szerkeszthető díjkorlátok** | Konfigurálható fordulatszám, minimális rés és maximális egyidejű rendszerszinten |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Auth kapuzás + szolgáltató blokkolása a `/models` végponthoz |
| 🔒 **Proxy láthatósága** | Színkódolt jelvények: 🟢 globális, 🟡 szolgáltató, 🔵 kapcsolatonként IP kijelzővel |
| 🌐 **3-szintű proxykonfiguráció** | Proxyk konfigurálása globális, szolgáltatónkénti vagy kapcsolatonkénti szinten |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Model Khusus** | Tambahkan ID model apa pun ke penyedia mana pun |
| 🌐 **Router Wildcard** | Rutekan pola `provider/*` ke penyedia mana pun secara dinamis |
| 🧠 **Memikirkan Anggaran** | Mode passthrough, otomatis, kustom, dan adaptif untuk model penalaran |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Injeksi Perintah Sistem** | Perintah sistem global diterapkan di semua permintaan |
| 📄 **API Respons** | Dukungan penuh OpenAI Responses API (`/v1/responses`) untuk Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **Spoofing Sidik Jari TLS** | Lewati deteksi bot berbasis TLS melalui wreq-js |
| 🌐 **Pemfilteran IP** | Daftar yang diizinkan/daftar blokir untuk kontrol akses API |
| 📊 **Batas Tarif yang Dapat Diedit** | RPM yang dapat dikonfigurasi, celah minimum, dan maks secara bersamaan pada tingkat sistem |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Perlindungan Titik Akhir API** | Gerbang autentikasi + pemblokiran penyedia untuk titik akhir `/models` |
| 🔒 **Visibilitas Proksi** | Lencana berkode warna: 🟢 global, 🟡 penyedia, 🔵 per koneksi dengan tampilan IP |
| 🌐 **Konfigurasi Proksi 3 Tingkat** | Konfigurasikan proxy di tingkat global, per penyedia, atau per koneksi |
+4
View File
@@ -465,6 +465,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **कस्टम मॉडल** | किसी भी प्रदाता से कोई भी मॉडल आईडी जोड़ें |
| 🌐 **वाइल्डकार्ड राउटर** | `provider/*` पैटर्न को गतिशील रूप से किसी भी प्रदाता तक रूट करें |
| 🧠 **सोच बजट** | तर्क मॉडल के लिए पासथ्रू, ऑटो, कस्टम और अनुकूली मोड |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **सिस्टम प्रॉम्प्ट इंजेक्शन** | ग्लोबल सिस्टम प्रॉम्प्ट सभी अनुरोधों पर लागू किया गया |
| 📄 **प्रतिक्रियाएं एपीआई** | कोडेक्स के लिए पूर्ण ओपनएआई रिस्पॉन्स एपीआई (`/v1/responses`) समर्थन |
@@ -490,6 +492,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **टीएलएस फ़िंगरप्रिंट स्पूफ़िंग** | Wreq-js के माध्यम से टीएलएस-आधारित बॉट डिटेक्शन को बायपास करें |
| 🌐 **आईपी फ़िल्टरिंग** | एपीआई अभिगम नियंत्रण के लिए अनुमति सूची/अवरुद्ध सूची |
| 📊 **संपादन योग्य दर सीमाएँ** | सिस्टम स्तर पर कॉन्फ़िगर करने योग्य आरपीएम, न्यूनतम अंतर और अधिकतम समवर्ती |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **एपीआई एंडपॉइंट सुरक्षा** | `/models` समापन बिंदु के लिए ऑथेंटिक गेटिंग + प्रदाता अवरोधन |
| 🔒 **प्रॉक्सी दृश्यता** | रंग-कोडित बैज: 🟢 वैश्विक, 🟡 प्रदाता, 🔵 आईपी डिस्प्ले के साथ प्रति-कनेक्शन |
| 🌐 **3-स्तरीय प्रॉक्सी कॉन्फ़िगरेशन** | वैश्विक, प्रति-प्रदाता, या प्रति-कनेक्शन स्तर पर प्रॉक्सी कॉन्फ़िगर करें |
+13 -9
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modelli personalizzati** | Aggiungi qualsiasi ID modello a qualsiasi provider |
| 🌐 **Router wildcard** | Instrada pattern `provider/*` verso qualsiasi provider dinamicamente |
| 🧠 **Budget di ragionamento** | Modalità passthrough, auto, custom e adaptive per modelli di ragionamento |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Iniezione System Prompt** | System prompt globale applicato a tutte le richieste |
| 📄 **API Responses** | Supporto completo per OpenAI Responses API (`/v1/responses`) per Codex |
@@ -584,15 +586,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ Resilienza & Sicurezza
| Funzionalità | Cosa Fa |
| ------------------------------- | -------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Apertura/chiusura auto per provider con soglie configurabili |
| 🛡️ **Anti-Thundering Herd** | Mutex + semaforo rate-limit per provider con API key |
| 🧠 **Cache semantica** | Cache a due livelli (firma + semantica) riduce costi e latenza |
| ⚡ **Idempotenza richieste** | Finestra dedup 5s per richieste duplicate |
| 🔒 **Spoofing TLS Fingerprint** | Bypass rilevamento bot tramite wreq-js |
| 🌐 **Filtro IP** | Allowlist/blocklist per controllo accesso API |
| 📊 **Rate limit modificabili** | RPM, gap minimo e concorrenza massima configurabili |
| Funzionalità | Cosa Fa |
| ------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Apertura/chiusura auto per provider con soglie configurabili |
| 🛡️ **Anti-Thundering Herd** | Mutex + semaforo rate-limit per provider con API key |
| 🧠 **Cache semantica** | Cache a due livelli (firma + semantica) riduce costi e latenza |
| ⚡ **Idempotenza richieste** | Finestra dedup 5s per richieste duplicate |
| 🔒 **Spoofing TLS Fingerprint** | Bypass rilevamento bot tramite wreq-js |
| 🌐 **Filtro IP** | Allowlist/blocklist per controllo accesso API |
| 📊 **Rate limit modificabili** | RPM, gap minimo e concorrenza massima configurabili |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 Osservabilità & Analytics
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **カスタムモデル** | 任意のモデル ID を任意のプロバイダーに追加する |
| 🌐 **ワイルドカードルーター** | `provider/*` パターンを任意のプロバイダーに動的にルーティングする |
| 🧠 **予算を考える** | 推論モデルのパススルー、自動、カスタム、および適応モード |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **システム プロンプト インジェクション** | すべてのリクエストに適用されるグローバル システム プロンプト |
| 📄 **レスポンス API** | Codex の OpenAI Response API (`/v1/responses`) の完全なサポート |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS 指紋スプーフィング** | wreq-js 経由で TLS ベースのボット検出をバイパスする |
| 🌐 **IP フィルタリング** | API アクセス制御の許可リスト/ブロックリスト |
| 📊 **編集可能なレート制限** | システム レベルで構成可能な RPM、最小ギャップ、最大同時実行 |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API エンドポイント保護** | `/models` エンドポイントの認証ゲート + プロバイダー ブロック |
| 🔒 **プロキシの可視性** | 色分けされたバッジ: 🟢 グローバル、🟡 プロバイダー、🔵 IP 表示による接続ごと |
| 🌐 **3 レベルのプロキシ構成** | グローバル、プロバイダーごと、または接続ごとのレベルでプロキシを構成する |
+29 -25
View File
@@ -557,19 +557,21 @@ npm run electron:build:linux # Linux (.AppImage)
### 🧠 코어 라우팅 및 인텔리전스
| 기능 | 그것이 하는 일 |
| --------------------------- | ------------------------------------------------------------------------- |
| 🎯 **스마트 4계층 폴백** | 자동 경로: 구독 → API 키 → 저렴한 → 무료 |
| 📊 **실시간 할당량 추적** | 라이브 토큰 수 + 공급자별 카운트다운 재설정 |
| 🔄 **형식 번역** | OpenAI ⇔ Claude ⇔ Gemini ⇔ Cursor ⇔ Kiro 심리스 + 반응위생 |
| 👥 **다중 계정 지원** | 지능적인 선택을 통해 공급자당 여러 계정 |
| 🔄 **자동 토큰 새로고침** | 재시도 시 OAuth 토큰이 자동으로 새로 고쳐집니다. |
| 🎨 **맞춤형 콤보** | 6가지 전략: 채우기 우선, 라운드 로빈, p2c, 무작위, 최소 사용, 비용 최적화 |
| 🧩 **맞춤형 모델** | 모든 공급자에 모델 ID 추가 |
| 🌐 **와일드카드 라우터** | `provider/*` 패턴을 모든 공급자에게 동적으로 라우팅 |
| 🧠 **예산 고려** | 추론 모델을 위한 패스스루, 자동, 사용자 정의 및 적응형 모드 |
| 💬 **시스템 프롬프트 삽입** | 모든 요청에 ​​전역 시스템 프롬프트 적용 |
| 📄 **응답 API** | Codex에 대한 전체 OpenAI 응답 API(`/v1/responses`) 지원 |
| 기능 | 그것이 하는 일 |
| ----------------------------- | ----------------------------------------------------------------------------- |
| 🎯 **스마트 4계층 폴백** | 자동 경로: 구독 → API 키 → 저렴한 → 무료 |
| 📊 **실시간 할당량 추적** | 라이브 토큰 수 + 공급자별 카운트다운 재설정 |
| 🔄 **형식 번역** | OpenAI ⇔ Claude ⇔ Gemini ⇔ Cursor ⇔ Kiro 심리스 + 반응위생 |
| 👥 **다중 계정 지원** | 지능적인 선택을 통해 공급자당 여러 계정 |
| 🔄 **자동 토큰 새로고침** | 재시도 시 OAuth 토큰이 자동으로 새로 고쳐집니다. |
| 🎨 **맞춤형 콤보** | 6가지 전략: 채우기 우선, 라운드 로빈, p2c, 무작위, 최소 사용, 비용 최적화 |
| 🧩 **맞춤형 모델** | 모든 공급자에 모델 ID 추가 |
| 🌐 **와일드카드 라우터** | `provider/*` 패턴을 모든 공급자에게 동적으로 라우팅 |
| 🧠 **예산 고려** | 추론 모델을 위한 패스스루, 자동, 사용자 정의 및 적응형 모드 |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **시스템 프롬프트 삽입** | 모든 요청에 ​​전역 시스템 프롬프트 적용 |
| 📄 **응답 API** | Codex에 대한 전체 OpenAI 응답 API(`/v1/responses`) 지원 |
### 🎵 다중 모드 API
@@ -584,18 +586,20 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ 복원력 및 보안
| 기능 | 그것이 하는 일 |
| ---------------------------- | -------------------------------------------------------------------------- |
| 🔌 **회로 차단기** | 구성 가능한 임계값을 사용하여 공급자별 자동 열기/닫기 |
| 🛡️ **천둥 방지 무리** | API 키 공급자에 대한 뮤텍스 + 세마포 속도 제한 |
| 🧠 **의미론적 캐시** | 2계층 캐시(서명 + 의미 체계)로 비용 및 대기 시간 감소 |
| ⚡ **멱등성 요청** | 중복 요청에 대한 5초 중복 제거 기간 |
| 🔒 **TLS 지문 스푸핑** | wreq-js를 통해 TLS 기반 봇 감지 우회 |
| 🌐 **IP 필터링** | API 액세스 제어를 위한 허용 목록/차단 목록 |
| 📊 **편집 가능한 속도 제한** | 시스템 수준에서 구성 가능한 RPM, 최소 간격 및 최대 동시 |
| 🛡 **API 엔드포인트 보호** | `/models` 엔드포인트에 대한 인증 게이팅 + 공급자 차단 |
| 🔒 **프록시 가시성** | 색상으로 구분된 배지: 🟢 글로벌, 🟡 공급자, 🔵 IP 디스플레이를 통한 연결별 |
| 🌐 **3단계 프록시 구성** | 글로벌, 공급자별 또는 연결별 수준에서 프록시 구성 |
| 기능 | 그것이 하는 일 |
| ------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **회로 차단기** | 구성 가능한 임계값을 사용하여 공급자별 자동 열기/닫기 |
| 🛡️ **천둥 방지 무리** | API 키 공급자에 대한 뮤텍스 + 세마포 속도 제한 |
| 🧠 **의미론적 캐시** | 2계층 캐시(서명 + 의미 체계)로 비용 및 대기 시간 감소 |
| ⚡ **멱등성 요청** | 중복 요청에 대한 5초 중복 제거 기간 |
| 🔒 **TLS 지문 스푸핑** | wreq-js를 통해 TLS 기반 봇 감지 우회 |
| 🌐 **IP 필터링** | API 액세스 제어를 위한 허용 목록/차단 목록 |
| 📊 **편집 가능한 속도 제한** | 시스템 수준에서 구성 가능한 RPM, 최소 간격 및 최대 동시 |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API 엔드포인트 보호** | `/models` 엔드포인트에 대한 인증 게이팅 + 공급자 차단 |
| 🔒 **프록시 가시성** | 색상으로 구분된 배지: 🟢 글로벌, 🟡 공급자, 🔵 IP 디스플레이를 통한 연결별 |
| 🌐 **3단계 프록시 구성** | 글로벌, 공급자별 또는 연결별 수준에서 프록시 구성 |
### 📊 관찰 가능성 및 분석
+46 -1
View File
@@ -32,7 +32,7 @@ _Connect any AI-powered IDE or CLI tool through OmniRoute — free API gateway f
<table>
<tr>
<td align="center" width="110">
<a href="https://github.com/cline/cline">
<a href="https://github.com/openclaw/openclaw">
<img src="./public/providers/openclaw.png" alt="OpenClaw" width="48"/><br/>
<b>OpenClaw</b>
</a><br/>
@@ -607,6 +607,8 @@ When minimized, OmniRoute lives in your system tray with quick actions:
| 🧩 **Custom Models** | Add any model ID to any provider |
| 🌐 **Wildcard Router** | Route `provider/*` patterns to any provider dynamically |
| 🧠 **Thinking Budget** | Passthrough, auto, custom, and adaptive modes for reasoning models |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Global system prompt applied across all requests |
| 📄 **Responses API** | Full OpenAI Responses API (`/v1/responses`) support for Codex |
@@ -634,6 +636,8 @@ When minimized, OmniRoute lives in your system tray with quick actions:
| 🔒 **TLS Fingerprint Spoofing** | Bypass TLS-based bot detection via wreq-js |
| 🌐 **IP Filtering** | Allowlist/blocklist for API access control |
| 📊 **Editable Rate Limits** | Configurable RPM, min gap, and max concurrent at system level |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Auth gating + provider blocking for the `/models` endpoint |
| 🔒 **Proxy Visibility** | Color-coded badges: 🟢 global, 🟡 provider, 🔵 per-connection with IP display |
| 🌐 **3-Level Proxy Config** | Configure proxies at global, per-provider, or per-connection level |
@@ -1091,6 +1095,47 @@ Settings → API Configuration:
Model: if/kimi-k2-thinking
```
### OpenCode
**Step 1:** Add OmniRoute as a custom provider:
```bash
opencode
/connect
# Select "Other" → Enter ID: "omniroute" → Enter your OmniRoute API key
```
**Step 2:** Create/edit `opencode.json` in your project root:
```json
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"omniroute": {
"npm": "@ai-sdk/openai-compatible",
"name": "OmniRoute",
"options": {
"baseURL": "http://localhost:20128/v1"
},
"models": {
"cc/claude-sonnet-4-20250514": { "name": "Claude Sonnet 4" },
"gg/gemini-2.5-pro": { "name": "Gemini 2.5 Pro" },
"if/kimi-k2-thinking": { "name": "Kimi K2 (Free)" }
}
}
}
}
```
**Step 3:** Select the model in OpenCode:
```bash
/models
# Select any OmniRoute model from the list
```
> **Tip:** Add any model available in your OmniRoute `/v1/models` endpoint to the `models` section. Use the format `provider/model-id` from your OmniRoute dashboard.
</details>
---
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Model Tersuai** | Tambahkan sebarang ID model pada mana-mana pembekal |
| 🌐 **Penghala Wildcard** | Halakan corak `provider/*` kepada mana-mana pembekal secara dinamik |
| 🧠 **Anggaran Berfikir** | Mod laluan, auto, tersuai dan adaptif untuk model penaakulan |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Suntikan** | Gesaan sistem global digunakan merentas semua permintaan |
| 📄 **API Respons** | Sokongan OpenAI Responses API (`/v1/responses`) penuh untuk Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | Pintas pengesanan bot berasaskan TLS melalui wreq-js |
| 🌐 **Penapisan IP** | Senarai kebenaran/senarai sekat untuk kawalan akses API |
| 📊 **Had Kadar Boleh Diedit** | RPM boleh dikonfigurasikan, jurang min dan serentak maksimum pada tahap sistem |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Perlindungan Titik Akhir API** | Gating pengesahan + penyekatan penyedia untuk titik akhir `/models` |
| 🔒 **Keterlihatan Proksi** | Lencana berkod warna: 🟢 global, 🟡 pembekal, 🔵 setiap sambungan dengan paparan IP |
| 🌐 **Konfigurasi Proksi 3 Tahap** | Konfigurasikan proksi pada peringkat global, setiap pembekal atau setiap sambungan |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Aangepaste modellen** | Voeg elke model-ID toe aan elke provider |
| 🌐 **Wildcard-router** | Stuur `provider/*` patronen dynamisch naar elke provider |
| 🧠 **Denkbudget** | Passthrough-, automatische, aangepaste en adaptieve modi voor redeneermodellen |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Systeempromptinjectie** | Algemene systeemprompt toegepast op alle verzoeken |
| 📄 **Reacties-API** | Volledige OpenAI Responses API (`/v1/responses`) ondersteuning voor Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS-vingerafdrukspoofing** | Omzeil TLS-gebaseerde botdetectie via wreq-js |
| 🌐 **IP-filtering** | Toelatingslijst/blokkeerlijst voor API-toegangscontrole |
| 📊 **Bewerkbare tarieflimieten** | Configureerbare RPM, minimale tussenruimte en maximale gelijktijdigheid op systeemniveau |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API-eindpuntbescherming** | Auth-gating + providerblokkering voor het `/models` eindpunt |
| 🔒 **Proxyzichtbaarheid** | Kleurgecodeerde badges: 🟢 wereldwijd, 🟡 provider, 🔵 per verbinding met IP-display |
| 🌐 **Proxyconfiguratie op 3 niveaus** | Configureer proxy's op globaal, per provider of per verbindingsniveau |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Egendefinerte modeller** | Legg til hvilken som helst modell-ID til en hvilken som helst leverandør |
| 🌐 **Wildcard-ruter** | Ruter `provider/*`-mønstre til enhver leverandør dynamisk |
| 🧠 **Tenkebudsjett** | Passthrough, auto, egendefinerte og adaptive moduser for resonnerende modeller |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Systemprompt-injeksjon** | Global systemforespørsel brukt på alle forespørsler |
| 📄 **Responses API** | Full støtte for OpenAI Responses API (`/v1/responses`) for Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS-fingeravtrykkspoofing** | Omgå TLS-basert botdeteksjon via wreq-js |
| 🌐 **IP-filtrering** | Tillatelsesliste/blokkeringsliste for API-tilgangskontroll |
| 📊 **Redigerbare satsgrenser** | Konfigurerbar RPM, min gap og maks samtidig på systemnivå |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API-endepunktbeskyttelse** | Auth-gate + leverandørblokkering for `/models`-endepunktet |
| 🔒 **Proxy-synlighet** | Fargekodede merker: 🟢 global, 🟡 leverandør, 🔵 per tilkobling med IP-skjerm |
| 🌐 **3-nivå proxy-konfigurasjon** | Konfigurer proxyer på globalt nivå, per leverandør eller per tilkoblingsnivå |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Mga Custom na Modelo** | Magdagdag ng anumang ID ng modelo sa anumang provider |
| 🌐 **Wildcard Router** | Iruta ang `provider/*` na mga pattern sa anumang provider nang dynamic na |
| 🧠 **Badyet sa Pag-iisip** | Passthrough, auto, custom, at adaptive mode para sa mga modelo ng pangangatwiran |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Inilapat ang global system prompt sa lahat ng kahilingan |
| 📄 **Responses API** | Buong OpenAI Responses API (`/v1/responses`) na suporta para sa Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | I-bypass ang TLS-based na bot detection sa pamamagitan ng wreq-js |
| 🌐 **Pag-filter ng IP** | Allowlist/blocklist para sa API access control |
| 📊 **Mga Nae-edit na Limitasyon sa Rate** | Configurable RPM, min gap, at max na kasabay sa antas ng system |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Proteksyon sa Endpoint ng API** | Auth gating + pagharang ng provider para sa `/models` endpoint |
| 🔒 **Proxy Visibility** | Mga color-coded na badge: 🟢 global, 🟡 provider, 🔵 per-connection na may IP display |
| 🌐 **3-Level Proxy Config** | I-configure ang mga proxy sa global, per-provider, o per-connection level |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modele niestandardowe** | Dodaj dowolny identyfikator modelu do dowolnego dostawcy |
| 🌐 **Router z dziką kartą** | Dynamicznie kieruj `provider/*` wzorce do dowolnego dostawcy |
| 🧠 **Myślący budżet** | Tryby przekazywania, automatyczne, niestandardowe i adaptacyjne dla modeli wnioskowania |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Wstrzyknięcie monitu systemowego** | Globalny monit systemowy stosowany do wszystkich żądań |
| 📄 **API odpowiedzi** | Pełna obsługa OpenAI Responses API (`/v1/responses`) dla Codexu |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **Podrabianie odcisków palców TLS** | Pomiń wykrywanie botów opartych na TLS poprzez wreq-js |
| 🌐 **Filtrowanie IP** | Lista dozwolonych/blokowanych dla kontroli dostępu API |
| 📊 **Edytowalne limity stawek** | Konfigurowalne obroty, minimalna przerwa i maksymalna równowaga na poziomie systemu |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Ochrona punktu końcowego API** | Bramkowanie uwierzytelniania + blokowanie dostawcy dla punktu końcowego `/models` |
| 🔒 **Widoczność proxy** | Oznaczone kolorami plakietki: 🟢 globalny, 🟡 dostawca, 🔵 na połączenie z wyświetlaczem IP |
| 🌐 **3-poziomowa konfiguracja serwera proxy** | Skonfiguruj serwery proxy na poziomie globalnym, dla dostawcy lub dla połączenia |
+29 -25
View File
@@ -574,19 +574,21 @@ Quando minimizado, o OmniRoute fica na bandeja do sistema com ações rápidas:
### 🧠 Roteamento e Inteligência
| Funcionalidade | O que Faz |
| ----------------------------------------- | ------------------------------------------------------------------------------- |
| 🎯 **Fallback Inteligente 4 Tiers** | Auto-roteamento: Assinatura → API Key → Barato → Gratuito |
| 📊 **Rastreamento de Cota em Tempo Real** | Contagem de tokens ao vivo + countdown de reset por provedor |
| 🔄 **Tradução de Formato** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro transparente |
| 👥 **Suporte Multi-Conta** | Múltiplas contas por provedor com seleção inteligente |
| 🔄 **Renovação Automática de Token** | Tokens OAuth renovam automaticamente com retry |
| 🎨 **Combos Personalizados** | 6 estratégias: fill-first, round-robin, p2c, random, least-used, cost-optimized |
| 🧩 **Modelos Personalizados** | Adicione qualquer ID de modelo a qualquer provedor |
| 🌐 **Roteador Wildcard** | Roteie padrões `provider/*` para qualquer provedor dinamicamente |
| 🧠 **Budget de Raciocínio** | Modos passthrough, auto, custom e adaptativo para modelos de raciocínio |
| 💬 **Injeção de System Prompt** | System prompt global aplicado em todas as requisições |
| 📄 **API Responses** | Suporte completo à API Responses da OpenAI (`/v1/responses`) para Codex |
| Funcionalidade | O que Faz |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| 🎯 **Fallback Inteligente 4 Tiers** | Auto-roteamento: Assinatura → API Key → Barato → Gratuito |
| 📊 **Rastreamento de Cota em Tempo Real** | Contagem de tokens ao vivo + countdown de reset por provedor |
| 🔄 **Tradução de Formato** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro transparente |
| 👥 **Suporte Multi-Conta** | Múltiplas contas por provedor com seleção inteligente |
| 🔄 **Renovação Automática de Token** | Tokens OAuth renovam automaticamente com retry |
| 🎨 **Combos Personalizados** | 6 estratégias: fill-first, round-robin, p2c, random, least-used, cost-optimized |
| 🧩 **Modelos Personalizados** | Adicione qualquer ID de modelo a qualquer provedor |
| 🌐 **Roteador Wildcard** | Roteie padrões `provider/*` para qualquer provedor dinamicamente |
| 🧠 **Budget de Raciocínio** | Modos passthrough, auto, custom e adaptativo para modelos de raciocínio |
| **Aliases de Modelo** | Redireciona IDs de modelos depreciados para substitutos atuais (built-in + custom) |
| **Degradação em Background** | Redireciona tarefas em background (títulos, resumos) para modelos mais baratos |
| 💬 **Injeção de System Prompt** | System prompt global aplicado em todas as requisições |
| 📄 **API Responses** | Suporte completo à API Responses da OpenAI (`/v1/responses`) para Codex |
### 🎵 APIs Multi-Modal
@@ -603,18 +605,20 @@ Quando minimizado, o OmniRoute fica na bandeja do sistema com ações rápidas:
### 🛡️ Resiliência e Segurança
| Funcionalidade | O que Faz |
| ---------------------------------- | --------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-abertura/fechamento por provedor com limites configuráveis |
| 🛡️ **Anti-Thundering Herd** | Mutex + semáforo rate-limit para provedores com API key |
| 🧠 **Cache Semântico** | Cache de duas camadas (assinatura + semântico) reduz custo e latência |
| ⚡ **Idempotência de Requisição** | Janela de dedup de 5s para requisições duplicadas |
| 🔒 **Spoofing de Fingerprint TLS** | Bypass de detecção de bot via TLS com wreq-js |
| 🌐 **Filtragem de IP** | Allowlist/blocklist para controle de acesso à API |
| 📊 **Rate Limits Editáveis** | RPM, gap mínimo e concorrência máxima configuráveis |
| 🛡 **Proteção de Endpoint API** | Gateway de Auth + bloqueio de provedores para o endpoint `/models` |
| 🔒 **Visibilidade de Proxy** | Badges coloridos: 🟢 global, 🟡 provedor, 🔵 por-conexão com exibição de IP |
| 🌐 **Proxy em 3 Níveis** | Configure proxies em nível global, por provedor ou por conexão |
| Funcionalidade | O que Faz |
| ----------------------------------- | ----------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Auto-abertura/fechamento por provedor com limites configuráveis |
| 🛡️ **Anti-Thundering Herd** | Mutex + semáforo rate-limit para provedores com API key |
| 🧠 **Cache Semântico** | Cache de duas camadas (assinatura + semântico) reduz custo e latência |
| ⚡ **Idempotência de Requisição** | Janela de dedup de 5s para requisições duplicadas |
| 🔒 **Spoofing de Fingerprint TLS** | Bypass de detecção de bot via TLS com wreq-js |
| 🌐 **Filtragem de IP** | Allowlist/blocklist para controle de acesso à API |
| 📊 **Rate Limits Editáveis** | RPM, gap mínimo e concorrência máxima configuráveis |
| 💾 **Persistência de Rate Limits** | Limites aprendidos persistem via SQLite com debounce de 60s + 24h de validade |
| 🔄 **Resiliência de Token Refresh** | Circuit breaker por provedor (5 falhas→30min) + timeout de 30s por tentativa |
| 🛡 **Proteção de Endpoint API** | Gateway de Auth + bloqueio de provedores para o endpoint `/models` |
| 🔒 **Visibilidade de Proxy** | Badges coloridos: 🟢 global, 🟡 provedor, 🔵 por-conexão com exibição de IP |
| 🌐 **Proxy em 3 Níveis** | Configure proxies em nível global, por provedor ou por conexão |
### 📊 Observabilidade e Analytics
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modelos Personalizados** | Adicione qualquer ID de modelo a qualquer provedor |
| 🌐 **Roteador curinga** | Rotear padrões `provider/*` para qualquer provedor dinamicamente |
| 🧠 **Pensando no Orçamento** | Modos de passagem, automático, personalizado e adaptativo para modelos de raciocínio |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Injeção imediata do sistema** | Prompt de sistema global aplicado em todas as solicitações |
| 📄 **API de respostas** | Suporte completo à API de respostas OpenAI (`/v1/responses`) para Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **Falsificação de impressão digital TLS** | Ignore a detecção de bot baseada em TLS via wreq-js |
| 🌐 **Filtragem de IP** | Lista de permissões/lista de bloqueio para controle de acesso à API |
| 📊 **Limites de taxas editáveis** | RPM configurável, intervalo mínimo e simultâneo máximo no nível do sistema |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Proteção de endpoint de API** | Autenticação + bloqueio de provedor para o endpoint `/models` |
| 🔒 **Visibilidade do proxy** | Crachás codificados por cores: 🟢 global, 🟡 provedor, 🔵 por conexão com display IP |
| 🌐 **Configuração de proxy de 3 níveis** | Configurar proxies em nível global, por provedor ou por conexão |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Modele personalizate** | Adăugați orice ID de model oricărui furnizor |
| 🌐 **Wildcard Router** | Dirijați dinamic modelele `provider/*` către orice furnizor |
| 🧠 **Buget de gândire** | Moduri de trecere, automat, personalizat și adaptiv pentru modelele de raționament |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Prompt de sistem global aplicat pentru toate solicitările |
| 📄 **Responses API** | Compatibilitate completă cu OpenAI Responses API (`/v1/responses`) pentru Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | Ocoliți detectarea botului bazată pe TLS prin wreq-js |
| 🌐 **Filtrare IP** | Lista permisă/lista blocată pentru controlul accesului API |
| 📊 **Limite de rată editabile** | RPM configurabil, interval minim și concurență maximă la nivel de sistem |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Protecție API Endpoint** | Autentificare + blocare furnizor pentru punctul final `/models` |
| 🔒 **Vizibilitatea proxy** | Ecusoane cu coduri de culoare: 🟢 global, 🟡 furnizor, 🔵 per conexiune cu afișaj IP |
| 🌐 **Configurare proxy pe 3 niveluri** | Configurați proxy-uri la nivel global, per furnizor sau per conexiune |
+13 -9
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Пользовательские модели** | Добавьте любой ID модели к любому провайдеру |
| 🌐 **Wildcard-маршрутизатор** | Маршрутизируйте паттерны `provider/*` к любому провайдеру динамически |
| 🧠 **Бюджет рассуждений** | Режимы passthrough, auto, custom и adaptive для моделей рассуждений |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Инъекция System Prompt** | Глобальный system prompt для всех запросов |
| 📄 **API Responses** | Полная поддержка OpenAI Responses API (`/v1/responses`) для Codex |
@@ -584,15 +586,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ Устойчивость и безопасность
| Функция | Что делает |
| -------------------------------- | -------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Авто-открытие/закрытие по провайдеру с настраиваемыми порогами |
| 🛡️ **Anti-Thundering Herd** | Mutex + семафор для API key провайдеров |
| 🧠 **Семантический кеш** | Двухуровневый кеш (сигнатура + семантика) снижает стоимость |
| ⚡ **Идемпотентность запросов** | 5с окно дедупликации для дублирующихся запросов |
| 🔒 **Спуфинг TLS Fingerprint** | Обход обнаружения ботов через wreq-js |
| 🌐 **Фильтрация IP** | Allowlist/blocklist для контроля доступа к API |
| 📊 **Настраиваемые Rate Limits** | Настраиваемые RPM, минимальный интервал, макс. конкуррентность |
| Функция | Что делает |
| -------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **Circuit Breaker** | Авто-открытие/закрытие по провайдеру с настраиваемыми порогами |
| 🛡️ **Anti-Thundering Herd** | Mutex + семафор для API key провайдеров |
| 🧠 **Семантический кеш** | Двухуровневый кеш (сигнатура + семантика) снижает стоимость |
| ⚡ **Идемпотентность запросов** | 5с окно дедупликации для дублирующихся запросов |
| 🔒 **Спуфинг TLS Fingerprint** | Обход обнаружения ботов через wreq-js |
| 🌐 **Фильтрация IP** | Allowlist/blocklist для контроля доступа к API |
| 📊 **Настраиваемые Rate Limits** | Настраиваемые RPM, минимальный интервал, макс. конкуррентность |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 Наблюдаемость и аналитика
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Vlastné modely** | Pridajte akékoľvek ID modelu k akémukoľvek poskytovateľovi |
| 🌐 **Wildcard Router** | Dynamicky smerujte vzory `provider/*` k akémukoľvek poskytovateľovi |
| 🧠 **Premýšľajúci rozpočet** | Priechodný, automatický, vlastný a adaptívny režim pre modely uvažovania |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Promptné vstrekovanie systému** | Globálna systémová výzva aplikovaná na všetky požiadavky |
| 📄 **Responses API** | Plná podpora OpenAI Responses API (`/v1/responses`) pre Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 ** Spoofing odtlačkov prstov TLS** | Obíďte detekciu botov na báze TLS cez wreq-js |
| 🌐 **Filtrovanie IP** | Zoznam povolených/blokovaných pre riadenie prístupu API |
| 📊 **Upraviteľné limity sadzieb** | Konfigurovateľné otáčky za minútu, minimálna medzera a maximálna súbežná rýchlosť na systémovej úrovni |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Auth gating + blokovanie poskytovateľa pre koncový bod `/models` |
| 🔒 **Viditeľnosť proxy** | Farebne rozlíšené odznaky: 🟢 globálne, 🟡 poskytovateľ, 🔵 na pripojenie s IP displejom |
| 🌐 **3-úrovňová konfigurácia proxy** | Nakonfigurujte proxy na globálnej úrovni, na úrovni jednotlivých poskytovateľov alebo na úrovni pripojenia |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Anpassade modeller** | Lägg till valfritt modell-ID till valfri leverantör |
| 🌐 **Wildcard-router** | Dirigera `provider/*`-mönster till valfri leverantör dynamiskt |
| 🧠 **Tänkande budget** | Genomgång, auto, anpassade och adaptiva lägen för resonerande modeller |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt Injection** | Global systemprompt tillämpas på alla förfrågningar |
| 📄 **Responses API** | Fullständigt stöd för OpenAI Responses API (`/v1/responses`) för Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **TLS Fingerprint Spoofing** | Förbi TLS-baserad botdetektering via wreq-js |
| 🌐 **IP-filtrering** | Tillåtelselista/blockeringslista för API-åtkomstkontroll |
| 📊 **Redigerbara hastighetsgränser** | Konfigurerbart RPM, min gap och max samtidiga på systemnivå |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Auth gating + leverantörsblockering för `/models` slutpunkt |
| 🔒 **Proxysynlighet** | Färgkodade märken: 🟢 global, 🟡 leverantör, 🔵 per anslutning med IP-display |
| 🌐 **Proxykonfiguration med 3 nivåer** | Konfigurera proxyservrar på global nivå, per leverantör eller per anslutningsnivå |
+16 -12
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **โมเดลที่กำหนดเอง** | เพิ่ม ID รุ่นใด ๆ ให้กับผู้ให้บริการ |
| 🌐 **เราเตอร์ตัวแทน** | กำหนดเส้นทางรูปแบบ `provider/*` ไปยังผู้ให้บริการใดๆ แบบไดนามิก |
| 🧠 **คิดงบประมาณ** | โหมดส่งผ่าน, อัตโนมัติ, กำหนดเอง และแบบปรับได้สำหรับโมเดลการให้เหตุผล |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💌 **ระบบพร้อมฉีด** | พร้อมท์ระบบสากลใช้กับคำขอทั้งหมด |
| 📄 **API ตอบกลับ** | รองรับ OpenAI Responses API เต็มรูปแบบ (`/v1/responses`) สำหรับ Codex |
@@ -584,18 +586,20 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ ความยืดหยุ่นและความปลอดภัย
| คุณสมบัติ | มันทำอะไร |
| --------------------------------- | ------------------------------------------------------------------------- |
| 🔌 **เซอร์กิตเบรกเกอร์** | เปิด/ปิดอัตโนมัติต่อผู้ให้บริการพร้อมเกณฑ์ที่กำหนดค่าได้ |
| 🛡️ **ฝูงต่อต้านฟ้าร้อง** | Mutex + ขีดจำกัดอัตราเซมาฟอร์สำหรับผู้ให้บริการคีย์ API |
| 🧠 **แคชความหมาย** | แคชสองชั้น (ลายเซ็น + ความหมาย) ช่วยลดต้นทุน & เวลาแฝง |
| ⚡ **ขอ Idempotency** | หน้าต่าง dedup 5s สำหรับคำขอซ้ำ |
| 🔒 **การปลอมแปลงลายนิ้วมือ TLS** | เลี่ยงการตรวจจับบอทที่ใช้ TLS ผ่าน wreq-js |
| 🌐 **การกรอง IP** | รายการที่อนุญาต/รายการบล็อกสำหรับการควบคุมการเข้าถึง API |
| 📊 **ขีดจำกัดอัตราที่แก้ไขได้** | RPM ที่กำหนดค่าได้ ช่องว่างขั้นต่ำ และสูงสุดพร้อมกันที่ระดับระบบ |
| 🛡 **การป้องกันปลายทาง API** | การตรวจสอบสิทธิ์ + การบล็อกผู้ให้บริการสำหรับปลายทาง `/models` |
| 🔒 **การมองเห็นพร็อกซี** | ป้ายรหัสสี: 🟢 ทั่วโลก 🟡 ผู้ให้บริการ 🔵 ต่อการเชื่อมต่อพร้อมจอแสดงผล IP |
| 🌐 **การกำหนดค่าพร็อกซี 3 ระดับ** | กำหนดค่าพร็อกซีในระดับโกลบอล ต่อผู้ให้บริการ หรือต่อการเชื่อมต่อ |
| คุณสมบัติ | มันทำอะไร |
| --------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **เซอร์กิตเบรกเกอร์** | เปิด/ปิดอัตโนมัติต่อผู้ให้บริการพร้อมเกณฑ์ที่กำหนดค่าได้ |
| 🛡️ **ฝูงต่อต้านฟ้าร้อง** | Mutex + ขีดจำกัดอัตราเซมาฟอร์สำหรับผู้ให้บริการคีย์ API |
| 🧠 **แคชความหมาย** | แคชสองชั้น (ลายเซ็น + ความหมาย) ช่วยลดต้นทุน & เวลาแฝง |
| ⚡ **ขอ Idempotency** | หน้าต่าง dedup 5s สำหรับคำขอซ้ำ |
| 🔒 **การปลอมแปลงลายนิ้วมือ TLS** | เลี่ยงการตรวจจับบอทที่ใช้ TLS ผ่าน wreq-js |
| 🌐 **การกรอง IP** | รายการที่อนุญาต/รายการบล็อกสำหรับการควบคุมการเข้าถึง API |
| 📊 **ขีดจำกัดอัตราที่แก้ไขได้** | RPM ที่กำหนดค่าได้ ช่องว่างขั้นต่ำ และสูงสุดพร้อมกันที่ระดับระบบ |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **การป้องกันปลายทาง API** | การตรวจสอบสิทธิ์ + การบล็อกผู้ให้บริการสำหรับปลายทาง `/models` |
| 🔒 **การมองเห็นพร็อกซี** | ป้ายรหัสสี: 🟢 ทั่วโลก 🟡 ผู้ให้บริการ 🔵 ต่อการเชื่อมต่อพร้อมจอแสดงผล IP |
| 🌐 **การกำหนดค่าพร็อกซี 3 ระดับ** | กำหนดค่าพร็อกซีในระดับโกลบอล ต่อผู้ให้บริการ หรือต่อการเชื่อมต่อ |
### 📊 ความสามารถในการสังเกตและการวิเคราะห์
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Користувацькі моделі** | Додайте будь-який ідентифікатор моделі до будь-якого постачальника |
| 🌐 **Wildcard Router** | Динамічно направляйте шаблони `provider/*` до будь-якого постачальника |
| 🧠 **Мислення про бюджет** | Наскрізний, автоматичний, настроюваний і адаптивний режими для моделей міркування |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Швидке впровадження системи** | Глобальне системне підказка застосовується до всіх запитів |
| 📄 **API відповідей** | Повна підтримка OpenAI Responses API (`/v1/responses`) для Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **Підробка відбитків пальців TLS** | Обійти виявлення ботів на основі TLS через wreq-js |
| 🌐 **IP-фільтрація** | Білий/чорний список для керування доступом API |
| 📊 **Редаговані ліміти ставок** | Конфігурація RPM, мінімальний проміжок і максимальна одночасність на рівні системи |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **API Endpoint Protection** | Аутентифікація + блокування постачальника для кінцевої точки `/models` |
| 🔒 **Видимість проксі** | Кольорові значки: 🟢 глобальний, 🟡 постачальник, 🔵 кожне підключення з відображенням IP |
| 🌐 **3-рівнева конфігурація проксі** | Налаштуйте проксі-сервери на глобальному рівні, на рівні кожного постачальника чи кожного підключення |
+4
View File
@@ -568,6 +568,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🧩 **Mẫu tùy chỉnh** | Thêm bất kỳ ID mẫu nào vào bất kỳ nhà cung cấp nào |
| 🌐 **Bộ định tuyến ký tự đại diện** | Định tuyến động các mẫu `provider/*` tới bất kỳ nhà cung cấp nào |
| 🧠 **Ngân sách suy nghĩ** | Các chế độ truyền qua, tự động, tùy chỉnh và thích ứng cho các mô hình lý luận |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| ⚡ **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **Tiêm nhắc nhở hệ thống** | Lời nhắc hệ thống toàn cầu được áp dụng cho tất cả các yêu cầu |
| 📄 **API phản hồi** | Hỗ trợ đầy đủ API phản hồi OpenAI (`/v1/responses`) cho Codex |
@@ -593,6 +595,8 @@ npm run electron:build:linux # Linux (.AppImage)
| 🔒 **Giả mạo vân tay TLS** | Bỏ qua việc phát hiện bot dựa trên TLS thông qua wreq-js |
| 🌐 **Lọc IP** | Danh sách cho phép/danh sách chặn để kiểm soát truy cập API |
| 📊 **Giới hạn tỷ lệ có thể chỉnh sửa** | RPM có thể định cấu hình, khoảng cách tối thiểu và đồng thời tối đa ở cấp hệ thống |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
| 🛡 **Bảo vệ điểm cuối API** | Kiểm soát xác thực + chặn nhà cung cấp cho điểm cuối `/models` |
| 🔒 **Khả năng hiển thị proxy** | Huy hiệu được mã hóa màu: 🟢 toàn cầu, 🟡 nhà cung cấp, 🔵 mỗi kết nối với màn hình IP |
| 🌐 **Cấu hình proxy 3 cấp** | Định cấu hình proxy ở cấp độ toàn cầu, theo nhà cung cấp hoặc theo từng kết nối |
+26 -22
View File
@@ -557,19 +557,21 @@ npm run electron:build:linux # Linux (.AppImage)
### 🧠 路由与智能
| 功能 | 功能描述 |
| ------------------------- | -------------------------------------------------------------------------- |
| 🎯 **智能 4 层故障转移** | 自动路由:订阅 → API Key → 低价 → 免费 |
| 📊 **实时配额追踪** | 实时 Token 计数 + 每个提供商的重置倒计时 |
| 🔄 **格式转换** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro 无缝切换 |
| 👥 **多账号支持** | 每个提供商多个账号,智能选择 |
| 🔄 **自动令牌刷新** | OAuth 令牌自动刷新并重试 |
| 🎨 **自定义组合** | 6 种策略:fill-first、round-robin、p2c、random、least-used、cost-optimized |
| 🧩 **自定义模型** | 为任何提供商添加任何模型 ID |
| 🌐 **通配符路由** | 动态路由 `provider/*` 模式到任何提供商 |
| 🧠 **推理预算** | passthrough、auto、custom 和 adaptive 模式用于推理模型 |
| 💬 **System Prompt 注入** | 全局 System Prompt 应用于所有请求 |
| 📄 **Responses API** | 完整支持 OpenAI Responses API (`/v1/responses`) 用于 Codex |
| 功能 | 功能描述 |
| ----------------------------- | ----------------------------------------------------------------------------- |
| 🎯 **智能 4 层故障转移** | 自动路由:订阅 → API Key → 低价 → 免费 |
| 📊 **实时配额追踪** | 实时 Token 计数 + 每个提供商的重置倒计时 |
| 🔄 **格式转换** | OpenAI ↔ Claude ↔ Gemini ↔ Cursor ↔ Kiro 无缝切换 |
| 👥 **多账号支持** | 每个提供商多个账号,智能选择 |
| 🔄 **自动令牌刷新** | OAuth 令牌自动刷新并重试 |
| 🎨 **自定义组合** | 6 种策略:fill-first、round-robin、p2c、random、least-used、cost-optimized |
| 🧩 **自定义模型** | 为任何提供商添加任何模型 ID |
| 🌐 **通配符路由** | 动态路由 `provider/*` 模式到任何提供商 |
| 🧠 **推理预算** | passthrough、auto、custom 和 adaptive 模式用于推理模型 |
| 🔀 **Model Aliases** | Auto-forward deprecated model IDs to current replacements (built-in + custom) |
| **Background Degradation** | Auto-route background tasks (titles, summaries) to cheaper models |
| 💬 **System Prompt 注入** | 全局 System Prompt 应用于所有请求 |
| 📄 **Responses API** | 完整支持 OpenAI Responses API (`/v1/responses`) 用于 Codex |
### 🎵 多模态 API
@@ -584,15 +586,17 @@ npm run electron:build:linux # Linux (.AppImage)
### 🛡️ 弹性与安全
| 功能 | 功能描述 |
| --------------------- | -------------------------------------- |
| 🔌 **断路器** | 每个提供商自动打开/关闭,可配置阈值 |
| 🛡️ **反惊群** | Mutex + 信号量限速用于 API Key 提供商 |
| 🧠 **语义缓存** | 两层缓存(签名 + 语义)降低成本和延迟 |
| ⚡ **请求幂等性** | 5 秒去重窗口防止重复请求 |
| 🔒 **TLS 指纹伪装** | 通过 wreq-js 绕过基于 TLS 的机器人检测 |
| 🌐 **IP 过滤** | 白名单/黑名单用于 API 访问控制 |
| 📊 **可编辑速率限制** | 可配置的 RPM、最小间隔和最大并发 |
| 功能 | 功能描述 |
| ------------------------------- | ---------------------------------------------------------------------------- |
| 🔌 **断路器** | 每个提供商自动打开/关闭,可配置阈值 |
| 🛡️ **反惊群** | Mutex + 信号量限速用于 API Key 提供商 |
| 🧠 **语义缓存** | 两层缓存(签名 + 语义)降低成本和延迟 |
| ⚡ **请求幂等性** | 5 秒去重窗口防止重复请求 |
| 🔒 **TLS 指纹伪装** | 通过 wreq-js 绕过基于 TLS 的机器人检测 |
| 🌐 **IP 过滤** | 白名单/黑名单用于 API 访问控制 |
| 📊 **可编辑速率限制** | 可配置的 RPM、最小间隔和最大并发 |
| 💾 **Rate Limit Persistence** | Learned limits survive restarts via SQLite with 60s debounce + 24h staleness |
| 🔄 **Token Refresh Resilience** | Per-provider circuit breaker (5 fails→30min) + 30s timeout per attempt |
### 📊 可观察性与分析
+1 -1
View File
@@ -48,7 +48,7 @@ Four modes for debugging API translations: **Playground** (format converter), **
## ⚙️ Settings
General settings, system storage, backup management (export/import database), appearance (dark/light mode), security (includes API endpoint protection and custom provider blocking), routing, resilience, and advanced configuration.
General settings, system storage, backup management (export/import database), appearance (dark/light mode), security (includes API endpoint protection and custom provider blocking), routing (model aliases, background task degradation), resilience (rate limit persistence), and advanced configuration.
![Settings Dashboard](screenshots/06-settings.png)
+1 -1
View File
@@ -16,7 +16,7 @@ Administrer AI-udbyderforbindelser: OAuth-udbydere (Claude Code, Codex, Gemini C
## 🎨 Kombinationer
Opret modelrouting-kombinationer med 6 strategier: Fyld-først, round-robin, power-of-to-choices, tilfældig, mindst brugt og omkostningsoptimeret. Hver combo kæder flere modeller med automatisk fallback.
Opret modelrouting (model aliases, background task degradation)-kombinationer med 6 strategier: Fyld-først, round-robin, power-of-to-choices, tilfældig, mindst brugt og omkostningsoptimeret. Hver combo kæder flere modeller med automatisk fallback.
![Combos Dashboard](screenshots/02-combos.png)
+1 -1
View File
@@ -16,7 +16,7 @@ Gestisci le connessioni dei provider AI: provider OAuth (Claude Code, Codex, Gem
## 🎨Combo
Crea combinazioni di routing del modello con 6 strategie: riempimento prima, round robin, scelta potenza di due, casuale, meno utilizzata e con ottimizzazione dei costi. Ogni combo concatena più modelli con fallback automatico.
Crea combinazioni di routing (model aliases, background task degradation) del modello con 6 strategie: riempimento prima, round robin, scelta potenza di due, casuale, meno utilizzata e con ottimizzazione dei costi. Ogni combo concatena più modelli con fallback automatico.
![Combos Dashboard](screenshots/02-combos.png)
+1 -1
View File
@@ -48,7 +48,7 @@ Vier modi voor het debuggen van API-vertalingen: **Playground** (formaatconverte
## ⚙️ Instellingen
Algemene instellingen, systeemopslag, back-upbeheer (database exporteren/importeren), uiterlijk (donker/licht-modus), beveiliging (inclusief API-eindpuntbescherming en aangepaste providerblokkering), routing, veerkracht en geavanceerde configuratie.
Algemene instellingen, systeemopslag, back-upbeheer (database exporteren/importeren), uiterlijk (donker/licht-modus), beveiliging (inclusief API-eindpuntbescherming en aangepaste providerblokkering), routing (model aliases, background task degradation), veerkracht en geavanceerde configuratie.
![Settings Dashboard](screenshots/06-settings.png)
+1 -1
View File
@@ -48,7 +48,7 @@ Apat na mode para sa pag-debug ng mga pagsasalin ng API: **Playground** (format
## ⚙️ Mga Setting
Mga pangkalahatang setting, system storage, backup management (export/import database), hitsura (dark/light mode), seguridad (kasama ang API endpoint protection at custom provider blocking), routing, resilience, at advanced configuration.
Mga pangkalahatang setting, system storage, backup management (export/import database), hitsura (dark/light mode), seguridad (kasama ang API endpoint protection at custom provider blocking), routing (model aliases, background task degradation), resilience, at advanced configuration.
![Settings Dashboard](screenshots/06-settings.png)
+1 -1
View File
@@ -16,7 +16,7 @@ Zarządzaj połączeniami dostawców AI: dostawcy OAuth (Claude Code, Codex, Gem
## 🎨 Kombinacje
Twórz kombinacje routingu modeli za pomocą 6 strategii: najpierw wypełnij, okrężnie, siła dwóch wyborów, losowa, najrzadziej używana i zoptymalizowana pod względem kosztów. Każda kombinacja łączy wiele modeli z automatycznym cofaniem.
Twórz kombinacje routing (model aliases, background task degradation)u modeli za pomocą 6 strategii: najpierw wypełnij, okrężnie, siła dwóch wyborów, losowa, najrzadziej używana i zoptymalizowana pod względem kosztów. Każda kombinacja łączy wiele modeli z automatycznym cofaniem.
![Combos Dashboard](screenshots/02-combos.png)
+1 -1
View File
@@ -16,7 +16,7 @@ Hantera AI-leverantörsanslutningar: OAuth-leverantörer (Claude Code, Codex, Ge
## 🎨 Combos
Skapa modell routing-kombinationer med 6 strategier: fyll först, round-robin, kraft-av-två-val, slumpmässig, minst använda och kostnadsoptimerad. Varje combo kedjer flera modeller med automatisk reserv.
Skapa modell routing (model aliases, background task degradation)-kombinationer med 6 strategier: fyll först, round-robin, kraft-av-två-val, slumpmässig, minst använda och kostnadsoptimerad. Varje combo kedjer flera modeller med automatisk reserv.
![Combos Dashboard](screenshots/02-combos.png)
+10 -4
View File
@@ -111,7 +111,10 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientSecretDefault: "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",
},
models: [
{ id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" },
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
{ id: "gemini-3.1-flash", name: "Gemini 3.1 Flash" },
{ id: "gemini-3-pro-preview", name: "Gemini 3.0 Pro Preview" },
{ id: "gemini-3-flash-preview", name: "Gemini 3.0 Flash Preview" },
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" },
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" },
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite" },
@@ -137,8 +140,10 @@ export const REGISTRY: Record<string, RegistryEntry> = {
clientSecretDefault: "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",
},
models: [
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash Preview" },
{ id: "gemini-3-pro-preview", name: "Gemini 3 Pro Preview" },
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
{ id: "gemini-3.1-flash", name: "Gemini 3.1 Flash" },
{ id: "gemini-3-flash-preview", name: "Gemini 3.0 Flash Preview" },
{ id: "gemini-3-pro-preview", name: "Gemini 3.0 Pro Preview" },
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro" },
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" },
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite" },
@@ -268,7 +273,8 @@ export const REGISTRY: Record<string, RegistryEntry> = {
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
{ id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro High" },
{ id: "gemini-3.1-pro-low", name: "Gemini 3.1 Pro Low" },
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
{ id: "gemini-3.1-flash", name: "Gemini 3.1 Flash" },
{ id: "gemini-3-flash", name: "Gemini 3.0 Flash" },
{ id: "gpt-oss-120b-medium", name: "GPT OSS 120B Medium" },
],
},
+19 -7
View File
@@ -24,14 +24,25 @@ export function getProviderProfile(provider) {
// In-memory map: "provider:connectionId:model" → { reason, until, lockedAt }
const modelLockouts = new Map();
// Auto-cleanup expired lockouts every 15 seconds
const _cleanupTimer = setInterval(() => {
const now = Date.now();
for (const [key, entry] of modelLockouts) {
if (now > entry.until) modelLockouts.delete(key);
// Auto-cleanup expired lockouts every 15 seconds (lazy init for Cloudflare Workers compatibility)
let _cleanupTimer: ReturnType<typeof setInterval> | null = null;
function ensureCleanupTimer() {
if (_cleanupTimer) return;
try {
_cleanupTimer = setInterval(() => {
const now = Date.now();
for (const [key, entry] of modelLockouts) {
if (now > entry.until) modelLockouts.delete(key);
}
}, 15_000);
if (typeof _cleanupTimer === "object" && "unref" in _cleanupTimer) {
(_cleanupTimer as any).unref(); // Don't prevent process exit (Node.js only)
}
} catch {
// Cloudflare Workers may not support setInterval outside handlers — skip cleanup timer
}
}, 15_000);
_cleanupTimer.unref(); // Don't prevent process exit
}
/**
* Lock a specific model on a specific account
@@ -43,6 +54,7 @@ _cleanupTimer.unref(); // Don't prevent process exit
*/
export function lockModel(provider, connectionId, model, reason, cooldownMs) {
if (!model) return; // No model → skip model-level locking
ensureCleanupTimer();
const key = `${provider}:${connectionId}:${model}`;
modelLockouts.set(key, {
reason,
+187
View File
@@ -0,0 +1,187 @@
/**
* Background Task Detector Feature 3
*
* Detects when CLI tools send "background" requests (title generation,
* summarization, short descriptions) and provides model degradation
* recommendations to save premium model quota.
*
* Detection heuristics:
* - System prompt patterns indicating background/utility tasks
* - Very short conversations with summary-like system prompts
* - X-Request-Priority header
*/
// ── Configuration ───────────────────────────────────────────────────────────
interface DegradationConfig {
enabled: boolean;
degradationMap: Record<string, string>; // original → cheaper model
detectionPatterns: string[]; // regex patterns for system prompt matching
stats: {
detected: number;
tokensSaved: number;
};
}
const DEFAULT_DETECTION_PATTERNS = [
"generate a title",
"generate title",
"create a title",
"create a short",
"summarize this",
"summarize the",
"write a brief",
"write a summary",
"one-line summary",
"one line summary",
"short description",
"brief description",
"conversation title",
"chat title",
"name this conversation",
"name this chat",
"title for this",
"suggest a title",
"label this",
];
const DEFAULT_DEGRADATION_MAP: Record<string, string> = {
// Premium → Cheap alternatives
"claude-opus-4-6": "gemini-2.5-flash",
"claude-opus-4-6-thinking": "gemini-2.5-flash",
"claude-opus-4-5-20251101": "gemini-2.5-flash",
"claude-sonnet-4-5-20250929": "gemini-2.5-flash",
"claude-sonnet-4-20250514": "gemini-2.5-flash",
"claude-sonnet-4": "gemini-2.5-flash",
"gemini-3.1-pro": "gemini-3.1-flash",
"gemini-3.1-pro-high": "gemini-3.1-flash",
"gemini-3-pro-preview": "gemini-3-flash-preview",
"gemini-2.5-pro": "gemini-2.5-flash",
"gpt-4o": "gpt-4o-mini",
"gpt-5": "gpt-5-mini",
"gpt-5.1": "gpt-5-mini",
"gpt-5.1-codex": "gpt-5.1-codex-mini",
};
// ── State ───────────────────────────────────────────────────────────────────
let _config: DegradationConfig = {
enabled: false, // Disabled by default — user must opt in
degradationMap: { ...DEFAULT_DEGRADATION_MAP },
detectionPatterns: [...DEFAULT_DETECTION_PATTERNS],
stats: { detected: 0, tokensSaved: 0 },
};
// ── Config Management ───────────────────────────────────────────────────────
/**
* Set the background degradation config (called from settings API or startup).
*/
export function setBackgroundDegradationConfig(config: Partial<DegradationConfig>): void {
_config = {
..._config,
...config,
stats: _config.stats, // preserve stats across config changes
};
}
/**
* Get current background degradation config.
*/
export function getBackgroundDegradationConfig(): DegradationConfig {
return {
..._config,
degradationMap: { ..._config.degradationMap },
detectionPatterns: [..._config.detectionPatterns],
stats: { ..._config.stats },
};
}
/**
* Reset stats counters.
*/
export function resetStats(): void {
_config.stats = { detected: 0, tokensSaved: 0 };
}
// ── Detection ───────────────────────────────────────────────────────────────
/**
* Check if a request is a background/utility task.
*
* @param {object} body - Request body
* @param {object} [headers] - Request headers (optional)
* @returns {boolean} True if the request looks like a background task
*/
export function isBackgroundTask(
body: any,
headers: Record<string, string> | null = null
): boolean {
if (!body || typeof body !== "object") return false;
// 1. Check explicit header
if (headers) {
const priority =
headers["x-request-priority"] || headers["X-Request-Priority"] || headers["x-initiator"];
if (priority === "background" || priority === "Background") return true;
}
// 2. Check system prompt for background task patterns
const messages = body.messages || body.input || [];
if (!Array.isArray(messages) || messages.length === 0) return false;
// Find system message
const systemMsg = messages.find((m: any) => m.role === "system" || m.role === "developer");
if (!systemMsg) return false;
const systemContent =
typeof systemMsg.content === "string" ? systemMsg.content.toLowerCase() : "";
if (!systemContent) return false;
// Check against detection patterns
const matched = _config.detectionPatterns.some((pattern) =>
systemContent.includes(pattern.toLowerCase())
);
if (!matched) return false;
// 3. Additional heuristic: background tasks typically have very few messages
// (system + 1-2 user messages)
const userMessages = messages.filter((m: any) => m.role === "user");
if (userMessages.length > 3) return false; // Too many turns for a background task
return true;
}
/**
* Get the degraded (cheaper) model for a given model.
*
* @param {string} originalModel - The original model ID
* @returns {string} The cheaper model or original if no mapping exists
*/
export function getDegradedModel(originalModel: string): string {
if (!originalModel) return originalModel;
const degraded = _config.degradationMap[originalModel];
if (degraded) {
_config.stats.detected++;
return degraded;
}
return originalModel;
}
/**
* Get default degradation map (for UI reset).
*/
export function getDefaultDegradationMap(): Record<string, string> {
return { ...DEFAULT_DEGRADATION_MAP };
}
/**
* Get default detection patterns (for UI reset).
*/
export function getDefaultDetectionPatterns(): string[] {
return [...DEFAULT_DETECTION_PATTERNS];
}
+125
View File
@@ -0,0 +1,125 @@
/**
* Model Deprecation Auto-Forward Feature 2
*
* Maps deprecated model IDs to their replacements so user configs
* don't break when providers rename or retire models.
*
* Supports both built-in aliases (static) and custom aliases (persisted via Settings API).
*/
// ── Built-in Deprecation Aliases ────────────────────────────────────────────
// These are known renames/retirements across providers.
// Format: deprecated ID → current ID
const BUILT_IN_ALIASES: Record<string, string> = {
// Gemini legacy → current
"gemini-pro": "gemini-2.5-pro",
"gemini-pro-vision": "gemini-2.5-pro",
"gemini-1.5-pro": "gemini-2.5-pro",
"gemini-1.5-flash": "gemini-2.5-flash",
"gemini-1.0-pro": "gemini-2.5-pro",
"gemini-2.0-flash": "gemini-2.5-flash",
// Claude legacy → current
"claude-3-opus-20240229": "claude-opus-4-20250514",
"claude-3-sonnet-20240229": "claude-sonnet-4-20250514",
"claude-3-haiku-20240307": "claude-3-5-sonnet-20241022",
"claude-3-5-sonnet-latest": "claude-sonnet-4-20250514",
"claude-3-5-haiku-latest": "claude-3-5-sonnet-20241022",
// OpenAI legacy → current
"gpt-4-turbo-preview": "gpt-4-turbo",
"gpt-4-0125-preview": "gpt-4-turbo",
"gpt-4-1106-preview": "gpt-4-turbo",
"gpt-3.5-turbo-0125": "gpt-3.5-turbo",
};
// ── Custom Aliases (persisted via Settings API) ─────────────────────────────
let _customAliases: Record<string, string> = {};
/**
* Set custom aliases (called from settings API or startup).
*/
export function setCustomAliases(aliases: Record<string, string>): void {
_customAliases = { ...aliases };
}
/**
* Get current custom aliases.
*/
export function getCustomAliases(): Record<string, string> {
return { ..._customAliases };
}
/**
* Get the full alias map (built-in + custom).
* Custom aliases take precedence over built-in.
*/
export function getAllAliases(): Record<string, string> {
return { ...BUILT_IN_ALIASES, ..._customAliases };
}
/**
* Resolve a model alias to its current ID.
* Custom aliases override built-in ones.
*
* @param {string} modelId - The model ID to resolve
* @returns {string} The resolved model ID, or the original if not deprecated
*/
export function resolveModelAlias(modelId: string): string {
if (!modelId) return modelId;
// Check custom aliases first (higher priority)
if (_customAliases[modelId]) return _customAliases[modelId];
// Then check built-in
if (BUILT_IN_ALIASES[modelId]) return BUILT_IN_ALIASES[modelId];
return modelId;
}
/**
* Get a deprecation notice if the model is deprecated.
*
* @param {string} modelId - The model ID to check
* @returns {string | null} Deprecation message or null if not deprecated
*/
export function getDeprecationNotice(modelId: string): string | null {
if (!modelId) return null;
const resolved = resolveModelAlias(modelId);
if (resolved === modelId) return null;
return `Model "${modelId}" is deprecated. Forwarding to "${resolved}".`;
}
/**
* Check if a model is deprecated.
*/
export function isDeprecated(modelId: string): boolean {
return getDeprecationNotice(modelId) !== null;
}
/**
* Add a custom alias.
*/
export function addCustomAlias(from: string, to: string): void {
_customAliases[from] = to;
}
/**
* Remove a custom alias.
*/
export function removeCustomAlias(from: string): boolean {
if (_customAliases[from]) {
delete _customAliases[from];
return true;
}
return false;
}
/**
* Get the built-in aliases (read-only reference).
*/
export function getBuiltInAliases(): Record<string, string> {
return { ...BUILT_IN_ALIASES };
}
+87
View File
@@ -19,6 +19,11 @@ const limiters = new Map();
// Store connections that have rate limit protection enabled
const enabledConnections = new Set();
// Store learned limits for persistence (debounced)
const learnedLimits: Record<string, any> = {};
let persistTimer: ReturnType<typeof setTimeout> | null = null;
const PERSIST_DEBOUNCE_MS = 60_000; // Debounce persistence to every 60s max
// Track initialization
let initialized = false;
@@ -82,6 +87,9 @@ export async function initializeRateLimits() {
`🛡️ [RATE-LIMIT] Loaded ${explicitCount} explicit + ${autoCount} auto-enabled (API key) protection(s)`
);
}
// Load persisted learned limits
await loadPersistedLimits();
} catch (err) {
console.error("[RATE-LIMIT] Failed to load settings:", err.message);
}
@@ -314,6 +322,9 @@ export function updateFromHeaders(provider, connectionId, headers, status, model
}
limiter.updateSettings(updates);
// Persist learned limits (debounced)
recordLearnedLimit(provider, connectionId, { limit, remaining, minTime: updates.minTime });
}
}
@@ -360,6 +371,82 @@ export function getAllRateLimitStatus() {
return result;
}
/**
* Get all learned limits (for dashboard display).
*/
export function getLearnedLimits() {
return { ...learnedLimits };
}
// ─── Persistence ────────────────────────────────────────────────────────────
/**
* Record a learned limit for debounced persistence.
*/
function recordLearnedLimit(provider: string, connectionId: string, limits: any) {
const key = `${provider}:${connectionId}`;
learnedLimits[key] = {
...limits,
provider,
connectionId,
lastUpdated: Date.now(),
};
// Debounce: save at most once per PERSIST_DEBOUNCE_MS
if (!persistTimer) {
persistTimer = setTimeout(async () => {
persistTimer = null;
try {
const { updateSettings } = await import("@/lib/db/settings");
await updateSettings({ learnedRateLimits: JSON.stringify(learnedLimits) });
console.log(
`💾 [RATE-LIMIT] Persisted learned limits for ${Object.keys(learnedLimits).length} provider(s)`
);
} catch (err) {
console.error("[RATE-LIMIT] Failed to persist learned limits:", err.message);
}
}, PERSIST_DEBOUNCE_MS);
}
}
/**
* Load persisted learned limits on startup.
*/
async function loadPersistedLimits() {
try {
const { getSettings } = await import("@/lib/db/settings");
const settings = await getSettings();
const raw = settings?.learnedRateLimits;
if (!raw) return;
const parsed = JSON.parse(raw);
let count = 0;
for (const [key, data] of Object.entries<any>(parsed)) {
// Skip stale entries (older than 24h)
if (data.lastUpdated && Date.now() - data.lastUpdated > 24 * 60 * 60 * 1000) continue;
learnedLimits[key] = data;
// Apply to limiter if it exists and has rate limit enabled
if (data.connectionId && enabledConnections.has(data.connectionId)) {
const limiter = limiters.get(key);
if (limiter && data.limit) {
const minTime = data.minTime || Math.max(0, Math.floor(60000 / data.limit) - 10);
limiter.updateSettings({ minTime });
count++;
}
}
}
if (count > 0) {
console.log(`📥 [RATE-LIMIT] Restored ${count} learned rate limit(s) from persistence`);
}
} catch (err) {
console.error("[RATE-LIMIT] Failed to load persisted limits:", err.message);
}
}
/**
* Update rate limiter based on API response body (JSON error responses).
* Providers embed retry info in JSON payloads in different formats.
+97 -9
View File
@@ -21,6 +21,15 @@ export const EFFORT_BUDGETS = {
high: 131072,
};
// thinkingLevel string → budget token mapping
// Used when clients send string-based thinking levels (e.g., VS Code Copilot)
export const THINKING_LEVEL_MAP = {
none: 0,
low: 1024,
medium: 10240,
high: 131072,
};
// Default config (passthrough = backward compatible)
export const DEFAULT_THINKING_CONFIG = {
mode: ThinkingMode.PASSTHROUGH,
@@ -45,10 +54,81 @@ export function getThinkingBudgetConfig() {
return { ..._config };
}
/**
* Normalize thinkingLevel string fields into numeric budget.
* Handles: body.thinkingLevel, body.thinking_level,
* and Gemini's generationConfig.thinkingConfig.thinkingLevel
*
* @param {object} body - Request body
* @returns {object} Body with string thinkingLevel converted to numeric budget
*/
export function normalizeThinkingLevel(body) {
if (!body || typeof body !== "object") return body;
const result = { ...body };
// Handle top-level thinkingLevel or thinking_level string fields
const levelStr = result.thinkingLevel || result.thinking_level;
if (typeof levelStr === "string" && THINKING_LEVEL_MAP[levelStr] !== undefined) {
const budget = THINKING_LEVEL_MAP[levelStr];
// Convert to Claude thinking format as canonical representation
result.thinking = {
type: budget > 0 ? "enabled" : "disabled",
budget_tokens: budget,
};
delete result.thinkingLevel;
delete result.thinking_level;
}
// Handle Gemini's generationConfig.thinkingConfig.thinkingLevel
const geminiLevel =
result.generationConfig?.thinkingConfig?.thinkingLevel ||
result.generationConfig?.thinking_config?.thinkingLevel;
if (typeof geminiLevel === "string" && THINKING_LEVEL_MAP[geminiLevel] !== undefined) {
const budget = THINKING_LEVEL_MAP[geminiLevel];
result.generationConfig = {
...result.generationConfig,
thinking_config: { thinking_budget: budget },
};
// Clean up camelCase variant if it was the source
if (result.generationConfig.thinkingConfig) {
delete result.generationConfig.thinkingConfig;
}
}
return result;
}
/**
* Ensure models with -thinking suffix have thinking config injected.
* Prevents 400 errors from Claude API when thinking params are missing.
*
* @param {object} body - Request body
* @returns {object} Body with thinking config auto-injected if needed
*/
export function ensureThinkingConfig(body) {
if (!body || typeof body !== "object") return body;
const model = body.model || "";
// Only auto-inject for models with -thinking suffix
if (!model.endsWith("-thinking")) return body;
// If thinking config already present, don't override
if (body.thinking) return body;
const result = { ...body };
result.thinking = {
type: "enabled",
budget_tokens: EFFORT_BUDGETS.medium, // 10240 default
};
return result;
}
/**
* Apply thinking budget control to a request body.
* Called before format-specific translation.
*
* Pipeline: normalizeThinkingLevel ensureThinkingConfig mode processing
*
* @param {object} body - Request body (any format)
* @param {object} [config] - Override config (defaults to stored config)
* @returns {object} Modified body
@@ -57,21 +137,27 @@ export function applyThinkingBudget(body, config = null) {
const cfg = config || _config;
if (!body || typeof body !== "object") return body;
// Pre-processing: convert string thinkingLevel to numeric budget
let processed = normalizeThinkingLevel(body);
// Pre-processing: auto-inject thinking config for -thinking suffix models
processed = ensureThinkingConfig(processed);
switch (cfg.mode) {
case ThinkingMode.AUTO:
return stripThinkingConfig(body);
return stripThinkingConfig(processed);
case ThinkingMode.PASSTHROUGH:
return body; // No changes
return processed;
case ThinkingMode.CUSTOM:
return setCustomBudget(body, cfg.customBudget);
return setCustomBudget(processed, cfg.customBudget);
case ThinkingMode.ADAPTIVE:
return applyAdaptiveBudget(body, cfg);
return applyAdaptiveBudget(processed, cfg);
default:
return body;
return processed;
}
}
@@ -151,9 +237,10 @@ function applyAdaptiveBudget(body, cfg) {
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
if (msg.role === "user") {
lastMsgLength = typeof msg.content === "string"
? msg.content.length
: JSON.stringify(msg.content || "").length;
lastMsgLength =
typeof msg.content === "string"
? msg.content.length
: JSON.stringify(msg.content || "").length;
break;
}
}
@@ -173,7 +260,7 @@ function applyAdaptiveBudget(body, cfg) {
/**
* Check if model name suggests thinking capability
*/
function hasThinkingCapableModel(body) {
export function hasThinkingCapableModel(body) {
const model = body.model || "";
return (
model.includes("claude") ||
@@ -181,6 +268,7 @@ function hasThinkingCapableModel(body) {
model.includes("o3") ||
model.includes("o4") ||
model.includes("gemini") ||
model.endsWith("-thinking") ||
model.includes("thinking")
);
}
+112 -9
View File
@@ -323,9 +323,13 @@ export async function refreshQwenToken(refreshToken, log) {
}
if (errorCode === "invalid_request") {
log?.error?.("TOKEN_REFRESH", "Qwen refresh token is invalid or expired. Re-authentication required.", {
status: response.status,
});
log?.error?.(
"TOKEN_REFRESH",
"Qwen refresh token is invalid or expired. Re-authentication required.",
{
status: response.status,
}
);
return { error: "invalid_request" };
}
@@ -720,8 +724,11 @@ export function supportsTokenRefresh(provider) {
* Callers should stop retrying and request re-authentication.
*/
export function isUnrecoverableRefreshError(result) {
return result && typeof result === "object" &&
(result.error === "refresh_token_reused" || result.error === "invalid_request");
return (
result &&
typeof result === "object" &&
(result.error === "refresh_token_reused" || result.error === "invalid_request")
);
}
/**
@@ -841,12 +848,103 @@ export async function getAllAccessTokens(userInfo, log) {
/**
* Refresh token with retry and exponential backoff
* Retries on failure with increasing delay: 1s, 2s, 3s...
*
* Includes:
* - Per-provider circuit breaker (5 consecutive failures 30min pause)
* - 30s timeout per refresh attempt to prevent hanging connections
*
* @param {function} refreshFn - Async function that returns token or null
* @param {number} maxRetries - Max retry attempts (default 3)
* @param {object} log - Logger instance (optional)
* @param {string} provider - Provider ID for circuit breaker tracking (optional)
* @returns {Promise<object|null>} Token result or null if all retries fail
*/
export async function refreshWithRetry(refreshFn, maxRetries = 3, log = null) {
// ─── Circuit Breaker State ──────────────────────────────────────────────────
const _circuitBreaker: Record<string, { failures: number; blockedUntil: number }> = {};
const CIRCUIT_BREAKER_THRESHOLD = 5; // consecutive failures before tripping
const CIRCUIT_BREAKER_COOLDOWN = 30 * 60 * 1000; // 30 minutes
const REFRESH_TIMEOUT_MS = 30_000; // 30s max per refresh attempt
/**
* Check if a provider is circuit-breaker blocked.
*/
export function isProviderBlocked(provider: string): boolean {
const state = _circuitBreaker[provider];
if (!state) return false;
if (state.blockedUntil > Date.now()) return true;
// Cooldown expired — reset
delete _circuitBreaker[provider];
return false;
}
/**
* Get circuit breaker status for all providers (for diagnostics).
*/
export function getCircuitBreakerStatus(): Record<string, any> {
const result: Record<string, any> = {};
for (const [provider, state] of Object.entries(_circuitBreaker)) {
result[provider] = {
failures: state.failures,
blocked: state.blockedUntil > Date.now(),
blockedUntil:
state.blockedUntil > Date.now() ? new Date(state.blockedUntil).toISOString() : null,
remainingMs: Math.max(0, state.blockedUntil - Date.now()),
};
}
return result;
}
/**
* Record a successful refresh resets circuit breaker for provider.
*/
function recordSuccess(provider: string) {
if (_circuitBreaker[provider]) {
delete _circuitBreaker[provider];
}
}
/**
* Record a failed refresh increments circuit breaker counter.
*/
function recordFailure(provider: string, log: any = null) {
if (!_circuitBreaker[provider]) {
_circuitBreaker[provider] = { failures: 0, blockedUntil: 0 };
}
_circuitBreaker[provider].failures++;
if (_circuitBreaker[provider].failures >= CIRCUIT_BREAKER_THRESHOLD) {
_circuitBreaker[provider].blockedUntil = Date.now() + CIRCUIT_BREAKER_COOLDOWN;
log?.error?.(
"TOKEN_REFRESH",
`🔴 Circuit breaker tripped for ${provider}: ${CIRCUIT_BREAKER_THRESHOLD} consecutive failures. ` +
`Blocked for ${CIRCUIT_BREAKER_COOLDOWN / 60000}min. Provider needs re-authentication.`
);
}
}
/**
* Execute a function with a timeout.
*/
async function withTimeout<T>(fn: () => Promise<T>, timeoutMs: number): Promise<T | null> {
return Promise.race([
fn(),
new Promise<null>((resolve) => setTimeout(() => resolve(null), timeoutMs)),
]);
}
export async function refreshWithRetry(
refreshFn,
maxRetries = 3,
log = null,
provider = "unknown"
) {
// Circuit breaker check
if (isProviderBlocked(provider)) {
log?.warn?.("TOKEN_REFRESH", `⚡ Circuit breaker active for ${provider}, skipping refresh`);
return null;
}
for (let attempt = 0; attempt < maxRetries; attempt++) {
if (attempt > 0) {
const delay = attempt * 1000;
@@ -855,13 +953,18 @@ export async function refreshWithRetry(refreshFn, maxRetries = 3, log = null) {
}
try {
const result = await refreshFn();
if (result) return result;
const result = await withTimeout(refreshFn, REFRESH_TIMEOUT_MS);
if (result) {
recordSuccess(provider);
return result;
}
} catch (error) {
log?.warn?.("TOKEN_REFRESH", `Attempt ${attempt + 1}/${maxRetries} failed: ${error.message}`);
}
}
log?.error?.("TOKEN_REFRESH", `All ${maxRetries} retry attempts failed`);
// All retries exhausted — record failure for circuit breaker
recordFailure(provider, log);
log?.error?.("TOKEN_REFRESH", `All ${maxRetries} retry attempts failed for ${provider}`);
return null;
}
@@ -180,7 +180,9 @@ function openaiToGeminiBase(model, body, stream) {
functionDeclarations.push({
name: t.name,
description: t.description || "",
parameters: t.input_schema || { type: "object", properties: {} },
parameters: cleanJSONSchemaForAntigravity(
t.input_schema || { type: "object", properties: {} }
),
});
}
// OpenAI format
@@ -189,7 +191,9 @@ function openaiToGeminiBase(model, body, stream) {
functionDeclarations.push({
name: fn.name,
description: fn.description || "",
parameters: fn.parameters || { type: "object", properties: {} },
parameters: cleanJSONSchemaForAntigravity(
fn.parameters || { type: "object", properties: {} }
),
});
}
}
@@ -206,9 +210,7 @@ function openaiToGeminiBase(model, body, stream) {
// Extract the schema (may be nested under .schema key)
const schema = body.response_format.json_schema.schema || body.response_format.json_schema;
if (schema && typeof schema === "object") {
// Remove unsupported keywords for Gemini (it uses a subset of JSON Schema)
const { $schema, additionalProperties, ...cleanSchema } = schema;
result.generationConfig.responseSchema = cleanSchema;
result.generationConfig.responseSchema = cleanJSONSchemaForAntigravity(schema);
}
} else if (body.response_format.type === "json_object") {
result.generationConfig.responseMimeType = "application/json";
@@ -364,6 +364,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
// Flush: send final chunk with finish_reason
if (!state.finishReasonSent && state.started) {
state.finishReasonSent = true;
const hadToolCalls = (state.toolCallIndex || 0) > 0;
return {
id: state.chatId || `chatcmpl-${Date.now()}`,
object: "chat.completion.chunk",
@@ -373,7 +374,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
{
index: 0,
delta: {},
finish_reason: "stop",
finish_reason: hadToolCalls ? "tool_calls" : "stop",
},
],
};
@@ -468,6 +469,8 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
tool_calls: [
{
index: state.toolCallIndex,
id: state.currentToolCallId,
type: "function",
function: { arguments: argsDelta },
},
],
@@ -517,7 +520,9 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
if (!state.finishReasonSent) {
state.finishReasonSent = true;
state.finishReason = "stop"; // Mark for usage injection in stream.js
const hadToolCalls = (state.toolCallIndex || 0) > 0;
const reason = hadToolCalls ? "tool_calls" : "stop";
state.finishReason = reason; // Mark for usage injection in stream.js
const finalChunk: Record<string, any> = {
id: state.chatId,
@@ -528,7 +533,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) {
{
index: 0,
delta: {},
finish_reason: "stop",
finish_reason: reason,
},
],
};
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "1.7.2",
"version": "1.7.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "1.7.2",
"version": "1.7.10",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "1.7.2",
"version": "1.7.10",
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
"type": "module",
"bin": {
@@ -7,7 +7,7 @@ import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { AI_PROVIDERS, getProviderByAlias } from "@/shared/constants/providers";
import { useTranslations } from "next-intl";
const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL;
const CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL || null;
const CLOUD_ACTION_TIMEOUT_MS = 15000;
export default function APIPageClient({ machineId }) {
@@ -29,6 +29,7 @@ export default function APIPageClient({ machineId }) {
const [syncStep, setSyncStep] = useState(""); // "syncing" | "verifying" | "disabling" | "done" | ""
const [modalSuccess, setModalSuccess] = useState(false); // show success state in modal before closing
const [selectedProvider, setSelectedProvider] = useState(null); // for provider models popup
const [cloudBaseUrl, setCloudBaseUrl] = useState(CLOUD_URL); // dynamic cloud URL from API response
const { copied, copy } = useCopyToClipboard();
@@ -204,6 +205,10 @@ export default function APIPageClient({ machineId }) {
if (data.createdKey) {
await fetchData();
}
// Update cloud URL from API response (fixes undefined/v1 when env var not set)
if (data.cloudUrl) {
setCloudBaseUrl(data.cloudUrl);
}
// Reload settings to ensure fresh state
await loadCloudSettings();
} else {
@@ -274,7 +279,7 @@ export default function APIPageClient({ machineId }) {
};
const [baseUrl, setBaseUrl] = useState("/v1");
const cloudEndpointNew = `${CLOUD_URL}/v1`;
const cloudEndpointNew = cloudBaseUrl ? `${cloudBaseUrl}/v1` : null;
// Hydration fix: Only access window on client side
useEffect(() => {
@@ -293,7 +298,7 @@ export default function APIPageClient({ machineId }) {
}
// Use new format endpoint (machineId embedded in key)
const currentEndpoint = cloudEnabled ? cloudEndpointNew : baseUrl;
const currentEndpoint = cloudEnabled && cloudEndpointNew ? cloudEndpointNew : baseUrl;
return (
<div className="flex flex-col gap-8">
@@ -382,7 +387,7 @@ export default function APIPageClient({ machineId }) {
</div>
</Card>
{/* Available Endpoints */}
{/* Available Endpoints */}
<Card>
<div className="flex items-center justify-between mb-4">
<div>
@@ -398,134 +403,200 @@ export default function APIPageClient({ machineId }) {
endpointData.audioTranscription,
endpointData.audioSpeech,
endpointData.moderation,
].filter((a) => a.length > 0).length,
].filter((a) => a.length > 0).length + 2,
})}
</p>
</div>
</div>
<div className="flex flex-col gap-3">
{/* Chat Completions */}
<EndpointSection
icon="chat"
iconColor="text-blue-500"
iconBg="bg-blue-500/10"
title={t("chatCompletions")}
path="/v1/chat/completions"
description={t("chatDesc")}
models={endpointData.chat}
expanded={expandedEndpoint === "chat"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "chat" ? null : "chat")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Core APIs */}
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<span className="material-symbols-outlined text-sm text-primary">hub</span>
<h3 className="text-xs font-semibold text-text-muted uppercase tracking-wider">
{t("categoryCore") || "Core APIs"}
</h3>
<div className="flex-1 h-px bg-border/50" />
</div>
<div className="flex flex-col gap-3">
{/* Chat Completions */}
<EndpointSection
icon="chat"
iconColor="text-blue-500"
iconBg="bg-blue-500/10"
title={t("chatCompletions")}
path="/v1/chat/completions"
description={t("chatDesc")}
models={endpointData.chat}
expanded={expandedEndpoint === "chat"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "chat" ? null : "chat")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Embeddings */}
<EndpointSection
icon="data_array"
iconColor="text-emerald-500"
iconBg="bg-emerald-500/10"
title={t("embeddings")}
path="/v1/embeddings"
description={t("embeddingsDesc")}
models={endpointData.embeddings}
expanded={expandedEndpoint === "embeddings"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "embeddings" ? null : "embeddings")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Responses API */}
<EndpointSection
icon="code"
iconColor="text-indigo-500"
iconBg="bg-indigo-500/10"
title={t("responses") || "Responses API"}
path="/v1/responses"
description={t("responsesDesc") || "OpenAI Responses API for Codex and advanced agentic workflows"}
models={endpointData.chat}
expanded={expandedEndpoint === "responses"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "responses" ? null : "responses")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
</div>
</div>
{/* Image Generation */}
<EndpointSection
icon="image"
iconColor="text-purple-500"
iconBg="bg-purple-500/10"
title={t("imageGeneration")}
path="/v1/images/generations"
description={t("imageDesc")}
models={endpointData.images}
expanded={expandedEndpoint === "images"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "images" ? null : "images")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Media & Multi-Modal */}
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<span className="material-symbols-outlined text-sm text-purple-400">perm_media</span>
<h3 className="text-xs font-semibold text-text-muted uppercase tracking-wider">
{t("categoryMedia") || "Media & Multi-Modal"}
</h3>
<div className="flex-1 h-px bg-border/50" />
</div>
<div className="flex flex-col gap-3">
{/* Embeddings */}
<EndpointSection
icon="data_array"
iconColor="text-emerald-500"
iconBg="bg-emerald-500/10"
title={t("embeddings")}
path="/v1/embeddings"
description={t("embeddingsDesc")}
models={endpointData.embeddings}
expanded={expandedEndpoint === "embeddings"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "embeddings" ? null : "embeddings")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Rerank */}
<EndpointSection
icon="sort"
iconColor="text-amber-500"
iconBg="bg-amber-500/10"
title={t("rerank")}
path="/v1/rerank"
description={t("rerankDesc")}
models={endpointData.rerank}
expanded={expandedEndpoint === "rerank"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "rerank" ? null : "rerank")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Image Generation */}
<EndpointSection
icon="image"
iconColor="text-purple-500"
iconBg="bg-purple-500/10"
title={t("imageGeneration")}
path="/v1/images/generations"
description={t("imageDesc")}
models={endpointData.images}
expanded={expandedEndpoint === "images"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "images" ? null : "images")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Audio Transcription */}
<EndpointSection
icon="mic"
iconColor="text-rose-500"
iconBg="bg-rose-500/10"
title={t("audioTranscription")}
path="/v1/audio/transcriptions"
description={t("audioTranscriptionDesc")}
models={endpointData.audioTranscription}
expanded={expandedEndpoint === "audioTranscription"}
onToggle={() =>
setExpandedEndpoint(
expandedEndpoint === "audioTranscription" ? null : "audioTranscription"
)
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Audio Transcription */}
<EndpointSection
icon="mic"
iconColor="text-rose-500"
iconBg="bg-rose-500/10"
title={t("audioTranscription")}
path="/v1/audio/transcriptions"
description={t("audioTranscriptionDesc")}
models={endpointData.audioTranscription}
expanded={expandedEndpoint === "audioTranscription"}
onToggle={() =>
setExpandedEndpoint(
expandedEndpoint === "audioTranscription" ? null : "audioTranscription"
)
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Audio Speech (TTS) */}
<EndpointSection
icon="record_voice_over"
iconColor="text-cyan-500"
iconBg="bg-cyan-500/10"
title={t("textToSpeech")}
path="/v1/audio/speech"
description={t("textToSpeechDesc")}
models={endpointData.audioSpeech}
expanded={expandedEndpoint === "audioSpeech"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "audioSpeech" ? null : "audioSpeech")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Audio Speech (TTS) */}
<EndpointSection
icon="record_voice_over"
iconColor="text-cyan-500"
iconBg="bg-cyan-500/10"
title={t("textToSpeech")}
path="/v1/audio/speech"
description={t("textToSpeechDesc")}
models={endpointData.audioSpeech}
expanded={expandedEndpoint === "audioSpeech"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "audioSpeech" ? null : "audioSpeech")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
</div>
</div>
{/* Moderations */}
<EndpointSection
icon="shield"
iconColor="text-orange-500"
iconBg="bg-orange-500/10"
title={t("moderations")}
path="/v1/moderations"
description={t("moderationsDesc")}
models={endpointData.moderation}
expanded={expandedEndpoint === "moderation"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "moderation" ? null : "moderation")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Utility & Management */}
<div>
<div className="flex items-center gap-2 mb-3">
<span className="material-symbols-outlined text-sm text-amber-400">build</span>
<h3 className="text-xs font-semibold text-text-muted uppercase tracking-wider">
{t("categoryUtility") || "Utility & Management"}
</h3>
<div className="flex-1 h-px bg-border/50" />
</div>
<div className="flex flex-col gap-3">
{/* Rerank */}
<EndpointSection
icon="sort"
iconColor="text-amber-500"
iconBg="bg-amber-500/10"
title={t("rerank")}
path="/v1/rerank"
description={t("rerankDesc")}
models={endpointData.rerank}
expanded={expandedEndpoint === "rerank"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "rerank" ? null : "rerank")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* Moderations */}
<EndpointSection
icon="shield"
iconColor="text-orange-500"
iconBg="bg-orange-500/10"
title={t("moderations")}
path="/v1/moderations"
description={t("moderationsDesc")}
models={endpointData.moderation}
expanded={expandedEndpoint === "moderation"}
onToggle={() =>
setExpandedEndpoint(expandedEndpoint === "moderation" ? null : "moderation")
}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
{/* List Models */}
<EndpointSection
icon="list"
iconColor="text-teal-500"
iconBg="bg-teal-500/10"
title={t("listModels") || "List Models"}
path="/v1/models"
description={t("listModelsDesc") || "List all available models across all connected providers"}
models={[]}
expanded={expandedEndpoint === "models"}
onToggle={() => setExpandedEndpoint(expandedEndpoint === "models" ? null : "models")}
copy={copy}
copied={copied}
baseUrl={currentEndpoint}
/>
</div>
</div>
</Card>
@@ -1,16 +1,30 @@
"use client";
import { useState, useEffect } from "react";
import { Card, Toggle } from "@/shared/components";
import { Button, Card, Toggle } from "@/shared/components";
import { useTheme } from "@/shared/hooks/useTheme";
import useThemeStore, { COLOR_THEMES } from "@/store/themeStore";
import { cn } from "@/shared/utils/cn";
import { useTranslations } from "next-intl";
export default function AppearanceTab() {
const { theme, setTheme, isDark } = useTheme();
const { colorTheme, customColor, setColorTheme, setCustomColorTheme } = useThemeStore();
const t = useTranslations("settings");
const [settings, setSettings] = useState<Record<string, any>>({});
const [loading, setLoading] = useState(true);
const [customThemeColor, setCustomThemeColor] = useState(customColor || "#3b82f6");
const isValidHex = /^#([0-9a-fA-F]{6})$/.test(customThemeColor.startsWith("#") ? customThemeColor : `#${customThemeColor}`);
// Subscribe to store changes (e.g. from another tab) via Zustand external subscription
useEffect(() => {
const unsubscribe = useThemeStore.subscribe((state) => {
if (state.customColor && state.customColor !== customThemeColor) {
setCustomThemeColor(state.customColor);
}
});
return unsubscribe;
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const themeOptionLabels: Record<string, string> = {
light: t("themeLight"),
dark: t("themeDark"),
@@ -47,6 +61,16 @@ export default function AppearanceTab() {
}
};
const presetThemes = [
{ id: "coral", color: COLOR_THEMES.coral, label: t("themeCoral") },
{ id: "blue", color: COLOR_THEMES.blue, label: t("themeBlue") },
{ id: "red", color: COLOR_THEMES.red, label: t("themeRed") },
{ id: "green", color: COLOR_THEMES.green, label: t("themeGreen") },
{ id: "violet", color: COLOR_THEMES.violet, label: t("themeViolet") },
{ id: "orange", color: COLOR_THEMES.orange, label: t("themeOrange") },
{ id: "cyan", color: COLOR_THEMES.cyan, label: t("themeCyan") },
];
return (
<Card>
<div className="flex items-center gap-3 mb-4">
@@ -94,6 +118,56 @@ export default function AppearanceTab() {
</div>
</div>
<div className="pt-4 border-t border-border">
<p className="font-medium mb-1">{t("themeAccent")}</p>
<p className="text-sm text-text-muted mb-3">{t("themeAccentDesc")}</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mb-3">
{presetThemes.map((item) => {
const active = colorTheme === item.id;
return (
<button
key={item.id}
onClick={() => setColorTheme(item.id)}
className={cn(
"flex items-center justify-between gap-2 p-2 rounded-lg border transition-colors",
active
? "border-primary bg-primary/10 text-primary"
: "border-border hover:bg-surface/50 text-text-main"
)}
>
<span className="flex items-center gap-2">
<span
className="size-4 rounded-full border border-black/10 dark:border-white/20"
style={{ backgroundColor: item.color }}
/>
<span className="text-sm font-medium">{item.label}</span>
</span>
</button>
);
})}
</div>
<div className="flex items-center gap-2">
<input
type="color"
value={customThemeColor}
onChange={(e) => setCustomThemeColor(e.target.value)}
className="h-10 w-12 rounded border border-border bg-surface cursor-pointer"
aria-label={t("themeCustom")}
/>
<input
type="text"
value={customThemeColor}
onChange={(e) => setCustomThemeColor(e.target.value)}
placeholder="#3b82f6"
maxLength={7}
className={`flex-1 h-10 px-3 rounded-lg bg-surface border text-sm text-text-main focus:outline-none ${isValidHex ? "border-border focus:border-primary" : "border-red-400 focus:border-red-500"}`}
/>
<Button onClick={() => setCustomColorTheme(customThemeColor)} disabled={!isValidHex}>{t("themeCreate")}</Button>
</div>
</div>
<div className="pt-4 border-t border-border">
<div className="flex items-center justify-between">
<div>
@@ -0,0 +1,261 @@
"use client";
import { useState, useEffect } from "react";
import { Card } from "@/shared/components";
import { useTranslations } from "next-intl";
export default function BackgroundDegradationTab() {
const [config, setConfig] = useState({
enabled: false,
degradationMap: {},
detectionPatterns: [],
stats: { detected: 0, tokensSaved: 0 },
});
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [status, setStatus] = useState("");
const [newFrom, setNewFrom] = useState("");
const [newTo, setNewTo] = useState("");
const [newPattern, setNewPattern] = useState("");
const t = useTranslations("settings");
useEffect(() => {
fetch("/api/settings/background-degradation")
.then((res) => res.json())
.then((data) => {
setConfig(data);
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
const save = async (updates) => {
const newConfig = { ...config, ...updates };
setConfig(newConfig);
setSaving(true);
setStatus("");
try {
const { stats, ...persistable } = newConfig;
const res = await fetch("/api/settings/background-degradation", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(persistable),
});
if (res.ok) {
const data = await res.json();
setConfig(data);
setStatus("saved");
setTimeout(() => setStatus(""), 2000);
} else {
setStatus("error");
}
} catch {
setStatus("error");
} finally {
setSaving(false);
}
};
const addMapping = () => {
if (!newFrom.trim() || !newTo.trim()) return;
const map = { ...config.degradationMap, [newFrom.trim()]: newTo.trim() };
save({ degradationMap: map });
setNewFrom("");
setNewTo("");
};
const removeMapping = (key) => {
const map = { ...config.degradationMap };
delete map[key];
save({ degradationMap: map });
};
const addPattern = () => {
if (!newPattern.trim()) return;
const patterns = [...config.detectionPatterns, newPattern.trim()];
save({ detectionPatterns: patterns });
setNewPattern("");
};
const removePattern = (idx) => {
const patterns = config.detectionPatterns.filter((_, i) => i !== idx);
save({ detectionPatterns: patterns });
};
const mapEntries = Object.entries(config.degradationMap || {}) as [string, string][];
return (
<Card>
<div className="flex items-center gap-3 mb-5">
<div className="p-2 rounded-lg bg-sky-500/10 text-sky-500">
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
speed
</span>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold">
{t("backgroundDegradationTitle") || "Background Task Degradation"}
</h3>
<p className="text-sm text-text-muted">
{t("backgroundDegradationDesc") ||
"Auto-redirect background requests (titles, summaries) to cheaper models"}
</p>
</div>
{status === "saved" && (
<span className="text-xs font-medium text-emerald-500 flex items-center gap-1">
<span className="material-symbols-outlined text-[14px]">check_circle</span>{" "}
{t("saved") || "Saved"}
</span>
)}
</div>
{/* Toggle */}
<div className="flex items-center justify-between p-4 rounded-lg bg-surface/30 border border-border/30 mb-4">
<div>
<p className="text-sm font-medium">
{t("enableDegradation") || "Enable Background Degradation"}
</p>
<p className="text-xs text-text-muted mt-0.5">
{t("enableDegradationHint") ||
"Automatically use cheaper models for background utility tasks"}
</p>
</div>
<button
onClick={() => save({ enabled: !config.enabled })}
disabled={loading || saving}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
config.enabled ? "bg-sky-500" : "bg-white/10"
}`}
>
<span
className={`inline-block h-4 w-4 rounded-full bg-white transition-transform ${
config.enabled ? "translate-x-6" : "translate-x-1"
}`}
/>
</button>
</div>
{/* Stats */}
{config.stats && config.stats.detected > 0 && (
<div className="flex items-center gap-4 p-3 rounded-lg bg-sky-500/5 border border-sky-500/20 mb-4">
<div className="flex items-center gap-2">
<span className="material-symbols-outlined text-[16px] text-sky-400">analytics</span>
<span className="text-xs text-text-muted">
{t("tasksDetected") || "Tasks detected"}:
</span>
<span className="text-sm font-mono font-semibold text-sky-400">
{config.stats.detected}
</span>
</div>
</div>
)}
{config.enabled && (
<>
{/* Degradation Map */}
<div className="mb-4">
<p className="text-xs font-medium text-text-muted uppercase tracking-wider mb-2">
{t("degradationMap") || "Model Degradation Map"}
</p>
{/* Add new mapping */}
<div className="flex items-center gap-2 mb-3">
<input
type="text"
placeholder={t("premiumModel") || "Premium model"}
value={newFrom}
onChange={(e) => setNewFrom(e.target.value)}
className="flex-1 px-3 py-2 rounded-lg text-sm bg-surface border border-border/50 focus:border-sky-500/50 focus:outline-none"
/>
<span className="text-text-muted text-lg"></span>
<input
type="text"
placeholder={t("cheapModel") || "Cheap model"}
value={newTo}
onChange={(e) => setNewTo(e.target.value)}
className="flex-1 px-3 py-2 rounded-lg text-sm bg-surface border border-border/50 focus:border-sky-500/50 focus:outline-none"
/>
<button
onClick={addMapping}
disabled={saving || !newFrom.trim() || !newTo.trim()}
className="px-3 py-2 rounded-lg text-sm font-medium bg-sky-500/10 text-sky-500 hover:bg-sky-500/20 disabled:opacity-50 transition-all"
>
{t("add") || "Add"}
</button>
</div>
{/* Existing mappings */}
{mapEntries.length > 0 && (
<div className="rounded-lg border border-border/30 divide-y divide-border/20 max-h-48 overflow-y-auto">
{mapEntries.map(([from, to]) => (
<div key={from} className="flex items-center gap-3 px-4 py-2">
<code className="text-xs text-orange-400/80 flex-1 truncate">{from}</code>
<span className="material-symbols-outlined text-[14px] text-text-muted">
arrow_forward
</span>
<code className="text-xs text-sky-400/80 flex-1 truncate">{to}</code>
<button
onClick={() => removeMapping(from)}
disabled={saving}
className="p-1 rounded hover:bg-red-500/10 text-text-muted hover:text-red-400 transition-all"
>
<span className="material-symbols-outlined text-[16px]">close</span>
</button>
</div>
))}
</div>
)}
</div>
{/* Detection Patterns */}
<details className="group">
<summary className="text-xs font-medium text-text-muted uppercase tracking-wider cursor-pointer flex items-center gap-1 mb-2">
<span className="material-symbols-outlined text-[14px] group-open:rotate-90 transition-transform">
chevron_right
</span>
{t("detectionPatterns") || "Detection Patterns"} (
{config.detectionPatterns?.length || 0})
</summary>
{/* Add new pattern */}
<div className="flex items-center gap-2 mb-3">
<input
type="text"
placeholder={t("newPattern") || 'e.g. "generate a title"'}
value={newPattern}
onChange={(e) => setNewPattern(e.target.value)}
className="flex-1 px-3 py-2 rounded-lg text-sm bg-surface border border-border/50 focus:border-sky-500/50 focus:outline-none"
/>
<button
onClick={addPattern}
disabled={saving || !newPattern.trim()}
className="px-3 py-2 rounded-lg text-sm font-medium bg-sky-500/10 text-sky-500 hover:bg-sky-500/20 disabled:opacity-50 transition-all"
>
{t("add") || "Add"}
</button>
</div>
{/* Existing patterns */}
<div className="flex flex-wrap gap-2">
{(config.detectionPatterns || []).map((pattern, idx) => (
<span
key={idx}
className="inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs bg-sky-500/10 text-sky-400 border border-sky-500/20"
>
{pattern}
<button
onClick={() => removePattern(idx)}
className="hover:text-red-400 transition-colors"
disabled={saving}
>
<span className="material-symbols-outlined text-[12px]">close</span>
</button>
</span>
))}
</div>
</details>
</>
)}
</Card>
);
}
@@ -0,0 +1,179 @@
"use client";
import { useState, useEffect } from "react";
import { Card } from "@/shared/components";
import { useTranslations } from "next-intl";
export default function ModelAliasesTab() {
const [builtIn, setBuiltIn] = useState({});
const [custom, setCustom] = useState({});
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [status, setStatus] = useState("");
const [newFrom, setNewFrom] = useState("");
const [newTo, setNewTo] = useState("");
const t = useTranslations("settings");
useEffect(() => {
fetch("/api/settings/model-aliases")
.then((res) => res.json())
.then((data) => {
setBuiltIn(data.builtIn || {});
setCustom(data.custom || {});
setLoading(false);
})
.catch(() => setLoading(false));
}, []);
const addAlias = async () => {
if (!newFrom.trim() || !newTo.trim()) return;
setSaving(true);
try {
const res = await fetch("/api/settings/model-aliases", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ from: newFrom.trim(), to: newTo.trim() }),
});
if (res.ok) {
const data = await res.json();
setCustom(data.custom);
setNewFrom("");
setNewTo("");
setStatus("saved");
setTimeout(() => setStatus(""), 2000);
}
} catch {
setStatus("error");
} finally {
setSaving(false);
}
};
const removeAlias = async (from) => {
setSaving(true);
try {
const res = await fetch("/api/settings/model-aliases", {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ from }),
});
if (res.ok) {
const data = await res.json();
setCustom(data.custom);
setStatus("saved");
setTimeout(() => setStatus(""), 2000);
}
} catch {
setStatus("error");
} finally {
setSaving(false);
}
};
const builtInEntries = Object.entries(builtIn);
const customEntries = Object.entries(custom);
return (
<Card>
<div className="flex items-center gap-3 mb-5">
<div className="p-2 rounded-lg bg-amber-500/10 text-amber-500">
<span className="material-symbols-outlined text-[20px]" aria-hidden="true">
swap_horiz
</span>
</div>
<div>
<h3 className="text-lg font-semibold">{t("modelAliasesTitle") || "Model Aliases"}</h3>
<p className="text-sm text-text-muted">
{t("modelAliasesDesc") || "Auto-forward deprecated model IDs to their replacements"}
</p>
</div>
{status === "saved" && (
<span className="ml-auto text-xs font-medium text-emerald-500 flex items-center gap-1">
<span className="material-symbols-outlined text-[14px]">check_circle</span>{" "}
{t("saved") || "Saved"}
</span>
)}
</div>
{/* Add custom alias */}
<div className="p-4 rounded-lg bg-surface/30 border border-border/30 mb-4">
<p className="text-sm font-medium mb-3">
{t("addCustomAlias") || "Add Custom Alias"}
</p>
<div className="flex items-center gap-2">
<input
type="text"
placeholder={t("deprecatedModelId") || "Deprecated model ID"}
value={newFrom}
onChange={(e) => setNewFrom(e.target.value)}
className="flex-1 px-3 py-2 rounded-lg text-sm bg-surface border border-border/50 focus:border-amber-500/50 focus:outline-none"
/>
<span className="text-text-muted text-lg"></span>
<input
type="text"
placeholder={t("newModelId") || "New model ID"}
value={newTo}
onChange={(e) => setNewTo(e.target.value)}
className="flex-1 px-3 py-2 rounded-lg text-sm bg-surface border border-border/50 focus:border-amber-500/50 focus:outline-none"
/>
<button
onClick={addAlias}
disabled={saving || !newFrom.trim() || !newTo.trim()}
className="px-4 py-2 rounded-lg text-sm font-medium bg-amber-500/10 text-amber-500 hover:bg-amber-500/20 disabled:opacity-50 transition-all"
>
{t("add") || "Add"}
</button>
</div>
</div>
{/* Custom aliases */}
{customEntries.length > 0 && (
<div className="mb-4">
<p className="text-xs font-medium text-text-muted uppercase tracking-wider mb-2">
{t("customAliases") || "Custom Aliases"}
</p>
<div className="rounded-lg border border-border/30 divide-y divide-border/20">
{customEntries.map(([from, to]) => (
<div key={from} className="flex items-center gap-3 px-4 py-2.5">
<code className="text-xs text-red-400/80 flex-1 truncate">{from}</code>
<span className="material-symbols-outlined text-[14px] text-text-muted">
arrow_forward
</span>
<code className="text-xs text-emerald-400/80 flex-1 truncate">{to}</code>
<button
onClick={() => removeAlias(from)}
disabled={saving}
className="p-1 rounded hover:bg-red-500/10 text-text-muted hover:text-red-400 transition-all"
>
<span className="material-symbols-outlined text-[16px]">close</span>
</button>
</div>
))}
</div>
</div>
)}
{/* Built-in aliases (collapsed by default) */}
<details className="group">
<summary className="text-xs font-medium text-text-muted uppercase tracking-wider cursor-pointer flex items-center gap-1 mb-2">
<span className="material-symbols-outlined text-[14px] group-open:rotate-90 transition-transform">
chevron_right
</span>
{t("builtInAliases") || "Built-in Aliases"} ({builtInEntries.length})
</summary>
<div className="rounded-lg border border-border/30 divide-y divide-border/20 max-h-60 overflow-y-auto">
{builtInEntries.map(([from, to]) => (
<div key={from} className="flex items-center gap-3 px-4 py-2 opacity-60">
<code className="text-xs text-red-400/60 flex-1 truncate">{from}</code>
<span className="material-symbols-outlined text-[14px] text-text-muted">
arrow_forward
</span>
<code className="text-xs text-emerald-400/60 flex-1 truncate">{to}</code>
<span className="material-symbols-outlined text-[14px] text-text-muted">lock</span>
</div>
))}
</div>
</details>
</Card>
);
}
@@ -13,6 +13,8 @@ import ProxyTab from "./components/ProxyTab";
import AppearanceTab from "./components/AppearanceTab";
import ThinkingBudgetTab from "./components/ThinkingBudgetTab";
import SystemPromptTab from "./components/SystemPromptTab";
import ModelAliasesTab from "./components/ModelAliasesTab";
import BackgroundDegradationTab from "./components/BackgroundDegradationTab";
import CacheStatsCard from "./components/CacheStatsCard";
import ResilienceTab from "./components/ResilienceTab";
@@ -94,6 +96,8 @@ export default function SettingsPage() {
<div className="flex flex-col gap-6">
<RoutingTab />
<ComboDefaultsTab />
<ModelAliasesTab />
<BackgroundDegradationTab />
</div>
)}
+82 -31
View File
@@ -6,7 +6,7 @@ import {
requestDeviceCode,
pollForToken,
} from "@/lib/oauth/providers";
import { createProviderConnection, isCloudEnabled } from "@/models";
import { createProviderConnection, updateProviderConnection, getProviderConnections, isCloudEnabled } from "@/models";
import { getConsistentMachineId } from "@/shared/utils/machineId";
import { syncToCloud } from "@/lib/cloudSync";
import { startLocalServer } from "@/lib/oauth/utils/server";
@@ -170,16 +170,33 @@ export async function POST(
exchangeTokens(provider, code, redirectUri, codeVerifier, state)
);
// Save to database
const connection: any = await createProviderConnection({
provider,
authType: "oauth",
...tokenData,
expiresAt: tokenData.expiresIn
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
: null,
testStatus: "active",
});
// Upsert: update existing connection if same provider+email, else create new
const expiresAt = tokenData.expiresIn
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
: null;
let connection: any;
if (tokenData.email) {
const existing = await getProviderConnections({ provider });
const match = existing.find((c: any) => c.email === tokenData.email && c.authType === "oauth");
if (match) {
connection = await updateProviderConnection(match.id, {
...tokenData,
expiresAt,
testStatus: "active",
isActive: true,
});
}
}
if (!connection) {
connection = await createProviderConnection({
provider,
authType: "oauth",
...tokenData,
expiresAt,
testStatus: "active",
});
}
// Auto sync to Cloud if enabled
await syncToCloudIfEnabled();
@@ -218,16 +235,33 @@ export async function POST(
}
if (result.success) {
// Save to database
const connection: any = await createProviderConnection({
provider,
authType: "oauth",
...result.tokens,
expiresAt: result.tokens.expiresIn
? new Date(Date.now() + result.tokens.expiresIn * 1000).toISOString()
: null,
testStatus: "active",
});
// Upsert: update existing connection if same provider+email, else create new
const expiresAt = result.tokens.expiresIn
? new Date(Date.now() + result.tokens.expiresIn * 1000).toISOString()
: null;
let connection: any;
if (result.tokens.email) {
const existing = await getProviderConnections({ provider });
const match = existing.find((c: any) => c.email === result.tokens.email && c.authType === "oauth");
if (match) {
connection = await updateProviderConnection(match.id, {
...result.tokens,
expiresAt,
testStatus: "active",
isActive: true,
});
}
}
if (!connection) {
connection = await createProviderConnection({
provider,
authType: "oauth",
...result.tokens,
expiresAt,
testStatus: "active",
});
}
// Auto sync to Cloud if enabled
await syncToCloudIfEnabled();
@@ -312,16 +346,33 @@ export async function POST(
exchangeTokens(provider, params.code, redirectUri, codeVerifier, params.state)
);
// Save to database
const connection: any = await createProviderConnection({
provider,
authType: "oauth",
...tokenData,
expiresAt: tokenData.expiresIn
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
: null,
testStatus: "active",
});
// Upsert: update existing connection if same provider+email, else create new
const expiresAt = tokenData.expiresIn
? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString()
: null;
let connection: any;
if (tokenData.email) {
const existing = await getProviderConnections({ provider });
const match = existing.find((c: any) => c.email === tokenData.email && c.authType === "oauth");
if (match) {
connection = await updateProviderConnection(match.id, {
...tokenData,
expiresAt,
testStatus: "active",
isActive: true,
});
}
}
if (!connection) {
connection = await createProviderConnection({
provider,
authType: "oauth",
...tokenData,
expiresAt,
testStatus: "active",
});
}
await syncToCloudIfEnabled();
@@ -0,0 +1,60 @@
import { NextResponse } from "next/server";
import {
getBackgroundDegradationConfig,
setBackgroundDegradationConfig,
resetStats,
} from "@omniroute/open-sse/services/backgroundTaskDetector.ts";
import { updateSettings } from "@/lib/db/settings";
/**
* GET /api/settings/background-degradation
* Returns the current background degradation configuration.
*/
export async function GET() {
try {
return NextResponse.json(getBackgroundDegradationConfig());
} catch (error) {
console.error("[API ERROR] /api/settings/background-degradation GET:", error);
return NextResponse.json({ error: "Failed to get config" }, { status: 500 });
}
}
/**
* PUT /api/settings/background-degradation
* Update the background degradation configuration.
* Body: { enabled?: boolean, degradationMap?: {...}, detectionPatterns?: [...] }
*/
export async function PUT(request) {
try {
const config = await request.json();
setBackgroundDegradationConfig(config);
// Persist to database (excluding stats)
const { stats, ...persistable } = getBackgroundDegradationConfig();
await updateSettings({ backgroundDegradation: JSON.stringify(persistable) });
return NextResponse.json({ success: true, ...getBackgroundDegradationConfig() });
} catch (error) {
console.error("[API ERROR] /api/settings/background-degradation PUT:", error);
return NextResponse.json({ error: "Failed to update config" }, { status: 500 });
}
}
/**
* POST /api/settings/background-degradation
* Reset stats counters.
* Body: { action: "reset-stats" }
*/
export async function POST(request) {
try {
const { action } = await request.json();
if (action === "reset-stats") {
resetStats();
return NextResponse.json({ success: true, stats: getBackgroundDegradationConfig().stats });
}
return NextResponse.json({ error: "Unknown action" }, { status: 400 });
} catch (error) {
console.error("[API ERROR] /api/settings/background-degradation POST:", error);
return NextResponse.json({ error: "Failed to execute action" }, { status: 500 });
}
}
@@ -0,0 +1,90 @@
import { NextResponse } from "next/server";
import {
getAllAliases,
getCustomAliases,
getBuiltInAliases,
setCustomAliases,
addCustomAlias,
removeCustomAlias,
} from "@omniroute/open-sse/services/modelDeprecation.ts";
import { getSettings, updateSettings } from "@/lib/db/settings";
/**
* GET /api/settings/model-aliases
* Returns the full alias map, separated into built-in and custom.
*/
export async function GET() {
try {
return NextResponse.json({
builtIn: getBuiltInAliases(),
custom: getCustomAliases(),
all: getAllAliases(),
});
} catch (error) {
console.error("[API ERROR] /api/settings/model-aliases GET:", error);
return NextResponse.json({ error: "Failed to get model aliases" }, { status: 500 });
}
}
/**
* PUT /api/settings/model-aliases
* Update the custom aliases map.
* Body: { aliases: { "old-model": "new-model", ... } }
*/
export async function PUT(request) {
try {
const { aliases } = await request.json();
if (!aliases || typeof aliases !== "object") {
return NextResponse.json({ error: "Missing or invalid 'aliases' object" }, { status: 400 });
}
setCustomAliases(aliases);
await updateSettings({ modelAliases: JSON.stringify(aliases) });
return NextResponse.json({ success: true, custom: getCustomAliases() });
} catch (error) {
console.error("[API ERROR] /api/settings/model-aliases PUT:", error);
return NextResponse.json({ error: "Failed to update model aliases" }, { status: 500 });
}
}
/**
* POST /api/settings/model-aliases
* Add a single custom alias.
* Body: { from: "old-model", to: "new-model" }
*/
export async function POST(request) {
try {
const { from, to } = await request.json();
if (!from || !to) {
return NextResponse.json({ error: "Missing 'from' or 'to'" }, { status: 400 });
}
addCustomAlias(from, to);
await updateSettings({ modelAliases: JSON.stringify(getCustomAliases()) });
return NextResponse.json({ success: true, custom: getCustomAliases() });
} catch (error) {
console.error("[API ERROR] /api/settings/model-aliases POST:", error);
return NextResponse.json({ error: "Failed to add alias" }, { status: 500 });
}
}
/**
* DELETE /api/settings/model-aliases
* Remove a custom alias.
* Body: { from: "old-model" }
*/
export async function DELETE(request) {
try {
const { from } = await request.json();
if (!from) {
return NextResponse.json({ error: "Missing 'from'" }, { status: 400 });
}
const removed = removeCustomAlias(from);
if (!removed) {
return NextResponse.json({ error: "Alias not found" }, { status: 404 });
}
await updateSettings({ modelAliases: JSON.stringify(getCustomAliases()) });
return NextResponse.json({ success: true, custom: getCustomAliases() });
} catch (error) {
console.error("[API ERROR] /api/settings/model-aliases DELETE:", error);
return NextResponse.json({ error: "Failed to remove alias" }, { status: 500 });
}
}
+6
View File
@@ -114,11 +114,15 @@ async function syncAndVerify(machineId: string, createdKey: any, existingKeys: a
);
}
// Build the cloud URL for the frontend to use
const cloudUrl = CLOUD_URL ? `${CLOUD_URL}/${machineId}` : null;
// Step 2: Verify connection by pinging the cloud (with retry)
const apiKey = createdKey || existingKeys[0]?.key;
if (!apiKey) {
return NextResponse.json({
...syncResult,
cloudUrl,
verified: false,
verifyError: "No API key available",
});
@@ -146,6 +150,7 @@ async function syncAndVerify(machineId: string, createdKey: any, existingKeys: a
if (pingResponse.ok) {
return NextResponse.json({
...syncResult,
cloudUrl,
verified: true,
});
}
@@ -163,6 +168,7 @@ async function syncAndVerify(machineId: string, createdKey: any, existingKeys: a
// Sync succeeded but verify failed — still return success with warning
return NextResponse.json({
...syncResult,
cloudUrl,
verified: false,
verifyError: lastVerifyError || "Verification failed after retries",
});
+3 -2
View File
@@ -141,9 +141,10 @@ body {
-moz-osx-font-smoothing: grayscale;
}
/* Selection */
/* Selection — with fallback for browsers that don't support color-mix() */
::selection {
background-color: rgba(229, 77, 94, 0.2);
background-color: rgba(229, 77, 94, 0.22);
background-color: color-mix(in srgb, var(--color-primary) 22%, transparent);
color: var(--color-primary);
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "تم قطع اتصال الخادم",
"serverDisconnectedMsg": "تم إيقاف الخادم الوكيل أو يتم إعادة تشغيله.",
"expandSidebar": "قم بتوسيع الشريط الجانبي",
"collapseSidebar": "طي الشريط الجانبي"
"collapseSidebar": "طي الشريط الجانبي",
"media": "الوسائط"
},
"header": {
"logout": "تسجيل الخروج",
@@ -611,7 +612,14 @@
"embedding": "التضمين",
"image": "صورة",
"custom": "مخصص",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "واجهة Responses من OpenAI لـ Codex وسير العمل الوكيلية المتقدمة",
"listModelsDesc": "قائمة جميع النماذج المتاحة عبر جميع المزودين المتصلين",
"settingsApi": "Settings API",
"settingsApiDesc": "قراءة وتعديل إعدادات OmniRoute عبر API",
"categoryCore": "واجهات API الأساسية",
"categoryMedia": "الوسائط والمتعدد الوسائط",
"categoryUtility": "أدوات فرعية وإدارة"
},
"health": {
"title": "صحة النظام",
@@ -1088,6 +1096,17 @@
"themeSystem": "النظام",
"hideHealthLogs": "إخفاء سجلات التحقق من الصحة",
"hideHealthLogsDesc": "عند التشغيل، قم بمنع رسائل [HealthCheck] في وحدة تحكم الخادم",
"themeAccent": "لون السمة",
"themeAccentDesc": "اختر لونًا جاهزًا أو أنشئ سمتك الخاصة بلون واحد",
"themeCreate": "إنشاء سمة",
"themeCustom": "سمة مخصصة",
"themeCoral": "مرجاني",
"themeBlue": "أزرق",
"themeRed": "أحمر",
"themeGreen": "أخضر",
"themeViolet": "بنفسجي",
"themeOrange": "برتقالي",
"themeCyan": "سماوي",
"promptCache": "ذاكرة التخزين المؤقت الفوري",
"flushCache": "مسح ذاكرة التخزين المؤقت",
"flushing": "فلاشينغ…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "الرموز المميزة المستخدمة لإنشاء إدخالات ذاكرة التخزين المؤقت (الرجوع إلى معدل الإدخال)",
"customPricingNote": "يمكنك تجاوز التسعير الافتراضي لنماذج محددة. تحظى التجاوزات المخصصة بالأولوية على الأسعار التي يتم اكتشافها تلقائيًا.",
"editPricing": "تحرير التسعير",
"viewFullDetails": "عرض التفاصيل الكاملة"
"viewFullDetails": "عرض التفاصيل الكاملة",
"modelAliasesTitle": "أسماء بديلة للنماذج",
"addCustomAlias": "إضافة اسم بديل مخصص",
"deprecatedModelId": "معرف النموذج المهمل",
"newModelId": "معرف النموذج الجديد",
"customAliases": "أسماء بديلة مخصصة",
"builtInAliases": "أسماء بديلة مدمجة",
"backgroundDegradationTitle": "تقليل المهام في الخلفية",
"backgroundDegradationDesc": "اكتشاف المهام في الخلفية تلقائيًا (العناوين والملخصات) وتوجيهها إلى نماذج أرخص",
"enableDegradation": "تفعيل تقليل المهام في الخلفية",
"enableDegradationHint": "عند التفعيل، يتم توجيه المهام في الخلفية مثل توليد العناوين والملخصات تلقائيًا إلى نماذج أرخص",
"tasksDetected": "المهام المكتشفة",
"degradationMap": "خريطة تقليل النماذج",
"premiumModel": "نموذج متميز",
"cheapModel": "نموذج رخيص",
"detectionPatterns": "أنماط الكشف",
"newPattern": "مثال: \"أنشئ عنوانًا\""
},
"translator": {
"title": "مترجم",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "يتم توفير OmniRoute \"كما هو\" دون أي ضمان من أي نوع. نحن لسنا مسؤولين عن أي تكاليف يتم تكبدها من خلال استخدام واجهة برمجة التطبيقات (API)، أو انقطاع الخدمة، أو فقدان البيانات. احتفظ دائمًا بنسخ احتياطية من التكوين الخاص بك.",
"termsSection6Title": "6. المصدر المفتوح",
"termsSection6Text": "OmniRoute هو برنامج مفتوح المصدر. ولك الحرية في فحصه وتعديله وتوزيعه بموجب شروط ترخيصه."
},
"media": {
"title": "استوديو الوسائط",
"subtitle": "أنشئ صورًا وفيديوهات وموسيقى",
"model": "Model",
"prompt": "Prompt",
"generate": "إنشاء",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Връзката със сървъра е прекъсната",
"serverDisconnectedMsg": "Прокси сървърът е спрян или се рестартира.",
"expandSidebar": "Разширете страничната лента",
"collapseSidebar": "Свиване на страничната лента"
"collapseSidebar": "Свиване на страничната лента",
"media": "Медия"
},
"header": {
"logout": "Изход",
@@ -611,7 +612,14 @@
"embedding": "Вграждане",
"image": "Изображение",
"custom": "обичай",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Основни API",
"categoryMedia": "Медии и мултимодални",
"categoryUtility": "Помощни средства и управление"
},
"health": {
"title": "Здраве на системата",
@@ -1088,6 +1096,17 @@
"themeSystem": "система",
"hideHealthLogs": "Скриване на журналите за проверка на здравето",
"hideHealthLogsDesc": "Когато е ВКЛЮЧЕНО, потиска съобщенията [HealthCheck] в сървърната конзола",
"themeAccent": "Цвят на темата",
"themeAccentDesc": "Изберете готов цвят или създайте своя тема с един цвят",
"themeCreate": "Създай тема",
"themeCustom": "Персонална тема",
"themeCoral": "Коралов",
"themeBlue": "Син",
"themeRed": "Червен",
"themeGreen": "Зелен",
"themeViolet": "Виолетов",
"themeOrange": "Оранжев",
"themeCyan": "Циан",
"promptCache": "Кеш на подканите",
"flushCache": "Прочистване на кеша",
"flushing": "Зачервяване...",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Токени, използвани за създаване на записи в кеша (резервен към скоростта на въвеждане)",
"customPricingNote": "Можете да замените цените по подразбиране за конкретни модели. Персонализираните замени имат приоритет пред автоматично разпознатото ценообразуване.",
"editPricing": "Редактиране на цените",
"viewFullDetails": "Вижте пълните подробности"
"viewFullDetails": "Вижте пълните подробности",
"modelAliasesTitle": "Псевдоними на модели",
"addCustomAlias": "Добави потребителски псевдоним",
"deprecatedModelId": "Остарял ID на модел",
"newModelId": "Нов ID на модел",
"customAliases": "Потребителски псевдоними",
"builtInAliases": "Вградени псевдоними",
"backgroundDegradationTitle": "Деградация на фонови задачи",
"backgroundDegradationDesc": "Автоматично открива фонови задачи (заглавия, резюмета) и пренасочва към по-евтини модели",
"enableDegradation": "Активирай деградация на фонови задачи",
"enableDegradationHint": "Когато е активирано, фоновите задачи като генериране на заглавия и резюмета се пренасочват автоматично към по-евтини модели",
"tasksDetected": "Открити задачи",
"degradationMap": "Карта на деградация на модели",
"premiumModel": "Премиум модел",
"cheapModel": "Евтин модел",
"detectionPatterns": "Модели за откриване",
"newPattern": "напр. \"генерирай заглавие\""
},
"translator": {
"title": "Преводач",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute се предоставя „както е“ без каквато и да е гаранция. Ние не носим отговорност за каквито и да било разходи, възникнали поради използване на API, прекъсвания на услугата или загуба на данни. Винаги поддържайте резервни копия на вашата конфигурация.",
"termsSection6Title": "6. Отворен код",
"termsSection6Text": "OmniRoute е софтуер с отворен код. Вие сте свободни да го инспектирате, модифицирате и разпространявате съгласно условията на неговия лиценз."
},
"media": {
"title": "Медиен център",
"subtitle": "Генерирайте изображения, видеа и музика",
"model": "Model",
"prompt": "Prompt",
"generate": "Генерирай",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server afbrudt",
"serverDisconnectedMsg": "Proxyserveren er blevet stoppet eller genstarter.",
"expandSidebar": "Udvid sidebjælken",
"collapseSidebar": "Skjul sidebjælken"
"collapseSidebar": "Skjul sidebjælken",
"media": "Medier"
},
"header": {
"logout": "Log ud",
@@ -611,7 +612,14 @@
"embedding": "Indlejring",
"image": "Billede",
"custom": "skik",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Kerne-API'er",
"categoryMedia": "Medier & Multi-Modal",
"categoryUtility": "Værktøjer & Administration"
},
"health": {
"title": "Systemsundhed",
@@ -1088,6 +1096,17 @@
"themeSystem": "System",
"hideHealthLogs": "Skjul logs til sundhedstjek",
"hideHealthLogsDesc": "Når ON, skal du undertrykke [HealthCheck]-meddelelser i serverkonsollen",
"themeAccent": "Temafarve",
"themeAccentDesc": "Vælg en forudindstillet farve eller opret dit eget tema med én farve",
"themeCreate": "Opret tema",
"themeCustom": "Brugerdefineret tema",
"themeCoral": "Koral",
"themeBlue": "Blå",
"themeRed": "Rød",
"themeGreen": "Grøn",
"themeViolet": "Lilla",
"themeOrange": "Orange",
"themeCyan": "Cyan",
"promptCache": "Spørg cache",
"flushCache": "Skyl cache",
"flushing": "Skyller...",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens, der bruges til at oprette cacheposter (tilbage til inputhastighed)",
"customPricingNote": "Du kan tilsidesætte standardpriser for specifikke modeller. Tilpassede tilsidesættelser har prioritet frem for automatisk registrerede priser.",
"editPricing": "Rediger prissætning",
"viewFullDetails": "Se alle detaljer"
"viewFullDetails": "Se alle detaljer",
"modelAliasesTitle": "Model Aliaser",
"addCustomAlias": "Tilføj Tilpasset Alias",
"deprecatedModelId": "Forældet model-ID",
"newModelId": "Nyt model-ID",
"customAliases": "Tilpassede Aliaser",
"builtInAliases": "Indbyggede Aliaser",
"backgroundDegradationTitle": "Baggrundsopgave Degradering",
"backgroundDegradationDesc": "Opdager automatisk baggrundsopgaver (titler, resuméer) og dirigerer til billigere modeller",
"enableDegradation": "Aktiver Baggrundsdegradation",
"enableDegradationHint": "Når aktiveret, dirigeres baggrundsopgaver som titelgenerering og resuméer automatisk til billigere modeller",
"tasksDetected": "Opgaver opdaget",
"degradationMap": "Modeldegraderingsschema",
"premiumModel": "Premium model",
"cheapModel": "Billig model",
"detectionPatterns": "Detektionsmønstre",
"newPattern": "f.eks. \"generer en titel\""
},
"translator": {
"title": "Oversætter",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute leveres \"som den er\" uden garanti af nogen art. Vi er ikke ansvarlige for omkostninger, der påløber som følge af API-brug, serviceforstyrrelser eller tab af data. Vedligehold altid sikkerhedskopier af din konfiguration.",
"termsSection6Title": "6. Open Source",
"termsSection6Text": "OmniRoute er open source-software. Du kan frit inspicere, ændre og distribuere den i henhold til licensbetingelserne."
},
"media": {
"title": "Medieværksted",
"subtitle": "Generér billeder, videoer og musik",
"model": "Model",
"prompt": "Prompt",
"generate": "Generer",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Serververbindung getrennt",
"serverDisconnectedMsg": "Der Proxyserver wurde gestoppt oder wird neu gestartet.",
"expandSidebar": "Seitenleiste erweitern",
"collapseSidebar": "Seitenleiste einklappen"
"collapseSidebar": "Seitenleiste einklappen",
"media": "Medien"
},
"header": {
"logout": "Abmelden",
@@ -611,7 +612,14 @@
"embedding": "Einbetten",
"image": "Bild",
"custom": "Brauch",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API für Codex und fortgeschrittene agentische Workflows",
"listModelsDesc": "Alle verfügbaren Modelle aller verbundenen Anbieter auflisten",
"settingsApi": "Settings API",
"settingsApiDesc": "OmniRoute-Konfiguration per API lesen und ändern",
"categoryCore": "Kern-APIs",
"categoryMedia": "Medien & Multi-Modal",
"categoryUtility": "Hilfsmittel & Verwaltung"
},
"health": {
"title": "Systemgesundheit",
@@ -1088,6 +1096,17 @@
"themeSystem": "System",
"hideHealthLogs": "Gesundheitsprüfungsprotokolle ausblenden",
"hideHealthLogsDesc": "Wenn diese Option aktiviert ist, werden [HealthCheck]-Meldungen in der Serverkonsole unterdrückt",
"themeAccent": "Themenfarbe",
"themeAccentDesc": "Wähle eine voreingestellte Farbe oder erstelle dein eigenes Thema mit einer Farbe",
"themeCreate": "Thema erstellen",
"themeCustom": "Benutzerdefiniertes Thema",
"themeCoral": "Koralle",
"themeBlue": "Blau",
"themeRed": "Rot",
"themeGreen": "Grün",
"themeViolet": "Violett",
"themeOrange": "Orange",
"themeCyan": "Cyan",
"promptCache": "Prompt-Cache",
"flushCache": "Cache leeren",
"flushing": "Spülen…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Token, die zum Erstellen von Cache-Einträgen verwendet werden (Fallback auf Eingaberate)",
"customPricingNote": "Sie können die Standardpreise für bestimmte Modelle überschreiben. Benutzerdefinierte Überschreibungen haben Vorrang vor automatisch erkannten Preisen.",
"editPricing": "Preise bearbeiten",
"viewFullDetails": "Vollständige Details anzeigen"
"viewFullDetails": "Vollständige Details anzeigen",
"modelAliasesTitle": "Modell-Aliase",
"addCustomAlias": "Benutzerdefinierten Alias hinzufügen",
"deprecatedModelId": "Veraltete Modell-ID",
"newModelId": "Neue Modell-ID",
"customAliases": "Benutzerdefinierte Aliase",
"builtInAliases": "Integrierte Aliase",
"backgroundDegradationTitle": "Hintergrundaufgaben-Degradierung",
"backgroundDegradationDesc": "Erkennt automatisch Hintergrundaufgaben (Titel, Zusammenfassungen) und leitet an günstigere Modelle weiter",
"enableDegradation": "Hintergrund-Degradierung aktivieren",
"enableDegradationHint": "Wenn aktiviert, werden Hintergrundaufgaben wie Titelgenerierung und Zusammenfassungen automatisch an günstigere Modelle weitergeleitet",
"tasksDetected": "Aufgaben erkannt",
"degradationMap": "Modell-Degradierungskarte",
"premiumModel": "Premium-Modell",
"cheapModel": "Günstiges Modell",
"detectionPatterns": "Erkennungsmuster",
"newPattern": "z.B. \"einen Titel generieren\""
},
"translator": {
"title": "Übersetzer",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute wird „wie besehen“ ohne Gewährleistung jeglicher Art bereitgestellt. Wir sind nicht verantwortlich für Kosten, die durch API-Nutzung, Dienstunterbrechungen oder Datenverlust entstehen. Bewahren Sie immer Backups Ihrer Konfiguration auf.",
"termsSection6Title": "6. Open Source",
"termsSection6Text": "OmniRoute ist Open-Source-Software. Es steht Ihnen frei, es im Rahmen der Lizenzbedingungen zu prüfen, zu ändern und zu verbreiten."
},
"media": {
"title": "Medien-Playground",
"subtitle": "Erstelle Bilder, Videos und Musik",
"model": "Model",
"prompt": "Prompt",
"generate": "Generieren",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+58 -3
View File
@@ -89,7 +89,27 @@
"serverDisconnected": "Server Disconnected",
"serverDisconnectedMsg": "The proxy server has been stopped or is restarting.",
"expandSidebar": "Expand sidebar",
"collapseSidebar": "Collapse sidebar"
"collapseSidebar": "Collapse sidebar",
"themes": "Themes",
"presetColors": "Popular colors",
"createTheme": "Create theme",
"chooseColor": "Pick one color",
"themeCoral": "Coral",
"themeBlue": "Blue",
"themeRed": "Red",
"themeGreen": "Green",
"themeViolet": "Violet",
"themeOrange": "Orange",
"themeCyan": "Cyan"
},
"themesPage": {
"title": "Themes",
"description": "Choose a preset theme or create your own with a single color",
"presetColors": "Popular colors",
"customTheme": "Custom theme",
"customThemeDesc": "Click create theme and pick one color",
"createTheme": "Create theme",
"activePreset": "Active theme"
},
"header": {
"logout": "Logout",
@@ -113,7 +133,9 @@
"openaiCompatible": "OpenAI Compatible",
"anthropicCompatible": "Anthropic Compatible",
"media": "Media",
"mediaDescription": "Generate images, videos, and music"
"mediaDescription": "Generate images, videos, and music",
"themes": "Themes",
"themesDescription": "Choose a color theme for the whole dashboard panel"
},
"home": {
"quickStart": "Quick Start",
@@ -265,7 +287,7 @@
},
"media": {
"title": "Media Playground",
"subtitle": "Generate images, videos, and music using your configured providers",
"subtitle": "Generate images, videos, and music",
"model": "Model",
"prompt": "Prompt",
"generate": "Generate",
@@ -591,6 +613,13 @@
"textToSpeechDesc": "Convert text to natural-sounding speech",
"moderations": "Moderations",
"moderationsDesc": "Content moderation and safety classification",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"settingsApi": "Settings API",
"categoryCore": "Core APIs",
"categoryMedia": "Media & Multi-Modal",
"categoryUtility": "Utility & Management",
"enableCloudTitle": "Enable Cloud Proxy",
"whatYouGet": "What you will get",
"cloudBenefitAccess": "Access your API from anywhere in the world",
@@ -1106,6 +1135,16 @@
"themeSystem": "System",
"hideHealthLogs": "Hide Health Check Logs",
"hideHealthLogsDesc": "When ON, suppress [HealthCheck] messages in server console",
"themeAccent": "Theme color",
"themeAccentDesc": "Choose a preset color or create your own theme with one color",
"themeCreate": "Create theme",
"themeCustom": "Custom theme",
"themeBlue": "Blue",
"themeRed": "Red",
"themeGreen": "Green",
"themeViolet": "Violet",
"themeOrange": "Orange",
"themeCyan": "Cyan",
"promptCache": "Prompt Cache",
"flushCache": "Flush Cache",
"flushing": "Flushing…",
@@ -1180,7 +1219,23 @@
"stickyLimit": "Sticky Limit",
"stickyLimitDesc": "Calls per account before switching",
"modelAliases": "Model Aliases",
"modelAliasesTitle": "Model Aliases",
"modelAliasesDesc": "Wildcard patterns to remap model names • Use * and ?",
"addCustomAlias": "Add Custom Alias",
"deprecatedModelId": "Deprecated model ID",
"newModelId": "New model ID",
"customAliases": "Custom Aliases",
"builtInAliases": "Built-in Aliases",
"backgroundDegradationTitle": "Background Task Degradation",
"backgroundDegradationDesc": "Auto-detect background tasks (titles, summaries) and route to cheaper models",
"enableDegradation": "Enable Background Degradation",
"enableDegradationHint": "When enabled, background tasks like title generation and summarization are routed to cheaper models automatically",
"tasksDetected": "Tasks detected",
"degradationMap": "Model Degradation Map",
"premiumModel": "Premium model",
"cheapModel": "Cheap model",
"detectionPatterns": "Detection Patterns",
"newPattern": "e.g. \"generate a title\"",
"aliasPatternPlaceholder": "claude-sonnet-*",
"aliasTargetPlaceholder": "claude-sonnet-4-20250514",
"pattern": "Pattern",
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Servidor desconectado",
"serverDisconnectedMsg": "El servidor proxy se ha detenido o se está reiniciando.",
"expandSidebar": "Expandir barra lateral",
"collapseSidebar": "Contraer barra lateral"
"collapseSidebar": "Contraer barra lateral",
"media": "Multimedia"
},
"header": {
"logout": "Cerrar sesión",
@@ -611,7 +612,14 @@
"embedding": "incrustar",
"image": "Imagen",
"custom": "personalizado",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "API Responses de OpenAI para Codex y flujos de trabajo agénticos avanzados",
"listModelsDesc": "Listar todos los modelos disponibles en todos los proveedores conectados",
"settingsApi": "Settings API",
"settingsApiDesc": "Leer y modificar la configuración de OmniRoute a través de la API",
"categoryCore": "APIs Principales",
"categoryMedia": "Medios y Multi-Modal",
"categoryUtility": "Utilidades y Gestión"
},
"health": {
"title": "Estado del sistema",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistema",
"hideHealthLogs": "Ocultar registros de verificación de estado",
"hideHealthLogsDesc": "Cuando está activado, suprime los mensajes [HealthCheck] en la consola del servidor",
"themeAccent": "Color del tema",
"themeAccentDesc": "Elige un color predefinido o crea tu propio tema con un solo color",
"themeCreate": "Crear tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Rojo",
"themeGreen": "Verde",
"themeViolet": "Violeta",
"themeOrange": "Naranja",
"themeCyan": "Cian",
"promptCache": "Caché de aviso",
"flushCache": "Vaciar caché",
"flushing": "Sonrojándose…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens utilizados para crear entradas de caché (retroceso a la tasa de entrada)",
"customPricingNote": "Puede anular los precios predeterminados para modelos específicos. Las anulaciones personalizadas tienen prioridad sobre los precios detectados automáticamente.",
"editPricing": "Editar precios",
"viewFullDetails": "Ver todos los detalles"
"viewFullDetails": "Ver todos los detalles",
"modelAliasesTitle": "Aliases de Modelo",
"addCustomAlias": "Agregar Alias Personalizado",
"deprecatedModelId": "ID del modelo obsoleto",
"newModelId": "Nuevo ID del modelo",
"customAliases": "Aliases Personalizados",
"builtInAliases": "Aliases Integrados",
"backgroundDegradationTitle": "Degradación de Tareas en Segundo Plano",
"backgroundDegradationDesc": "Detecta automáticamente tareas en segundo plano (títulos, resúmenes) y redirige a modelos más baratos",
"enableDegradation": "Activar Degradación en Segundo Plano",
"enableDegradationHint": "Cuando está activado, las tareas en segundo plano como generación de títulos y resúmenes se redirigen automáticamente a modelos más baratos",
"tasksDetected": "Tareas detectadas",
"degradationMap": "Mapa de Degradación de Modelos",
"premiumModel": "Modelo premium",
"cheapModel": "Modelo económico",
"detectionPatterns": "Patrones de Detección",
"newPattern": "ej: \"generar un título\""
},
"translator": {
"title": "Traductor",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute se proporciona \"tal cual\" sin garantía de ningún tipo. No somos responsables de los costos incurridos por el uso de API, interrupciones del servicio o pérdida de datos. Mantenga siempre copias de seguridad de su configuración.",
"termsSection6Title": "6. Código abierto",
"termsSection6Text": "OmniRoute es un software de código abierto. Usted es libre de inspeccionarlo, modificarlo y distribuirlo según los términos de su licencia."
},
"media": {
"title": "Zona multimedia",
"subtitle": "Genera imágenes, videos y música",
"model": "Model",
"prompt": "Prompt",
"generate": "Generar",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Palvelin yhteys katkaistu",
"serverDisconnectedMsg": "Välityspalvelin on pysäytetty tai käynnistyy uudelleen.",
"expandSidebar": "Laajenna sivupalkki",
"collapseSidebar": "Tiivistä sivupalkki"
"collapseSidebar": "Tiivistä sivupalkki",
"media": "Media"
},
"header": {
"logout": "Kirjaudu ulos",
@@ -611,7 +612,14 @@
"embedding": "Upottaminen",
"image": "Kuva",
"custom": "mukautettu",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Ydin-API:t",
"categoryMedia": "Media & Monimuotoinen",
"categoryUtility": "Apuohjelmat & Hallinta"
},
"health": {
"title": "Järjestelmän terveys",
@@ -1088,6 +1096,17 @@
"themeSystem": "Järjestelmä",
"hideHealthLogs": "Piilota kuntotarkastuslokit",
"hideHealthLogsDesc": "Kun PÄÄLLÄ, estä [HealthCheck]-viestit palvelinkonsolissa",
"themeAccent": "Teeman väri",
"themeAccentDesc": "Valitse valmis väri tai luo oma teemasi yhdellä värillä",
"themeCreate": "Luo teema",
"themeCustom": "Mukautettu teema",
"themeCoral": "Koralli",
"themeBlue": "Sininen",
"themeRed": "Punainen",
"themeGreen": "Vihreä",
"themeViolet": "Violetti",
"themeOrange": "Oranssi",
"themeCyan": "Syaani",
"promptCache": "Kehotusvälimuisti",
"flushCache": "Tyhjennä välimuisti",
"flushing": "Huuhdellaan…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Välimuistimerkintöjen luomiseen käytetyt tunnukset (varausarvo syöttönopeuteen)",
"customPricingNote": "Voit ohittaa tiettyjen mallien oletushinnoittelun. Mukautetut ohitukset ovat etusijalla automaattisesti tunnistettuihin hinnoitteluun nähden.",
"editPricing": "Muokkaa hinnoittelua",
"viewFullDetails": "Näytä täydelliset tiedot"
"viewFullDetails": "Näytä täydelliset tiedot",
"modelAliasesTitle": "Mallialias",
"addCustomAlias": "Lisää Mukautettu Alias",
"deprecatedModelId": "Vanhentunut malli-ID",
"newModelId": "Uusi malli-ID",
"customAliases": "Mukautetut Aliakset",
"builtInAliases": "Sisäänrakennetut Aliakset",
"backgroundDegradationTitle": "Taustatyö Degradointi",
"backgroundDegradationDesc": "Tunnistaa automaattisesti taustatyöt (otsikot, yhteenvedot) ja ohjaa halvempiin malleihin",
"enableDegradation": "Ota Taustadegraadointi Käyttöön",
"enableDegradationHint": "Kun käytössä, taustatyöt kuten otsikoiden luominen ja yhteenvedot ohjataan automaattisesti halvempiin malleihin",
"tasksDetected": "Tehtävät tunnistettu",
"degradationMap": "Mallin Degradaatiokartta",
"premiumModel": "Premium-malli",
"cheapModel": "Halpa malli",
"detectionPatterns": "Tunnistusmallit",
"newPattern": "esim. \"luo otsikko\""
},
"translator": {
"title": "Kääntäjä",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute toimitetaan \"sellaisenaan\" ilman minkäänlaista takuuta. Emme ole vastuussa kustannuksista, jotka aiheutuvat API:n käytöstä, palveluhäiriöistä tai tietojen katoamisesta. Pidä aina varmuuskopiot asetuksistasi.",
"termsSection6Title": "6. Avoin lähdekoodi",
"termsSection6Text": "OmniRoute on avoimen lähdekoodin ohjelmisto. Voit vapaasti tarkastaa, muokata ja jakaa sitä sen lisenssiehtojen mukaisesti."
},
"media": {
"title": "Mediatyöpaja",
"subtitle": "Luo kuvia, videoita ja musiikkia",
"model": "Model",
"prompt": "Prompt",
"generate": "Luo",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Serveur déconnecté",
"serverDisconnectedMsg": "Le serveur proxy a été arrêté ou est en train de redémarrer.",
"expandSidebar": "Développer la barre latérale",
"collapseSidebar": "Réduire la barre latérale"
"collapseSidebar": "Réduire la barre latérale",
"media": "Médias"
},
"header": {
"logout": "Déconnexion",
@@ -611,7 +612,14 @@
"embedding": "Intégration",
"image": "Images",
"custom": "personnalisé",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "API Responses d'OpenAI pour Codex et workflows agentiques avancés",
"listModelsDesc": "Lister tous les modèles disponibles sur tous les fournisseurs connectés",
"settingsApi": "Settings API",
"settingsApiDesc": "Lire et modifier la configuration d'OmniRoute via l'API",
"categoryCore": "APIs Principales",
"categoryMedia": "Médias et Multi-Modal",
"categoryUtility": "Utilitaires et Gestion"
},
"health": {
"title": "Santé du système",
@@ -1088,6 +1096,17 @@
"themeSystem": "Système",
"hideHealthLogs": "Masquer les journaux de contrôle de santé",
"hideHealthLogsDesc": "Lorsqu'il est activé, supprimez les messages [HealthCheck] dans la console du serveur",
"themeAccent": "Couleur du thème",
"themeAccentDesc": "Choisissez une couleur prédéfinie ou créez votre propre thème avec une seule couleur",
"themeCreate": "Créer un thème",
"themeCustom": "Thème personnalisé",
"themeCoral": "Corail",
"themeBlue": "Bleu",
"themeRed": "Rouge",
"themeGreen": "Vert",
"themeViolet": "Violet",
"themeOrange": "Orange",
"themeCyan": "Cyan",
"promptCache": "Cache d'invite",
"flushCache": "Vider le cache",
"flushing": "Rinçage…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Jetons utilisés pour créer des entrées de cache (repli sur le débit d'entrée)",
"customPricingNote": "Vous pouvez remplacer le prix par défaut pour des modèles spécifiques. Les remplacements personnalisés ont la priorité sur les prix détectés automatiquement.",
"editPricing": "Modifier le prix",
"viewFullDetails": "Afficher tous les détails"
"viewFullDetails": "Afficher tous les détails",
"modelAliasesTitle": "Alias de Modèles",
"addCustomAlias": "Ajouter un Alias Personnalisé",
"deprecatedModelId": "ID du modèle obsolète",
"newModelId": "Nouvel ID de modèle",
"customAliases": "Alias Personnalisés",
"builtInAliases": "Alias Intégrés",
"backgroundDegradationTitle": "Dégradation des Tâches en Arrière-plan",
"backgroundDegradationDesc": "Détecte automatiquement les tâches en arrière-plan (titres, résumés) et redirige vers des modèles moins chers",
"enableDegradation": "Activer la Dégradation en Arrière-plan",
"enableDegradationHint": "Lorsqu'activé, les tâches en arrière-plan comme la génération de titres et les résumés sont redirigées automatiquement vers des modèles moins chers",
"tasksDetected": "Tâches détectées",
"degradationMap": "Carte de Dégradation des Modèles",
"premiumModel": "Modèle premium",
"cheapModel": "Modèle économique",
"detectionPatterns": "Modèles de Détection",
"newPattern": "ex: \"générer un titre\""
},
"translator": {
"title": "Traducteur",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute est fourni « tel quel », sans garantie d'aucune sorte. Nous ne sommes pas responsables des coûts occasionnés par l'utilisation de l'API, les interruptions de service ou la perte de données. Conservez toujours des sauvegardes de votre configuration.",
"termsSection6Title": "6. Ouvrir la source",
"termsSection6Text": "OmniRoute est un logiciel open source. Vous êtes libre de l'inspecter, de le modifier et de le distribuer selon les termes de sa licence."
},
"media": {
"title": "Espace média",
"subtitle": "Générez des images, des vidéos et de la musique",
"model": "Model",
"prompt": "Prompt",
"generate": "Générer",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "השרת מנותק",
"serverDisconnectedMsg": "שרת ה-proxy נעצר או מופעל מחדש.",
"expandSidebar": "הרחב את סרגל הצד",
"collapseSidebar": "כווץ את סרגל הצד"
"collapseSidebar": "כווץ את סרגל הצד",
"media": "מדיה"
},
"header": {
"logout": "התנתק",
@@ -611,7 +612,14 @@
"embedding": "הטבעה",
"image": "תמונה",
"custom": "מותאם אישית",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "ממשקי API ליבה",
"categoryMedia": "מדיה ומולטי-מודלי",
"categoryUtility": "כלים וניהול"
},
"health": {
"title": "בריאות המערכת",
@@ -1088,6 +1096,17 @@
"themeSystem": "מערכת",
"hideHealthLogs": "הסתר יומני בדיקת בריאות",
"hideHealthLogsDesc": "כאשר מופעל, דחק הודעות [HealthCheck] במסוף השרת",
"themeAccent": "צבע ערכת נושא",
"themeAccentDesc": "בחר צבע מוכן מראש או צור ערכת נושא משלך עם צבע אחד",
"themeCreate": "צור ערכת נושא",
"themeCustom": "ערכת נושא מותאמת אישית",
"themeCoral": "אלמוג",
"themeBlue": "כחול",
"themeRed": "אדום",
"themeGreen": "ירוק",
"themeViolet": "סגול",
"themeOrange": "כתום",
"themeCyan": "ציאן",
"promptCache": "הפקודה מטמון",
"flushCache": "לשטוף את המטמון",
"flushing": "שוטף…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "אסימונים המשמשים ליצירת ערכי מטמון (חזרה לקצב קלט)",
"customPricingNote": "אתה יכול לעקוף את תמחור ברירת המחדל עבור דגמים ספציפיים. עקיפות מותאמות אישית מקבלות עדיפות על פני תמחור שזוהה אוטומטית.",
"editPricing": "ערוך תמחור",
"viewFullDetails": "צפה בפרטים המלאים"
"viewFullDetails": "צפה בפרטים המלאים",
"modelAliasesTitle": "כינויי מודלים",
"addCustomAlias": "הוסף כינוי מותאם אישית",
"deprecatedModelId": "מזהה מודל מיושן",
"newModelId": "מזהה מודל חדש",
"customAliases": "כינויים מותאמים אישית",
"builtInAliases": "כינויים מובנים",
"backgroundDegradationTitle": "הפחתת משימות רקע",
"backgroundDegradationDesc": "זיהוי אוטומטי של משימות רקע (כותרות, סיכומים) והפניה למודלים זולים יותר",
"enableDegradation": "הפעל הפחתת משימות רקע",
"enableDegradationHint": "כאשר מופעל, משימות רקע כמו יצירת כותרות וסיכומים מופנות אוטומטית למודלים זולים יותר",
"tasksDetected": "משימות שזוהו",
"degradationMap": "מפת הפחתת מודלים",
"premiumModel": "מודל פרימיום",
"cheapModel": "מודל זול",
"detectionPatterns": "דפוסי זיהוי",
"newPattern": "לדוגמה: \"צור כותרת\""
},
"translator": {
"title": "מתרגם",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute מסופק \"כמות שהוא\" ללא אחריות מכל סוג שהוא. איננו אחראים לכל עלויות שייגרמו כתוצאה משימוש ב-API, שיבושים בשירות או אובדן נתונים. שמור תמיד גיבויים של התצורה שלך.",
"termsSection6Title": "6. קוד פתוח",
"termsSection6Text": "OmniRoute היא תוכנת קוד פתוח. אתה חופשי לבדוק, לשנות ולהפיץ אותו תחת תנאי הרישיון שלו."
},
"media": {
"title": "אולפן מדיה",
"subtitle": "צור תמונות, סרטונים ומוזיקה",
"model": "Model",
"prompt": "Prompt",
"generate": "צור",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "A szerver leválasztva",
"serverDisconnectedMsg": "A proxyszerver leállt vagy újraindul.",
"expandSidebar": "Az oldalsáv kibontása",
"collapseSidebar": "Oldalsáv összecsukása"
"collapseSidebar": "Oldalsáv összecsukása",
"media": "Média"
},
"header": {
"logout": "Kijelentkezés",
@@ -611,7 +612,14 @@
"embedding": "Beágyazás",
"image": "Kép",
"custom": "szokás",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Alap API-k",
"categoryMedia": "Média és Multi-Modális",
"categoryUtility": "Segédeszközök és Kezelés"
},
"health": {
"title": "Rendszer egészsége",
@@ -1088,6 +1096,17 @@
"themeSystem": "Rendszer",
"hideHealthLogs": "Állapotellenőrzési naplók elrejtése",
"hideHealthLogsDesc": "Ha BE van kapcsolva, tiltsa le a [HealthCheck] üzeneteket a kiszolgálókonzolon",
"themeAccent": "Téma színe",
"themeAccentDesc": "Válassz előre beállított színt, vagy készíts saját témát egy színből",
"themeCreate": "Téma létrehozása",
"themeCustom": "Egyéni téma",
"themeCoral": "Korall",
"themeBlue": "Kék",
"themeRed": "Piros",
"themeGreen": "Zöld",
"themeViolet": "Ibolya",
"themeOrange": "Narancs",
"themeCyan": "Cián",
"promptCache": "Prompt Cache",
"flushCache": "Öblítse ki a gyorsítótárat",
"flushing": "Öblítés…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "A gyorsítótár bejegyzéseinek létrehozására használt tokenek (vissza a beviteli sebességre)",
"customPricingNote": "Egyes modelleknél felülbírálhatja az alapértelmezett árazást. Az egyéni felülbírálások elsőbbséget élveznek az automatikusan észlelt árképzéssel szemben.",
"editPricing": "Árak szerkesztése",
"viewFullDetails": "Teljes részletek megtekintése"
"viewFullDetails": "Teljes részletek megtekintése",
"modelAliasesTitle": "Modell aliasok",
"addCustomAlias": "Egyéni Alias Hozzáadása",
"deprecatedModelId": "Elavult modell ID",
"newModelId": "Új modell ID",
"customAliases": "Egyéni aliasok",
"builtInAliases": "Beépített aliasok",
"backgroundDegradationTitle": "Háttérfeladat-degradáció",
"backgroundDegradationDesc": "Automatikusan felismeri a háttérfeladatokat (címek, összefoglalók) és olcsóbb modellekre irányítja",
"enableDegradation": "Háttérfeladat-degradáció engedélyezése",
"enableDegradationHint": "Ha engedélyezve van, a háttérfeladatok, mint a címek generálása és összefoglalások, automatikusan olcsóbb modellekre irányítódnak",
"tasksDetected": "Feladatok érzékelve",
"degradationMap": "Modell-degradáció térkép",
"premiumModel": "Prémium modell",
"cheapModel": "Olcsó modell",
"detectionPatterns": "Érzékelési minták",
"newPattern": "pl. \"generálj egy címet\""
},
"translator": {
"title": "Fordító",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "Az OmniRoute \"ahogy van\" mindenféle garancia nélkül biztosított. Nem vállalunk felelősséget az API használatából, a szolgáltatás megszakadásából vagy az adatvesztésből eredő költségekért. Mindig készítsen biztonsági másolatot a konfigurációjáról.",
"termsSection6Title": "6. Nyílt forráskód",
"termsSection6Text": "Az OmniRoute egy nyílt forráskódú szoftver. Ön szabadon megvizsgálhatja, módosíthatja és terjesztheti a licenc feltételei szerint."
},
"media": {
"title": "Média játszótér",
"subtitle": "Képek, videók és zene generálása",
"model": "Model",
"prompt": "Prompt",
"generate": "Generálás",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server Terputus",
"serverDisconnectedMsg": "Server proxy telah dihentikan atau dimulai ulang.",
"expandSidebar": "Luaskan bilah sisi",
"collapseSidebar": "Ciutkan bilah sisi"
"collapseSidebar": "Ciutkan bilah sisi",
"media": "Media"
},
"header": {
"logout": "Keluar",
@@ -611,7 +612,14 @@
"embedding": "Penyematan",
"image": "Gambar",
"custom": "adat",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "API Inti",
"categoryMedia": "Media & Multi-Modal",
"categoryUtility": "Utilitas & Manajemen"
},
"health": {
"title": "Kesehatan Sistem",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistem",
"hideHealthLogs": "Sembunyikan Log Pemeriksaan Kesehatan",
"hideHealthLogsDesc": "Saat AKTIF, sembunyikan pesan [HealthCheck] di konsol server",
"themeAccent": "Warna tema",
"themeAccentDesc": "Pilih warna preset atau buat tema Anda sendiri dengan satu warna",
"themeCreate": "Buat tema",
"themeCustom": "Tema kustom",
"themeCoral": "Koral",
"themeBlue": "Biru",
"themeRed": "Merah",
"themeGreen": "Hijau",
"themeViolet": "Ungu",
"themeOrange": "Oranye",
"themeCyan": "Sian",
"promptCache": "Tembolok Cepat",
"flushCache": "Siram Cache",
"flushing": "Pembilasan…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Token yang digunakan untuk membuat entri cache (pengembalian ke tingkat input)",
"customPricingNote": "Anda dapat mengganti harga default untuk model tertentu. Penggantian khusus lebih diprioritaskan dibandingkan harga yang terdeteksi otomatis.",
"editPricing": "Sunting Harga",
"viewFullDetails": "Lihat Detail Lengkap"
"viewFullDetails": "Lihat Detail Lengkap",
"modelAliasesTitle": "Alias Model",
"addCustomAlias": "Tambah Alias Kustom",
"deprecatedModelId": "ID model yang sudah usang",
"newModelId": "ID model baru",
"customAliases": "Alias Kustom",
"builtInAliases": "Alias Bawaan",
"backgroundDegradationTitle": "Degradasi Tugas Latar Belakang",
"backgroundDegradationDesc": "Deteksi otomatis tugas latar belakang (judul, ringkasan) dan arahkan ke model yang lebih murah",
"enableDegradation": "Aktifkan Degradasi Latar Belakang",
"enableDegradationHint": "Saat diaktifkan, tugas latar belakang seperti pembuatan judul dan ringkasan diarahkan ke model yang lebih murah secara otomatis",
"tasksDetected": "Tugas terdeteksi",
"degradationMap": "Peta Degradasi Model",
"premiumModel": "Model premium",
"cheapModel": "Model murah",
"detectionPatterns": "Pola Deteksi",
"newPattern": "cth. \"buat judul\""
},
"translator": {
"title": "Penerjemah",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute disediakan \"sebagaimana adanya\" tanpa jaminan apa pun. Kami tidak bertanggung jawab atas segala biaya yang timbul akibat penggunaan API, gangguan layanan, atau kehilangan data. Selalu simpan cadangan konfigurasi Anda.",
"termsSection6Title": "6. Sumber Terbuka",
"termsSection6Text": "OmniRoute adalah perangkat lunak sumber terbuka. Anda bebas memeriksa, memodifikasi, dan mendistribusikannya berdasarkan ketentuan lisensinya."
},
"media": {
"title": "Playground Media",
"subtitle": "Hasilkan gambar, video, dan musik",
"model": "Model",
"prompt": "Prompt",
"generate": "Hasilkan",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "सर्वर डिसकनेक्ट हो गया",
"serverDisconnectedMsg": "प्रॉक्सी सर्वर बंद कर दिया गया है या पुनः प्रारंभ हो रहा है।",
"expandSidebar": "साइडबार का विस्तार करें",
"collapseSidebar": "साइडबार को संक्षिप्त करें"
"collapseSidebar": "साइडबार को संक्षिप्त करें",
"media": "मीडिया"
},
"header": {
"logout": "लॉगआउट करें",
@@ -611,7 +612,14 @@
"embedding": "एंबेडिंग",
"image": "छवि",
"custom": "कस्टम",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "कोर API",
"categoryMedia": "मीडिया और मल्टी-मोडल",
"categoryUtility": "उपयोगिता और प्रबंधन"
},
"health": {
"title": "सिस्टम स्वास्थ्य",
@@ -1088,6 +1096,17 @@
"themeSystem": "सिस्टम",
"hideHealthLogs": "स्वास्थ्य जांच लॉग छुपाएं",
"hideHealthLogsDesc": "चालू होने पर, सर्वर कंसोल में [हेल्थचेक] संदेशों को दबाएँ",
"themeAccent": "थीम का रंग",
"themeAccentDesc": "एक प्रीसेट रंग चुनें या एक ही रंग से अपनी थीम बनाएं",
"themeCreate": "थीम बनाएं",
"themeCustom": "कस्टम थीम",
"themeCoral": "Koral",
"themeBlue": "नीला",
"themeRed": "लाल",
"themeGreen": "हरा",
"themeViolet": "बैंगनी",
"themeOrange": "नारंगी",
"themeCyan": "सियान",
"promptCache": "शीघ्र कैश",
"flushCache": "कैश फ्लश करें",
"flushing": "निस्तब्धता...",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "कैश प्रविष्टियाँ बनाने के लिए उपयोग किए जाने वाले टोकन (इनपुट दर पर फ़ॉलबैक)",
"customPricingNote": "आप विशिष्ट मॉडलों के लिए डिफ़ॉल्ट मूल्य निर्धारण को ओवरराइड कर सकते हैं। कस्टम ओवरराइड्स को स्वतः-पता लगाए गए मूल्य-निर्धारण पर प्राथमिकता दी जाती है।",
"editPricing": "मूल्य निर्धारण संपादित करें",
"viewFullDetails": "पूर्ण विवरण देखें"
"viewFullDetails": "पूर्ण विवरण देखें",
"modelAliasesTitle": "Model Alias",
"addCustomAlias": "Kustom Alias Tambahkan",
"deprecatedModelId": "ID model yang tidak digunakan lagi",
"newModelId": "ID model baru",
"customAliases": "Alias Kustom",
"builtInAliases": "Alias Bawaan",
"backgroundDegradationTitle": "पृष्ठभूमि कार्य अवनमन",
"backgroundDegradationDesc": "पृष्ठभूमि कार्यों (शीर्षक, सारांश) को स्वचालित रूप से पहचानें और सस्ते मॉडल पर रूट करें",
"enableDegradation": "बैकग्राउंड डिग्रेडेशन सक्षम करें",
"enableDegradationHint": "जब सक्षम होता है, तो शीर्षक निर्माण और सारांश जैसे पृष्ठभूमि कार्य स्वचालित रूप से सस्ते मॉडल पर रूट किए जाते हैं",
"tasksDetected": "पता लगाए गए कार्य",
"degradationMap": "मॉडल अवनमन मानचित्र",
"premiumModel": "प्रीमियम मॉडल",
"cheapModel": "सस्ता मॉडल",
"detectionPatterns": "पता लगाने के पैटर्न",
"newPattern": "उदा. \"एक शीर्षक बनाएं\""
},
"translator": {
"title": "अनुवादक",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "ओम्निरूट को किसी भी प्रकार की वारंटी के बिना \"जैसा है\" प्रदान किया जाता है। हम एपीआई उपयोग, सेवा व्यवधान या डेटा हानि के कारण होने वाली किसी भी लागत के लिए ज़िम्मेदार नहीं हैं। हमेशा अपने कॉन्फ़िगरेशन का बैकअप बनाए रखें।",
"termsSection6Title": "6. खुला स्रोत",
"termsSection6Text": "ओमनीरूट ओपन-सोर्स सॉफ्टवेयर है। आप इसके लाइसेंस की शर्तों के तहत इसका निरीक्षण, संशोधन और वितरण करने के लिए स्वतंत्र हैं।"
},
"media": {
"title": "मीडिया प्लेग्राउंड",
"subtitle": "छवियाँ, वीडियो और संगीत उत्पन्न करें",
"model": "Model",
"prompt": "Prompt",
"generate": "उत्पन्न करें",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server disconnesso",
"serverDisconnectedMsg": "Il server proxy è stato arrestato o si sta riavviando.",
"expandSidebar": "Espandi la barra laterale",
"collapseSidebar": "Comprimi la barra laterale"
"collapseSidebar": "Comprimi la barra laterale",
"media": "Media"
},
"header": {
"logout": "Esci",
@@ -611,7 +612,14 @@
"embedding": "Incorporamento",
"image": "Immagine",
"custom": "costume",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "API Responses di OpenAI per Codex e workflow agentici avanzati",
"listModelsDesc": "Elenca tutti i modelli disponibili su tutti i provider connessi",
"settingsApi": "Settings API",
"settingsApiDesc": "Leggere e modificare la configurazione di OmniRoute tramite API",
"categoryCore": "API Principali",
"categoryMedia": "Media e Multi-Modale",
"categoryUtility": "Utilità e Gestione"
},
"health": {
"title": "Salute del sistema",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistema",
"hideHealthLogs": "Nascondi i registri di controllo dello stato",
"hideHealthLogsDesc": "Quando è attivo, elimina i messaggi [HealthCheck] nella console del server",
"themeAccent": "Colore del tema",
"themeAccentDesc": "Scegli un colore predefinito o crea il tuo tema con un solo colore",
"themeCreate": "Crea tema",
"themeCustom": "Tema personalizzato",
"themeCoral": "Corallo",
"themeBlue": "Blu",
"themeRed": "Rosso",
"themeGreen": "Verde",
"themeViolet": "Viola",
"themeOrange": "Arancione",
"themeCyan": "Ciano",
"promptCache": "Cache dei suggerimenti",
"flushCache": "Svuota cache",
"flushing": "Lavaggio…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Token utilizzati per creare voci nella cache (fallback alla velocità di input)",
"customPricingNote": "Puoi sostituire i prezzi predefiniti per modelli specifici. Le sostituzioni personalizzate hanno la priorità sui prezzi rilevati automaticamente.",
"editPricing": "Modifica prezzi",
"viewFullDetails": "Visualizza i dettagli completi"
"viewFullDetails": "Visualizza i dettagli completi",
"modelAliasesTitle": "Alias dei Modelli",
"addCustomAlias": "Aggiungi Alias Personalizzato",
"deprecatedModelId": "ID modello deprecato",
"newModelId": "Nuovo ID modello",
"customAliases": "Alias Personalizzati",
"builtInAliases": "Alias Integrati",
"backgroundDegradationTitle": "Degradazione Attività in Background",
"backgroundDegradationDesc": "Rileva automaticamente le attività in background (titoli, riassunti) e reindirizza a modelli più economici",
"enableDegradation": "Abilita Degradazione in Background",
"enableDegradationHint": "Quando abilitato, le attività in background come generazione titoli e riassunti vengono reindirizzate automaticamente a modelli più economici",
"tasksDetected": "Attività rilevate",
"degradationMap": "Mappa di Degradazione dei Modelli",
"premiumModel": "Modello premium",
"cheapModel": "Modello economico",
"detectionPatterns": "Pattern di Rilevamento",
"newPattern": "es: \"genera un titolo\""
},
"translator": {
"title": "Traduttore",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute viene fornito \"così com'è\" senza garanzie di alcun tipo. Non siamo responsabili per eventuali costi sostenuti per l'utilizzo dell'API, interruzioni del servizio o perdita di dati. Mantieni sempre i backup della tua configurazione.",
"termsSection6Title": "6. Sorgente aperta",
"termsSection6Text": "OmniRoute è un software open source. Sei libero di esaminarlo, modificarlo e distribuirlo secondo i termini della sua licenza."
},
"media": {
"title": "Area Media",
"subtitle": "Genera immagini, video e musica",
"model": "Model",
"prompt": "Prompt",
"generate": "Genera",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "サーバーが切断されました",
"serverDisconnectedMsg": "プロキシ サーバーが停止しているか、再起動中です。",
"expandSidebar": "サイドバーを展開する",
"collapseSidebar": "サイドバーを折りたたむ"
"collapseSidebar": "サイドバーを折りたたむ",
"media": "メディア"
},
"header": {
"logout": "ログアウト",
@@ -611,7 +612,14 @@
"embedding": "埋め込み",
"image": "画像",
"custom": "カスタム",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "Codexおよび高度なエージェントワークフロー向けOpenAI Responses API",
"listModelsDesc": "接続されたすべてのプロバイダーの利用可能なモデルを一覧表示",
"settingsApi": "Settings API",
"settingsApiDesc": "API経由でOmniRouteの設定を読み取り・変更",
"categoryCore": "コアAPI",
"categoryMedia": "メディア&マルチモーダル",
"categoryUtility": "ユーティリティ&管理"
},
"health": {
"title": "システムの健全性",
@@ -1088,6 +1096,17 @@
"themeSystem": "システム",
"hideHealthLogs": "ヘルスチェックログを非表示にする",
"hideHealthLogsDesc": "ON の場合、サーバー コンソールで [HealthCheck] メッセージを抑制します",
"themeAccent": "テーマカラー",
"themeAccentDesc": "プリセットカラーを選ぶか、1色で独自のテーマを作成します",
"themeCreate": "テーマを作成",
"themeCustom": "カスタムテーマ",
"themeCoral": "コーラル",
"themeBlue": "青",
"themeRed": "赤",
"themeGreen": "緑",
"themeViolet": "紫",
"themeOrange": "オレンジ",
"themeCyan": "シアン",
"promptCache": "プロンプトキャッシュ",
"flushCache": "キャッシュのフラッシュ",
"flushing": "フラッシング…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "キャッシュ エントリの作成に使用されるトークン (入力レートへのフォールバック)",
"customPricingNote": "特定のモデルのデフォルトの価格をオーバーライドできます。カスタム オーバーライドは、自動検出された価格設定よりも優先されます。",
"editPricing": "価格の編集",
"viewFullDetails": "詳細を表示"
"viewFullDetails": "詳細を表示",
"modelAliasesTitle": "モデルエイリアス",
"addCustomAlias": "カスタムエイリアスを追加",
"deprecatedModelId": "非推奨モデルID",
"newModelId": "新しいモデルID",
"customAliases": "カスタムエイリアス",
"builtInAliases": "組み込みエイリアス",
"backgroundDegradationTitle": "バックグラウンドタスク降格",
"backgroundDegradationDesc": "バックグラウンドタスク(タイトル、要約)を自動検出し、安価なモデルにルーティング",
"enableDegradation": "バックグラウンド降格を有効化",
"enableDegradationHint": "有効にすると、タイトル生成や要約などのバックグラウンドタスクが自動的に安価なモデルにルーティングされます",
"tasksDetected": "検出されたタスク",
"degradationMap": "モデル降格マップ",
"premiumModel": "プレミアムモデル",
"cheapModel": "安価なモデル",
"detectionPatterns": "検出パターン",
"newPattern": "例:\"タイトルを生成\""
},
"translator": {
"title": "翻訳者",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute は、いかなる種類の保証もなく「現状のまま」提供されます。 API の使用、サービスの中断、またはデータの損失によって発生する費用については、当社は責任を負いません。構成のバックアップを常に維持してください。",
"termsSection6Title": "6. オープンソース",
"termsSection6Text": "OmniRoute はオープンソース ソフトウェアです。ライセンス条項に基づいて、自由に検査、変更、配布することができます。"
},
"media": {
"title": "メディアプレイグラウンド",
"subtitle": "画像、動画、音楽を生成",
"model": "Model",
"prompt": "Prompt",
"generate": "生成",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "서버 연결 끊김",
"serverDisconnectedMsg": "프록시 서버가 중지되었거나 다시 시작되는 중입니다.",
"expandSidebar": "사이드바 확장",
"collapseSidebar": "사이드바 접기"
"collapseSidebar": "사이드바 접기",
"media": "미디어"
},
"header": {
"logout": "로그아웃",
@@ -611,7 +612,14 @@
"embedding": "임베딩",
"image": "이미지",
"custom": "관습",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "Codex 및 고급 에이전트 워크플로용 OpenAI Responses API",
"listModelsDesc": "연결된 모든 공급자의 사용 가능한 모든 모델 나열",
"settingsApi": "Settings API",
"settingsApiDesc": "API를 통해 OmniRoute 구성 읽기 및 수정",
"categoryCore": "핵심 API",
"categoryMedia": "미디어 및 멀티모달",
"categoryUtility": "유틸리티 및 관리"
},
"health": {
"title": "시스템 상태",
@@ -1088,6 +1096,17 @@
"themeSystem": "시스템",
"hideHealthLogs": "상태 확인 로그 숨기기",
"hideHealthLogsDesc": "ON일 때, 서버 콘솔에서 [HealthCheck] 메시지를 억제합니다.",
"themeAccent": "테마 색상",
"themeAccentDesc": "미리 설정된 색상을 선택하거나 단일 색상으로 나만의 테마를 만드세요",
"themeCreate": "테마 만들기",
"themeCustom": "사용자 지정 테마",
"themeCoral": "코랄",
"themeBlue": "파랑",
"themeRed": "빨강",
"themeGreen": "초록",
"themeViolet": "보라",
"themeOrange": "주황",
"themeCyan": "시안",
"promptCache": "프롬프트 캐시",
"flushCache": "캐시 플러시",
"flushing": "플러싱…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "캐시 항목을 생성하는 데 사용되는 토큰(입력 속도로 대체)",
"customPricingNote": "특정 모델의 기본 가격을 재정의할 수 있습니다. 맞춤 재정의는 자동 감지된 가격보다 우선 적용됩니다.",
"editPricing": "가격 편집",
"viewFullDetails": "전체 세부정보 보기"
"viewFullDetails": "전체 세부정보 보기",
"modelAliasesTitle": "모델 별칭",
"addCustomAlias": "사용자 지정 별칭 추가",
"deprecatedModelId": "사용 중단된 모델 ID",
"newModelId": "새 모델 ID",
"customAliases": "사용자 지정 별칭",
"builtInAliases": "기본 제공 별칭",
"backgroundDegradationTitle": "백그라운드 작업 다운그레이드",
"backgroundDegradationDesc": "백그라운드 작업(제목, 요약)을 자동 감지하여 저렴한 모델로 라우팅",
"enableDegradation": "백그라운드 다운그레이드 활성화",
"enableDegradationHint": "활성화하면 제목 생성 및 요약과 같은 백그라운드 작업이 자동으로 저렴한 모델로 라우팅됩니다",
"tasksDetected": "감지된 작업",
"degradationMap": "모델 다운그레이드 맵",
"premiumModel": "프리미엄 모델",
"cheapModel": "저렴한 모델",
"detectionPatterns": "감지 패턴",
"newPattern": "예: \"제목 생성\""
},
"translator": {
"title": "번역기",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute는 어떠한 종류의 보증도 없이 \"있는 그대로\" 제공됩니다. API 사용, 서비스 중단 또는 데이터 손실로 인해 발생하는 비용에 대해 당사는 책임을 지지 않습니다. 항상 구성의 백업을 유지하십시오.",
"termsSection6Title": "6. 오픈 소스",
"termsSection6Text": "OmniRoute는 오픈 소스 소프트웨어입니다. 라이센스 조건에 따라 자유롭게 검사, 수정 및 배포할 수 있습니다."
},
"media": {
"title": "미디어 플레이그라운드",
"subtitle": "이미지, 비디오, 음악 생성",
"model": "Model",
"prompt": "Prompt",
"generate": "생성",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Pelayan Terputus Sambungan",
"serverDisconnectedMsg": "Pelayan proksi telah dihentikan atau dimulakan semula.",
"expandSidebar": "Kembangkan bar sisi",
"collapseSidebar": "Runtuhkan bar sisi"
"collapseSidebar": "Runtuhkan bar sisi",
"media": "Media"
},
"header": {
"logout": "Log keluar",
@@ -611,7 +612,14 @@
"embedding": "Membenamkan",
"image": "Imej",
"custom": "adat",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "API Teras",
"categoryMedia": "Media & Multi-Modal",
"categoryUtility": "Utiliti & Pengurusan"
},
"health": {
"title": "Kesihatan Sistem",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistem",
"hideHealthLogs": "Sembunyikan Log Pemeriksaan Kesihatan",
"hideHealthLogsDesc": "Apabila HIDUP, sekat mesej [HealthCheck] dalam konsol pelayan",
"themeAccent": "Warna tema",
"themeAccentDesc": "Pilih warna pratetap atau cipta tema anda sendiri dengan satu warna",
"themeCreate": "Cipta tema",
"themeCustom": "Tema tersuai",
"themeCoral": "Koral",
"themeBlue": "Biru",
"themeRed": "Merah",
"themeGreen": "Hijau",
"themeViolet": "Ungu",
"themeOrange": "Oren",
"themeCyan": "Sian",
"promptCache": "Cache Prompt",
"flushCache": "Flush Cache",
"flushing": "Membilas…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Token yang digunakan untuk mencipta entri cache (sandar kepada kadar input)",
"customPricingNote": "Anda boleh mengatasi harga lalai untuk model tertentu. Penggantian tersuai diutamakan berbanding harga yang dikesan secara automatik.",
"editPricing": "Edit Harga",
"viewFullDetails": "Lihat Butiran Penuh"
"viewFullDetails": "Lihat Butiran Penuh",
"modelAliasesTitle": "Alias Model",
"addCustomAlias": "Tambah Alias Tersuai",
"deprecatedModelId": "ID model yang ditamatkan",
"newModelId": "ID model baharu",
"customAliases": "Alias Tersuai",
"builtInAliases": "Alias Terbina Dalam",
"backgroundDegradationTitle": "Degradasi Tugas Latar Belakang",
"backgroundDegradationDesc": "Kesan tugas latar belakang secara automatik (tajuk, ringkasan) dan halakan ke model yang lebih murah",
"enableDegradation": "Dayakan Degradasi Latar Belakang",
"enableDegradationHint": "Apabila diaktifkan, tugas latar belakang seperti penjanaan tajuk dan ringkasan dihalakan ke model yang lebih murah secara automatik",
"tasksDetected": "Tugas dikesan",
"degradationMap": "Peta Degradasi Model",
"premiumModel": "Model premium",
"cheapModel": "Model murah",
"detectionPatterns": "Corak Pengesanan",
"newPattern": "cth. \"jana tajuk\""
},
"translator": {
"title": "Penterjemah",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute disediakan \"seadanya\" tanpa sebarang jenis waranti. Kami tidak bertanggungjawab untuk sebarang kos yang ditanggung melalui penggunaan API, gangguan perkhidmatan atau kehilangan data. Sentiasa kekalkan sandaran konfigurasi anda.",
"termsSection6Title": "6. Sumber Terbuka",
"termsSection6Text": "OmniRoute ialah perisian sumber terbuka. Anda bebas untuk memeriksa, mengubah suai dan mengedarkannya di bawah syarat lesennya."
},
"media": {
"title": "Ruang Media",
"subtitle": "Jana imej, video dan muzik",
"model": "Model",
"prompt": "Prompt",
"generate": "Jana",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Serververbinding verbroken",
"serverDisconnectedMsg": "De proxyserver is gestopt of wordt opnieuw opgestart.",
"expandSidebar": "Vouw zijbalk uit",
"collapseSidebar": "Zijbalk samenvouwen"
"collapseSidebar": "Zijbalk samenvouwen",
"media": "Media"
},
"header": {
"logout": "Uitloggen",
@@ -611,7 +612,14 @@
"embedding": "Inbedding",
"image": "Afbeelding",
"custom": "gewoonte",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Kern-API's",
"categoryMedia": "Media & Multi-Modaal",
"categoryUtility": "Hulpmiddelen & Beheer"
},
"health": {
"title": "Systeemgezondheid",
@@ -1088,6 +1096,17 @@
"themeSystem": "Systeem",
"hideHealthLogs": "Statuschecklogboeken verbergen",
"hideHealthLogsDesc": "Indien AAN: onderdruk [HealthCheck]-berichten in de serverconsole",
"themeAccent": "Themakleur",
"themeAccentDesc": "Kies een vooraf ingestelde kleur of maak je eigen thema met één kleur",
"themeCreate": "Thema maken",
"themeCustom": "Aangepast thema",
"themeCoral": "Koraal",
"themeBlue": "Blauw",
"themeRed": "Rood",
"themeGreen": "Groen",
"themeViolet": "Violet",
"themeOrange": "Oranje",
"themeCyan": "Cyaan",
"promptCache": "Snelle cache",
"flushCache": "Cache leegmaken",
"flushing": "Doorspoelen…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens die worden gebruikt om cache-items te maken (terugval op invoersnelheid)",
"customPricingNote": "U kunt de standaardprijzen voor specifieke modellen overschrijven. Aangepaste overschrijvingen hebben voorrang op automatisch gedetecteerde prijzen.",
"editPricing": "Prijzen bewerken",
"viewFullDetails": "Bekijk volledige details"
"viewFullDetails": "Bekijk volledige details",
"modelAliasesTitle": "Model Aliases",
"addCustomAlias": "Aangepast Alias Toevoegen",
"deprecatedModelId": "Verouderd model-ID",
"newModelId": "Nieuw model-ID",
"customAliases": "Aangepaste Aliases",
"builtInAliases": "Ingebouwde Aliases",
"backgroundDegradationTitle": "Achtergrondtaak Degradatie",
"backgroundDegradationDesc": "Detecteert automatisch achtergrondtaken (titels, samenvattingen) en routeert naar goedkopere modellen",
"enableDegradation": "Achtergrond Degradatie Inschakelen",
"enableDegradationHint": "Wanneer ingeschakeld, worden achtergrondtaken zoals titelgeneratie en samenvattingen automatisch naar goedkopere modellen gerouteerd",
"tasksDetected": "Taken gedetecteerd",
"degradationMap": "Model Degradatie Kaart",
"premiumModel": "Premium model",
"cheapModel": "Goedkoop model",
"detectionPatterns": "Detectiepatronen",
"newPattern": "bijv. \"genereer een titel\""
},
"translator": {
"title": "Vertaler",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute wordt geleverd \"zoals het is\" zonder enige vorm van garantie. Wij zijn niet verantwoordelijk voor eventuele kosten die voortvloeien uit API-gebruik, serviceonderbrekingen of gegevensverlies. Zorg altijd voor back-ups van uw configuratie.",
"termsSection6Title": "6. Open-source",
"termsSection6Text": "OmniRoute is open source-software. U bent vrij om het te inspecteren, wijzigen en distribueren onder de voorwaarden van de licentie."
},
"media": {
"title": "Media-werkplaats",
"subtitle": "Genereer afbeeldingen, videos en muziek",
"model": "Model",
"prompt": "Prompt",
"generate": "Genereren",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server frakoblet",
"serverDisconnectedMsg": "Proxy-serveren er stoppet eller starter på nytt.",
"expandSidebar": "Utvid sidefeltet",
"collapseSidebar": "Skjul sidefeltet"
"collapseSidebar": "Skjul sidefeltet",
"media": "Medier"
},
"header": {
"logout": "Logg ut",
@@ -611,7 +612,14 @@
"embedding": "Innebygging",
"image": "Bilde",
"custom": "tilpasset",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Kjerne-API-er",
"categoryMedia": "Media & Multi-Modal",
"categoryUtility": "Verktøy & Administrasjon"
},
"health": {
"title": "Systemhelse",
@@ -1088,6 +1096,17 @@
"themeSystem": "System",
"hideHealthLogs": "Skjul helsesjekklogger",
"hideHealthLogsDesc": "Når PÅ, undertrykk [HealthCheck]-meldinger i serverkonsollen",
"themeAccent": "Temafarge",
"themeAccentDesc": "Velg en forhåndsinnstilt farge eller lag ditt eget tema med én farge",
"themeCreate": "Opprett tema",
"themeCustom": "Egendefinert tema",
"themeCoral": "Korall",
"themeBlue": "Blå",
"themeRed": "Rød",
"themeGreen": "Grønn",
"themeViolet": "Fiolett",
"themeOrange": "Oransje",
"themeCyan": "Cyan",
"promptCache": "Spør Cache",
"flushCache": "Skyll cache",
"flushing": "Skyller …",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens som brukes til å lage cache-oppføringer (tilbake til inngangshastighet)",
"customPricingNote": "Du kan overstyre standardpriser for spesifikke modeller. Egendefinerte overstyringer prioriteres fremfor automatisk oppdagede priser.",
"editPricing": "Rediger priser",
"viewFullDetails": "Se alle detaljer"
"viewFullDetails": "Se alle detaljer",
"modelAliasesTitle": "Modellaliaser",
"addCustomAlias": "Legg til Tilpasset Alias",
"deprecatedModelId": "Utdatert modell-ID",
"newModelId": "Ny modell-ID",
"customAliases": "Tilpassede Aliaser",
"builtInAliases": "Innebygde Aliaser",
"backgroundDegradationTitle": "Bakgrunnsoppgave Degradering",
"backgroundDegradationDesc": "Oppdager automatisk bakgrunnsoppgaver (titler, sammendrag) og ruter til billigere modeller",
"enableDegradation": "Aktiver Bakgrunnsdegradation",
"enableDegradationHint": "Når aktivert, rutes bakgrunnsoppgaver som tittelegenerering og sammendrag automatisk til billigere modeller",
"tasksDetected": "Oppgaver oppdaget",
"degradationMap": "Modelldegraderingsschema",
"premiumModel": "Premiummodell",
"cheapModel": "Billig modell",
"detectionPatterns": "Deteksjonsmønstre",
"newPattern": "f.eks. \"generer en tittel\""
},
"translator": {
"title": "Oversetter",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute leveres \"som den er\" uten garanti av noe slag. Vi er ikke ansvarlige for kostnader som påløper gjennom API-bruk, tjenesteavbrudd eller tap av data. Oppretthold alltid sikkerhetskopier av konfigurasjonen din.",
"termsSection6Title": "6. Åpen kildekode",
"termsSection6Text": "OmniRoute er åpen kildekode-programvare. Du står fritt til å inspisere, modifisere og distribuere den under lisensvilkårene."
},
"media": {
"title": "Medialab",
"subtitle": "Generer bilder, videoer og musikk",
"model": "Model",
"prompt": "Prompt",
"generate": "Generer",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Nadiskonekta ang Server",
"serverDisconnectedMsg": "Ang proxy server ay itinigil o nagre-restart.",
"expandSidebar": "Palawakin ang sidebar",
"collapseSidebar": "I-collapse ang sidebar"
"collapseSidebar": "I-collapse ang sidebar",
"media": "Media"
},
"header": {
"logout": "Mag-logout",
@@ -611,7 +612,14 @@
"embedding": "Pag-embed",
"image": "Imahe",
"custom": "kaugalian",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Mga Core na API",
"categoryMedia": "Media at Multi-Modal",
"categoryUtility": "Mga Utility at Pamamahala"
},
"health": {
"title": "Kalusugan ng System",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistema",
"hideHealthLogs": "Itago ang Health Check Logs",
"hideHealthLogsDesc": "Kapag NAKA-ON, sugpuin ang mga mensahe ng [HealthCheck] sa server console",
"themeAccent": "Kulay ng tema",
"themeAccentDesc": "Pumili ng preset na kulay o gumawa ng sarili mong tema gamit ang isang kulay",
"themeCreate": "Gumawa ng tema",
"themeCustom": "Custom na tema",
"themeCoral": "Koral",
"themeBlue": "Asul",
"themeRed": "Pula",
"themeGreen": "Berde",
"themeViolet": "Lila",
"themeOrange": "Kahel",
"themeCyan": "Siyan",
"promptCache": "Prompt Cache",
"flushCache": "Flush Cache",
"flushing": "Nag-flush...",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Mga token na ginamit upang lumikha ng mga entry sa cache (fallback sa rate ng pag-input)",
"customPricingNote": "Maaari mong i-override ang default na pagpepresyo para sa mga partikular na modelo. Mas inuuna ang mga custom na override kaysa sa awtomatikong natukoy na pagpepresyo.",
"editPricing": "I-edit ang Pagpepresyo",
"viewFullDetails": "Tingnan ang Buong Detalye"
"viewFullDetails": "Tingnan ang Buong Detalye",
"modelAliasesTitle": "Mga Alias ng Model",
"addCustomAlias": "Magdagdag ng Custom na Alias",
"deprecatedModelId": "Deprecated na Model ID",
"newModelId": "Bagong Model ID",
"customAliases": "Mga Custom na Alias",
"builtInAliases": "Mga Built-in na Alias",
"backgroundDegradationTitle": "Background Task Degradation",
"backgroundDegradationDesc": "Auto-detect ang mga background task (titulo, buod) at i-route sa mas murang modelo",
"enableDegradation": "I-enable ang Background Degradation",
"enableDegradationHint": "Kapag naka-enable, ang mga background task tulad ng paggawa ng titulo at buod ay awtomatikong nire-route sa mas murang modelo",
"tasksDetected": "Mga nadetect na task",
"degradationMap": "Mapa ng Model Degradation",
"premiumModel": "Premium na modelo",
"cheapModel": "Murang modelo",
"detectionPatterns": "Mga Pattern ng Detection",
"newPattern": "hal. \"gumawa ng titulo\""
},
"translator": {
"title": "Tagasalin",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "Ang OmniRoute ay ibinigay \"as is\" nang walang anumang uri ng warranty. Hindi kami mananagot para sa anumang mga gastos na natamo sa pamamagitan ng paggamit ng API, pagkaantala ng serbisyo, o pagkawala ng data. Palaging panatilihin ang mga backup ng iyong configuration.",
"termsSection6Title": "6. Open Source",
"termsSection6Text": "Ang OmniRoute ay open-source na software. Malaya kang suriin, baguhin, at ipamahagi ito sa ilalim ng mga tuntunin ng lisensya nito."
},
"media": {
"title": "Media Playground",
"subtitle": "Bumuo ng mga larawan, video, at musika",
"model": "Model",
"prompt": "Prompt",
"generate": "Bumuo",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Serwer odłączony",
"serverDisconnectedMsg": "Serwer proxy został zatrzymany lub uruchamia się ponownie.",
"expandSidebar": "Rozwiń pasek boczny",
"collapseSidebar": "Zwiń pasek boczny"
"collapseSidebar": "Zwiń pasek boczny",
"media": "Media"
},
"header": {
"logout": "Wyloguj się",
@@ -611,7 +612,14 @@
"embedding": "Osadzanie",
"image": "Obraz",
"custom": "niestandardowe",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Główne API",
"categoryMedia": "Media i Multi-Modal",
"categoryUtility": "Narzędzia i Zarządzanie"
},
"health": {
"title": "Stan systemu",
@@ -1088,6 +1096,17 @@
"themeSystem": "Systemu",
"hideHealthLogs": "Ukryj dzienniki kontroli stanu",
"hideHealthLogsDesc": "Gdy opcja jest włączona, pomiń komunikaty [HealthCheck] w konsoli serwera",
"themeAccent": "Kolor motywu",
"themeAccentDesc": "Wybierz gotowy kolor lub utwórz własny motyw z jednego koloru",
"themeCreate": "Utwórz motyw",
"themeCustom": "Motyw niestandardowy",
"themeCoral": "Koralowy",
"themeBlue": "Niebieski",
"themeRed": "Czerwony",
"themeGreen": "Zielony",
"themeViolet": "Fioletowy",
"themeOrange": "Pomarańczowy",
"themeCyan": "Cyjan",
"promptCache": "Natychmiastowa pamięć podręczna",
"flushCache": "Opróżnij pamięć podręczną",
"flushing": "Płukanie…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokeny używane do tworzenia wpisów w pamięci podręcznej (powrót do szybkości wprowadzania)",
"customPricingNote": "Możesz zastąpić domyślne ceny dla określonych modeli. Zastąpienia niestandardowe mają pierwszeństwo przed automatycznie wykrytymi cenami.",
"editPricing": "Edytuj ceny",
"viewFullDetails": "Zobacz pełne szczegóły"
"viewFullDetails": "Zobacz pełne szczegóły",
"modelAliasesTitle": "Aliasy modeli",
"addCustomAlias": "Dodaj niestandardowy alias",
"deprecatedModelId": "Przestarzały ID modelu",
"newModelId": "Nowy ID modelu",
"customAliases": "Niestandardowe aliasy",
"builtInAliases": "Wbudowane aliasy",
"backgroundDegradationTitle": "Degradacja zadań w tle",
"backgroundDegradationDesc": "Automatycznie wykrywa zadania w tle (tytuły, streszczenia) i kieruje do tańszych modeli",
"enableDegradation": "Włącz degradację zadań w tle",
"enableDegradationHint": "Gdy włączone, zadania w tle, takie jak generowanie tytułów i podsumowań, są automatycznie kierowane do tańszych modeli",
"tasksDetected": "Wykryte zadania",
"degradationMap": "Mapa degradacji modeli",
"premiumModel": "Model premium",
"cheapModel": "Tani model",
"detectionPatterns": "Wzorce wykrywania",
"newPattern": "np. \"wygeneruj tytuł\""
},
"translator": {
"title": "Tłumacz",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "Usługa OmniRoute jest dostarczana w stanie takim, w jakim jest, bez jakiejkolwiek gwarancji. Nie ponosimy odpowiedzialności za jakiekolwiek koszty powstałe w wyniku korzystania z API, zakłóceń w świadczeniu usług lub utraty danych. Zawsze twórz kopie zapasowe swojej konfiguracji.",
"termsSection6Title": "6. Otwarte źródło",
"termsSection6Text": "OmniRoute jest oprogramowaniem typu open source. Możesz go przeglądać, modyfikować i rozpowszechniać zgodnie z warunkami licencji."
},
"media": {
"title": "Studio multimediów",
"subtitle": "Generuj obrazy, wideo i muzykę",
"model": "Model",
"prompt": "Prompt",
"generate": "Generuj",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+37 -3
View File
@@ -265,7 +265,7 @@
},
"media": {
"title": "Playground de Mídia",
"subtitle": "Gere imagens, vídeos e músicas usando seus provedores configurados",
"subtitle": "Gere imagens, vídeos e música",
"model": "Modelo",
"prompt": "Prompt",
"generate": "Gerar",
@@ -629,7 +629,14 @@
"embedding": "Embedding",
"image": "Imagem",
"custom": "custom",
"modelsCount": "{count, plural, one {# modelo} other {# modelos}}"
"modelsCount": "{count, plural, one {# modelo} other {# modelos}}",
"responsesDesc": "API Responses do OpenAI para Codex e fluxos de trabalho agênticos avançados",
"listModelsDesc": "Listar todos os modelos disponíveis em todos os provedores conectados",
"settingsApi": "Settings API",
"settingsApiDesc": "Ler e modificar a configuração do OmniRoute via API",
"categoryCore": "APIs Principais",
"categoryMedia": "Mídia e Multi-Modal",
"categoryUtility": "Utilidades e Gerenciamento"
},
"health": {
"title": "Saúde do Sistema",
@@ -1106,6 +1113,17 @@
"themeSystem": "Sistema",
"hideHealthLogs": "Ocultar Logs de Health Check",
"hideHealthLogsDesc": "Quando ATIVADO, suprime mensagens [HealthCheck] no console do servidor",
"themeAccent": "Cor do tema",
"themeAccentDesc": "Escolha uma cor predefinida ou crie seu próprio tema com uma cor",
"themeCreate": "Criar tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Vermelho",
"themeGreen": "Verde",
"themeViolet": "Violeta",
"themeOrange": "Laranja",
"themeCyan": "Ciano",
"promptCache": "Cache de Prompt",
"flushCache": "Limpar Cache",
"flushing": "Limpando…",
@@ -1398,7 +1416,23 @@
"cacheCreationTokenDesc": "Tokens usados para criar entradas de cache (fallback para taxa de input)",
"customPricingNote": "Você pode sobrescrever preços padrão para modelos específicos. Sobrescritas personalizadas têm prioridade sobre preços detectados automaticamente.",
"editPricing": "Editar Preços",
"viewFullDetails": "Ver Detalhes Completos"
"viewFullDetails": "Ver Detalhes Completos",
"modelAliasesTitle": "Aliases de Modelo",
"addCustomAlias": "Adicionar Alias Personalizado",
"deprecatedModelId": "ID do modelo depreciado",
"newModelId": "Novo ID do modelo",
"customAliases": "Aliases Personalizados",
"builtInAliases": "Aliases Integrados",
"backgroundDegradationTitle": "Degradação de Tarefas em Background",
"backgroundDegradationDesc": "Detecta automaticamente tarefas em background (títulos, resumos) e roteia para modelos mais baratos",
"enableDegradation": "Ativar Degradação em Background",
"enableDegradationHint": "Quando ativado, tarefas em background como geração de títulos e resumos são roteadas automaticamente para modelos mais baratos",
"tasksDetected": "Tarefas detectadas",
"degradationMap": "Mapa de Degradação de Modelos",
"premiumModel": "Modelo premium",
"cheapModel": "Modelo barato",
"detectionPatterns": "Padrões de Detecção",
"newPattern": "ex: \"gerar um título\""
},
"translator": {
"title": "Tradutor",
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Servidor desconectado",
"serverDisconnectedMsg": "O servidor proxy foi interrompido ou está reiniciando.",
"expandSidebar": "Expandir barra lateral",
"collapseSidebar": "Recolher barra lateral"
"collapseSidebar": "Recolher barra lateral",
"media": "Mídia"
},
"header": {
"logout": "Sair",
@@ -611,7 +612,14 @@
"embedding": "Incorporação",
"image": "Imagem",
"custom": "personalizado",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "API Responses do OpenAI para Codex e fluxos de trabalho agênticos avançados",
"listModelsDesc": "Listar todos os modelos disponíveis em todos os fornecedores conectados",
"settingsApi": "Settings API",
"settingsApiDesc": "Ler e modificar a configuração do OmniRoute via API",
"categoryCore": "APIs Principais",
"categoryMedia": "Mídia e Multi-Modal",
"categoryUtility": "Utilidades e Gestão"
},
"health": {
"title": "Saúde do sistema",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistema",
"hideHealthLogs": "Ocultar registros de verificação de integridade",
"hideHealthLogsDesc": "Quando ativado, suprime mensagens [HealthCheck] no console do servidor",
"themeAccent": "Cor do tema",
"themeAccentDesc": "Escolha uma cor predefinida ou crie seu próprio tema com uma cor",
"themeCreate": "Criar tema",
"themeCustom": "Tema personalizado",
"themeCoral": "Coral",
"themeBlue": "Azul",
"themeRed": "Vermelho",
"themeGreen": "Verde",
"themeViolet": "Violeta",
"themeOrange": "Laranja",
"themeCyan": "Ciano",
"promptCache": "Cache de prompt",
"flushCache": "Liberar cache",
"flushing": "Rubor…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens usados para criar entradas de cache (fallback para taxa de entrada)",
"customPricingNote": "Você pode substituir o preço padrão de modelos específicos. As substituições personalizadas têm prioridade sobre os preços detectados automaticamente.",
"editPricing": "Editar preços",
"viewFullDetails": "Ver detalhes completos"
"viewFullDetails": "Ver detalhes completos",
"modelAliasesTitle": "Aliases de Modelo",
"addCustomAlias": "Adicionar Alias Personalizado",
"deprecatedModelId": "ID do modelo depreciado",
"newModelId": "Novo ID do modelo",
"customAliases": "Aliases Personalizados",
"builtInAliases": "Aliases Integrados",
"backgroundDegradationTitle": "Degradação de Tarefas em Background",
"backgroundDegradationDesc": "Detecta automaticamente tarefas em background (títulos, resumos) e roteia para modelos mais baratos",
"enableDegradation": "Ativar Degradação em Background",
"enableDegradationHint": "Quando ativado, tarefas em background como geração de títulos e resumos são roteadas automaticamente para modelos mais baratos",
"tasksDetected": "Tarefas detectadas",
"degradationMap": "Mapa de Degradação de Modelos",
"premiumModel": "Modelo premium",
"cheapModel": "Modelo barato",
"detectionPatterns": "Padrões de Deteção",
"newPattern": "ex: \"gerar um título\""
},
"translator": {
"title": "Tradutor",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute é fornecido \"como está\" sem qualquer tipo de garantia. Não somos responsáveis por quaisquer custos incorridos através do uso da API, interrupções de serviço ou perda de dados. Sempre mantenha backups de sua configuração.",
"termsSection6Title": "6. Código aberto",
"termsSection6Text": "OmniRoute é um software de código aberto. Você é livre para inspecioná-lo, modificá-lo e distribuí-lo sob os termos de sua licença."
},
"media": {
"title": "Playground de Mídia",
"subtitle": "Gere imagens, vídeos e música",
"model": "Model",
"prompt": "Prompt",
"generate": "Gerar",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server deconectat",
"serverDisconnectedMsg": "Serverul proxy a fost oprit sau repornește.",
"expandSidebar": "Extinde bara laterală",
"collapseSidebar": "Restrângeți bara laterală"
"collapseSidebar": "Restrângeți bara laterală",
"media": "Media"
},
"header": {
"logout": "Deconectare",
@@ -611,7 +612,14 @@
"embedding": "Încorporarea",
"image": "Imagine",
"custom": "personalizat",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "API-uri de Bază",
"categoryMedia": "Media și Multi-Modal",
"categoryUtility": "Utilități și Gestionare"
},
"health": {
"title": "Sănătatea sistemului",
@@ -1088,6 +1096,17 @@
"themeSystem": "Sistem",
"hideHealthLogs": "Ascundeți jurnalele de verificare a stării de sănătate",
"hideHealthLogsDesc": "Când este ACTIVAT, suprimați mesajele [HealthCheck] din consola serverului",
"themeAccent": "Culoarea temei",
"themeAccentDesc": "Alege o culoare presetată sau creează-ți propria temă cu o singură culoare",
"themeCreate": "Creează temă",
"themeCustom": "Temă personalizată",
"themeCoral": "Coral",
"themeBlue": "Albastru",
"themeRed": "Roșu",
"themeGreen": "Verde",
"themeViolet": "Violet",
"themeOrange": "Portocaliu",
"themeCyan": "Cian",
"promptCache": "Prompt Cache",
"flushCache": "Spălați memoria cache",
"flushing": "Spălare…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Jetoane utilizate pentru a crea intrări în cache (retur la rata de intrare)",
"customPricingNote": "Puteți suprascrie prețurile implicite pentru anumite modele. Anulările personalizate au prioritate față de prețurile detectate automat.",
"editPricing": "Editați prețul",
"viewFullDetails": "Vezi detalii complete"
"viewFullDetails": "Vezi detalii complete",
"modelAliasesTitle": "Aliasuri de model",
"addCustomAlias": "Adaugă Alias Personalizat",
"deprecatedModelId": "ID model depreciat",
"newModelId": "ID model nou",
"customAliases": "Aliasuri personalizate",
"builtInAliases": "Aliasuri încorporate",
"backgroundDegradationTitle": "Degradare sarcini în fundal",
"backgroundDegradationDesc": "Detectează automat sarcinile în fundal (titluri, rezumate) și redirecționează către modele mai ieftine",
"enableDegradation": "Activează degradarea sarcinilor în fundal",
"enableDegradationHint": "Când este activat, sarcinile în fundal precum generarea de titluri și rezumate sunt redirecționate automat către modele mai ieftine",
"tasksDetected": "Sarcini detectate",
"degradationMap": "Hartă de degradare a modelelor",
"premiumModel": "Model premium",
"cheapModel": "Model ieftin",
"detectionPatterns": "Modele de detecție",
"newPattern": "ex: \"generează un titlu\""
},
"translator": {
"title": "Traducător",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute este furnizat „ca atare”, fără garanții de niciun fel. Nu suntem responsabili pentru costurile suportate prin utilizarea API-ului, întreruperile serviciilor sau pierderea datelor. Păstrați întotdeauna copii de siguranță ale configurației dvs.",
"termsSection6Title": "6. Open Source",
"termsSection6Text": "OmniRoute este un software open-source. Sunteți liber să îl inspectați, să modificați și să îl distribuiți în conformitate cu termenii licenței sale."
},
"media": {
"title": "Studio media",
"subtitle": "Generează imagini, videoclipuri și muzică",
"model": "Model",
"prompt": "Prompt",
"generate": "Generează",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+75 -4
View File
@@ -88,7 +88,28 @@
"serverDisconnected": "Сервер отключен",
"serverDisconnectedMsg": "Прокси-сервер остановлен или перезагружается.",
"expandSidebar": "Развернуть боковую панель",
"collapseSidebar": "Свернуть боковую панель"
"collapseSidebar": "Свернуть боковую панель",
"themes": "Темы",
"presetColors": "Популярные цвета",
"createTheme": "Создать тему",
"chooseColor": "Выберите один цвет",
"themeCoral": "Коралловый",
"themeBlue": "Синий",
"themeRed": "Красный",
"themeGreen": "Зелёный",
"themeViolet": "Фиолетовый",
"themeOrange": "Оранжевый",
"themeCyan": "Голубой",
"media": "Медиа"
},
"themesPage": {
"title": "Темы",
"description": "Выбери готовую тему или создай свою из одного цвета",
"presetColors": "Популярные цвета",
"customTheme": "Своя тема",
"customThemeDesc": "Нажми создать тему и выбери один цвет",
"createTheme": "Создать тему",
"activePreset": "Активная тема"
},
"header": {
"logout": "Выход из системы",
@@ -110,7 +131,9 @@
"settings": "Настройки",
"settingsDescription": "Управляйте своими предпочтениями",
"openaiCompatible": "Совместимость с OpenAI",
"anthropicCompatible": "Антропная совместимость"
"anthropicCompatible": "Антропная совместимость",
"themes": "Темы",
"themesDescription": "Выберите цветовую тему для всей панели"
},
"home": {
"quickStart": "Быстрый старт",
@@ -611,7 +634,14 @@
"embedding": "Встраивание",
"image": "Изображение",
"custom": "обычай",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API для Codex и продвинутых агентных рабочих процессов",
"listModelsDesc": "Список всех доступных моделей по всем подключённым провайдерам",
"settingsApi": "Settings API",
"settingsApiDesc": "Чтение и изменение конфигурации OmniRoute через API",
"categoryCore": "Основные API",
"categoryMedia": "Медиа и мультимодальность",
"categoryUtility": "Утилиты и управление"
},
"health": {
"title": "Здоровье системы",
@@ -1088,6 +1118,16 @@
"themeSystem": "Система",
"hideHealthLogs": "Скрыть журналы проверки работоспособности",
"hideHealthLogsDesc": "Если включено, подавлять сообщения [HealthCheck] в консоли сервера.",
"themeAccent": "Цвет темы",
"themeAccentDesc": "Выберите готовый цвет или создайте свою тему из одного цвета",
"themeCreate": "Создать тему",
"themeCustom": "Своя тема",
"themeBlue": "Синий",
"themeRed": "Красный",
"themeGreen": "Зелёный",
"themeViolet": "Фиолетовый",
"themeOrange": "Оранжевый",
"themeCyan": "Голубой",
"promptCache": "Подскажите кэш",
"flushCache": "Очистить кэш",
"flushing": "Промывка…",
@@ -1380,7 +1420,23 @@
"cacheCreationTokenDesc": "Токены, используемые для создания записей в кэше (возврат к скорости ввода)",
"customPricingNote": "Вы можете переопределить цены по умолчанию для определенных моделей. Пользовательские переопределения имеют приоритет над ценами, определяемыми автоматически.",
"editPricing": "Изменить цену",
"viewFullDetails": "Посмотреть полную информацию"
"viewFullDetails": "Посмотреть полную информацию",
"modelAliasesTitle": "Псевдонимы моделей",
"addCustomAlias": "Добавить пользовательский псевдоним",
"deprecatedModelId": "Устаревший ID модели",
"newModelId": "Новый ID модели",
"customAliases": "Пользовательские псевдонимы",
"builtInAliases": "Встроенные псевдонимы",
"backgroundDegradationTitle": "Деградация фоновых задач",
"backgroundDegradationDesc": "Автоматически обнаруживает фоновые задачи (заголовки, резюме) и перенаправляет на более дешёвые модели",
"enableDegradation": "Включить деградацию фоновых задач",
"enableDegradationHint": "Когда включено, фоновые задачи, такие как генерация заголовков и резюмирование, автоматически перенаправляются на более дешёвые модели",
"tasksDetected": "Обнаружено задач",
"degradationMap": "Карта деградации моделей",
"premiumModel": "Премиум-модель",
"cheapModel": "Дешёвая модель",
"detectionPatterns": "Шаблоны обнаружения",
"newPattern": "напр. \"создать заголовок\""
},
"translator": {
"title": "Переводчик",
@@ -2036,5 +2092,20 @@
"termsSection5Text": "OmniRoute предоставляется «как есть» без каких-либо гарантий. Мы не несем ответственности за любые расходы, понесенные в результате использования API, перебоев в обслуживании или потери данных. Всегда сохраняйте резервные копии вашей конфигурации.",
"termsSection6Title": "6. Открытый исходный код",
"termsSection6Text": "OmniRoute — это программное обеспечение с открытым исходным кодом. Вы можете свободно просматривать, изменять и распространять его в соответствии с условиями лицензии."
},
"media": {
"title": "Медиа-площадка",
"subtitle": "Генерируйте изображения, видео и музыку",
"model": "Model",
"prompt": "Prompt",
"generate": "Сгенерировать",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server odpojený",
"serverDisconnectedMsg": "Proxy server bol zastavený alebo sa reštartuje.",
"expandSidebar": "Rozbaliť bočný panel",
"collapseSidebar": "Zbaliť bočný panel"
"collapseSidebar": "Zbaliť bočný panel",
"media": "Médiá"
},
"header": {
"logout": "Odhlásiť sa",
@@ -611,7 +612,14 @@
"embedding": "Vkladanie",
"image": "Obrázok",
"custom": "zvykom",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Základné API",
"categoryMedia": "Médiá a multi-modálne",
"categoryUtility": "Nástroje a správa"
},
"health": {
"title": "Zdravie systému",
@@ -1088,6 +1096,17 @@
"themeSystem": "Systém",
"hideHealthLogs": "Skryť denníky kontroly stavu",
"hideHealthLogsDesc": "Keď je ZAPNUTÉ, potláčajte správy [HealthCheck] v konzole servera",
"themeAccent": "Farba témy",
"themeAccentDesc": "Vyberte prednastavenú farbu alebo si vytvorte vlastnú tému jednou farbou",
"themeCreate": "Vytvoriť tému",
"themeCustom": "Vlastná téma",
"themeCoral": "Koralový",
"themeBlue": "Modrá",
"themeRed": "Červená",
"themeGreen": "Zelená",
"themeViolet": "Fialová",
"themeOrange": "Oranžová",
"themeCyan": "Azúrová",
"promptCache": "Prompt Cache",
"flushCache": "Vyprázdniť vyrovnávaciu pamäť",
"flushing": "Splachovanie…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokeny používané na vytváranie záznamov vo vyrovnávacej pamäti (záložná rýchlosť vstupu)",
"customPricingNote": "Predvolené ceny pre konkrétne modely môžete prepísať. Vlastné prepísania majú prednosť pred automaticky zistenými cenami.",
"editPricing": "Upraviť ceny",
"viewFullDetails": "Zobraziť úplné podrobnosti"
"viewFullDetails": "Zobraziť úplné podrobnosti",
"modelAliasesTitle": "Aliasy modelov",
"addCustomAlias": "Pridať vlastný alias",
"deprecatedModelId": "Zastaraný ID modelu",
"newModelId": "Nový ID modelu",
"customAliases": "Vlastné aliasy",
"builtInAliases": "Vstavané aliasy",
"backgroundDegradationTitle": "Degradácia úloh na pozadí",
"backgroundDegradationDesc": "Automaticky detekuje úlohy na pozadí (titulky, súhrny) a presmeruje na lacnejšie modely",
"enableDegradation": "Povoliť degradáciu úloh na pozadí",
"enableDegradationHint": "Keď je povolené, úlohy na pozadí, ako generovanie titukov a súhrnov, sú automaticky presmerované na lacnejšie modely",
"tasksDetected": "Zistené úlohy",
"degradationMap": "Mapa degradácie modelov",
"premiumModel": "Prémiový model",
"cheapModel": "Lacný model",
"detectionPatterns": "Vzory detekcie",
"newPattern": "napr. \"vygenerovať titulok\""
},
"translator": {
"title": "Prekladateľ",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute sa poskytuje „tak ako je“ bez záruky akéhokoľvek druhu. Nezodpovedáme za žiadne náklady vzniknuté používaním API, prerušením služieb alebo stratou údajov. Vždy si zálohujte svoju konfiguráciu.",
"termsSection6Title": "6. Otvorený zdroj",
"termsSection6Text": "OmniRoute je softvér s otvoreným zdrojovým kódom. Môžete ho voľne kontrolovať, upravovať a distribuovať v súlade s podmienkami jeho licencie."
},
"media": {
"title": "Mediálne ihrisko",
"subtitle": "Generujte obrázky, videá a hudbu",
"model": "Model",
"prompt": "Prompt",
"generate": "Generovať",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Server frånkopplad",
"serverDisconnectedMsg": "Proxyservern har stoppats eller startar om.",
"expandSidebar": "Expandera sidofältet",
"collapseSidebar": "Dölj sidofältet"
"collapseSidebar": "Dölj sidofältet",
"media": "Media"
},
"header": {
"logout": "Logga ut",
@@ -611,7 +612,14 @@
"embedding": "Inbäddning",
"image": "Bild",
"custom": "anpassad",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Kärn-API:er",
"categoryMedia": "Media & Multimodal",
"categoryUtility": "Verktyg & Hantering"
},
"health": {
"title": "Systemhälsa",
@@ -1088,6 +1096,17 @@
"themeSystem": "System",
"hideHealthLogs": "Dölj loggar för hälsokontroll",
"hideHealthLogsDesc": "När PÅ, dämpa [HealthCheck]-meddelanden i serverkonsolen",
"themeAccent": "Temafärg",
"themeAccentDesc": "Välj en förinställd färg eller skapa ditt eget tema med en färg",
"themeCreate": "Skapa tema",
"themeCustom": "Anpassat tema",
"themeCoral": "Korall",
"themeBlue": "Blå",
"themeRed": "Röd",
"themeGreen": "Grön",
"themeViolet": "Violett",
"themeOrange": "Orange",
"themeCyan": "Cyan",
"promptCache": "Fråga Cache",
"flushCache": "Spola cachen",
"flushing": "Spolar...",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Tokens som används för att skapa cacheposter (återgång till inmatningshastighet)",
"customPricingNote": "Du kan åsidosätta standardpriser för specifika modeller. Anpassade åsidosättningar har prioritet framför automatiskt identifierade priser.",
"editPricing": "Redigera prissättning",
"viewFullDetails": "Visa fullständiga detaljer"
"viewFullDetails": "Visa fullständiga detaljer",
"modelAliasesTitle": "Modellalias",
"addCustomAlias": "Lägg Till Anpassat Alias",
"deprecatedModelId": "Föråldrat modell-ID",
"newModelId": "Nytt modell-ID",
"customAliases": "Anpassade Alias",
"builtInAliases": "Inbyggda Alias",
"backgroundDegradationTitle": "Bakgrundsuppgifter Degradering",
"backgroundDegradationDesc": "Upptäcker automatiskt bakgrundsuppgifter (titlar, sammanfattningar) och dirigerar till billigare modeller",
"enableDegradation": "Aktivera Bakgrundsdegradation",
"enableDegradationHint": "När aktiverat, dirigeras bakgrundsuppgifter som titelgenerering och sammanfattningar automatiskt till billigare modeller",
"tasksDetected": "Uppgifter upptäckta",
"degradationMap": "Modelldegraderingsschema",
"premiumModel": "Premiummodell",
"cheapModel": "Billig modell",
"detectionPatterns": "Detektionsmönster",
"newPattern": "t.ex. \"generera en titel\""
},
"translator": {
"title": "Översättare",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute tillhandahålls \"i befintligt skick\" utan garanti av något slag. Vi ansvarar inte för några kostnader som uppstår genom API-användning, tjänstavbrott eller dataförlust. Behåll alltid säkerhetskopior av din konfiguration.",
"termsSection6Title": "6. Öppen källkod",
"termsSection6Text": "OmniRoute är programvara med öppen källkod. Du är fri att inspektera, modifiera och distribuera den enligt villkoren i dess licens."
},
"media": {
"title": "Medialabb",
"subtitle": "Generera bilder, videor och musik",
"model": "Model",
"prompt": "Prompt",
"generate": "Generera",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "เซิร์ฟเวอร์ถูกตัดการเชื่อมต่อ",
"serverDisconnectedMsg": "พร็อกซีเซิร์ฟเวอร์ถูกหยุดหรือกำลังรีสตาร์ท",
"expandSidebar": "ขยายแถบด้านข้าง",
"collapseSidebar": "ยุบแถบด้านข้าง"
"collapseSidebar": "ยุบแถบด้านข้าง",
"media": "สื่อ"
},
"header": {
"logout": "ออกจากระบบ",
@@ -611,7 +612,14 @@
"embedding": "การฝัง",
"image": "รูปภาพ",
"custom": "กำหนดเอง",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API สำหรับ Codex และเวิร์กโฟลว์เอเจนต์ขั้นสูง",
"listModelsDesc": "แสดงรายการโมเดลที่ใช้ได้ทั้งหมดจากผู้ให้บริการที่เชื่อมต่อ",
"settingsApi": "Settings API",
"settingsApiDesc": "อ่านและแก้ไขการกำหนดค่า OmniRoute ผ่าน API",
"categoryCore": "API หลัก",
"categoryMedia": "สื่อและมัลติโมดอล",
"categoryUtility": "ยูทิลิตี้และการจัดการ"
},
"health": {
"title": "สุขภาพของระบบ",
@@ -1088,6 +1096,17 @@
"themeSystem": "ระบบ",
"hideHealthLogs": "ซ่อนบันทึกการตรวจสุขภาพ",
"hideHealthLogsDesc": "เมื่อเปิด ให้ระงับข้อความ [HealthCheck] ในคอนโซลเซิร์ฟเวอร์",
"themeAccent": "สีธีม",
"themeAccentDesc": "เลือกสีสำเร็จรูปหรือสร้างธีมของคุณเองด้วยสีเดียว",
"themeCreate": "สร้างธีม",
"themeCustom": "ธีมกำหนดเอง",
"themeCoral": "คอรัล",
"themeBlue": "น้ำเงิน",
"themeRed": "แดง",
"themeGreen": "เขียว",
"themeViolet": "ม่วง",
"themeOrange": "ส้ม",
"themeCyan": "ไซแอน",
"promptCache": "แคชพร้อมท์",
"flushCache": "ล้างแคช",
"flushing": "กำลังฟลัชชิง…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "โทเค็นที่ใช้ในการสร้างรายการแคช (สำรองไปยังอัตราการป้อนข้อมูล)",
"customPricingNote": "คุณสามารถแทนที่ราคาเริ่มต้นสำหรับรุ่นเฉพาะได้ การแทนที่แบบกำหนดเองจะมีลำดับความสำคัญมากกว่าการกำหนดราคาที่ตรวจพบอัตโนมัติ",
"editPricing": "แก้ไขราคา",
"viewFullDetails": "ดูรายละเอียดทั้งหมด"
"viewFullDetails": "ดูรายละเอียดทั้งหมด",
"modelAliasesTitle": "นามแฝงโมเดล",
"addCustomAlias": "เพิ่มนามแฝงที่กำหนดเอง",
"deprecatedModelId": "ID โมเดลที่เลิกใช้",
"newModelId": "ID โมเดลใหม่",
"customAliases": "นามแฝงที่กำหนดเอง",
"builtInAliases": "นามแฝงในตัว",
"backgroundDegradationTitle": "การลดระดับงานพื้นหลัง",
"backgroundDegradationDesc": "ตรวจจับงานพื้นหลังโดยอัตโนมัติ (ชื่อเรื่อง, สรุป) และเปลี่ยนเส้นทางไปยังโมเดลที่ถูกกว่า",
"enableDegradation": "เปิดใช้งานการลดระดับงานพื้นหลัง",
"enableDegradationHint": "เมื่อเปิดใช้งาน งานพื้นหลังเช่นการสร้างชื่อเรื่องและสรุปจะถูกเปลี่ยนเส้นทางไปยังโมเดลที่ถูกกว่าโดยอัตโนมัติ",
"tasksDetected": "งานที่ตรวจพบ",
"degradationMap": "แผนผังการลดระดับโมเดล",
"premiumModel": "โมเดลพรีเมียม",
"cheapModel": "โมเดลราคาถูก",
"detectionPatterns": "รูปแบบการตรวจจับ",
"newPattern": "เช่น \"สร้างชื่อเรื่อง\""
},
"translator": {
"title": "นักแปล",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute มีให้ \"ตามสภาพ\" โดยไม่มีการรับประกันใดๆ เราจะไม่รับผิดชอบต่อค่าใช้จ่ายใดๆ ที่เกิดขึ้นจากการใช้ API การหยุดชะงักของบริการ หรือการสูญหายของข้อมูล สำรองข้อมูลการกำหนดค่าของคุณไว้เสมอ",
"termsSection6Title": "6. โอเพ่นซอร์ส",
"termsSection6Text": "OmniRoute เป็นซอฟต์แวร์โอเพ่นซอร์ส คุณมีอิสระที่จะตรวจสอบ แก้ไข และแจกจ่ายภายใต้เงื่อนไขของใบอนุญาต"
},
"media": {
"title": "สนามทดลองสื่อ",
"subtitle": "สร้างภาพ วิดีโอ และเพลง",
"model": "Model",
"prompt": "Prompt",
"generate": "สร้าง",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Сервер відключено",
"serverDisconnectedMsg": "Проксі-сервер зупинено або перезавантажується.",
"expandSidebar": "Розгорнути бічну панель",
"collapseSidebar": "Згорнути бічну панель"
"collapseSidebar": "Згорнути бічну панель",
"media": "Медіа"
},
"header": {
"logout": "Вийти",
@@ -611,7 +612,14 @@
"embedding": "Вбудовування",
"image": "Зображення",
"custom": "звичай",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "OpenAI Responses API for Codex and advanced agentic workflows",
"listModelsDesc": "List all available models across all connected providers",
"settingsApi": "Settings API",
"settingsApiDesc": "Read and modify OmniRoute configuration via API",
"categoryCore": "Основні API",
"categoryMedia": "Медіа та мультимодальність",
"categoryUtility": "Утиліти та управління"
},
"health": {
"title": "Здоров'я системи",
@@ -1088,6 +1096,17 @@
"themeSystem": "система",
"hideHealthLogs": "Приховати журнали перевірки справності",
"hideHealthLogsDesc": "Коли ввімкнено, блокувати повідомлення [HealthCheck] на консолі сервера",
"themeAccent": "Колір теми",
"themeAccentDesc": "Оберіть готовий колір або створіть власну тему з одного кольору",
"themeCreate": "Створити тему",
"themeCustom": "Користувацька тема",
"themeCoral": "Кораловий",
"themeBlue": "Синій",
"themeRed": "Червоний",
"themeGreen": "Зелений",
"themeViolet": "Фіолетовий",
"themeOrange": "Помаранчевий",
"themeCyan": "Ціан",
"promptCache": "Кеш підказок",
"flushCache": "Очистити кеш",
"flushing": "Промивання…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Токени, що використовуються для створення записів кешу (резервний вихід до швидкості введення)",
"customPricingNote": "Ви можете змінити ціни за умовчанням для певних моделей. Спеціальні зміни мають пріоритет над автоматично визначеними цінами.",
"editPricing": "Редагувати ціни",
"viewFullDetails": "Переглянути повну інформацію"
"viewFullDetails": "Переглянути повну інформацію",
"modelAliasesTitle": "Псевдоніми моделей",
"addCustomAlias": "Додати власний псевдонім",
"deprecatedModelId": "Застарілий ID моделі",
"newModelId": "Новий ID моделі",
"customAliases": "Власні псевдоніми",
"builtInAliases": "Вбудовані псевдоніми",
"backgroundDegradationTitle": "Деградація фонових завдань",
"backgroundDegradationDesc": "Автоматично виявляє фонові завдання (заголовки, резюме) і перенаправляє на дешевші моделі",
"enableDegradation": "Увімкнути деградацію фонових завдань",
"enableDegradationHint": "Коли увімкнено, фонові завдання, такі як генерація заголовків і підсумування, автоматично перенаправляються на дешевші моделі",
"tasksDetected": "Виявлено завдань",
"degradationMap": "Карта деградації моделей",
"premiumModel": "Преміум-модель",
"cheapModel": "Дешева модель",
"detectionPatterns": "Шаблони виявлення",
"newPattern": "напр. \"згенерувати заголовок\""
},
"translator": {
"title": "Перекладач",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute надається «як є» без будь-яких гарантій. Ми не несемо відповідальності за будь-які витрати, пов’язані з використанням API, перебоями в роботі служби або втратою даних. Завжди створюйте резервні копії вашої конфігурації.",
"termsSection6Title": "6. Відкритий код",
"termsSection6Text": "OmniRoute — це програмне забезпечення з відкритим кодом. Ви можете вільно перевіряти, змінювати та поширювати його відповідно до умов його ліцензії."
},
"media": {
"title": "Медіа-майданчик",
"subtitle": "Генеруйте зображення, відео та музику",
"model": "Model",
"prompt": "Prompt",
"generate": "Згенерувати",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "Máy chủ bị ngắt kết nối",
"serverDisconnectedMsg": "Máy chủ proxy đã bị dừng hoặc đang khởi động lại.",
"expandSidebar": "Mở rộng thanh bên",
"collapseSidebar": "Thu gọn thanh bên"
"collapseSidebar": "Thu gọn thanh bên",
"media": "Phương tiện"
},
"header": {
"logout": "Đăng xuất",
@@ -611,7 +612,14 @@
"embedding": "Nhúng",
"image": "Hình ảnh",
"custom": "tùy chỉnh",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "API Responses của OpenAI cho Codex và quy trình làm việc tác tử nâng cao",
"listModelsDesc": "Liệt kê tất cả các mô hình có sẵn từ tất cả các nhà cung cấp đã kết nối",
"settingsApi": "Settings API",
"settingsApiDesc": "Đọc và sửa đổi cấu hình OmniRoute qua API",
"categoryCore": "API Cốt Lõi",
"categoryMedia": "Phương Tiện & Đa Phương Thức",
"categoryUtility": "Tiện Ích & Quản Lý"
},
"health": {
"title": "Tình trạng hệ thống",
@@ -1088,6 +1096,17 @@
"themeSystem": "Hệ thống",
"hideHealthLogs": "Ẩn nhật ký kiểm tra sức khỏe",
"hideHealthLogsDesc": "Khi BẬT, hãy chặn thông báo [HealthCheck] trong bảng điều khiển máy chủ",
"themeAccent": "Màu chủ đề",
"themeAccentDesc": "Chọn màu đặt sẵn hoặc tạo chủ đề của riêng bạn bằng một màu",
"themeCreate": "Tạo chủ đề",
"themeCustom": "Chủ đề tùy chỉnh",
"themeCoral": "San hô",
"themeBlue": "Xanh dương",
"themeRed": "Đỏ",
"themeGreen": "Xanh lá",
"themeViolet": "Tím",
"themeOrange": "Cam",
"themeCyan": "Lục lam",
"promptCache": "Bộ nhớ đệm nhắc nhở",
"flushCache": "Xóa bộ nhớ đệm",
"flushing": "Đang xả…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "Mã thông báo được sử dụng để tạo mục nhập bộ đệm (dự phòng tốc độ đầu vào)",
"customPricingNote": "Bạn có thể ghi đè giá mặc định cho các kiểu máy cụ thể. Ghi đè tùy chỉnh được ưu tiên hơn giá được tự động phát hiện.",
"editPricing": "Chỉnh sửa giá",
"viewFullDetails": "Xem chi tiết đầy đủ"
"viewFullDetails": "Xem chi tiết đầy đủ",
"modelAliasesTitle": "Bí danh mô hình",
"addCustomAlias": "Thêm Bí Danh Tùy Chỉnh",
"deprecatedModelId": "ID mô hình ngừng sử dụng",
"newModelId": "ID mô hình mới",
"customAliases": "Bí Danh Tùy Chỉnh",
"builtInAliases": "Bí Danh Tích Hợp",
"backgroundDegradationTitle": "Giảm Cấp Tác Vụ Nền",
"backgroundDegradationDesc": "Tự động phát hiện tác vụ nền (tiêu đề, tóm tắt) và chuyển hướng sang mô hình rẻ hơn",
"enableDegradation": "Bật Giảm Cấp Nền",
"enableDegradationHint": "Khi bật, các tác vụ nền như tạo tiêu đề và tóm tắt sẽ tự động được chuyển hướng sang các mô hình rẻ hơn",
"tasksDetected": "Tác vụ được phát hiện",
"degradationMap": "Bản Đồ Giảm Cấp Mô Hình",
"premiumModel": "Mô hình cao cấp",
"cheapModel": "Mô hình giá rẻ",
"detectionPatterns": "Mẫu Phát Hiện",
"newPattern": "vd: \"tạo tiêu đề\""
},
"translator": {
"title": "Người phiên dịch",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute được cung cấp \"nguyên trạng\" mà không có bất kỳ hình thức bảo hành nào. Chúng tôi không chịu trách nhiệm về bất kỳ chi phí nào phát sinh do sử dụng API, gián đoạn dịch vụ hoặc mất dữ liệu. Luôn duy trì bản sao lưu cấu hình của bạn.",
"termsSection6Title": "6. Nguồn mở",
"termsSection6Text": "OmniRoute là phần mềm mã nguồn mở. Bạn có thể tự do kiểm tra, sửa đổi và phân phối nó theo các điều khoản trong giấy phép."
},
"media": {
"title": "Sân chơi phương tiện",
"subtitle": "Tạo hình ảnh, video và âm nhạc",
"model": "Model",
"prompt": "Prompt",
"generate": "Tạo",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+53 -3
View File
@@ -88,7 +88,8 @@
"serverDisconnected": "服务器已断开连接",
"serverDisconnectedMsg": "代理服务器已停止或正在重新启动。",
"expandSidebar": "展开侧边栏",
"collapseSidebar": "折叠侧边栏"
"collapseSidebar": "折叠侧边栏",
"media": "媒体"
},
"header": {
"logout": "退出",
@@ -611,7 +612,14 @@
"embedding": "嵌入",
"image": "图片",
"custom": "定制",
"modelsCount": "{count, plural, one {# model} other {# models}}"
"modelsCount": "{count, plural, one {# model} other {# models}}",
"responsesDesc": "用于 Codex 和高级智能体工作流的 OpenAI Responses API",
"listModelsDesc": "列出所有已连接提供商的所有可用模型",
"settingsApi": "Settings API",
"settingsApiDesc": "通过 API 读取和修改 OmniRoute 配置",
"categoryCore": "核心 API",
"categoryMedia": "媒体和多模态",
"categoryUtility": "实用工具和管理"
},
"health": {
"title": "系统健康状况",
@@ -1088,6 +1096,17 @@
"themeSystem": "系统",
"hideHealthLogs": "隐藏健康检查日志",
"hideHealthLogsDesc": "当打开时,抑制服务器控制台中的 [HealthCheck] 消息",
"themeAccent": "主题颜色",
"themeAccentDesc": "选择预设颜色,或使用单一颜色创建你自己的主题",
"themeCreate": "创建主题",
"themeCustom": "自定义主题",
"themeCoral": "珊瑚色",
"themeBlue": "蓝色",
"themeRed": "红色",
"themeGreen": "绿色",
"themeViolet": "紫色",
"themeOrange": "橙色",
"themeCyan": "青色",
"promptCache": "提示缓存",
"flushCache": "刷新缓存",
"flushing": "法拉盛…",
@@ -1380,7 +1399,23 @@
"cacheCreationTokenDesc": "用于创建缓存条目的令牌(回退到输入速率)",
"customPricingNote": "您可以覆盖特定型号的默认定价。自定义覆盖优先于自动检测的定价。",
"editPricing": "编辑定价",
"viewFullDetails": "查看完整详情"
"viewFullDetails": "查看完整详情",
"modelAliasesTitle": "模型别名",
"addCustomAlias": "添加自定义别名",
"deprecatedModelId": "已弃用的模型 ID",
"newModelId": "新模型 ID",
"customAliases": "自定义别名",
"builtInAliases": "内置别名",
"backgroundDegradationTitle": "后台任务降级",
"backgroundDegradationDesc": "自动检测后台任务(标题、摘要)并路由到更便宜的模型",
"enableDegradation": "启用后台任务降级",
"enableDegradationHint": "启用后,标题生成和摘要等后台任务会自动路由到更便宜的模型",
"tasksDetected": "检测到的任务",
"degradationMap": "模型降级映射",
"premiumModel": "高级模型",
"cheapModel": "低成本模型",
"detectionPatterns": "检测模式",
"newPattern": "例如:\"生成标题\""
},
"translator": {
"title": "翻译者",
@@ -2036,5 +2071,20 @@
"termsSection5Text": "OmniRoute 按“原样”提供,不提供任何形式的保证。我们不对因 API 使用、服务中断或数据丢失而产生的任何费用负责。始终维护配置的备份。",
"termsSection6Title": "6. 开源",
"termsSection6Text": "OmniRoute 是开源软件。您可以根据其许可条款自由检查、修改和分发它。"
},
"media": {
"title": "媒体工作台",
"subtitle": "生成图像、视频和音乐",
"model": "Model",
"prompt": "Prompt",
"generate": "生成",
"generating": "Generating...",
"loadingModels": "Loading available models...",
"noModels": "No models available. Configure providers with media capabilities first.",
"error": "Generation Failed",
"result": "Result",
"imageDescription": "Generate images from text prompts using OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI and more.",
"videoDescription": "Create videos with AnimateDiff, Stable Video Diffusion via ComfyUI or SD WebUI.",
"musicDescription": "Compose music using Stable Audio Open or MusicGen via ComfyUI."
}
}
+1
View File
@@ -90,6 +90,7 @@ function usePageInfo(pathname: string | null) {
return { title: t("endpoint"), description: t("endpointDescription"), breadcrumbs: [] };
if (pathname.includes("/profile"))
return { title: t("settings"), description: t("settingsDescription"), breadcrumbs: [] };
// Note: /themes page removed theme settings live in /settings → AppearanceTab
return { title: "", description: "", breadcrumbs: [] };
}
-1
View File
@@ -11,7 +11,6 @@ import Button from "./Button";
import { ConfirmModal } from "./Modal";
import CloudSyncStatus from "./CloudSyncStatus";
import { useTranslations } from "next-intl";
// Nav items use i18n keys resolved inside the component
const navItemDefs = [
{ href: "/dashboard", i18nKey: "home", icon: "home", exact: true },
+68 -3
View File
@@ -6,7 +6,11 @@ import { THEME_CONFIG } from "@/shared/constants/config";
interface ThemeState {
theme: string;
colorTheme: string;
customColor: string;
setTheme: (theme: string) => void;
setColorTheme: (colorTheme: string) => void;
setCustomColorTheme: (color: string) => void;
toggleTheme: () => void;
initTheme: () => void;
}
@@ -15,12 +19,25 @@ const useThemeStore = create<ThemeState>()(
persist(
(set, get) => ({
theme: THEME_CONFIG.defaultTheme,
colorTheme: "coral",
customColor: "#3b82f6",
setTheme: (theme) => {
set({ theme });
applyTheme(theme);
},
setColorTheme: (colorTheme) => {
set({ colorTheme });
applyColorTheme(colorTheme, get().customColor);
},
setCustomColorTheme: (color) => {
const normalized = normalizeHexColor(color);
set({ colorTheme: "custom", customColor: normalized });
applyColorTheme("custom", normalized);
},
toggleTheme: () => {
const currentTheme = get().theme;
const newTheme = currentTheme === "dark" ? "light" : "dark";
@@ -29,8 +46,9 @@ const useThemeStore = create<ThemeState>()(
},
initTheme: () => {
const theme = get().theme;
const { theme, colorTheme, customColor } = get();
applyTheme(theme);
applyColorTheme(colorTheme, customColor);
},
}),
{
@@ -39,13 +57,22 @@ const useThemeStore = create<ThemeState>()(
)
);
// Apply theme to document
export const COLOR_THEMES: Record<string, string> = {
coral: "#e54d5e",
blue: "#3b82f6",
red: "#ef4444",
green: "#22c55e",
violet: "#8b5cf6",
orange: "#f97316",
cyan: "#06b6d4",
};
// Apply light/dark theme to document
function applyTheme(theme: string) {
if (typeof window === "undefined") return;
const root = document.documentElement;
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
const effectiveTheme = theme === "system" ? systemTheme : theme;
if (effectiveTheme === "dark") {
@@ -55,4 +82,42 @@ function applyTheme(theme: string) {
}
}
function applyColorTheme(colorTheme: string, customColor: string) {
if (typeof window === "undefined") return;
const root = document.documentElement;
const baseColor =
colorTheme === "custom"
? normalizeHexColor(customColor)
: COLOR_THEMES[colorTheme] || COLOR_THEMES.coral;
const hoverColor = shadeHexColor(baseColor, -0.14);
root.style.setProperty("--color-primary", baseColor);
root.style.setProperty("--color-primary-hover", hoverColor);
}
function normalizeHexColor(color: string) {
const value = (color || "").trim();
const hex = value.startsWith("#") ? value : `#${value}`;
const valid = /^#([0-9a-fA-F]{6})$/.test(hex);
return valid ? hex.toLowerCase() : "#3b82f6";
}
function shadeHexColor(hex: string, percent: number) {
const normalized = normalizeHexColor(hex).slice(1);
const r = parseInt(normalized.slice(0, 2), 16);
const g = parseInt(normalized.slice(2, 4), 16);
const b = parseInt(normalized.slice(4, 6), 16);
const shade = (channel: number) => {
const target = percent < 0 ? 0 : 255;
const amount = Math.round((target - channel) * Math.abs(percent));
const next = percent < 0 ? channel - amount : channel + amount;
return Math.max(0, Math.min(255, next));
};
const toHex = (channel: number) => channel.toString(16).padStart(2, "0");
return `#${toHex(shade(r))}${toHex(shade(g))}${toHex(shade(b))}`;
}
export default useThemeStore;
@@ -0,0 +1,130 @@
import test from "node:test";
import assert from "node:assert/strict";
const {
isBackgroundTask,
getDegradedModel,
setBackgroundDegradationConfig,
getBackgroundDegradationConfig,
getDefaultDegradationMap,
getDefaultDetectionPatterns,
resetStats,
} = await import("../../open-sse/services/backgroundTaskDetector.ts");
// ─── isBackgroundTask ───────────────────────────────────────────────────────
test("isBackgroundTask: returns true for title generation pattern", () => {
setBackgroundDegradationConfig({ enabled: true });
const body = {
model: "claude-sonnet-4",
messages: [
{ role: "system", content: "Generate a title for this conversation" },
{ role: "user", content: "How to deploy a Next.js app" },
],
};
assert.equal(isBackgroundTask(body), true);
});
test("isBackgroundTask: returns true for summarize pattern", () => {
const body = {
model: "claude-sonnet-4",
messages: [
{ role: "system", content: "Summarize this conversation briefly" },
{ role: "user", content: "We discussed deployment techniques" },
],
};
assert.equal(isBackgroundTask(body), true);
});
test("isBackgroundTask: returns false for normal chat", () => {
const body = {
model: "claude-sonnet-4",
messages: [
{ role: "system", content: "You are a helpful coding assistant" },
{ role: "user", content: "Help me write a function" },
],
};
assert.equal(isBackgroundTask(body), false);
});
test("isBackgroundTask: returns false for many-turn conversations", () => {
const messages = [
{ role: "system", content: "Generate a title" },
...Array.from({ length: 10 }, (_, i) => ({
role: i % 2 === 0 ? "user" : "assistant",
content: `Message ${i}`,
})),
];
const body = { model: "claude-sonnet-4", messages };
assert.equal(isBackgroundTask(body), false); // Too many turns
});
test("isBackgroundTask: detects X-Request-Priority header", () => {
const body = {
model: "claude-sonnet-4",
messages: [{ role: "user", content: "hello" }],
};
const headers = { "x-request-priority": "background" };
assert.equal(isBackgroundTask(body, headers), true);
});
test("isBackgroundTask: returns false for null/undefined body", () => {
assert.equal(isBackgroundTask(null), false);
assert.equal(isBackgroundTask(undefined), false);
});
test("isBackgroundTask: returns false for empty messages", () => {
assert.equal(isBackgroundTask({ messages: [] }), false);
});
// ─── getDegradedModel ───────────────────────────────────────────────────────
test("getDegradedModel: returns cheaper model from map", () => {
resetStats();
assert.equal(getDegradedModel("claude-opus-4-6"), "gemini-2.5-flash");
assert.equal(getDegradedModel("gemini-2.5-pro"), "gemini-2.5-flash");
assert.equal(getDegradedModel("gpt-4o"), "gpt-4o-mini");
});
test("getDegradedModel: returns original if no mapping exists", () => {
assert.equal(getDegradedModel("some-unknown-model"), "some-unknown-model");
});
test("getDegradedModel: handles null/empty", () => {
assert.equal(getDegradedModel(""), "");
assert.equal(getDegradedModel(null), null);
});
test("getDegradedModel: increments stats counter", () => {
resetStats();
getDegradedModel("claude-opus-4-6"); // known mapping
const config = getBackgroundDegradationConfig();
assert.equal(config.stats.detected, 1);
});
// ─── Config Management ──────────────────────────────────────────────────────
test("getBackgroundDegradationConfig: returns config copy", () => {
const config = getBackgroundDegradationConfig();
assert.ok(typeof config.enabled === "boolean");
assert.ok(typeof config.degradationMap === "object");
assert.ok(Array.isArray(config.detectionPatterns));
});
test("setBackgroundDegradationConfig: updates config", () => {
setBackgroundDegradationConfig({ enabled: true });
assert.equal(getBackgroundDegradationConfig().enabled, true);
setBackgroundDegradationConfig({ enabled: false }); // reset
});
test("getDefaultDegradationMap: returns non-empty map", () => {
const map = getDefaultDegradationMap();
assert.ok(Object.keys(map).length > 0);
assert.ok(map["claude-opus-4-6"]);
});
test("getDefaultDetectionPatterns: returns non-empty array", () => {
const patterns = getDefaultDetectionPatterns();
assert.ok(patterns.length > 0);
assert.ok(patterns.includes("generate a title"));
});
+105
View File
@@ -0,0 +1,105 @@
import test from "node:test";
import assert from "node:assert/strict";
const {
resolveModelAlias,
getDeprecationNotice,
isDeprecated,
setCustomAliases,
getCustomAliases,
addCustomAlias,
removeCustomAlias,
getAllAliases,
getBuiltInAliases,
} = await import("../../open-sse/services/modelDeprecation.ts");
// ─── resolveModelAlias ──────────────────────────────────────────────────────
test("resolveModelAlias: returns original for non-deprecated model", () => {
assert.equal(resolveModelAlias("claude-opus-4-6"), "claude-opus-4-6");
});
test("resolveModelAlias: resolves deprecated Gemini model", () => {
assert.equal(resolveModelAlias("gemini-pro"), "gemini-2.5-pro");
assert.equal(resolveModelAlias("gemini-1.5-pro"), "gemini-2.5-pro");
assert.equal(resolveModelAlias("gemini-1.5-flash"), "gemini-2.5-flash");
});
test("resolveModelAlias: resolves deprecated Claude model", () => {
assert.equal(resolveModelAlias("claude-3-opus-20240229"), "claude-opus-4-20250514");
assert.equal(resolveModelAlias("claude-3-5-sonnet-latest"), "claude-sonnet-4-20250514");
});
test("resolveModelAlias: resolves deprecated OpenAI model", () => {
assert.equal(resolveModelAlias("gpt-4-turbo-preview"), "gpt-4-turbo");
assert.equal(resolveModelAlias("gpt-3.5-turbo-0125"), "gpt-3.5-turbo");
});
test("resolveModelAlias: handles null/empty", () => {
assert.equal(resolveModelAlias(""), "");
assert.equal(resolveModelAlias(null), null);
assert.equal(resolveModelAlias(undefined), undefined);
});
// ─── getDeprecationNotice ───────────────────────────────────────────────────
test("getDeprecationNotice: returns message for deprecated model", () => {
const notice = getDeprecationNotice("gemini-pro");
assert.ok(notice);
assert.ok(notice.includes("deprecated"));
assert.ok(notice.includes("gemini-2.5-pro"));
});
test("getDeprecationNotice: returns null for non-deprecated model", () => {
assert.equal(getDeprecationNotice("claude-opus-4-6"), null);
});
test("getDeprecationNotice: returns null for empty/null", () => {
assert.equal(getDeprecationNotice(""), null);
assert.equal(getDeprecationNotice(null), null);
});
// ─── isDeprecated ───────────────────────────────────────────────────────────
test("isDeprecated: true for deprecated model", () => {
assert.equal(isDeprecated("gemini-pro"), true);
});
test("isDeprecated: false for current model", () => {
assert.equal(isDeprecated("claude-opus-4-6"), false);
});
// ─── Custom Aliases ─────────────────────────────────────────────────────────
test("custom aliases override built-in", () => {
setCustomAliases({ "gemini-pro": "gemini-3.1-pro" });
assert.equal(resolveModelAlias("gemini-pro"), "gemini-3.1-pro"); // custom wins
setCustomAliases({}); // reset
});
test("addCustomAlias and removeCustomAlias", () => {
addCustomAlias("my-old-model", "my-new-model");
assert.equal(resolveModelAlias("my-old-model"), "my-new-model");
const removed = removeCustomAlias("my-old-model");
assert.equal(removed, true);
assert.equal(resolveModelAlias("my-old-model"), "my-old-model");
});
test("removeCustomAlias: returns false for non-existent", () => {
assert.equal(removeCustomAlias("nonexistent"), false);
});
test("getAllAliases: includes both built-in and custom", () => {
addCustomAlias("test-from", "test-to");
const all = getAllAliases();
assert.ok(all["gemini-pro"]); // built-in
assert.equal(all["test-from"], "test-to"); // custom
removeCustomAlias("test-from"); // cleanup
});
test("getBuiltInAliases: returns built-in aliases", () => {
const builtIn = getBuiltInAliases();
assert.ok(builtIn["gemini-pro"]);
assert.ok(builtIn["claude-3-opus-20240229"]);
});
+125
View File
@@ -8,6 +8,10 @@ const {
ThinkingMode,
EFFORT_BUDGETS,
DEFAULT_THINKING_CONFIG,
THINKING_LEVEL_MAP,
normalizeThinkingLevel,
ensureThinkingConfig,
hasThinkingCapableModel,
} = await import("../../open-sse/services/thinkingBudget.ts");
// ─── Config Management ──────────────────────────────────────────────────────
@@ -159,3 +163,124 @@ test("EFFORT_BUDGETS has expected keys", () => {
assert.ok(EFFORT_BUDGETS.medium > EFFORT_BUDGETS.low);
assert.ok(EFFORT_BUDGETS.high > EFFORT_BUDGETS.medium);
});
// ─── thinkingLevel String Conversion (Feature 4) ────────────────────────────
test("THINKING_LEVEL_MAP has all expected levels", () => {
assert.equal(THINKING_LEVEL_MAP.none, 0);
assert.equal(THINKING_LEVEL_MAP.low, 1024);
assert.equal(THINKING_LEVEL_MAP.medium, 10240);
assert.equal(THINKING_LEVEL_MAP.high, 131072);
});
test("normalizeThinkingLevel: converts thinkingLevel 'high' to budget", () => {
const body = {
model: "claude-sonnet-4",
thinkingLevel: "high",
messages: [{ role: "user", content: "hello" }],
};
const result = normalizeThinkingLevel(body);
assert.equal(result.thinking.type, "enabled");
assert.equal(result.thinking.budget_tokens, 131072);
assert.equal(result.thinkingLevel, undefined);
});
test("normalizeThinkingLevel: converts thinking_level 'low' to budget", () => {
const body = {
model: "claude-sonnet-4",
thinking_level: "low",
messages: [{ role: "user", content: "hello" }],
};
const result = normalizeThinkingLevel(body);
assert.equal(result.thinking.type, "enabled");
assert.equal(result.thinking.budget_tokens, 1024);
assert.equal(result.thinking_level, undefined);
});
test("normalizeThinkingLevel: converts 'none' to disabled", () => {
const body = { model: "claude-sonnet-4", thinkingLevel: "none" };
const result = normalizeThinkingLevel(body);
assert.equal(result.thinking.type, "disabled");
assert.equal(result.thinking.budget_tokens, 0);
});
test("normalizeThinkingLevel: converts Gemini thinkingConfig.thinkingLevel", () => {
const body = {
model: "gemini-2.5-pro",
generationConfig: {
thinkingConfig: { thinkingLevel: "high" },
},
};
const result = normalizeThinkingLevel(body);
assert.equal(result.generationConfig.thinking_config.thinking_budget, 131072);
assert.equal(result.generationConfig.thinkingConfig, undefined);
});
test("normalizeThinkingLevel: ignores unknown string values", () => {
const body = { model: "claude-sonnet-4", thinkingLevel: "ultra" };
const result = normalizeThinkingLevel(body);
assert.equal(result.thinking, undefined); // not converted
assert.equal(result.thinkingLevel, "ultra"); // preserved
});
// ─── -thinking Suffix Auto-Injection (Feature 5) ────────────────────────────
test("ensureThinkingConfig: auto-injects for -thinking suffix model", () => {
const body = {
model: "claude-opus-4-6-thinking",
messages: [{ role: "user", content: "hello" }],
};
const result = ensureThinkingConfig(body);
assert.equal(result.thinking.type, "enabled");
assert.equal(result.thinking.budget_tokens, EFFORT_BUDGETS.medium);
});
test("ensureThinkingConfig: does NOT override existing thinking config", () => {
const body = {
model: "claude-opus-4-6-thinking",
thinking: { type: "enabled", budget_tokens: 50000 },
messages: [{ role: "user", content: "hello" }],
};
const result = ensureThinkingConfig(body);
assert.equal(result.thinking.budget_tokens, 50000); // preserved
});
test("ensureThinkingConfig: does nothing for non-thinking models", () => {
const body = {
model: "claude-sonnet-4",
messages: [{ role: "user", content: "hello" }],
};
const result = ensureThinkingConfig(body);
assert.equal(result.thinking, undefined);
});
test("hasThinkingCapableModel: matches -thinking suffix", () => {
assert.ok(hasThinkingCapableModel({ model: "claude-opus-4-6-thinking" }));
assert.ok(hasThinkingCapableModel({ model: "kimi-k2-thinking" }));
assert.ok(hasThinkingCapableModel({ model: "custom-model-thinking" }));
});
test("applyThinkingBudget: thinkingLevel 'high' + PASSTHROUGH = converts and passes through", () => {
setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH });
const body = {
model: "claude-sonnet-4",
thinkingLevel: "high",
messages: [{ role: "user", content: "hello" }],
};
const result = applyThinkingBudget(body);
assert.equal(result.thinking.budget_tokens, 131072);
assert.equal(result.thinkingLevel, undefined);
setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG);
});
test("applyThinkingBudget: -thinking model without config + PASSTHROUGH = auto-inject", () => {
setThinkingBudgetConfig({ mode: ThinkingMode.PASSTHROUGH });
const body = {
model: "claude-opus-4-6-thinking",
messages: [{ role: "user", content: "hello" }],
};
const result = applyThinkingBudget(body);
assert.equal(result.thinking.type, "enabled");
assert.equal(result.thinking.budget_tokens, EFFORT_BUDGETS.medium);
setThinkingBudgetConfig(DEFAULT_THINKING_CONFIG);
});