feat(api): alias /v1/multimodal-embeddings to /v1/embeddings (#10568)

Jina-compatible clients POST /v1/multimodal-embeddings and currently get
HTTP 404 unknown_route from the catch-all.

Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ravi Tharuma
2026-08-18 15:52:24 +02:00
committed by GitHub
parent 947a7c64d4
commit c767494ae4
6 changed files with 59 additions and 1 deletions

View File

@@ -0,0 +1 @@
- **feat(api):** add `GET`/`POST` `/v1/multimodal-embeddings` as an alias of `/v1/embeddings` so Jina-compatible clients do not receive HTTP 404 `unknown_route` — thanks @RaviTharuma

View File

@@ -1371,6 +1371,38 @@ paths:
cost is computed per modality when pricing is available, otherwise
`0` (fail-open).
/api/v1/multimodal-embeddings:
post:
tags: [Embeddings]
summary: Create embeddings (Jina multimodal-embeddings alias)
description: >-
Same handler as `POST /api/v1/embeddings`. Provided so Jina-compatible
clients that call `/v1/multimodal-embeddings` do not receive HTTP 404
`unknown_route`.
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [input, model]
additionalProperties: true
responses:
"200":
description: Embedding vectors (same contract as POST /api/v1/embeddings).
"401":
$ref: "#/components/responses/Unauthorized"
get:
tags: [Embeddings]
summary: List embedding models (Jina multimodal-embeddings alias)
security:
- BearerAuth: []
responses:
"200":
description: Embedding model catalog (same as GET /api/v1/embeddings).
/api/v1/providers/{provider}/embeddings:
post:
tags: [Embeddings]

View File

@@ -0,0 +1,5 @@
/**
* Alias of `/v1/embeddings` for clients that call Jina's
* `/v1/multimodal-embeddings` path. Same handler, same catalog GET.
*/
export { GET, POST, OPTIONS } from "../embeddings/route";

View File

@@ -34,7 +34,7 @@ export const ENDPOINT_CATEGORIES: readonly EndpointCategory[] = [
id: "embeddings",
label: "Embeddings",
description: "Text embeddings generation",
prefixes: ["/v1/embeddings"],
prefixes: ["/v1/embeddings", "/v1/multimodal-embeddings"],
},
{
id: "images",

View File

@@ -44,6 +44,10 @@ test("resolveEndpointCategory: maps /v1/embeddings to 'embeddings'", () => {
assert.equal(resolveEndpointCategory("/v1/embeddings"), "embeddings");
});
test("resolveEndpointCategory: maps /v1/multimodal-embeddings to 'embeddings'", () => {
assert.equal(resolveEndpointCategory("/v1/multimodal-embeddings"), "embeddings");
});
test("resolveEndpointCategory: maps /v1/images/generations to 'images'", () => {
assert.equal(resolveEndpointCategory("/v1/images/generations"), "images");
});

View File

@@ -0,0 +1,16 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-mm-embed-alias-"));
const embeddings = await import("../../src/app/api/v1/embeddings/route.ts");
const multimodal = await import("../../src/app/api/v1/multimodal-embeddings/route.ts");
test("POST/GET/OPTIONS /v1/multimodal-embeddings are the embeddings handlers", () => {
assert.equal(multimodal.POST, embeddings.POST);
assert.equal(multimodal.GET, embeddings.GET);
assert.equal(multimodal.OPTIONS, embeddings.OPTIONS);
});