From 14093e20bb6c2c8793ac6b13f8b72a126f6bf68f Mon Sep 17 00:00:00 2001 From: p-sw Date: Wed, 10 Jun 2026 23:40:04 +0900 Subject: [PATCH] feat: implement EmbeddingProvider --- src/openrouter/embedding.ts | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/openrouter/embedding.ts diff --git a/src/openrouter/embedding.ts b/src/openrouter/embedding.ts new file mode 100644 index 0000000..98f3941 --- /dev/null +++ b/src/openrouter/embedding.ts @@ -0,0 +1,57 @@ +import { config } from "@/config"; +import { OpenRouter } from "@openrouter/sdk"; +import type { EmbeddingProvider } from "identitydb"; + +export const QWEN_EMBEDDING_MODEL = "qwen/qwen3-embedding-8b" as const; +export const QWEN_EMBEDDING_DIMENSIONS = 512 as const; + +export class OpenRouterEmbeddingProvider implements EmbeddingProvider { + readonly model: string = QWEN_EMBEDDING_MODEL; + readonly dimensions: number = QWEN_EMBEDDING_DIMENSIONS; + + private client: OpenRouter; + + constructor(apiKey: string = config.openrouterApiKey) { + this.client = new OpenRouter({ apiKey, appTitle: "boxbrain" }); + } + + async embed(input: string): Promise { + const result = await this.embedBatch([input]); + return result[0]!; + } + + async embedMany(inputs: string[]): Promise { + if (inputs.length === 0) return []; + return await this.embedBatch(inputs); + } + + private async embedBatch(inputs: string[]): Promise { + const response = await this.client.embeddings.generate({ + requestBody: { + model: this.model, + input: inputs, + dimensions: this.dimensions, + encodingFormat: "float", + }, + }); + if (typeof response === "string") { + throw new Error("OpenRouter returned a non-JSON embeddings response"); + } + const ordered = new Array(inputs.length); + for (const item of response.data) { + if (typeof item.embedding === "string") { + throw new Error( + "OpenRouter returned a base64 embedding but float was requested", + ); + } + const index = item.index ?? 0; + ordered[index] = item.embedding; + } + for (let i = 0; i < ordered.length; i += 1) { + if (!ordered[i]) { + throw new Error(`OpenRouter omitted embedding for input index ${i}`); + } + } + return ordered; + } +}