mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
feat(vercel-ai-gateway): support embeddings, images and credit usage
Extend Vercel AI Gateway beyond chat: add OpenAI-compatible embeddings and image generation endpoints, credit balance fetch on the usage dashboard, retry on 429, and models catalog fetcher. Thinking/reasoning mapping is omitted pending a project-wide refactor. Co-authored-by: Ngô Tấn Tài <tantai@newnol.io.vn> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
committed by
decolua
co-authored by
Cursor
parent
d9b030011f
commit
b33cbb0280
@@ -222,6 +222,21 @@ describe("buildEmbeddingsUrl", () => {
|
||||
expect(url).toBe("https://openrouter.ai/api/v1/embeddings");
|
||||
});
|
||||
|
||||
it("vercel-ai-gateway → https://ai-gateway.vercel.sh/v1/embeddings", async () => {
|
||||
vi.mocked(fetch).mockResolvedValueOnce(makeProviderResponse(VALID_EMBEDDING_RESPONSE));
|
||||
|
||||
await handleEmbeddingsCore(makeOptions({
|
||||
modelInfo: { provider: "vercel-ai-gateway", model: "openai/text-embedding-3-small" },
|
||||
credentials: { apiKey: "vag-test-key" },
|
||||
}));
|
||||
|
||||
const [url, init] = vi.mocked(fetch).mock.calls[0];
|
||||
const sent = JSON.parse(init.body);
|
||||
expect(url).toBe("https://ai-gateway.vercel.sh/v1/embeddings");
|
||||
expect(init.headers.Authorization).toBe("Bearer vag-test-key");
|
||||
expect(sent.model).toBe("openai/text-embedding-3-small");
|
||||
});
|
||||
|
||||
it("openai-compatible-* → uses baseUrl from providerSpecificData", async () => {
|
||||
vi.mocked(fetch).mockResolvedValueOnce(makeProviderResponse(VALID_EMBEDDING_RESPONSE));
|
||||
|
||||
|
||||
@@ -263,6 +263,38 @@ describe("handleImageGenerationCore", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("handles Vercel AI Gateway image generation as OpenAI-compatible", async () => {
|
||||
global.fetch.mockResolvedValueOnce(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
created: 1234567890,
|
||||
data: [{ url: "https://example.com/vercel-image.png" }],
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
)
|
||||
);
|
||||
|
||||
const result = await handleImageGenerationCore({
|
||||
body: { prompt: "A watercolor castle", n: 1, size: "1024x1024" },
|
||||
modelInfo: { provider: "vercel-ai-gateway", model: "openai/gpt-image-1" },
|
||||
credentials: { apiKey: "vag-test-key" },
|
||||
log: null,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(global.fetch).toHaveBeenCalledWith(
|
||||
"https://ai-gateway.vercel.sh/v1/images/generations",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
headers: expect.objectContaining({
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer vag-test-key",
|
||||
}),
|
||||
body: expect.stringContaining('"model":"openai/gpt-image-1"'),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("handles HuggingFace binary response", async () => {
|
||||
const imageBuffer = new Uint8Array([0x89, 0x50, 0x4e, 0x47]); // PNG header
|
||||
global.fetch.mockResolvedValueOnce(
|
||||
|
||||
Reference in New Issue
Block a user