unrag 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +611 -174
- package/package.json +12 -6
- package/registry/config/unrag.config.ts +9 -8
- package/registry/connectors/google-drive/_api-types.ts +60 -0
- package/registry/connectors/google-drive/client.ts +99 -38
- package/registry/connectors/google-drive/sync.ts +97 -69
- package/registry/connectors/google-drive/types.ts +76 -37
- package/registry/connectors/notion/client.ts +12 -3
- package/registry/connectors/notion/render.ts +62 -23
- package/registry/connectors/notion/sync.ts +30 -23
- package/registry/core/assets.ts +11 -10
- package/registry/core/config.ts +10 -25
- package/registry/core/context-engine.ts +71 -2
- package/registry/core/deep-merge.ts +45 -0
- package/registry/core/ingest.ts +117 -44
- package/registry/core/types.ts +96 -2
- package/registry/docs/unrag.md +6 -1
- package/registry/embedding/_shared.ts +25 -0
- package/registry/embedding/ai.ts +8 -68
- package/registry/embedding/azure.ts +88 -0
- package/registry/embedding/bedrock.ts +88 -0
- package/registry/embedding/cohere.ts +88 -0
- package/registry/embedding/google.ts +102 -0
- package/registry/embedding/mistral.ts +71 -0
- package/registry/embedding/ollama.ts +90 -0
- package/registry/embedding/openai.ts +88 -0
- package/registry/embedding/openrouter.ts +127 -0
- package/registry/embedding/together.ts +77 -0
- package/registry/embedding/vertex.ts +111 -0
- package/registry/embedding/voyage.ts +169 -0
- package/registry/extractors/audio-transcribe/index.ts +39 -23
- package/registry/extractors/file-docx/index.ts +8 -1
- package/registry/extractors/file-pptx/index.ts +22 -1
- package/registry/extractors/file-xlsx/index.ts +24 -1
- package/registry/extractors/image-caption-llm/index.ts +8 -3
- package/registry/extractors/image-ocr/index.ts +9 -4
- package/registry/extractors/pdf-llm/index.ts +9 -4
- package/registry/extractors/pdf-text-layer/index.ts +23 -2
- package/registry/extractors/video-frames/index.ts +8 -3
- package/registry/extractors/video-transcribe/index.ts +40 -24
- package/registry/manifest.json +346 -0
- package/registry/store/drizzle-postgres-pgvector/store.ts +26 -6
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cohere provider module interface.
|
|
7
|
+
*/
|
|
8
|
+
interface CohereModule {
|
|
9
|
+
cohere: {
|
|
10
|
+
embedding: (model: string) => EmbeddingModel<string>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type CohereEmbeddingConfig = {
|
|
15
|
+
model?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
inputType?: "search_document" | "search_query" | "classification" | "clustering";
|
|
18
|
+
truncate?: "NONE" | "START" | "END";
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const DEFAULT_TEXT_MODEL = "embed-english-v3.0";
|
|
22
|
+
|
|
23
|
+
const buildProviderOptions = (config: CohereEmbeddingConfig) => {
|
|
24
|
+
if (!config.inputType && !config.truncate) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
cohere: {
|
|
29
|
+
...(config.inputType ? { inputType: config.inputType } : {}),
|
|
30
|
+
...(config.truncate ? { truncate: config.truncate } : {}),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const createCohereEmbeddingProvider = (
|
|
36
|
+
config: CohereEmbeddingConfig = {}
|
|
37
|
+
): EmbeddingProvider => {
|
|
38
|
+
const { cohere } = requireOptional<CohereModule>({
|
|
39
|
+
id: "@ai-sdk/cohere",
|
|
40
|
+
installHint: "bun add @ai-sdk/cohere",
|
|
41
|
+
providerName: "cohere",
|
|
42
|
+
});
|
|
43
|
+
const model =
|
|
44
|
+
config.model ?? process.env.COHERE_EMBEDDING_MODEL ?? DEFAULT_TEXT_MODEL;
|
|
45
|
+
const timeoutMs = config.timeoutMs;
|
|
46
|
+
const providerOptions = buildProviderOptions(config);
|
|
47
|
+
const embeddingModel = cohere.embedding(model);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
name: `cohere:${model}`,
|
|
51
|
+
dimensions: undefined,
|
|
52
|
+
embed: async ({ text }) => {
|
|
53
|
+
const abortSignal = timeoutMs
|
|
54
|
+
? AbortSignal.timeout(timeoutMs)
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
const result = await embed({
|
|
58
|
+
model: embeddingModel,
|
|
59
|
+
value: text,
|
|
60
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
61
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!result.embedding) {
|
|
65
|
+
throw new Error("Embedding missing from Cohere response");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result.embedding;
|
|
69
|
+
},
|
|
70
|
+
embedMany: async (inputs) => {
|
|
71
|
+
const values = inputs.map((i) => i.text);
|
|
72
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
73
|
+
|
|
74
|
+
const result = await embedMany({
|
|
75
|
+
model: embeddingModel,
|
|
76
|
+
values,
|
|
77
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
78
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const { embeddings } = result;
|
|
82
|
+
if (!Array.isArray(embeddings)) {
|
|
83
|
+
throw new Error("Embeddings missing from Cohere embedMany response");
|
|
84
|
+
}
|
|
85
|
+
return embeddings;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Google AI provider module interface.
|
|
7
|
+
*/
|
|
8
|
+
interface GoogleModule {
|
|
9
|
+
google: {
|
|
10
|
+
embedding: (model: string) => EmbeddingModel<string>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type GoogleEmbeddingTaskType =
|
|
15
|
+
| "SEMANTIC_SIMILARITY"
|
|
16
|
+
| "CLASSIFICATION"
|
|
17
|
+
| "CLUSTERING"
|
|
18
|
+
| "RETRIEVAL_DOCUMENT"
|
|
19
|
+
| "RETRIEVAL_QUERY"
|
|
20
|
+
| "QUESTION_ANSWERING"
|
|
21
|
+
| "FACT_VERIFICATION"
|
|
22
|
+
| "CODE_RETRIEVAL_QUERY";
|
|
23
|
+
|
|
24
|
+
export type GoogleEmbeddingConfig = {
|
|
25
|
+
model?: string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
outputDimensionality?: number;
|
|
28
|
+
taskType?: GoogleEmbeddingTaskType;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const DEFAULT_TEXT_MODEL = "gemini-embedding-001";
|
|
32
|
+
|
|
33
|
+
const buildProviderOptions = (config: GoogleEmbeddingConfig) => {
|
|
34
|
+
if (config.outputDimensionality === undefined && config.taskType === undefined) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
google: {
|
|
39
|
+
...(config.outputDimensionality !== undefined
|
|
40
|
+
? { outputDimensionality: config.outputDimensionality }
|
|
41
|
+
: {}),
|
|
42
|
+
...(config.taskType ? { taskType: config.taskType } : {}),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const createGoogleEmbeddingProvider = (
|
|
48
|
+
config: GoogleEmbeddingConfig = {}
|
|
49
|
+
): EmbeddingProvider => {
|
|
50
|
+
const { google } = requireOptional<GoogleModule>({
|
|
51
|
+
id: "@ai-sdk/google",
|
|
52
|
+
installHint: "bun add @ai-sdk/google",
|
|
53
|
+
providerName: "google",
|
|
54
|
+
});
|
|
55
|
+
const model =
|
|
56
|
+
config.model ??
|
|
57
|
+
process.env.GOOGLE_GENERATIVE_AI_EMBEDDING_MODEL ??
|
|
58
|
+
DEFAULT_TEXT_MODEL;
|
|
59
|
+
const timeoutMs = config.timeoutMs;
|
|
60
|
+
const providerOptions = buildProviderOptions(config);
|
|
61
|
+
const embeddingModel = google.embedding(model);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
name: `google:${model}`,
|
|
65
|
+
dimensions: config.outputDimensionality,
|
|
66
|
+
embed: async ({ text }) => {
|
|
67
|
+
const abortSignal = timeoutMs
|
|
68
|
+
? AbortSignal.timeout(timeoutMs)
|
|
69
|
+
: undefined;
|
|
70
|
+
|
|
71
|
+
const result = await embed({
|
|
72
|
+
model: embeddingModel,
|
|
73
|
+
value: text,
|
|
74
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
75
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (!result.embedding) {
|
|
79
|
+
throw new Error("Embedding missing from Google response");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result.embedding;
|
|
83
|
+
},
|
|
84
|
+
embedMany: async (inputs) => {
|
|
85
|
+
const values = inputs.map((i) => i.text);
|
|
86
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
87
|
+
|
|
88
|
+
const result = await embedMany({
|
|
89
|
+
model: embeddingModel,
|
|
90
|
+
values,
|
|
91
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
92
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const { embeddings } = result;
|
|
96
|
+
if (!Array.isArray(embeddings)) {
|
|
97
|
+
throw new Error("Embeddings missing from Google embedMany response");
|
|
98
|
+
}
|
|
99
|
+
return embeddings;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Mistral provider module interface.
|
|
7
|
+
*/
|
|
8
|
+
interface MistralModule {
|
|
9
|
+
mistral: {
|
|
10
|
+
embedding: (model: string) => EmbeddingModel<string>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type MistralEmbeddingConfig = {
|
|
15
|
+
model?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const DEFAULT_TEXT_MODEL = "mistral-embed";
|
|
20
|
+
|
|
21
|
+
export const createMistralEmbeddingProvider = (
|
|
22
|
+
config: MistralEmbeddingConfig = {}
|
|
23
|
+
): EmbeddingProvider => {
|
|
24
|
+
const { mistral } = requireOptional<MistralModule>({
|
|
25
|
+
id: "@ai-sdk/mistral",
|
|
26
|
+
installHint: "bun add @ai-sdk/mistral",
|
|
27
|
+
providerName: "mistral",
|
|
28
|
+
});
|
|
29
|
+
const model =
|
|
30
|
+
config.model ?? process.env.MISTRAL_EMBEDDING_MODEL ?? DEFAULT_TEXT_MODEL;
|
|
31
|
+
const timeoutMs = config.timeoutMs;
|
|
32
|
+
const embeddingModel = mistral.embedding(model);
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
name: `mistral:${model}`,
|
|
36
|
+
dimensions: undefined,
|
|
37
|
+
embed: async ({ text }) => {
|
|
38
|
+
const abortSignal = timeoutMs
|
|
39
|
+
? AbortSignal.timeout(timeoutMs)
|
|
40
|
+
: undefined;
|
|
41
|
+
|
|
42
|
+
const result = await embed({
|
|
43
|
+
model: embeddingModel,
|
|
44
|
+
value: text,
|
|
45
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!result.embedding) {
|
|
49
|
+
throw new Error("Embedding missing from Mistral response");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result.embedding;
|
|
53
|
+
},
|
|
54
|
+
embedMany: async (inputs) => {
|
|
55
|
+
const values = inputs.map((i) => i.text);
|
|
56
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
57
|
+
|
|
58
|
+
const result = await embedMany({
|
|
59
|
+
model: embeddingModel,
|
|
60
|
+
values,
|
|
61
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const { embeddings } = result;
|
|
65
|
+
if (!Array.isArray(embeddings)) {
|
|
66
|
+
throw new Error("Embeddings missing from Mistral embedMany response");
|
|
67
|
+
}
|
|
68
|
+
return embeddings;
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ollama provider instance interface.
|
|
7
|
+
*/
|
|
8
|
+
interface OllamaProvider {
|
|
9
|
+
textEmbeddingModel: (model: string) => EmbeddingModel<string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Ollama provider module interface.
|
|
14
|
+
*/
|
|
15
|
+
interface OllamaModule {
|
|
16
|
+
createOllama: (config: { baseURL?: string; headers?: Record<string, string> }) => OllamaProvider;
|
|
17
|
+
ollama: OllamaProvider;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type OllamaEmbeddingConfig = {
|
|
21
|
+
model?: string;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
baseURL?: string;
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const DEFAULT_TEXT_MODEL = "nomic-embed-text";
|
|
28
|
+
|
|
29
|
+
const resolveProvider = (config: OllamaEmbeddingConfig): OllamaProvider => {
|
|
30
|
+
const { createOllama, ollama } = requireOptional<OllamaModule>({
|
|
31
|
+
id: "ollama-ai-provider-v2",
|
|
32
|
+
installHint: "bun add ollama-ai-provider-v2",
|
|
33
|
+
providerName: "ollama",
|
|
34
|
+
});
|
|
35
|
+
if (config.baseURL || config.headers) {
|
|
36
|
+
return createOllama({
|
|
37
|
+
...(config.baseURL ? { baseURL: config.baseURL } : {}),
|
|
38
|
+
...(config.headers ? { headers: config.headers } : {}),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return ollama;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const createOllamaEmbeddingProvider = (
|
|
45
|
+
config: OllamaEmbeddingConfig = {}
|
|
46
|
+
): EmbeddingProvider => {
|
|
47
|
+
const model =
|
|
48
|
+
config.model ?? process.env.OLLAMA_EMBEDDING_MODEL ?? DEFAULT_TEXT_MODEL;
|
|
49
|
+
const timeoutMs = config.timeoutMs;
|
|
50
|
+
const provider = resolveProvider(config);
|
|
51
|
+
const embeddingModel = provider.textEmbeddingModel(model);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
name: `ollama:${model}`,
|
|
55
|
+
dimensions: undefined,
|
|
56
|
+
embed: async ({ text }) => {
|
|
57
|
+
const abortSignal = timeoutMs
|
|
58
|
+
? AbortSignal.timeout(timeoutMs)
|
|
59
|
+
: undefined;
|
|
60
|
+
|
|
61
|
+
const result = await embed({
|
|
62
|
+
model: embeddingModel,
|
|
63
|
+
value: text,
|
|
64
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!result.embedding) {
|
|
68
|
+
throw new Error("Embedding missing from Ollama response");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return result.embedding;
|
|
72
|
+
},
|
|
73
|
+
embedMany: async (inputs) => {
|
|
74
|
+
const values = inputs.map((i) => i.text);
|
|
75
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
76
|
+
|
|
77
|
+
const result = await embedMany({
|
|
78
|
+
model: embeddingModel,
|
|
79
|
+
values,
|
|
80
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const { embeddings } = result;
|
|
84
|
+
if (!Array.isArray(embeddings)) {
|
|
85
|
+
throw new Error("Embeddings missing from Ollama embedMany response");
|
|
86
|
+
}
|
|
87
|
+
return embeddings;
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* OpenAI provider module interface.
|
|
7
|
+
*/
|
|
8
|
+
interface OpenAiModule {
|
|
9
|
+
openai: {
|
|
10
|
+
embedding: (model: string) => EmbeddingModel<string>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type OpenAiEmbeddingConfig = {
|
|
15
|
+
model?: string;
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
dimensions?: number;
|
|
18
|
+
user?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const DEFAULT_TEXT_MODEL = "text-embedding-3-small";
|
|
22
|
+
|
|
23
|
+
const buildProviderOptions = (config: OpenAiEmbeddingConfig) => {
|
|
24
|
+
if (config.dimensions === undefined && config.user === undefined) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
openai: {
|
|
29
|
+
...(config.dimensions !== undefined ? { dimensions: config.dimensions } : {}),
|
|
30
|
+
...(config.user ? { user: config.user } : {}),
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const createOpenAiEmbeddingProvider = (
|
|
36
|
+
config: OpenAiEmbeddingConfig = {}
|
|
37
|
+
): EmbeddingProvider => {
|
|
38
|
+
const { openai } = requireOptional<OpenAiModule>({
|
|
39
|
+
id: "@ai-sdk/openai",
|
|
40
|
+
installHint: "bun add @ai-sdk/openai",
|
|
41
|
+
providerName: "openai",
|
|
42
|
+
});
|
|
43
|
+
const model =
|
|
44
|
+
config.model ?? process.env.OPENAI_EMBEDDING_MODEL ?? DEFAULT_TEXT_MODEL;
|
|
45
|
+
const timeoutMs = config.timeoutMs;
|
|
46
|
+
const providerOptions = buildProviderOptions(config);
|
|
47
|
+
const embeddingModel = openai.embedding(model);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
name: `openai:${model}`,
|
|
51
|
+
dimensions: config.dimensions,
|
|
52
|
+
embed: async ({ text }) => {
|
|
53
|
+
const abortSignal = timeoutMs
|
|
54
|
+
? AbortSignal.timeout(timeoutMs)
|
|
55
|
+
: undefined;
|
|
56
|
+
|
|
57
|
+
const result = await embed({
|
|
58
|
+
model: embeddingModel,
|
|
59
|
+
value: text,
|
|
60
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
61
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
if (!result.embedding) {
|
|
65
|
+
throw new Error("Embedding missing from OpenAI response");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result.embedding;
|
|
69
|
+
},
|
|
70
|
+
embedMany: async (inputs) => {
|
|
71
|
+
const values = inputs.map((i) => i.text);
|
|
72
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
73
|
+
|
|
74
|
+
const result = await embedMany({
|
|
75
|
+
model: embeddingModel,
|
|
76
|
+
values,
|
|
77
|
+
...(providerOptions ? { providerOptions } : {}),
|
|
78
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const { embeddings } = result;
|
|
82
|
+
if (!Array.isArray(embeddings)) {
|
|
83
|
+
throw new Error("Embeddings missing from OpenAI embedMany response");
|
|
84
|
+
}
|
|
85
|
+
return embeddings;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
2
|
+
import { requireOptional } from "./_shared";
|
|
3
|
+
|
|
4
|
+
export type OpenRouterEmbeddingConfig = {
|
|
5
|
+
model?: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
baseURL?: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
referer?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OpenRouter embedding result item.
|
|
16
|
+
*/
|
|
17
|
+
interface EmbeddingDataItem {
|
|
18
|
+
embedding?: number[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* OpenRouter embedding response.
|
|
23
|
+
*/
|
|
24
|
+
interface EmbeddingResponse {
|
|
25
|
+
data?: EmbeddingDataItem[];
|
|
26
|
+
embedding?: number[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* OpenRouter client embeddings interface.
|
|
31
|
+
*/
|
|
32
|
+
interface EmbeddingsClient {
|
|
33
|
+
generate(
|
|
34
|
+
params: { input: string | string[]; model: string },
|
|
35
|
+
options?: { fetchOptions?: { signal?: AbortSignal } }
|
|
36
|
+
): Promise<EmbeddingResponse>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* OpenRouter client interface.
|
|
41
|
+
*/
|
|
42
|
+
interface OpenRouterClient {
|
|
43
|
+
embeddings: EmbeddingsClient;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* OpenRouter SDK module interface.
|
|
48
|
+
*/
|
|
49
|
+
interface OpenRouterModule {
|
|
50
|
+
OpenRouter: new (config: {
|
|
51
|
+
apiKey: string;
|
|
52
|
+
baseURL?: string;
|
|
53
|
+
headers?: Record<string, string>;
|
|
54
|
+
}) => OpenRouterClient;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const DEFAULT_TEXT_MODEL = "text-embedding-3-small";
|
|
58
|
+
|
|
59
|
+
const buildHeaders = (config: OpenRouterEmbeddingConfig) => {
|
|
60
|
+
const headers: Record<string, string> = { ...(config.headers ?? {}) };
|
|
61
|
+
if (config.referer) headers["HTTP-Referer"] = config.referer;
|
|
62
|
+
if (config.title) headers["X-Title"] = config.title;
|
|
63
|
+
return headers;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const createOpenRouterEmbeddingProvider = (
|
|
67
|
+
config: OpenRouterEmbeddingConfig = {}
|
|
68
|
+
): EmbeddingProvider => {
|
|
69
|
+
const { OpenRouter } = requireOptional<OpenRouterModule>({
|
|
70
|
+
id: "@openrouter/sdk",
|
|
71
|
+
installHint: "bun add @openrouter/sdk",
|
|
72
|
+
providerName: "openrouter",
|
|
73
|
+
});
|
|
74
|
+
const model =
|
|
75
|
+
config.model ?? process.env.OPENROUTER_EMBEDDING_MODEL ?? DEFAULT_TEXT_MODEL;
|
|
76
|
+
const timeoutMs = config.timeoutMs;
|
|
77
|
+
const headers = buildHeaders(config);
|
|
78
|
+
|
|
79
|
+
const client = new OpenRouter({
|
|
80
|
+
apiKey: config.apiKey ?? process.env.OPENROUTER_API_KEY ?? "",
|
|
81
|
+
...(config.baseURL ? { baseURL: config.baseURL } : {}),
|
|
82
|
+
...(Object.keys(headers).length ? { headers } : {}),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
name: `openrouter:${model}`,
|
|
87
|
+
dimensions: undefined,
|
|
88
|
+
embed: async ({ text }) => {
|
|
89
|
+
const abortSignal = timeoutMs
|
|
90
|
+
? AbortSignal.timeout(timeoutMs)
|
|
91
|
+
: undefined;
|
|
92
|
+
|
|
93
|
+
const result = await client.embeddings.generate(
|
|
94
|
+
{ input: text, model },
|
|
95
|
+
abortSignal ? { fetchOptions: { signal: abortSignal } } : undefined
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const embedding =
|
|
99
|
+
result.data?.[0]?.embedding ??
|
|
100
|
+
result.embedding;
|
|
101
|
+
if (!embedding) {
|
|
102
|
+
throw new Error("Embedding missing from OpenRouter response");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return embedding;
|
|
106
|
+
},
|
|
107
|
+
embedMany: async (inputs) => {
|
|
108
|
+
const values = inputs.map((i) => i.text);
|
|
109
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
110
|
+
|
|
111
|
+
const result = await client.embeddings.generate(
|
|
112
|
+
{ input: values, model },
|
|
113
|
+
abortSignal ? { fetchOptions: { signal: abortSignal } } : undefined
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const embeddings = result.data?.map(
|
|
117
|
+
(item) => item.embedding
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (!embeddings || embeddings.some((e) => !Array.isArray(e))) {
|
|
121
|
+
throw new Error("Embeddings missing from OpenRouter response");
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return embeddings as number[][];
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { embed, embedMany, type EmbeddingModel } from "ai";
|
|
2
|
+
import type { EmbeddingProvider } from "../core/types";
|
|
3
|
+
import { requireOptional } from "./_shared";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Together AI provider module interface.
|
|
7
|
+
*/
|
|
8
|
+
interface TogetherAiModule {
|
|
9
|
+
togetherai: {
|
|
10
|
+
embeddingModel?: (model: string) => EmbeddingModel<string>;
|
|
11
|
+
textEmbeddingModel?: (model: string) => EmbeddingModel<string>;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type TogetherEmbeddingConfig = {
|
|
16
|
+
model?: string;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const DEFAULT_TEXT_MODEL = "togethercomputer/m2-bert-80M-2k-retrieval";
|
|
21
|
+
|
|
22
|
+
export const createTogetherEmbeddingProvider = (
|
|
23
|
+
config: TogetherEmbeddingConfig = {}
|
|
24
|
+
): EmbeddingProvider => {
|
|
25
|
+
const { togetherai } = requireOptional<TogetherAiModule>({
|
|
26
|
+
id: "@ai-sdk/togetherai",
|
|
27
|
+
installHint: "bun add @ai-sdk/togetherai",
|
|
28
|
+
providerName: "together",
|
|
29
|
+
});
|
|
30
|
+
const model =
|
|
31
|
+
config.model ??
|
|
32
|
+
process.env.TOGETHER_AI_EMBEDDING_MODEL ??
|
|
33
|
+
DEFAULT_TEXT_MODEL;
|
|
34
|
+
const timeoutMs = config.timeoutMs;
|
|
35
|
+
const embeddingModel =
|
|
36
|
+
typeof togetherai.embeddingModel === "function"
|
|
37
|
+
? togetherai.embeddingModel(model)
|
|
38
|
+
: togetherai.textEmbeddingModel?.(model);
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
name: `together:${model}`,
|
|
42
|
+
dimensions: undefined,
|
|
43
|
+
embed: async ({ text }) => {
|
|
44
|
+
const abortSignal = timeoutMs
|
|
45
|
+
? AbortSignal.timeout(timeoutMs)
|
|
46
|
+
: undefined;
|
|
47
|
+
|
|
48
|
+
const result = await embed({
|
|
49
|
+
model: embeddingModel,
|
|
50
|
+
value: text,
|
|
51
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (!result.embedding) {
|
|
55
|
+
throw new Error("Embedding missing from Together.ai response");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return result.embedding;
|
|
59
|
+
},
|
|
60
|
+
embedMany: async (inputs) => {
|
|
61
|
+
const values = inputs.map((i) => i.text);
|
|
62
|
+
const abortSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
63
|
+
|
|
64
|
+
const result = await embedMany({
|
|
65
|
+
model: embeddingModel,
|
|
66
|
+
values,
|
|
67
|
+
...(abortSignal ? { abortSignal } : {}),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const { embeddings } = result;
|
|
71
|
+
if (!Array.isArray(embeddings)) {
|
|
72
|
+
throw new Error("Embeddings missing from Together.ai embedMany response");
|
|
73
|
+
}
|
|
74
|
+
return embeddings;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|