Video generation was xAI-only. Adds an adapter layer under
open-sse/handlers/videoProviders/ so /v1/videos/* can target OpenRouter or
Google Cloud credentials. A provider with no adapter keeps the exact previous
behaviour (raw body to {baseUrl}/{action}, poll {baseUrl}/{id}, verbatim
passthrough), so the xAI path is unchanged.
- openrouter: async job shape identical to xAI; creation POSTs to the /videos
collection root (no /generations suffix) and the registry HTTP-Referer /
X-Title headers are applied. Bodies pass through verbatim.
- vertex: two-way translation, since Veo does not speak the OpenAI-ish videos
shape. create -> :predictLongRunning { instances[], parameters{} }, poll ->
:fetchPredictOperation (Veo has no REST GET poll). The operation resource
name is base64url-encoded into the job id so GET /v1/videos/{id} stays a
flat path. Access tokens are minted from Service Account JSON via the
existing refreshVertexToken; raw API keys are rejected up front. The
operation response maps back onto the { id, status, video, videos } shape
clients already poll.
- videoCore: the request plan is rebuilt per attempt, so the 401 -> refresh
once -> retry once path picks up the refreshed token. Adapter validation
errors return 400 before any upstream call, so a malformed request can never
create a billable job.
- videoGeneration: GET /v1/videos/{id} resolves the provider from the pinned
x-connection-id connection, then ?provider=, then falls back to the xAI
default.
- registry: openrouter and vertex gain videoConfig, the video serviceKind and
video-kind models (Veo 3.1 / 3 / 2, Sora 2 Pro, Seedance 2.0).
72 lines
3.6 KiB
JavaScript
72 lines
3.6 KiB
JavaScript
export default {
|
|
id: "openrouter",
|
|
priority: 10,
|
|
hasFree: true,
|
|
alias: "openrouter",
|
|
display: {
|
|
name: "OpenRouter",
|
|
icon: "router",
|
|
color: "#F97316",
|
|
textIcon: "OR",
|
|
website: "https://openrouter.ai",
|
|
notice: {
|
|
text: "Free tier: 27+ free models, no credit card needed, 200 req/day. After 0 credit: 1,000 req/day.",
|
|
apiKeyUrl: "https://openrouter.ai/settings/keys",
|
|
},
|
|
},
|
|
category: "freeTier",
|
|
authType: "apikey",
|
|
authModes: ["apikey"],
|
|
transport: {
|
|
baseUrl: "https://openrouter.ai/api/v1/chat/completions",
|
|
thinkingFormat: "openai",
|
|
headers: {
|
|
"HTTP-Referer": "https://endpoint-proxy.local",
|
|
"X-Title": "Endpoint Proxy",
|
|
},
|
|
},
|
|
models: [
|
|
{ id: "openai/text-embedding-3-large", name: "OpenAI Text Embedding 3 Large", kind: "embedding" },
|
|
{ id: "openai/text-embedding-3-small", name: "OpenAI Text Embedding 3 Small", kind: "embedding" },
|
|
{ id: "openai/text-embedding-ada-002", name: "OpenAI Text Embedding Ada 002", kind: "embedding" },
|
|
{ id: "qwen/qwen3-embedding-8b", name: "Qwen3 Embedding 8B", kind: "embedding" },
|
|
{ id: "perplexity/pplx-embed-v1-4b", name: "Perplexity Embed V1 4B", kind: "embedding" },
|
|
{ id: "perplexity/pplx-embed-v1-0.6b", name: "Perplexity Embed V1 0.6B", kind: "embedding" },
|
|
{ id: "nvidia/llama-nemotron-embed-vl-1b-v2:free", name: "NVIDIA Nemotron Embed VL 1B V2 (Free)", kind: "embedding" },
|
|
{ id: "openai/gpt-4o-mini-tts", name: "GPT-4o Mini TTS", kind: "tts" },
|
|
{ id: "openai/tts-1-hd", name: "TTS-1 HD", kind: "tts" },
|
|
{ id: "openai/tts-1", name: "TTS-1", kind: "tts" },
|
|
{ id: "openai/dall-e-3", name: "DALL-E 3 (via OpenRouter)", params: ["size","quality","style","response_format"], kind: "image" },
|
|
{ id: "openai/gpt-image-1", name: "GPT Image 1 (via OpenRouter)", params: ["n","size","quality","response_format"], kind: "image" },
|
|
{ id: "google/imagen-3.0-generate-002", name: "Imagen 3 (via OpenRouter)", params: ["n","size"], kind: "image" },
|
|
{ id: "black-forest-labs/FLUX.1-schnell", name: "FLUX.1 Schnell (via OpenRouter)", params: ["n","size"], kind: "image" },
|
|
{ id: "google/veo-3.1", name: "Veo 3.1 (via OpenRouter)", params: ["duration","aspect_ratio","resolution"], kind: "video" },
|
|
{ id: "openai/sora-2-pro", name: "Sora 2 Pro (via OpenRouter)", params: ["duration","aspect_ratio","resolution"], kind: "video" },
|
|
{ id: "bytedance/seedance-2.0", name: "Seedance 2.0 (via OpenRouter)", params: ["duration","aspect_ratio","resolution"], kind: "video" },
|
|
],
|
|
serviceKinds: ["llm","embedding","tts","imageToText","video"],
|
|
ttsConfig: {
|
|
baseUrl: "https://openrouter.ai/api/v1/chat/completions",
|
|
defaultModel: "openai/gpt-4o-mini-tts",
|
|
headers: {"HTTP-Referer":"https://endpoint-proxy.local","X-Title":"Endpoint Proxy"},
|
|
},
|
|
embeddingConfig: {
|
|
baseUrl: "https://openrouter.ai/api/v1/embeddings",
|
|
authType: "apikey",
|
|
authHeader: "bearer",
|
|
headers: {"HTTP-Referer":"https://endpoint-proxy.local","X-Title":"Endpoint Proxy"},
|
|
},
|
|
imageConfig: {
|
|
baseUrl: "https://openrouter.ai/api/v1/images/generations",
|
|
headers: {"HTTP-Referer":"https://endpoint-proxy.local","X-Title":"Endpoint Proxy"},
|
|
},
|
|
// Async video jobs (POST /videos → { id, status }, GET /videos/{id} polls).
|
|
// Docs: https://openrouter.ai/docs/api/api-reference/videos
|
|
videoConfig: {
|
|
baseUrl: "https://openrouter.ai/api/v1/videos",
|
|
headers: {"HTTP-Referer":"https://endpoint-proxy.local","X-Title":"Endpoint Proxy"},
|
|
},
|
|
modelsFetcher: { url: "https://openrouter.ai/api/v1/models", type: "openrouter-free" },
|
|
passthroughModels: true,
|
|
};
|