mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Release v3.7.3 (#1724)
* Update image-model list * docs: update CHANGELOG.md with merged PR entries for v3.7.3 * chore(release): bump to v3.7.3 — changelog, docs, version sync * chore(workflow): update generate-release with Phase 2 validation step --------- Co-authored-by: backryun <bakryun0718@proton.me> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
2f1c904d74
commit
ae8a7d2dc5
@@ -213,11 +213,43 @@ Inform the user:
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Post-Merge (only after user confirms)
|
||||
## Phase 2: Post-Merge Validation (Local VPS)
|
||||
|
||||
> Run these steps only AFTER the user has merged the PR.
|
||||
> Run these steps only AFTER the user has merged the PR into `main` and all CI jobs have passed.
|
||||
|
||||
### 11. Create Git Tag and GitHub Release (MANDATORY)
|
||||
### 11. Deploy to Local VPS for Final Validation (MANDATORY)
|
||||
|
||||
Before cutting the official git tag and publishing to the world, deploy the `main` branch to the Local VPS for a final homologation test.
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git pull origin main
|
||||
|
||||
# Build and pack locally
|
||||
cd /home/diegosouzapw/dev/proxys/9router && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts
|
||||
|
||||
# Deploy to LOCAL VPS (192.168.0.15)
|
||||
scp omniroute-*.tgz root@192.168.0.15:/tmp/
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'"
|
||||
|
||||
# Verify
|
||||
curl -s -o /dev/null -w "LOCAL: HTTP %{http_code}\n" http://192.168.0.15:20128/
|
||||
```
|
||||
|
||||
### 12. 🛑 STOP — Notify User & Await Final OK
|
||||
|
||||
**This is a mandatory stop point.**
|
||||
Inform the user that the `main` branch is now running on the Local VPS.
|
||||
Wait for the user to manually test and give the **OK**.
|
||||
**DO NOT proceed to Phase 3 until the user confirms the local deploy is stable.**
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Official Launch
|
||||
|
||||
> Run these steps only AFTER the user gives the final OK from the Phase 2 local validation.
|
||||
|
||||
### 13. Create Git Tag and GitHub Release (MANDATORY)
|
||||
|
||||
// turbo
|
||||
|
||||
@@ -239,7 +271,7 @@ gh release create "v$VERSION" --title "v$VERSION" --notes "$NOTES" --target main
|
||||
|
||||
> **CRITICAL**: Docker Hub and npm MUST always publish the same version.
|
||||
> The Docker image is built automatically via GitHub Actions when a new tag is pushed.
|
||||
> After pushing the tag in step 11-12, **verify the workflow runs**:
|
||||
> After pushing the tag in step 13, **verify the workflow runs**:
|
||||
|
||||
```bash
|
||||
# Verify the Docker workflow triggered
|
||||
@@ -247,40 +279,30 @@ gh run list --repo diegosouzapw/OmniRoute --workflow docker-publish.yml --limit
|
||||
|
||||
# Wait for the Docker build to complete (usually 5–10 min)
|
||||
gh run watch --repo diegosouzapw/OmniRoute
|
||||
|
||||
# After completion, verify on Docker Hub:
|
||||
# https://hub.docker.com/r/diegosouzapw/omniroute/tags
|
||||
```
|
||||
|
||||
If the Docker build was not triggered automatically, trigger it manually:
|
||||
### 15. Publish to NPM (Optional/Automated)
|
||||
|
||||
Normally handled by CI, but if manual publish is required:
|
||||
|
||||
```bash
|
||||
gh workflow run docker-publish.yml --repo diegosouzapw/OmniRoute --ref v2.x.y
|
||||
npm publish
|
||||
```
|
||||
|
||||
### 15. Deploy to BOTH VPS environments (MANDATORY)
|
||||
### 16. Deploy to AKAMAI VPS (Production)
|
||||
|
||||
> Always deploy to **both** environments after every release.
|
||||
> See `/deploy-vps` workflow for detailed steps.
|
||||
Now that the release is officially cut, deploy it to the Akamai VPS.
|
||||
|
||||
```bash
|
||||
# Build and pack locally
|
||||
cd /home/diegosouzapw/dev/proxys/9router && rm -f omniroute-*.tgz && rm -rf .next/cache app/.next/cache && npm run build:cli && rm -rf app/logs app/coverage app/.git app/.app-build-backup* && npm pack --ignore-scripts
|
||||
|
||||
# Deploy to LOCAL VPS (192.168.0.15)
|
||||
scp omniroute-*.tgz root@192.168.0.15:/tmp/
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'"
|
||||
|
||||
# Deploy to AKAMAI VPS (69.164.221.35)
|
||||
scp omniroute-*.tgz root@69.164.221.35:/tmp/
|
||||
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Akamai done'"
|
||||
|
||||
# Verify both
|
||||
curl -s -o /dev/null -w "LOCAL: HTTP %{http_code}\n" http://192.168.0.15:20128/
|
||||
# Verify
|
||||
curl -s -o /dev/null -w "AKAMAI: HTTP %{http_code}\n" http://69.164.221.35:20128/
|
||||
```
|
||||
|
||||
### 16. Preserve release branch
|
||||
### 17. Preserve release branch
|
||||
|
||||
```bash
|
||||
# Branch is kept for historical purposes. Do not delete.
|
||||
|
||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -10,9 +10,17 @@
|
||||
|
||||
- **fix(claude):** strip existing billing headers from system array before injecting to prevent Anthropic prompt cache misses — stacked `x-anthropic-billing-header` blocks invalidated prefix matching, causing ~100% cache_create instead of cache_read (#1712)
|
||||
- **fix(claude):** strip `output_config.format` for non-Anthropic Claude-compatible providers during passthrough — third-party Claude endpoints (MiniMax, DeepSeek via aggregators) reject structured output fields with 400 errors (#1719)
|
||||
- **fix(combo):** set terminal error state on response quality validation failure — prevents misleading `ALL_ACCOUNTS_INACTIVE` 503 when the real issue is response quality validation (#1707)
|
||||
- **fix(combo):** set terminal error state on response quality validation failure — prevents misleading `ALL_ACCOUNTS_INACTIVE` 503 when the real issue is response quality validation (#1707, #1710)
|
||||
- **fix(combo):** treat combo fallback as target-level orchestration — all non-ok responses (including generic 400s) now fall through to the next target instead of being terminal; removes complex bad-request allowlist regex (#1713)
|
||||
- **fix(codex):** restore namespace MCP tools and hosted-tool whitelist — regression from #1581 that silently dropped all MCP tool groups and Responses-API hosted tools (#1715)
|
||||
- **fix(codex):** add neutral instructions for bare chat requests — Codex Responses backend rejects requests without `instructions`, making Codex unusable for normal chat (#1709)
|
||||
- **fix(proxy):** wrap proxy assignment queries in try-catch for missing `proxy_assignments` table — Electron installs where migration 004 hasn't run no longer crash with `no such table` error (#1706)
|
||||
- **fix(migration):** improve Windows file URL path resolution in migration runner — adds direct URL path extraction and `process.cwd()` fallback for CI-built bundles with leaked build-time paths (#1704)
|
||||
- **fix(ui):** fix light mode active request payload modal — add missing `--color-card` theme token, use opaque `bg-surface` instead of translucent `bg-card/70`, add backdrop blur (#1714)
|
||||
|
||||
### 🔄 Updates
|
||||
|
||||
- **chore(image-models):** refresh image generation model registry — replace stale FLUX aliases with FLUX Kontext / FLUX.2 mappings, remove deprecated FLUX Redux/Depth/Canny variants (#1722)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute-desktop",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"description": "OmniRoute Desktop Application",
|
||||
"main": "main.js",
|
||||
"author": {
|
||||
|
||||
4
llm.txt
4
llm.txt
@@ -8,7 +8,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
|
||||
**Key value:** One endpoint (`http://localhost:20128/v1`), unlimited models, zero downtime, minimal cost.
|
||||
|
||||
**Current version:** 3.7.2
|
||||
**Current version:** 3.7.3
|
||||
|
||||
## Tech Stack
|
||||
|
||||
@@ -279,7 +279,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
└── .env.example # Environment variable template
|
||||
```
|
||||
|
||||
## Key Features (v3.7.2)
|
||||
## Key Features (v3.7.3)
|
||||
|
||||
### Core Proxy
|
||||
- **160+ AI providers** with automatic format translation
|
||||
|
||||
@@ -37,57 +37,60 @@ interface ImageModelAliasEntry {
|
||||
|
||||
const IMAGE_MODEL_ALIASES: Record<string, ImageModelAliasEntry> = {
|
||||
"flux-kontext": {
|
||||
provider: "pollinations",
|
||||
model: "flux-kontext",
|
||||
name: "FLUX Kontext",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-pro",
|
||||
name: "FLUX Kontext Pro",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"flux-kontext-max": {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1.1-pro",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-max",
|
||||
name: "FLUX Kontext Max",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"flux-redux": {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1-redux",
|
||||
name: "FLUX Redux",
|
||||
"flux-2-max": {
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-2-max",
|
||||
name: "FLUX.2 Max",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"flux-depth": {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1-depth",
|
||||
name: "FLUX Depth",
|
||||
"flux-2-pro": {
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-2-pro",
|
||||
name: "FLUX.2 Pro",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"flux-canny": {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1-canny",
|
||||
name: "FLUX Canny",
|
||||
"flux-2-flex": {
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-2-flex",
|
||||
name: "FLUX.2 Flex",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"flux-dev-lora": {
|
||||
"flux-2-dev": {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1-dev-lora",
|
||||
name: "FLUX Dev LoRA",
|
||||
model: "black-forest-labs/FLUX.2-dev",
|
||||
name: "FLUX.2 Dev",
|
||||
listInCatalog: true,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
kontext: {
|
||||
provider: "pollinations",
|
||||
model: "flux-kontext",
|
||||
name: "FLUX Kontext",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-pro",
|
||||
name: "FLUX Kontext Pro",
|
||||
listInCatalog: false,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
"pollinations/kontext": {
|
||||
provider: "pollinations",
|
||||
model: "flux-kontext",
|
||||
name: "FLUX Kontext",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-pro",
|
||||
name: "FLUX Kontext Pro",
|
||||
listInCatalog: false,
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -110,11 +113,11 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "openai", // native OpenAI format
|
||||
models: [
|
||||
{ id: "gpt-image-1", name: "GPT Image 1" },
|
||||
{ id: "dall-e-3", name: "DALL-E 3" },
|
||||
{ id: "dall-e-2", name: "DALL-E 2" },
|
||||
{ id: "gpt-image-2", name: "GPT Image 2" },
|
||||
{ id: "gpt-image-1.5", name: "GPT Image 1.5" },
|
||||
{ id: "gpt-image-1-mini", name: "GPT Image 1 Mini" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "1024x1792", "1792x1024", "256x256", "512x512"],
|
||||
supportedSizes: ["1024x1024", "1024x1792", "1792x1024"],
|
||||
},
|
||||
|
||||
// Codex exposes image generation only as a Responses-API hosted tool under
|
||||
@@ -129,11 +132,11 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "codex-responses",
|
||||
models: [
|
||||
{ id: "gpt-5.5", name: "GPT 5.5 (Codex Image)" },
|
||||
{ id: "gpt-5.4", name: "GPT 5.4 (Codex Image)" },
|
||||
{ id: "gpt-5.3-codex", name: "GPT 5.3 Codex (Image)" },
|
||||
{ id: "gpt-5.1-codex-max", name: "GPT 5.1 Codex Max (Image)" },
|
||||
],
|
||||
supportedSizes: ["512x512", "1024x1024", "1024x1536", "1536x1024"],
|
||||
supportedSizes: ["1024x1024", "1024x1536", "1536x1024"],
|
||||
},
|
||||
|
||||
"chatgpt-web": {
|
||||
@@ -153,7 +156,7 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [{ id: "grok-2-image-1212", name: "Grok 2 Image" }],
|
||||
models: [{ id: "grok-imagine-image", name: "Grok Imagine Image" }],
|
||||
supportedSizes: ["1024x1024"],
|
||||
},
|
||||
|
||||
@@ -165,32 +168,39 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
format: "openai",
|
||||
models: [
|
||||
{
|
||||
id: "black-forest-labs/FLUX.1.1-pro",
|
||||
name: "FLUX 1.1 Pro",
|
||||
id: "black-forest-labs/FLUX.2-max",
|
||||
name: "FLUX.2 Max",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Advanced contextual image editing and image-to-image generation",
|
||||
},
|
||||
{ id: "black-forest-labs/FLUX.1-schnell-Free", name: "FLUX 1 Schnell (Free)" },
|
||||
{ id: "stabilityai/stable-diffusion-xl-base-1.0", name: "SDXL Base 1.0" },
|
||||
{
|
||||
id: "black-forest-labs/FLUX.1-redux",
|
||||
name: "FLUX.1 Redux",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Generate prompt-guided variations from an input image",
|
||||
},
|
||||
{
|
||||
id: "black-forest-labs/FLUX.1-depth",
|
||||
name: "FLUX.1 Depth",
|
||||
id: "black-forest-labs/FLUX.2-pro",
|
||||
name: "FLUX.2 Pro",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Depth-conditioned image generation from an input image",
|
||||
},
|
||||
{
|
||||
id: "black-forest-labs/FLUX.1-canny",
|
||||
name: "FLUX.1 Canny",
|
||||
id: "black-forest-labs/FLUX.2-flex",
|
||||
name: "FLUX.2 Flex",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "black-forest-labs/FLUX.2-dev",
|
||||
name: "FLUX.2 Dev",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{ id: "openai/gpt-image-1.5", name: "GPT Image 1.5", inputModalities: ["text", "image"] },
|
||||
{ id: "Wan-AI/Wan2.6-image", name: "Wan 2.6 Image", inputModalities: ["text", "image"] },
|
||||
{
|
||||
id: "Qwen/Qwen-Image-2.0-Pro",
|
||||
name: "Qwen Image 2.0 Pro",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{ id: "Qwen/Qwen-Image-2.0", name: "Qwen Image 2.0", inputModalities: ["text", "image"] },
|
||||
{ id: "google/flash-image-3.1", name: "NanoBanana 2", inputModalities: ["text", "image"] },
|
||||
{
|
||||
id: "google/gemini-3-pro-image",
|
||||
name: "NanoBanana Pro",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Canny edge guided image generation from an input image",
|
||||
},
|
||||
{ id: "black-forest-labs/FLUX.1-dev-lora", name: "FLUX.1 Dev LoRA" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "512x512"],
|
||||
},
|
||||
@@ -202,6 +212,9 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [
|
||||
{ id: "accounts/fireworks/flux-kontext-max", name: "FLUX Kontext Max" },
|
||||
{ id: "accounts/fireworks/flux-kontext-pro", name: "FLUX Kontext Pro" },
|
||||
{ id: "accounts/fireworks/flux-1-schnell-fp8", name: "FLUX.1 schnell" },
|
||||
{ id: "accounts/fireworks/models/flux-1-dev-fp8", name: "FLUX 1 Dev FP8" },
|
||||
{ id: "accounts/fireworks/models/stable-diffusion-xl-1024-v1-0", name: "SDXL 1024 v1.0" },
|
||||
],
|
||||
@@ -218,6 +231,7 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
supportedSizes: ["1024x1024"],
|
||||
},
|
||||
|
||||
//Curruntly no models serving
|
||||
nebius: {
|
||||
id: "nebius",
|
||||
baseUrl: "https://api.tokenfactory.nebius.com/v1/images/generations",
|
||||
@@ -225,11 +239,8 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [
|
||||
{ id: "black-forest-labs/flux-schnell", name: "FLUX.1 Schnell" },
|
||||
{ id: "black-forest-labs/flux-dev", name: "FLUX.1 Dev" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "512x512"],
|
||||
models: [{ id: "black-forest-labs/flux-schnell", name: "No Model yet" }],
|
||||
supportedSizes: ["1024x1024"],
|
||||
},
|
||||
|
||||
hyperbolic: {
|
||||
@@ -238,13 +249,10 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "hyperbolic", // custom: uses model_name, returns base64 images
|
||||
models: [
|
||||
{ id: "SDXL1.0-base", name: "SDXL 1.0 Base" },
|
||||
{ id: "SD2", name: "Stable Diffusion 2" },
|
||||
{ id: "FLUX.1-dev", name: "FLUX.1 Dev" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "512x512"],
|
||||
models: [{ id: "SDXL1.0-base", name: "No Model yet" }],
|
||||
supportedSizes: ["1024x1024"],
|
||||
},
|
||||
//Curruntly no models serving
|
||||
|
||||
nanobanana: {
|
||||
id: "nanobanana",
|
||||
@@ -294,12 +302,15 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [
|
||||
{ id: "openai/dall-e-3", name: "DALL-E 3 (via OpenRouter)" },
|
||||
{ id: "openai/gpt-image-1", name: "GPT Image 1 (via OpenRouter)" },
|
||||
{ id: "google/imagen-3.0-generate-002", name: "Imagen 3 (via OpenRouter)" },
|
||||
{ id: "black-forest-labs/FLUX.1-schnell", name: "FLUX.1 Schnell (via OpenRouter)" },
|
||||
{ id: "openai/gpt-5.4-image-2", name: "GPT Image 2 (via OpenRouter)" },
|
||||
{ id: "openai/gpt-5-image-mini", name: "GPT Image 1 Mini (via OpenRouter)" },
|
||||
{ id: "google/gemini-3.1-flash-image-preview", name: "Nano Banana 2 (via OpenRouter)" },
|
||||
{ id: "google/gemini-3-pro-image-preview", name: "Nano Banana Pro (via OpenRouter)" },
|
||||
{ id: "black-forest-labs/flux.2-max", name: "FLUX.2 Max (via OpenRouter)" },
|
||||
{ id: "black-forest-labs/flux.2-pro", name: "FLUX.2 Pro (via OpenRouter)" },
|
||||
{ id: "black-forest-labs/flux.2-flex", name: "FLUX.2 Flex (via OpenRouter)" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "1024x1792", "1792x1024", "256x256", "512x512"],
|
||||
supportedSizes: ["1024x1024", "1024x1792", "1792x1024"],
|
||||
},
|
||||
|
||||
pollinations: {
|
||||
@@ -310,25 +321,14 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [
|
||||
{ id: "klein", name: "FLUX.2 Klein 4B" },
|
||||
{ id: "flux", name: "Flux Schnell" },
|
||||
{ id: "zimage", name: "Z-Image Turbo" },
|
||||
{ id: "klein", name: "FLUX.2 Klein 4B" },
|
||||
{ id: "gptimage", name: "GPT Image 1 Mini" },
|
||||
{ id: "qwen-image", name: "Qwen Image Plus" },
|
||||
{ id: "wan-image", name: "Wan 2.7 Image" },
|
||||
{
|
||||
id: "flux-kontext",
|
||||
name: "FLUX.1 Kontext",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Context-aware image editing with optional source image",
|
||||
},
|
||||
{
|
||||
id: "flux-kontext-max",
|
||||
name: "FLUX.1 Kontext Max",
|
||||
inputModalities: ["text", "image"],
|
||||
description: "Higher quality Kontext editing with optional source image",
|
||||
},
|
||||
{ id: "gpt-image-2", name: "GPT Image 2" },
|
||||
{ id: "gptimage-large", name: "GPT Image 1.5" },
|
||||
{ id: "gptimage", name: "GPT Image 1 Mini" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "512x512"],
|
||||
},
|
||||
@@ -340,17 +340,17 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "key",
|
||||
format: "fal-ai",
|
||||
models: [
|
||||
{ id: "fal-ai/flux-pro/v1.1", name: "FLUX.1 Pro v1.1" },
|
||||
{ id: "fal-ai/flux-pro/v1.1-ultra", name: "FLUX.1 Pro v1.1 Ultra" },
|
||||
{ id: "fal-ai/flux/schnell", name: "FLUX.1 Schnell" },
|
||||
{ id: "fal-ai/flux-2-max", name: "FLUX.2 Max" },
|
||||
{ id: "fal-ai/flux-2-pro", name: "FLUX.2 Pro" },
|
||||
{ id: "fal-ai/flux-2-flex", name: "FLUX.2 Flex" },
|
||||
{ id: "bria/text-to-image/3.2", name: "Bria 3.2" },
|
||||
{ id: "fal-ai/bytedance/seedream/v3/text-to-image", name: "SeeDream V3" },
|
||||
{ id: "fal-ai/bytedance/seedream/v4.5/text-to-image", name: "SeeDream V4.5" },
|
||||
{ id: "fal-ai/bytedance/dreamina/v3.1/text-to-image", name: "Dreamina V3.1" },
|
||||
{ id: "fal-ai/ideogram/v3", name: "Ideogram V3" },
|
||||
{ id: "fal-ai/imagen4/preview", name: "Imagen 4 Preview" },
|
||||
{ id: "fal-ai/imagen4/preview/fast", name: "Imagen 4 Preview Fast" },
|
||||
{ id: "fal-ai/imagen4/preview/ultra", name: "Imagen 4 Preview Ultra" },
|
||||
{ id: "fal-ai/recraft/v3/text-to-image", name: "Recraft V3 via Fal" },
|
||||
{ id: "fal-ai/nano-banana-pro", name: "Nano Banana Pro" },
|
||||
{ id: "fal-ai/nano-banana-2", name: "Nano Banana 2" },
|
||||
{ id: "fal-ai/recraft/v4/pro/text-to-image", name: "Recraft V4 Pro via Fal" },
|
||||
{ id: "fal-ai/recraft/v4/text-to-image", name: "Recraft V4 via Fal" },
|
||||
{ id: "fal-ai/stable-diffusion-v35-medium", name: "Stable Diffusion v3.5 Medium" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "1024x1280", "1280x1024"],
|
||||
@@ -363,29 +363,26 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "stability-ai",
|
||||
models: [
|
||||
{ id: "sd3", name: "sd3" },
|
||||
{ id: "sd3-large", name: "sd3-large" },
|
||||
{ id: "sd3-large-turbo", name: "sd3-large-turbo" },
|
||||
{ id: "sd3-medium", name: "sd3-medium" },
|
||||
{ id: "sd3.5-large", name: "sd3.5-large" },
|
||||
{ id: "sd3.5-large-turbo", name: "sd3.5-large-turbo" },
|
||||
{ id: "sd3.5-medium", name: "sd3.5-medium" },
|
||||
{ id: "stable-image-ultra", name: "Stable Image Ultra" },
|
||||
{ id: "stable-image-core", name: "Stable Image Core" },
|
||||
{ id: "sd3.5-large-turbo", name: "sd3.5-large-turbo" },
|
||||
{ id: "sd3.5-large", name: "sd3.5-large" },
|
||||
{ id: "sd3.5-medium", name: "sd3.5-medium" },
|
||||
{ id: "sd3.5-flash", name: "sd3.5-flash" },
|
||||
{ id: "erase", name: "Erase", inputModalities: ["image"] },
|
||||
{ id: "inpaint", name: "Inpaint", inputModalities: ["text", "image"] },
|
||||
{ id: "outpaint", name: "Outpaint", inputModalities: ["text", "image"] },
|
||||
{ id: "erase", name: "Erase", inputModalities: ["image"] },
|
||||
{ id: "remove-background", name: "Remove Background", inputModalities: ["image"] },
|
||||
{ id: "search-and-replace", name: "Search and Replace", inputModalities: ["text", "image"] },
|
||||
{ id: "search-and-recolor", name: "Search and Recolor", inputModalities: ["text", "image"] },
|
||||
{ id: "remove-background", name: "Remove Background", inputModalities: ["image"] },
|
||||
{
|
||||
id: "replace-background-and-relight",
|
||||
name: "Replace Background and Relight",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{ id: "creative", name: "Creative Upscale", inputModalities: ["text", "image"] },
|
||||
{ id: "fast", name: "Fast Upscale", inputModalities: ["image"] },
|
||||
{ id: "conservative", name: "Conservative Upscale", inputModalities: ["image"] },
|
||||
{ id: "creative", name: "Creative Upscale", inputModalities: ["text", "image"] },
|
||||
{ id: "sketch", name: "Sketch Control", inputModalities: ["text", "image"] },
|
||||
{ id: "structure", name: "Structure Control", inputModalities: ["text", "image"] },
|
||||
{ id: "style", name: "Style Control", inputModalities: ["text", "image"] },
|
||||
@@ -401,16 +398,15 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "x-key",
|
||||
format: "black-forest-labs",
|
||||
models: [
|
||||
{ id: "flux-kontext-pro", name: "flux-kontext-pro", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-kontext-max", name: "flux-kontext-max", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-pro-1.0-fill", name: "flux-pro-1.0-fill", inputModalities: ["text", "image"] },
|
||||
{
|
||||
id: "flux-pro-1.0-expand",
|
||||
name: "flux-pro-1.0-expand",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{ id: "flux-pro-1.1", name: "flux-pro-1.1" },
|
||||
{ id: "flux-2-max", name: "FLUX.2 Max" },
|
||||
{ id: "flux-2-pro", name: "FLUX.2 Pro" },
|
||||
{ id: "flux-2-flex", name: "FLUX.2 Flex" },
|
||||
{ id: "flux-pro-1.1-ultra", name: "flux-pro-1.1-ultra" },
|
||||
{ id: "flux-pro-1.1", name: "flux-pro-1.1" },
|
||||
{ id: "flux-2-klein-9b", name: "flux 2 Klein 9B" },
|
||||
{ id: "flux-2-klein-4b", name: "flux 2 Klein 4B" },
|
||||
{ id: "flux-kontext-max", name: "flux-kontext-max", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-kontext-pro", name: "flux-kontext-pro", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-dev", name: "flux-dev" },
|
||||
{ id: "flux-pro", name: "flux-pro" },
|
||||
],
|
||||
@@ -424,8 +420,10 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authHeader: "bearer",
|
||||
format: "recraft",
|
||||
models: [
|
||||
{ id: "recraftv2", name: "recraftv2" },
|
||||
{ id: "recraftv3", name: "recraftv3" },
|
||||
{ id: "recraftv4_pro", name: "Recraft V4 Pro" },
|
||||
{ id: "recraftv4", name: "Recraft V4" },
|
||||
{ id: "recraftv3", name: "Recraft V3" },
|
||||
{ id: "recraftv2", name: "Recraft V2" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "1024x1280", "1280x1024"],
|
||||
},
|
||||
|
||||
@@ -32,24 +32,29 @@ import {
|
||||
import { fetchRemoteImage } from "@/shared/network/remoteImageFetch";
|
||||
|
||||
const OPENAI_IMAGE_TO_IMAGE_MODELS = new Set([
|
||||
"black-forest-labs/FLUX.1-redux",
|
||||
"black-forest-labs/FLUX.1-depth",
|
||||
"black-forest-labs/FLUX.1-canny",
|
||||
"black-forest-labs/FLUX.1.1-pro",
|
||||
"FLUX.1-redux",
|
||||
"FLUX.1-depth",
|
||||
"FLUX.1-canny",
|
||||
"FLUX.1.1-pro",
|
||||
"black-forest-labs/FLUX.2-max",
|
||||
"black-forest-labs/FLUX.2-pro",
|
||||
"black-forest-labs/FLUX.2-flex",
|
||||
"black-forest-labs/FLUX.2-dev",
|
||||
"openai/gpt-image-1.5",
|
||||
"Wan-AI/Wan2.6-image",
|
||||
"Qwen/Qwen-Image-2.0-Pro",
|
||||
"Qwen/Qwen-Image-2.0",
|
||||
"google/flash-image-3.1",
|
||||
"google/gemini-3-pro-image",
|
||||
"flux-kontext-max",
|
||||
"flux-kontext",
|
||||
"flux-kontext-pro",
|
||||
]);
|
||||
|
||||
const BFL_MODEL_ENDPOINTS = {
|
||||
"flux-2-max": "/v1/flux-2-max",
|
||||
"flux-2-pro": "/v1/flux-2-pro",
|
||||
"flux-2-flex": "/v1/flux-2-flex",
|
||||
"flux-2-klein-9b": "/v1/flux-2-klein-9b",
|
||||
"flux-2-klein-4b": "/v1/flux-2-klein-4b",
|
||||
"flux-kontext-pro": "/v1/flux-kontext-pro",
|
||||
"flux-kontext-max": "/v1/flux-kontext-max",
|
||||
"flux-pro-1.0-fill": "/v1/flux-pro-1.0-fill",
|
||||
"flux-pro-1.0-expand": "/v1/flux-pro-1.0-expand",
|
||||
"flux-pro-1.1": "/v1/flux-pro-1.1",
|
||||
"flux-pro-1.1-ultra": "/v1/flux-pro-1.1-ultra",
|
||||
"flux-dev": "/v1/flux-dev",
|
||||
@@ -57,22 +62,20 @@ const BFL_MODEL_ENDPOINTS = {
|
||||
};
|
||||
|
||||
const BFL_EDIT_MODELS = new Set([
|
||||
"flux-2-max",
|
||||
"flux-2-pro",
|
||||
"flux-2-flex",
|
||||
"flux-kontext-pro",
|
||||
"flux-kontext-max",
|
||||
"flux-pro-1.0-fill",
|
||||
"flux-pro-1.0-expand",
|
||||
]);
|
||||
|
||||
const BFL_FAILURE_STATUSES = new Set(["Error", "Failed", "Content Moderated", "Request Moderated"]);
|
||||
|
||||
const STABILITY_GENERATION_ENDPOINTS = {
|
||||
sd3: "/v2beta/stable-image/generate/sd3",
|
||||
"sd3-large": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3-large-turbo": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3-medium": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3.5-large": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3.5-large-turbo": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3.5-medium": "/v2beta/stable-image/generate/sd3",
|
||||
"sd3.5-flash": "/v2beta/stable-image/generate/sd3",
|
||||
"stable-image-ultra": "/v2beta/stable-image/generate/ultra",
|
||||
"stable-image-core": "/v2beta/stable-image/generate/core",
|
||||
};
|
||||
@@ -1058,7 +1061,7 @@ async function handleStabilityAIImageGeneration({
|
||||
|
||||
try {
|
||||
if (STABILITY_GENERATION_ENDPOINTS[model]) {
|
||||
if (model.startsWith("sd3") && model !== "sd3") {
|
||||
if (model.startsWith("sd3.5")) {
|
||||
upstreamBody.model = model;
|
||||
}
|
||||
|
||||
@@ -1070,7 +1073,7 @@ async function handleStabilityAIImageGeneration({
|
||||
upstreamBody.mode = "text-to-image";
|
||||
}
|
||||
|
||||
if (!model.startsWith("sd3") || !imageUrl) {
|
||||
if (!model.startsWith("sd3.5") || !imageUrl) {
|
||||
upstreamBody.aspect_ratio = body.aspect_ratio || mapImageSize(body.size);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@omniroute/open-sse",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -13808,7 +13808,7 @@
|
||||
},
|
||||
"open-sse": {
|
||||
"name": "@omniroute/open-sse",
|
||||
"version": "3.7.2"
|
||||
"version": "3.7.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "3.7.2",
|
||||
"version": "3.7.3",
|
||||
"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": {
|
||||
|
||||
@@ -274,7 +274,7 @@ test("getStaticModelsForProvider returns local image catalogs for image-only pro
|
||||
assert.ok(models, "xAI should expose local image models");
|
||||
assert.deepEqual(
|
||||
models.map((model) => model.id),
|
||||
["grok-2-image-1212"]
|
||||
["grok-imagine-image"]
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ test("getTransientBuildPaths leaves _tasks in place by default", () => {
|
||||
["legacy app snapshot", "local Wine prefix"]
|
||||
);
|
||||
assert.equal(
|
||||
paths.some((entry) => entry.sourcePath === "/repo/_tasks"),
|
||||
paths.some((entry) => path.basename(entry.sourcePath) === "_tasks"),
|
||||
false
|
||||
);
|
||||
});
|
||||
@@ -124,7 +124,7 @@ test("getTransientBuildPaths only moves _tasks when explicitly enabled", () => {
|
||||
const paths = getTransientBuildPaths("/repo", { OMNIROUTE_BUILD_MOVE_TASKS: "1" });
|
||||
|
||||
assert.equal(
|
||||
paths.some((entry) => entry.sourcePath === "/repo/_tasks"),
|
||||
paths.some((entry) => path.basename(entry.sourcePath) === "_tasks"),
|
||||
true
|
||||
);
|
||||
});
|
||||
@@ -170,6 +170,6 @@ test("syncStandaloneNativeAssets copies wreq-js native runtime into standalone o
|
||||
|
||||
assert.equal(changed, true);
|
||||
assert.equal(await fs.readFile(destinationNativeFile, "utf8"), "native module bytes");
|
||||
assert.match(logs[0] ?? "", /wreq-js\/rust/);
|
||||
assert.match((logs[0] ?? "").replaceAll("\\", "/"), /wreq-js\/rust/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ const QWEN_CONFIG_PATH = path.join(DUMMY_HOME, ".qwen", "settings.json");
|
||||
const QWEN_ENV_PATH = path.join(DUMMY_HOME, ".qwen", ".env");
|
||||
const OPENCODE_CONFIG_PATH = path.join(DUMMY_HOME, ".config", "opencode", "opencode.json");
|
||||
const originalXDG = process.env.XDG_CONFIG_HOME;
|
||||
const originalAppData = process.env.APPDATA;
|
||||
const originalJwtSecret = process.env.JWT_SECRET;
|
||||
|
||||
async function createAuthCookie() {
|
||||
@@ -51,6 +52,7 @@ test.beforeEach(async () => {
|
||||
// Force XDG_CONFIG_HOME so resolveOpencodeConfigPath resolves to our dummy dir
|
||||
// (CI runners often have XDG_CONFIG_HOME set, causing path mismatch)
|
||||
process.env.XDG_CONFIG_HOME = path.join(DUMMY_HOME, ".config");
|
||||
process.env.APPDATA = path.join(DUMMY_HOME, ".config");
|
||||
process.env.API_KEY_SECRET = "test-secret";
|
||||
await fs.mkdir(path.dirname(QWEN_CONFIG_PATH), { recursive: true }).catch(() => {});
|
||||
});
|
||||
@@ -59,6 +61,8 @@ test.afterEach(async () => {
|
||||
await fs.rm(DUMMY_HOME, { recursive: true, force: true }).catch(() => {});
|
||||
if (originalXDG === undefined) delete process.env.XDG_CONFIG_HOME;
|
||||
else process.env.XDG_CONFIG_HOME = originalXDG;
|
||||
if (originalAppData === undefined) delete process.env.APPDATA;
|
||||
else process.env.APPDATA = originalAppData;
|
||||
if (originalJwtSecret === undefined) delete process.env.JWT_SECRET;
|
||||
else process.env.JWT_SECRET = originalJwtSecret;
|
||||
});
|
||||
|
||||
@@ -54,10 +54,10 @@ test("handleImageGeneration routes OpenAI-compatible providers and forwards imag
|
||||
try {
|
||||
const result = await handleImageGeneration({
|
||||
body: {
|
||||
model: "openai/dall-e-3",
|
||||
model: "openai/gpt-image-2",
|
||||
prompt: "city skyline",
|
||||
n: 2,
|
||||
size: "1024x1792",
|
||||
size: "1024x1536",
|
||||
quality: "hd",
|
||||
response_format: "url",
|
||||
style: "vivid",
|
||||
@@ -70,10 +70,10 @@ test("handleImageGeneration routes OpenAI-compatible providers and forwards imag
|
||||
assert.equal(captured.url, "https://api.openai.com/v1/images/generations");
|
||||
assert.equal(captured.headers.Authorization, "Bearer image-key");
|
||||
assert.deepEqual(captured.body, {
|
||||
model: "dall-e-3",
|
||||
model: "gpt-image-2",
|
||||
prompt: "city skyline",
|
||||
n: 2,
|
||||
size: "1024x1792",
|
||||
size: "1024x1536",
|
||||
quality: "hd",
|
||||
response_format: "url",
|
||||
style: "vivid",
|
||||
@@ -250,32 +250,32 @@ test("handleImageGeneration treats unknown provider prefixes as invalid image mo
|
||||
|
||||
test("image registry resolves flux aliases and exposes planned catalog aliases", () => {
|
||||
assert.deepEqual(parseImageModel("flux-kontext"), {
|
||||
provider: "pollinations",
|
||||
model: "flux-kontext",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-pro",
|
||||
});
|
||||
assert.deepEqual(parseImageModel("pollinations/kontext"), {
|
||||
provider: "pollinations",
|
||||
model: "flux-kontext",
|
||||
provider: "black-forest-labs",
|
||||
model: "flux-kontext-pro",
|
||||
});
|
||||
assert.deepEqual(parseImageModel("flux-redux"), {
|
||||
assert.deepEqual(parseImageModel("flux-2-dev"), {
|
||||
provider: "together",
|
||||
model: "black-forest-labs/FLUX.1-redux",
|
||||
model: "black-forest-labs/FLUX.2-dev",
|
||||
});
|
||||
|
||||
const modelIds = new Set(getAllImageModels().map((model) => model.id));
|
||||
const fluxRedux = getAllImageModels().find((model) => model.id === "flux-redux");
|
||||
const flux2Dev = getAllImageModels().find((model) => model.id === "flux-2-dev");
|
||||
const fluxKontext = getAllImageModels().find((model) => model.id === "flux-kontext");
|
||||
for (const alias of [
|
||||
"flux-kontext",
|
||||
"flux-kontext-max",
|
||||
"flux-redux",
|
||||
"flux-depth",
|
||||
"flux-canny",
|
||||
"flux-dev-lora",
|
||||
"flux-2-max",
|
||||
"flux-2-pro",
|
||||
"flux-2-flex",
|
||||
"flux-2-dev",
|
||||
]) {
|
||||
assert.equal(modelIds.has(alias), true, `Expected alias ${alias} in image catalog`);
|
||||
}
|
||||
assert.deepEqual(fluxRedux?.inputModalities, ["text", "image"]);
|
||||
assert.deepEqual(flux2Dev?.inputModalities, ["text", "image"]);
|
||||
assert.deepEqual(fluxKontext?.inputModalities, ["text", "image"]);
|
||||
});
|
||||
|
||||
@@ -1130,7 +1130,7 @@ test("handleImageGeneration logs OpenAI-compatible upstream failures and transpo
|
||||
try {
|
||||
const failed = await handleImageGeneration({
|
||||
body: {
|
||||
model: "openai/dall-e-3",
|
||||
model: "openai/gpt-image-2",
|
||||
prompt: "broken upstream",
|
||||
},
|
||||
credentials: { apiKey: "image-key" },
|
||||
@@ -1151,7 +1151,7 @@ test("handleImageGeneration logs OpenAI-compatible upstream failures and transpo
|
||||
try {
|
||||
const errored = await handleImageGeneration({
|
||||
body: {
|
||||
model: "openai/dall-e-3",
|
||||
model: "openai/gpt-image-2",
|
||||
prompt: "transport issue",
|
||||
},
|
||||
credentials: { apiKey: "image-key" },
|
||||
|
||||
@@ -101,7 +101,7 @@ test("v1 image generation POST still requires prompts for text-input models", as
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
model: "openai/dall-e-3",
|
||||
model: "openai/gpt-image-2",
|
||||
image_url: "https://example.com/source.png",
|
||||
}),
|
||||
})
|
||||
@@ -109,5 +109,5 @@ test("v1 image generation POST still requires prompts for text-input models", as
|
||||
const body = (await response.json()) as any;
|
||||
|
||||
assert.equal(response.status, 400);
|
||||
assert.match(body.error.message, /Prompt is required for image model: openai\/dall-e-3/);
|
||||
assert.match(body.error.message, /Prompt is required for image model: openai\/gpt-image-2/);
|
||||
});
|
||||
|
||||
@@ -464,7 +464,7 @@ test("v1 models catalog includes media, moderation, rerank, video, and music mod
|
||||
const byId = new Map(body.data.map((item) => [item.id, item]));
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.equal((byId.get("openai/gpt-image-1") as any).type, "image");
|
||||
assert.equal((byId.get("openai/gpt-image-2") as any).type, "image");
|
||||
assert.equal((byId.get("openai/whisper-1") as any).type, "audio");
|
||||
assert.equal((byId.get("openai/whisper-1") as any).subtype, "transcription");
|
||||
assert.equal((byId.get("openai/omni-moderation-latest") as any).type, "moderation");
|
||||
@@ -484,10 +484,10 @@ test("v1 models catalog exposes image model input and output modalities for adva
|
||||
const byId = new Map(body.data.map((item) => [item.id, item]));
|
||||
|
||||
assert.equal(response.status, 200);
|
||||
assert.deepEqual((byId as any).get("flux-redux")?.input_modalities, ["text", "image"]);
|
||||
(assert as any).deepEqual((byId.get("flux-redux") as any).output_modalities, ["image"]);
|
||||
(assert as any).equal((byId.get("flux-redux") as any).type, "image");
|
||||
assert.ok((byId.get("flux-redux") as any).supported_sizes?.includes("1024x1024"));
|
||||
assert.deepEqual((byId as any).get("flux-2-dev")?.input_modalities, ["text", "image"]);
|
||||
(assert as any).deepEqual((byId.get("flux-2-dev") as any).output_modalities, ["image"]);
|
||||
(assert as any).equal((byId.get("flux-2-dev") as any).type, "image");
|
||||
assert.ok((byId.get("flux-2-dev") as any).supported_sizes?.includes("1024x1024"));
|
||||
(assert as any).deepEqual((byId.get("topaz/topaz-enhance") as any).input_modalities, ["image"]);
|
||||
assert.deepEqual((byId.get("topaz/topaz-enhance") as any).output_modalities, ["image"]);
|
||||
});
|
||||
|
||||
@@ -71,10 +71,10 @@ describe("OpenRouter & GitHub registry entries (#960)", () => {
|
||||
assert.ok(p.models.length >= 2, `Expected ≥2 models, got ${p.models.length}`);
|
||||
});
|
||||
|
||||
it("parses openrouter/openai/dall-e-3 correctly", () => {
|
||||
const result = parseImageModel("openrouter/openai/dall-e-3");
|
||||
it("parses openrouter/openai/gpt-5.4-image-2 correctly", () => {
|
||||
const result = parseImageModel("openrouter/openai/gpt-5.4-image-2");
|
||||
assert.equal(result.provider, "openrouter");
|
||||
assert.equal(result.model, "openai/dall-e-3");
|
||||
assert.equal(result.model, "openai/gpt-5.4-image-2");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("transformToOmniRoute", () => {
|
||||
litellm_provider: "openai",
|
||||
mode: "embedding",
|
||||
},
|
||||
"openai/dall-e-3": {
|
||||
"openai/gpt-image-2": {
|
||||
input_cost_per_token: 0,
|
||||
output_cost_per_token: 0,
|
||||
litellm_provider: "openai",
|
||||
|
||||
Reference in New Issue
Block a user