mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-08 00:02:20 +03:00
feat(providers): support Vercel AI Gateway embeddings and images (#5968)
* feat(providers): support Vercel AI Gateway embeddings and images
Extends the existing vercel-ai-gateway (alias vag) provider — currently
chat-only — with embeddings and image generation support, since the
gateway's OpenAI-compatible /v1 API also exposes /embeddings and
/images/generations. Adds entries to EMBEDDING_PROVIDERS
(embeddingRegistry.ts) and IMAGE_PROVIDERS (imageRegistry.ts) modeled
on the existing openai entries.
Out of scope for this PR (tracked as follow-ups): the /v1/credits
usage reader, retry:{429:2} tuning, and claude->reasoning_effort
mapping.
Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn>
Inspired-by: https://github.com/decolua/9router/pull/1704
* chore(changelog): restore release entries + add vercel-gateway media bullet
---------
Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn>
This commit is contained in:
committed by
GitHub
parent
1264d1f882
commit
b253d8a74a
@@ -12,6 +12,7 @@
|
||||
- **Discovery tool (Phase 2):** add the `discoveryResults` DB module (CRUD over the `discovery_results` table, migration 074) and wire the opt-in provider-discovery service to persist and read findings through it (`persistDiscoveryResult`, `getDiscoveryResults`, `getDiscoveryResultById`, `markVerified`, `deleteDiscoveryResult`) with `(provider, method, endpoint)` upsert de-duplication. Adds the `/api/discovery/*` HTTP surface — `GET /results`, `GET|DELETE /results/:id`, `POST /scan`, `POST /verify/:id` — under **strict loopback-only** authorization (`/api/discovery/` is in `LOCAL_ONLY_API_PREFIXES` and is NOT manage-scope-bypassable, so the `scan` route's outbound probes can never be reached from a tunnel/remote origin). Adds a **dashboard UI tab** (Tools → Discovery, `/dashboard/discovery`) to run scans and review, verify, or delete findings. The service stays **opt-in / default-off**.
|
||||
- **feat(proxy):** add Webshare proxy pool import and sync — a `WebshareProvider` (`FreeProxyProvider`) that paginates `proxy.webshare.io/api/v2/proxy/list/` gated on `FREE_PROXY_WEBSHARE_API_KEY`, SSRF-guards imported hosts, and tombstones retired proxy IDs via `pruneStaleFreeProxies()`. (thanks @ricatix)
|
||||
- **feat(api-keys):** track devices/connections per API key — an in-memory, TTL-evicted device fingerprint tracker (SHA-256 of masked IP + truncated user-agent) wired non-blocking into the chat path and surfaced via `GET /api/keys/[id]/devices` with a dashboard device-count chip. (thanks @mugnimaestra)
|
||||
- **feat(providers):** support Vercel AI Gateway embeddings and image generation. (thanks @newnol)
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
|
||||
@@ -93,6 +93,17 @@ export const EMBEDDING_PROVIDERS: Record<string, EmbeddingProvider> = {
|
||||
],
|
||||
},
|
||||
|
||||
"vercel-ai-gateway": {
|
||||
id: "vercel-ai-gateway",
|
||||
baseUrl: "https://ai-gateway.vercel.sh/v1/embeddings",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "text-embedding-3-small", name: "Text Embedding 3 Small", dimensions: 1536 },
|
||||
{ id: "text-embedding-3-large", name: "Text Embedding 3 Large", dimensions: 3072 },
|
||||
],
|
||||
},
|
||||
|
||||
upstage: {
|
||||
id: "upstage",
|
||||
baseUrl: "https://api.upstage.ai/v1/embeddings",
|
||||
|
||||
@@ -175,6 +175,20 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
supportedSizes: ["1024x1024", "2048x2048"],
|
||||
},
|
||||
|
||||
"vercel-ai-gateway": {
|
||||
id: "vercel-ai-gateway",
|
||||
alias: "vag",
|
||||
baseUrl: "https://ai-gateway.vercel.sh/v1/images/generations",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "openai",
|
||||
models: [
|
||||
{ id: "gpt-image-1", name: "GPT Image 1" },
|
||||
{ id: "black-forest-labs/flux-1.1-pro", name: "FLUX 1.1 Pro" },
|
||||
],
|
||||
supportedSizes: ["1024x1024", "1024x1792", "1792x1024"],
|
||||
},
|
||||
|
||||
together: {
|
||||
id: "together",
|
||||
baseUrl: "https://api.together.xyz/v1/images/generations",
|
||||
|
||||
54
tests/unit/vercel-ai-gateway-media.test.ts
Normal file
54
tests/unit/vercel-ai-gateway-media.test.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
EMBEDDING_PROVIDERS,
|
||||
getEmbeddingProvider,
|
||||
} from "../../open-sse/config/embeddingRegistry.ts";
|
||||
import { IMAGE_PROVIDERS, getImageProvider } from "../../open-sse/config/imageRegistry.ts";
|
||||
|
||||
describe("vercel-ai-gateway media registry entries (upstream #1704)", () => {
|
||||
describe("embeddingRegistry — vercel-ai-gateway", () => {
|
||||
it("is registered in EMBEDDING_PROVIDERS", () => {
|
||||
assert.ok(
|
||||
EMBEDDING_PROVIDERS["vercel-ai-gateway"],
|
||||
"vercel-ai-gateway should be in EMBEDDING_PROVIDERS"
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves vercel-ai-gateway provider config", () => {
|
||||
const p = getEmbeddingProvider("vercel-ai-gateway");
|
||||
assert.ok(p, "getEmbeddingProvider should resolve vercel-ai-gateway");
|
||||
assert.equal(p.id, "vercel-ai-gateway");
|
||||
assert.equal(p.baseUrl, "https://ai-gateway.vercel.sh/v1/embeddings");
|
||||
assert.equal(p.authType, "apikey");
|
||||
assert.equal(p.authHeader, "bearer");
|
||||
});
|
||||
|
||||
it("has at least one embedding model", () => {
|
||||
const p = getEmbeddingProvider("vercel-ai-gateway");
|
||||
assert.ok(p.models.length >= 1, `Expected ≥1 models, got ${p.models.length}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("imageRegistry — vercel-ai-gateway", () => {
|
||||
it("is registered in IMAGE_PROVIDERS", () => {
|
||||
assert.ok(IMAGE_PROVIDERS["vercel-ai-gateway"], "vercel-ai-gateway should be in IMAGE_PROVIDERS");
|
||||
});
|
||||
|
||||
it("resolves vercel-ai-gateway image provider config", () => {
|
||||
const p = getImageProvider("vercel-ai-gateway");
|
||||
assert.ok(p, "getImageProvider should resolve vercel-ai-gateway");
|
||||
assert.equal(p.id, "vercel-ai-gateway");
|
||||
assert.equal(p.alias, "vag");
|
||||
assert.equal(p.baseUrl, "https://ai-gateway.vercel.sh/v1/images/generations");
|
||||
assert.equal(p.authType, "apikey");
|
||||
assert.equal(p.authHeader, "bearer");
|
||||
assert.equal(p.format, "openai");
|
||||
});
|
||||
|
||||
it("has at least one image model", () => {
|
||||
const p = getImageProvider("vercel-ai-gateway");
|
||||
assert.ok(p.models.length >= 1, `Expected ≥1 models, got ${p.models.length}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user