mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
chore(deps): drop @langchain/community, hand-roll Jina embeddings
Replaces the single use of @langchain/community (JinaEmbeddings) with a small hand-rolled implementation that extends @langchain/core/embeddings directly and calls the Jina /embeddings endpoint via Obsidian's requestUrl (CORS-safe). The request shape and response handling mirror the upstream Python reference.
This commit is contained in:
parent
adf8c68a73
commit
734ef8ea4f
3 changed files with 138 additions and 1549 deletions
1603
package-lock.json
generated
1603
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -78,7 +78,6 @@
|
|||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@langchain/anthropic": "^1.0.0",
|
||||
"@langchain/classic": "^1.0.9",
|
||||
"@langchain/community": "^1.0.0",
|
||||
"@langchain/core": "^1.1.29",
|
||||
"@langchain/deepseek": "^1.0.0",
|
||||
"@langchain/google-genai": "^2.1.23",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,78 @@
|
|||
import { JinaEmbeddings, JinaEmbeddingsParams } from "@langchain/community/embeddings/jina";
|
||||
import { Embeddings, EmbeddingsParams } from "@langchain/core/embeddings";
|
||||
import { requestUrl } from "obsidian";
|
||||
|
||||
export class CustomJinaEmbeddings extends JinaEmbeddings {
|
||||
constructor(
|
||||
fields?: Partial<JinaEmbeddingsParams> & {
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
const DEFAULT_JINA_API_URL = "https://api.jina.ai/v1/embeddings";
|
||||
|
||||
interface JinaEmbeddingsConfig extends EmbeddingsParams {
|
||||
model?: string;
|
||||
modelName?: string;
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
dimensions?: number;
|
||||
}
|
||||
|
||||
interface JinaEmbeddingItem {
|
||||
index: number;
|
||||
embedding: number[];
|
||||
}
|
||||
|
||||
interface JinaEmbeddingResponse {
|
||||
data?: JinaEmbeddingItem[];
|
||||
detail?: unknown;
|
||||
}
|
||||
|
||||
export class CustomJinaEmbeddings extends Embeddings {
|
||||
private readonly model: string;
|
||||
private readonly apiKey: string;
|
||||
private readonly url: string;
|
||||
private readonly dimensions?: number;
|
||||
|
||||
constructor(config: JinaEmbeddingsConfig = {}) {
|
||||
super(config);
|
||||
this.model = config.model ?? config.modelName ?? "jina-embeddings-v2-base-en";
|
||||
this.apiKey = config.apiKey ?? "";
|
||||
this.url = config.baseUrl ?? DEFAULT_JINA_API_URL;
|
||||
this.dimensions = config.dimensions;
|
||||
}
|
||||
|
||||
async embedQuery(text: string): Promise<number[]> {
|
||||
const embeddings = await this._embed([text]);
|
||||
return embeddings[0];
|
||||
}
|
||||
|
||||
async embedDocuments(texts: string[]): Promise<number[][]> {
|
||||
return this._embed(texts);
|
||||
}
|
||||
|
||||
private async _embed(input: string[]): Promise<number[][]> {
|
||||
const body: Record<string, unknown> = { input, model: this.model };
|
||||
if (this.dimensions !== undefined) {
|
||||
body.dimensions = this.dimensions;
|
||||
}
|
||||
) {
|
||||
super(fields);
|
||||
if (fields?.baseUrl) {
|
||||
this.baseUrl = fields.baseUrl;
|
||||
|
||||
const response = await requestUrl({
|
||||
url: this.url,
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
"Accept-Encoding": "identity",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
throw: false,
|
||||
});
|
||||
|
||||
if (response.status >= 400) {
|
||||
throw new Error(`Jina embedding request failed: ${response.status} - ${response.text ?? ""}`);
|
||||
}
|
||||
|
||||
const resp = response.json as JinaEmbeddingResponse;
|
||||
if (!resp?.data) {
|
||||
throw new Error(
|
||||
typeof resp?.detail === "string" ? resp.detail : JSON.stringify(resp?.detail ?? resp)
|
||||
);
|
||||
}
|
||||
|
||||
return [...resp.data].sort((a, b) => a.index - b.index).map((item) => item.embedding);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue