@opensaas/stack-rag 0.1.6
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/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +10 -0
- package/CLAUDE.md +565 -0
- package/LICENSE +21 -0
- package/README.md +406 -0
- package/dist/config/index.d.ts +63 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +94 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/plugin.d.ts +38 -0
- package/dist/config/plugin.d.ts.map +1 -0
- package/dist/config/plugin.js +215 -0
- package/dist/config/plugin.js.map +1 -0
- package/dist/config/plugin.test.d.ts +2 -0
- package/dist/config/plugin.test.d.ts.map +1 -0
- package/dist/config/plugin.test.js +554 -0
- package/dist/config/plugin.test.js.map +1 -0
- package/dist/config/types.d.ts +249 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +5 -0
- package/dist/config/types.js.map +1 -0
- package/dist/fields/embedding.d.ts +85 -0
- package/dist/fields/embedding.d.ts.map +1 -0
- package/dist/fields/embedding.js +81 -0
- package/dist/fields/embedding.js.map +1 -0
- package/dist/fields/embedding.test.d.ts +2 -0
- package/dist/fields/embedding.test.d.ts.map +1 -0
- package/dist/fields/embedding.test.js +323 -0
- package/dist/fields/embedding.test.js.map +1 -0
- package/dist/fields/index.d.ts +6 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/index.js +5 -0
- package/dist/fields/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +18 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/providers/index.d.ts +38 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +68 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama.d.ts +49 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +151 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +41 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +126 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/providers.test.d.ts +2 -0
- package/dist/providers/providers.test.d.ts.map +1 -0
- package/dist/providers/providers.test.js +224 -0
- package/dist/providers/providers.test.js.map +1 -0
- package/dist/providers/types.d.ts +88 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/runtime/batch.d.ts +183 -0
- package/dist/runtime/batch.d.ts.map +1 -0
- package/dist/runtime/batch.js +240 -0
- package/dist/runtime/batch.js.map +1 -0
- package/dist/runtime/batch.test.d.ts +2 -0
- package/dist/runtime/batch.test.d.ts.map +1 -0
- package/dist/runtime/batch.test.js +251 -0
- package/dist/runtime/batch.test.js.map +1 -0
- package/dist/runtime/chunking.d.ts +42 -0
- package/dist/runtime/chunking.d.ts.map +1 -0
- package/dist/runtime/chunking.js +264 -0
- package/dist/runtime/chunking.js.map +1 -0
- package/dist/runtime/chunking.test.d.ts +2 -0
- package/dist/runtime/chunking.test.d.ts.map +1 -0
- package/dist/runtime/chunking.test.js +212 -0
- package/dist/runtime/chunking.test.js.map +1 -0
- package/dist/runtime/embeddings.d.ts +147 -0
- package/dist/runtime/embeddings.d.ts.map +1 -0
- package/dist/runtime/embeddings.js +201 -0
- package/dist/runtime/embeddings.js.map +1 -0
- package/dist/runtime/embeddings.test.d.ts +2 -0
- package/dist/runtime/embeddings.test.d.ts.map +1 -0
- package/dist/runtime/embeddings.test.js +366 -0
- package/dist/runtime/embeddings.test.js.map +1 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +18 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/search.d.ts +135 -0
- package/dist/runtime/search.d.ts.map +1 -0
- package/dist/runtime/search.js +101 -0
- package/dist/runtime/search.js.map +1 -0
- package/dist/storage/index.d.ts +41 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +73 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/json.d.ts +34 -0
- package/dist/storage/json.d.ts.map +1 -0
- package/dist/storage/json.js +82 -0
- package/dist/storage/json.js.map +1 -0
- package/dist/storage/pgvector.d.ts +53 -0
- package/dist/storage/pgvector.d.ts.map +1 -0
- package/dist/storage/pgvector.js +168 -0
- package/dist/storage/pgvector.js.map +1 -0
- package/dist/storage/sqlite-vss.d.ts +49 -0
- package/dist/storage/sqlite-vss.d.ts.map +1 -0
- package/dist/storage/sqlite-vss.js +148 -0
- package/dist/storage/sqlite-vss.js.map +1 -0
- package/dist/storage/storage.test.d.ts +2 -0
- package/dist/storage/storage.test.d.ts.map +1 -0
- package/dist/storage/storage.test.js +440 -0
- package/dist/storage/storage.test.js.map +1 -0
- package/dist/storage/types.d.ts +79 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +49 -0
- package/dist/storage/types.js.map +1 -0
- package/package.json +82 -0
- package/src/config/index.ts +116 -0
- package/src/config/plugin.test.ts +664 -0
- package/src/config/plugin.ts +257 -0
- package/src/config/types.ts +283 -0
- package/src/fields/embedding.test.ts +408 -0
- package/src/fields/embedding.ts +150 -0
- package/src/fields/index.ts +6 -0
- package/src/index.ts +33 -0
- package/src/mcp/index.ts +21 -0
- package/src/providers/index.ts +81 -0
- package/src/providers/ollama.ts +186 -0
- package/src/providers/openai.ts +161 -0
- package/src/providers/providers.test.ts +275 -0
- package/src/providers/types.ts +100 -0
- package/src/runtime/batch.test.ts +332 -0
- package/src/runtime/batch.ts +424 -0
- package/src/runtime/chunking.test.ts +258 -0
- package/src/runtime/chunking.ts +334 -0
- package/src/runtime/embeddings.test.ts +441 -0
- package/src/runtime/embeddings.ts +380 -0
- package/src/runtime/index.ts +51 -0
- package/src/runtime/search.ts +243 -0
- package/src/storage/index.ts +86 -0
- package/src/storage/json.ts +106 -0
- package/src/storage/pgvector.ts +206 -0
- package/src/storage/sqlite-vss.ts +193 -0
- package/src/storage/storage.test.ts +521 -0
- package/src/storage/types.ts +126 -0
- package/tsconfig.json +13 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +18 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ollama embedding provider
|
|
3
|
+
* Uses local Ollama instance for embedding generation
|
|
4
|
+
*/
|
|
5
|
+
export class OllamaEmbeddingProvider {
|
|
6
|
+
type = 'ollama';
|
|
7
|
+
model;
|
|
8
|
+
dimensions = 0; // Will be determined from first embedding
|
|
9
|
+
baseURL;
|
|
10
|
+
dimensionsInitialized = false;
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.baseURL = config.baseURL || 'http://localhost:11434';
|
|
13
|
+
this.model = config.model || 'nomic-embed-text';
|
|
14
|
+
// Remove trailing slash from baseURL
|
|
15
|
+
if (this.baseURL.endsWith('/')) {
|
|
16
|
+
this.baseURL = this.baseURL.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initialize dimensions by generating a test embedding
|
|
21
|
+
*/
|
|
22
|
+
async initializeDimensions() {
|
|
23
|
+
if (this.dimensionsInitialized) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const testEmbedding = await this.embed('test');
|
|
28
|
+
this.dimensions = testEmbedding.length;
|
|
29
|
+
this.dimensionsInitialized = true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
throw new Error(`Failed to initialize Ollama provider (ensure Ollama is running and model '${this.model}' is available): ${error.message}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Make HTTP request to Ollama API
|
|
37
|
+
*/
|
|
38
|
+
async makeRequest(endpoint, body) {
|
|
39
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
40
|
+
try {
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: {
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const errorText = await response.text();
|
|
50
|
+
throw new Error(`HTTP ${response.status}: ${errorText}`);
|
|
51
|
+
}
|
|
52
|
+
return (await response.json());
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
56
|
+
throw new Error(`Failed to connect to Ollama at ${this.baseURL}. Ensure Ollama is running.`);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate embedding for a single text
|
|
63
|
+
*/
|
|
64
|
+
async embed(text) {
|
|
65
|
+
if (!text || text.trim().length === 0) {
|
|
66
|
+
throw new Error('Cannot generate embedding for empty text');
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const response = await this.makeRequest('/api/embeddings', {
|
|
70
|
+
model: this.model,
|
|
71
|
+
prompt: text,
|
|
72
|
+
});
|
|
73
|
+
// Initialize dimensions if not yet done
|
|
74
|
+
if (!this.dimensionsInitialized) {
|
|
75
|
+
this.dimensions = response.embedding.length;
|
|
76
|
+
this.dimensionsInitialized = true;
|
|
77
|
+
}
|
|
78
|
+
return response.embedding;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
throw new Error(`Ollama embedding generation failed: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate embeddings for multiple texts in a batch
|
|
86
|
+
* Note: Ollama doesn't have native batch API, so we make parallel requests
|
|
87
|
+
*/
|
|
88
|
+
async embedBatch(texts) {
|
|
89
|
+
if (texts.length === 0) {
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
// Ensure dimensions are initialized
|
|
93
|
+
if (!this.dimensionsInitialized) {
|
|
94
|
+
await this.initializeDimensions();
|
|
95
|
+
}
|
|
96
|
+
// Filter out empty texts and keep track of indices
|
|
97
|
+
const validTexts = [];
|
|
98
|
+
const validIndices = [];
|
|
99
|
+
texts.forEach((text, index) => {
|
|
100
|
+
if (text && text.trim().length > 0) {
|
|
101
|
+
validTexts.push(text);
|
|
102
|
+
validIndices.push(index);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
if (validTexts.length === 0) {
|
|
106
|
+
throw new Error('Cannot generate embeddings for all empty texts');
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
// Make parallel requests (Ollama doesn't have batch API)
|
|
110
|
+
const embeddingPromises = validTexts.map((text) => this.embed(text));
|
|
111
|
+
const embeddings = await Promise.all(embeddingPromises);
|
|
112
|
+
// Create result array with correct size
|
|
113
|
+
const results = new Array(texts.length);
|
|
114
|
+
// Fill in embeddings for valid texts
|
|
115
|
+
embeddings.forEach((embedding, i) => {
|
|
116
|
+
const originalIndex = validIndices[i];
|
|
117
|
+
results[originalIndex] = embedding;
|
|
118
|
+
});
|
|
119
|
+
// Fill in empty arrays for invalid texts
|
|
120
|
+
for (let i = 0; i < texts.length; i++) {
|
|
121
|
+
if (!results[i]) {
|
|
122
|
+
results[i] = new Array(this.dimensions).fill(0);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
throw new Error(`Ollama batch embedding generation failed: ${error.message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Create an Ollama embedding provider instance
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* import { createOllamaProvider } from '@opensaas/stack-rag/providers'
|
|
138
|
+
*
|
|
139
|
+
* const provider = createOllamaProvider({
|
|
140
|
+
* type: 'ollama',
|
|
141
|
+
* baseURL: 'http://localhost:11434',
|
|
142
|
+
* model: 'nomic-embed-text'
|
|
143
|
+
* })
|
|
144
|
+
*
|
|
145
|
+
* const embedding = await provider.embed('Hello world')
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function createOllamaProvider(config) {
|
|
149
|
+
return new OllamaEmbeddingProvider(config);
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/providers/ollama.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { EmbeddingProvider } from './types.js';
|
|
2
|
+
import type { OpenAIEmbeddingConfig } from '../config/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* OpenAI embedding provider
|
|
5
|
+
* Requires the `openai` package to be installed
|
|
6
|
+
*/
|
|
7
|
+
export declare class OpenAIEmbeddingProvider implements EmbeddingProvider {
|
|
8
|
+
readonly type = "openai";
|
|
9
|
+
readonly model: string;
|
|
10
|
+
readonly dimensions: number;
|
|
11
|
+
private client;
|
|
12
|
+
private config;
|
|
13
|
+
constructor(config: OpenAIEmbeddingConfig);
|
|
14
|
+
private initializeClient;
|
|
15
|
+
/**
|
|
16
|
+
* Generate embedding for a single text
|
|
17
|
+
*/
|
|
18
|
+
embed(text: string): Promise<number[]>;
|
|
19
|
+
/**
|
|
20
|
+
* Generate embeddings for multiple texts in a batch
|
|
21
|
+
*/
|
|
22
|
+
embedBatch(texts: string[]): Promise<number[][]>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create an OpenAI embedding provider instance
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { createOpenAIProvider } from '@opensaas/stack-rag/providers'
|
|
30
|
+
*
|
|
31
|
+
* const provider = createOpenAIProvider({
|
|
32
|
+
* type: 'openai',
|
|
33
|
+
* apiKey: process.env.OPENAI_API_KEY!,
|
|
34
|
+
* model: 'text-embedding-3-small'
|
|
35
|
+
* })
|
|
36
|
+
*
|
|
37
|
+
* const embedding = await provider.embed('Hello world')
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createOpenAIProvider(config: OpenAIEmbeddingConfig): OpenAIEmbeddingProvider;
|
|
41
|
+
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,OAAO,KAAK,EAAE,qBAAqB,EAAwB,MAAM,oBAAoB,CAAA;AA0BrF;;;GAGG;AACH,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D,QAAQ,CAAC,IAAI,YAAW;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAE3B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAuB;gBAEzB,MAAM,EAAE,qBAAqB;IASzC,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAkB5C;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;CAiDvD;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,qBAAqB,GAAG,uBAAuB,CAE3F"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model dimensions mapping
|
|
3
|
+
*/
|
|
4
|
+
const MODEL_DIMENSIONS = {
|
|
5
|
+
'text-embedding-3-small': 1536,
|
|
6
|
+
'text-embedding-3-large': 3072,
|
|
7
|
+
'text-embedding-ada-002': 1536,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* OpenAI embedding provider
|
|
11
|
+
* Requires the `openai` package to be installed
|
|
12
|
+
*/
|
|
13
|
+
export class OpenAIEmbeddingProvider {
|
|
14
|
+
type = 'openai';
|
|
15
|
+
model;
|
|
16
|
+
dimensions;
|
|
17
|
+
client;
|
|
18
|
+
config;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.model = config.model || 'text-embedding-3-small';
|
|
22
|
+
this.dimensions = MODEL_DIMENSIONS[this.model] || 1536;
|
|
23
|
+
// Initialize OpenAI client
|
|
24
|
+
this.client = this.initializeClient();
|
|
25
|
+
}
|
|
26
|
+
initializeClient() {
|
|
27
|
+
try {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
29
|
+
const { OpenAI } = require('openai');
|
|
30
|
+
return new OpenAI({
|
|
31
|
+
apiKey: this.config.apiKey,
|
|
32
|
+
organization: this.config.organization,
|
|
33
|
+
baseURL: this.config.baseURL,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
throw new Error('OpenAI package not found. Install it with: npm install openai\n' +
|
|
38
|
+
'Error: ' +
|
|
39
|
+
error.message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Generate embedding for a single text
|
|
44
|
+
*/
|
|
45
|
+
async embed(text) {
|
|
46
|
+
if (!text || text.trim().length === 0) {
|
|
47
|
+
throw new Error('Cannot generate embedding for empty text');
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const response = await this.client.embeddings.create({
|
|
51
|
+
model: this.model,
|
|
52
|
+
input: text,
|
|
53
|
+
encoding_format: 'float',
|
|
54
|
+
});
|
|
55
|
+
return response.data[0].embedding;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
throw new Error(`OpenAI embedding generation failed: ${error.message}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate embeddings for multiple texts in a batch
|
|
63
|
+
*/
|
|
64
|
+
async embedBatch(texts) {
|
|
65
|
+
if (texts.length === 0) {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
// Filter out empty texts and keep track of indices
|
|
69
|
+
const validTexts = [];
|
|
70
|
+
const validIndices = [];
|
|
71
|
+
texts.forEach((text, index) => {
|
|
72
|
+
if (text && text.trim().length > 0) {
|
|
73
|
+
validTexts.push(text);
|
|
74
|
+
validIndices.push(index);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (validTexts.length === 0) {
|
|
78
|
+
throw new Error('Cannot generate embeddings for all empty texts');
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
// OpenAI supports batch embedding
|
|
82
|
+
const response = await this.client.embeddings.create({
|
|
83
|
+
model: this.model,
|
|
84
|
+
input: validTexts,
|
|
85
|
+
encoding_format: 'float',
|
|
86
|
+
});
|
|
87
|
+
// Create result array with correct size
|
|
88
|
+
const results = new Array(texts.length);
|
|
89
|
+
// Fill in embeddings for valid texts
|
|
90
|
+
response.data.forEach((item, i) => {
|
|
91
|
+
const originalIndex = validIndices[i];
|
|
92
|
+
results[originalIndex] = item.embedding;
|
|
93
|
+
});
|
|
94
|
+
// Fill in empty arrays for invalid texts
|
|
95
|
+
for (let i = 0; i < texts.length; i++) {
|
|
96
|
+
if (!results[i]) {
|
|
97
|
+
results[i] = new Array(this.dimensions).fill(0);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw new Error(`OpenAI batch embedding generation failed: ${error.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Create an OpenAI embedding provider instance
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* import { createOpenAIProvider } from '@opensaas/stack-rag/providers'
|
|
113
|
+
*
|
|
114
|
+
* const provider = createOpenAIProvider({
|
|
115
|
+
* type: 'openai',
|
|
116
|
+
* apiKey: process.env.OPENAI_API_KEY!,
|
|
117
|
+
* model: 'text-embedding-3-small'
|
|
118
|
+
* })
|
|
119
|
+
*
|
|
120
|
+
* const embedding = await provider.embed('Hello world')
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function createOpenAIProvider(config) {
|
|
124
|
+
return new OpenAIEmbeddingProvider(config);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,gBAAgB,GAAyC;IAC7D,wBAAwB,EAAE,IAAI;IAC9B,wBAAwB,EAAE,IAAI;IAC9B,wBAAwB,EAAE,IAAI;CAC/B,CAAA;AAiBD;;;GAGG;AACH,MAAM,OAAO,uBAAuB;IACzB,IAAI,GAAG,QAAQ,CAAA;IACf,KAAK,CAAQ;IACb,UAAU,CAAQ;IAEnB,MAAM,CAAc;IACpB,MAAM,CAAuB;IAErC,YAAY,MAA6B;QACvC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,wBAAwB,CAAA;QACrD,IAAI,CAAC,UAAU,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAA6B,CAAC,IAAI,IAAI,CAAA;QAE9E,2BAA2B;QAC3B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAA;IACvC,CAAC;IAEO,gBAAgB;QACtB,IAAI,CAAC;YACH,iEAAiE;YACjE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;YAEpC,OAAO,IAAI,MAAM,CAAC;gBAChB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;gBAC1B,YAAY,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY;gBACtC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO;aAC7B,CAAiB,CAAA;QACpB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,iEAAiE;gBAC/D,SAAS;gBACR,KAAe,CAAC,OAAO,CAC3B,CAAA;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,IAAY;QACtB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;QAC7D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;gBACnD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,IAAI;gBACX,eAAe,EAAE,OAAO;aACzB,CAAC,CAAA;YAEF,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACnC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uCAAwC,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;QACpF,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,KAAe;QAC9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,EAAE,CAAA;QACX,CAAC;QAED,mDAAmD;QACnD,MAAM,UAAU,GAAa,EAAE,CAAA;QAC/B,MAAM,YAAY,GAAa,EAAE,CAAA;QAEjC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YAC5B,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACrB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAA;QACnE,CAAC;QAED,IAAI,CAAC;YACH,kCAAkC;YAClC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;gBACnD,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,KAAK,EAAE,UAAU;gBACjB,eAAe,EAAE,OAAO;aACzB,CAAC,CAAA;YAEF,wCAAwC;YACxC,MAAM,OAAO,GAAe,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAEnD,qCAAqC;YACrC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAA6B,EAAE,CAAS,EAAE,EAAE;gBACjE,MAAM,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC,CAAA;gBACrC,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,SAAS,CAAA;YACzC,CAAC,CAAC,CAAA;YAEF,yCAAyC;YACzC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChB,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;gBACjD,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAA;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,6CAA8C,KAAe,CAAC,OAAO,EAAE,CAAC,CAAA;QAC1F,CAAC;IACH,CAAC;CACF;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAA6B;IAChE,OAAO,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAA;AAC5C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.test.d.ts","sourceRoot":"","sources":["../../src/providers/providers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { OpenAIEmbeddingProvider } from './openai.js';
|
|
3
|
+
import { OllamaEmbeddingProvider } from './ollama.js';
|
|
4
|
+
import { createEmbeddingProvider } from './index.js';
|
|
5
|
+
describe('Embedding Providers', () => {
|
|
6
|
+
describe('OpenAIEmbeddingProvider', () => {
|
|
7
|
+
describe('constructor', () => {
|
|
8
|
+
it('should initialize with default model', () => {
|
|
9
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
10
|
+
type: 'openai',
|
|
11
|
+
apiKey: 'test-key',
|
|
12
|
+
});
|
|
13
|
+
expect(provider.type).toBe('openai');
|
|
14
|
+
expect(provider.model).toBe('text-embedding-3-small');
|
|
15
|
+
expect(provider.dimensions).toBe(1536);
|
|
16
|
+
});
|
|
17
|
+
it('should initialize with custom model', () => {
|
|
18
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
19
|
+
type: 'openai',
|
|
20
|
+
apiKey: 'test-key',
|
|
21
|
+
model: 'text-embedding-3-large',
|
|
22
|
+
});
|
|
23
|
+
expect(provider.model).toBe('text-embedding-3-large');
|
|
24
|
+
expect(provider.dimensions).toBe(3072);
|
|
25
|
+
});
|
|
26
|
+
it('should support ada-002 model', () => {
|
|
27
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
28
|
+
type: 'openai',
|
|
29
|
+
apiKey: 'test-key',
|
|
30
|
+
model: 'text-embedding-ada-002',
|
|
31
|
+
});
|
|
32
|
+
expect(provider.model).toBe('text-embedding-ada-002');
|
|
33
|
+
expect(provider.dimensions).toBe(1536);
|
|
34
|
+
});
|
|
35
|
+
it('should throw error if openai package is not installed', () => {
|
|
36
|
+
// Skip this test - it's difficult to mock require() in ESM environment
|
|
37
|
+
// The actual error handling is tested when the package is genuinely missing
|
|
38
|
+
// This test would require more complex module mocking that isn't worth it
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('embed', () => {
|
|
42
|
+
it('should reject empty text', async () => {
|
|
43
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
44
|
+
type: 'openai',
|
|
45
|
+
apiKey: 'test-key',
|
|
46
|
+
});
|
|
47
|
+
await expect(provider.embed('')).rejects.toThrow('Cannot generate embedding for empty text');
|
|
48
|
+
await expect(provider.embed(' ')).rejects.toThrow('Cannot generate embedding for empty text');
|
|
49
|
+
});
|
|
50
|
+
it('should validate embedding dimensions', () => {
|
|
51
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
52
|
+
type: 'openai',
|
|
53
|
+
apiKey: 'test-key',
|
|
54
|
+
model: 'text-embedding-3-small',
|
|
55
|
+
});
|
|
56
|
+
expect(provider.dimensions).toBe(1536);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('embedBatch', () => {
|
|
60
|
+
it('should return empty array for empty input', async () => {
|
|
61
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
62
|
+
type: 'openai',
|
|
63
|
+
apiKey: 'test-key',
|
|
64
|
+
});
|
|
65
|
+
const result = await provider.embedBatch([]);
|
|
66
|
+
expect(result).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
it('should reject all empty texts', async () => {
|
|
69
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
70
|
+
type: 'openai',
|
|
71
|
+
apiKey: 'test-key',
|
|
72
|
+
});
|
|
73
|
+
await expect(provider.embedBatch(['', ' ', '\n'])).rejects.toThrow('Cannot generate embeddings for all empty texts');
|
|
74
|
+
});
|
|
75
|
+
it('should handle mixed valid and invalid texts', () => {
|
|
76
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
77
|
+
type: 'openai',
|
|
78
|
+
apiKey: 'test-key',
|
|
79
|
+
});
|
|
80
|
+
// We expect this to filter out empty texts and process valid ones
|
|
81
|
+
// The implementation should fill empty slots with zero vectors
|
|
82
|
+
expect(provider.dimensions).toBe(1536);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('OllamaEmbeddingProvider', () => {
|
|
87
|
+
describe('constructor', () => {
|
|
88
|
+
it('should initialize with default settings', () => {
|
|
89
|
+
const provider = new OllamaEmbeddingProvider({
|
|
90
|
+
type: 'ollama',
|
|
91
|
+
});
|
|
92
|
+
expect(provider.type).toBe('ollama');
|
|
93
|
+
expect(provider.model).toBe('nomic-embed-text');
|
|
94
|
+
expect(provider.dimensions).toBe(0); // Not initialized until first embedding
|
|
95
|
+
});
|
|
96
|
+
it('should initialize with custom model', () => {
|
|
97
|
+
const provider = new OllamaEmbeddingProvider({
|
|
98
|
+
type: 'ollama',
|
|
99
|
+
model: 'llama2',
|
|
100
|
+
});
|
|
101
|
+
expect(provider.model).toBe('llama2');
|
|
102
|
+
});
|
|
103
|
+
it('should initialize with custom baseURL', () => {
|
|
104
|
+
const provider = new OllamaEmbeddingProvider({
|
|
105
|
+
type: 'ollama',
|
|
106
|
+
baseURL: 'http://custom-host:8080',
|
|
107
|
+
});
|
|
108
|
+
expect(provider['baseURL']).toBe('http://custom-host:8080');
|
|
109
|
+
});
|
|
110
|
+
it('should remove trailing slash from baseURL', () => {
|
|
111
|
+
const provider = new OllamaEmbeddingProvider({
|
|
112
|
+
type: 'ollama',
|
|
113
|
+
baseURL: 'http://localhost:11434/',
|
|
114
|
+
});
|
|
115
|
+
expect(provider['baseURL']).toBe('http://localhost:11434');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('embed', () => {
|
|
119
|
+
it('should reject empty text', async () => {
|
|
120
|
+
const provider = new OllamaEmbeddingProvider({
|
|
121
|
+
type: 'ollama',
|
|
122
|
+
});
|
|
123
|
+
await expect(provider.embed('')).rejects.toThrow('Cannot generate embedding for empty text');
|
|
124
|
+
await expect(provider.embed(' ')).rejects.toThrow('Cannot generate embedding for empty text');
|
|
125
|
+
});
|
|
126
|
+
it('should provide helpful error when Ollama is not running', async () => {
|
|
127
|
+
const provider = new OllamaEmbeddingProvider({
|
|
128
|
+
type: 'ollama',
|
|
129
|
+
baseURL: 'http://localhost:99999', // Invalid port
|
|
130
|
+
});
|
|
131
|
+
// The error message will vary depending on the environment
|
|
132
|
+
// Just check that it throws an error
|
|
133
|
+
await expect(provider.embed('test')).rejects.toThrow();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('embedBatch', () => {
|
|
137
|
+
it('should return empty array for empty input', async () => {
|
|
138
|
+
const provider = new OllamaEmbeddingProvider({
|
|
139
|
+
type: 'ollama',
|
|
140
|
+
});
|
|
141
|
+
const result = await provider.embedBatch([]);
|
|
142
|
+
expect(result).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
it('should reject all empty texts', async () => {
|
|
145
|
+
const provider = new OllamaEmbeddingProvider({
|
|
146
|
+
type: 'ollama',
|
|
147
|
+
});
|
|
148
|
+
// This test will fail if Ollama is not running, which is expected
|
|
149
|
+
// The error could be about initialization or about empty texts
|
|
150
|
+
await expect(provider.embedBatch(['', ' ', '\n'])).rejects.toThrow();
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe('createEmbeddingProvider factory', () => {
|
|
155
|
+
it('should create OpenAI provider', () => {
|
|
156
|
+
const provider = createEmbeddingProvider({
|
|
157
|
+
type: 'openai',
|
|
158
|
+
apiKey: 'test-key',
|
|
159
|
+
});
|
|
160
|
+
expect(provider).toBeInstanceOf(OpenAIEmbeddingProvider);
|
|
161
|
+
expect(provider.type).toBe('openai');
|
|
162
|
+
});
|
|
163
|
+
it('should create Ollama provider', () => {
|
|
164
|
+
const provider = createEmbeddingProvider({
|
|
165
|
+
type: 'ollama',
|
|
166
|
+
});
|
|
167
|
+
expect(provider).toBeInstanceOf(OllamaEmbeddingProvider);
|
|
168
|
+
expect(provider.type).toBe('ollama');
|
|
169
|
+
});
|
|
170
|
+
it('should throw error for unknown provider type', () => {
|
|
171
|
+
expect(() => {
|
|
172
|
+
createEmbeddingProvider({
|
|
173
|
+
type: 'unknown',
|
|
174
|
+
});
|
|
175
|
+
}).toThrow(/Unknown embedding provider type/);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
describe('Provider interface compliance', () => {
|
|
179
|
+
it('OpenAI provider should implement EmbeddingProvider interface', () => {
|
|
180
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
181
|
+
type: 'openai',
|
|
182
|
+
apiKey: 'test-key',
|
|
183
|
+
});
|
|
184
|
+
expect(provider).toHaveProperty('type');
|
|
185
|
+
expect(provider).toHaveProperty('model');
|
|
186
|
+
expect(provider).toHaveProperty('dimensions');
|
|
187
|
+
expect(provider).toHaveProperty('embed');
|
|
188
|
+
expect(provider).toHaveProperty('embedBatch');
|
|
189
|
+
expect(typeof provider.embed).toBe('function');
|
|
190
|
+
expect(typeof provider.embedBatch).toBe('function');
|
|
191
|
+
});
|
|
192
|
+
it('Ollama provider should implement EmbeddingProvider interface', () => {
|
|
193
|
+
const provider = new OllamaEmbeddingProvider({
|
|
194
|
+
type: 'ollama',
|
|
195
|
+
});
|
|
196
|
+
expect(provider).toHaveProperty('type');
|
|
197
|
+
expect(provider).toHaveProperty('model');
|
|
198
|
+
expect(provider).toHaveProperty('dimensions');
|
|
199
|
+
expect(provider).toHaveProperty('embed');
|
|
200
|
+
expect(provider).toHaveProperty('embedBatch');
|
|
201
|
+
expect(typeof provider.embed).toBe('function');
|
|
202
|
+
expect(typeof provider.embedBatch).toBe('function');
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('Error handling', () => {
|
|
206
|
+
it('should handle API errors gracefully', async () => {
|
|
207
|
+
const provider = new OpenAIEmbeddingProvider({
|
|
208
|
+
type: 'openai',
|
|
209
|
+
apiKey: 'invalid-key',
|
|
210
|
+
});
|
|
211
|
+
// This will fail when actually calling the API
|
|
212
|
+
// but we're testing that it throws a descriptive error
|
|
213
|
+
await expect(provider.embed('test')).rejects.toThrow(/OpenAI embedding generation failed/);
|
|
214
|
+
});
|
|
215
|
+
it('should handle network errors for Ollama', async () => {
|
|
216
|
+
const provider = new OllamaEmbeddingProvider({
|
|
217
|
+
type: 'ollama',
|
|
218
|
+
baseURL: 'http://nonexistent-host:11434',
|
|
219
|
+
});
|
|
220
|
+
await expect(provider.embed('test')).rejects.toThrow();
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
//# sourceMappingURL=providers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.test.js","sourceRoot":"","sources":["../../src/providers/providers.test.ts"],"names":[],"mappings":"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"}
|