@unlimiting/qsc 0.1.1 → 0.1.2
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.
|
@@ -1,10 +1,29 @@
|
|
|
1
1
|
import type { EmbedderConfig } from "../config/index.js";
|
|
2
2
|
import type { Embedder } from "./index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Estimates the number of tokens in a string.
|
|
5
|
+
* Uses a rough approximation of ~4 characters per token for English text / code.
|
|
6
|
+
*/
|
|
7
|
+
export declare function estimateTokens(text: string): number;
|
|
8
|
+
/**
|
|
9
|
+
* Splits an array of texts into batches that respect both the item count limit
|
|
10
|
+
* (MAX_BATCH_SIZE) and the estimated token count limit (MAX_TOKENS_PER_REQUEST).
|
|
11
|
+
*
|
|
12
|
+
* If a single text exceeds MAX_CHARS_PER_CHUNK, it is truncated to fit within
|
|
13
|
+
* the token limit.
|
|
14
|
+
*
|
|
15
|
+
* Returns an array of batches, where each batch is an array of
|
|
16
|
+
* { index, text } objects preserving the original indices.
|
|
17
|
+
*/
|
|
18
|
+
export declare function splitBatchesByTokens(texts: string[]): {
|
|
19
|
+
index: number;
|
|
20
|
+
text: string;
|
|
21
|
+
}[][];
|
|
3
22
|
/**
|
|
4
23
|
* Creates an OpenAI-backed Embedder.
|
|
5
24
|
*
|
|
6
25
|
* Supports text-embedding-3-small (1536d) and text-embedding-3-large (3072d).
|
|
7
|
-
* Handles batch splitting and retry with exponential backoff for rate limits.
|
|
26
|
+
* Handles batch splitting by token count and retry with exponential backoff for rate limits.
|
|
8
27
|
*/
|
|
9
28
|
export declare function createOpenAIEmbedder(config: EmbedderConfig): Embedder;
|
|
10
29
|
//# sourceMappingURL=openai.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/embedder/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/embedder/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAoB3C;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EAAE,GACd;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,EAAE,CAoCrC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,QAAQ,CAwHrE"}
|
package/dist/embedder/openai.js
CHANGED
|
@@ -2,11 +2,68 @@ import OpenAI from "openai";
|
|
|
2
2
|
const MAX_BATCH_SIZE = 2048;
|
|
3
3
|
const MAX_RETRIES = 3;
|
|
4
4
|
const BASE_DELAY_MS = 1000;
|
|
5
|
+
/**
|
|
6
|
+
* Maximum tokens per OpenAI embedding API request.
|
|
7
|
+
* The actual API limit is 300,000 tokens. We use 250,000 as a safety margin
|
|
8
|
+
* since our token estimation (string length / 4) is approximate.
|
|
9
|
+
*/
|
|
10
|
+
const MAX_TOKENS_PER_REQUEST = 250_000;
|
|
11
|
+
/**
|
|
12
|
+
* Maximum characters for a single chunk before truncation.
|
|
13
|
+
* A single chunk cannot exceed the API token limit by itself.
|
|
14
|
+
* 250,000 tokens * 4 chars/token = 1,000,000 characters.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_CHARS_PER_CHUNK = MAX_TOKENS_PER_REQUEST * 4;
|
|
17
|
+
/**
|
|
18
|
+
* Estimates the number of tokens in a string.
|
|
19
|
+
* Uses a rough approximation of ~4 characters per token for English text / code.
|
|
20
|
+
*/
|
|
21
|
+
export function estimateTokens(text) {
|
|
22
|
+
return Math.ceil(text.length / 4);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Splits an array of texts into batches that respect both the item count limit
|
|
26
|
+
* (MAX_BATCH_SIZE) and the estimated token count limit (MAX_TOKENS_PER_REQUEST).
|
|
27
|
+
*
|
|
28
|
+
* If a single text exceeds MAX_CHARS_PER_CHUNK, it is truncated to fit within
|
|
29
|
+
* the token limit.
|
|
30
|
+
*
|
|
31
|
+
* Returns an array of batches, where each batch is an array of
|
|
32
|
+
* { index, text } objects preserving the original indices.
|
|
33
|
+
*/
|
|
34
|
+
export function splitBatchesByTokens(texts) {
|
|
35
|
+
const batches = [];
|
|
36
|
+
let currentBatch = [];
|
|
37
|
+
let currentTokens = 0;
|
|
38
|
+
for (let i = 0; i < texts.length; i++) {
|
|
39
|
+
let text = texts[i];
|
|
40
|
+
// Truncate single oversized chunks
|
|
41
|
+
if (text.length > MAX_CHARS_PER_CHUNK) {
|
|
42
|
+
text = text.slice(0, MAX_CHARS_PER_CHUNK);
|
|
43
|
+
}
|
|
44
|
+
const tokens = estimateTokens(text);
|
|
45
|
+
// If adding this text would exceed the token limit or batch size limit,
|
|
46
|
+
// flush the current batch first (unless it's empty)
|
|
47
|
+
if (currentBatch.length > 0 &&
|
|
48
|
+
(currentTokens + tokens > MAX_TOKENS_PER_REQUEST ||
|
|
49
|
+
currentBatch.length >= MAX_BATCH_SIZE)) {
|
|
50
|
+
batches.push(currentBatch);
|
|
51
|
+
currentBatch = [];
|
|
52
|
+
currentTokens = 0;
|
|
53
|
+
}
|
|
54
|
+
currentBatch.push({ index: i, text });
|
|
55
|
+
currentTokens += tokens;
|
|
56
|
+
}
|
|
57
|
+
if (currentBatch.length > 0) {
|
|
58
|
+
batches.push(currentBatch);
|
|
59
|
+
}
|
|
60
|
+
return batches;
|
|
61
|
+
}
|
|
5
62
|
/**
|
|
6
63
|
* Creates an OpenAI-backed Embedder.
|
|
7
64
|
*
|
|
8
65
|
* Supports text-embedding-3-small (1536d) and text-embedding-3-large (3072d).
|
|
9
|
-
* Handles batch splitting and retry with exponential backoff for rate limits.
|
|
66
|
+
* Handles batch splitting by token count and retry with exponential backoff for rate limits.
|
|
10
67
|
*/
|
|
11
68
|
export function createOpenAIEmbedder(config) {
|
|
12
69
|
const apiKey = process.env[config.api_key_env];
|
|
@@ -44,16 +101,53 @@ export function createOpenAIEmbedder(config) {
|
|
|
44
101
|
}
|
|
45
102
|
throw lastError;
|
|
46
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Attempts to embed a batch of texts. On failure (e.g. 400 token limit
|
|
106
|
+
* exceeded), recursively splits the batch in half and retries each half.
|
|
107
|
+
* When a batch of size 1 still fails, logs a warning and returns a zero
|
|
108
|
+
* vector so the rest of the pipeline can continue.
|
|
109
|
+
*/
|
|
110
|
+
async function embedBatchWithFallback(texts) {
|
|
111
|
+
try {
|
|
112
|
+
return await embedBatch(texts);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Only apply split-and-retry for 400 errors (token limit exceeded, etc.)
|
|
116
|
+
const status = error instanceof OpenAI.APIError ? error.status : undefined;
|
|
117
|
+
if (status !== 400) {
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
// Single item batch that still fails → skip with zero vector
|
|
121
|
+
if (texts.length <= 1) {
|
|
122
|
+
console.warn(`[qsc] Skipping chunk that exceeds token limit (length=${texts[0]?.length ?? 0}). Returning zero vector.`);
|
|
123
|
+
return [new Array(dimensions).fill(0)];
|
|
124
|
+
}
|
|
125
|
+
// Split in half and retry each sub-batch recursively
|
|
126
|
+
const mid = Math.ceil(texts.length / 2);
|
|
127
|
+
const firstHalf = texts.slice(0, mid);
|
|
128
|
+
const secondHalf = texts.slice(mid);
|
|
129
|
+
console.warn(`[qsc] Batch of ${texts.length} texts failed with 400 error. Splitting into [${firstHalf.length}, ${secondHalf.length}] and retrying.`);
|
|
130
|
+
const [firstResults, secondResults] = await Promise.all([
|
|
131
|
+
embedBatchWithFallback(firstHalf),
|
|
132
|
+
embedBatchWithFallback(secondHalf),
|
|
133
|
+
]);
|
|
134
|
+
return [...firstResults, ...secondResults];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
47
137
|
const embedder = {
|
|
48
138
|
async embed(texts) {
|
|
49
139
|
if (texts.length === 0)
|
|
50
140
|
return [];
|
|
51
|
-
// Split into batches respecting
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
141
|
+
// Split into batches respecting both item count and token limits
|
|
142
|
+
const batches = splitBatchesByTokens(texts);
|
|
143
|
+
const results = new Array(texts.length);
|
|
144
|
+
for (const batch of batches) {
|
|
145
|
+
const batchTexts = batch.map((item) => item.text);
|
|
146
|
+
const batchResults = await embedBatchWithFallback(batchTexts);
|
|
147
|
+
// Place results back in their original positions
|
|
148
|
+
for (let j = 0; j < batch.length; j++) {
|
|
149
|
+
results[batch[j].index] = batchResults[j];
|
|
150
|
+
}
|
|
57
151
|
}
|
|
58
152
|
return results;
|
|
59
153
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/embedder/openai.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAI5B,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,WAAW,GAAG,CAAC,CAAC;AACtB,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,0CAA0C,MAAM,CAAC,WAAW,cAAc,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;IAC3B,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IAErC,KAAK,UAAU,UAAU,CAAC,KAAe;QACvC,IAAI,SAAkB,CAAC;QAEvB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;oBAC9C,KAAK;oBACL,KAAK,EAAE,KAAK;oBACZ,UAAU;iBACX,CAAC,CAAC;gBAEH,wCAAwC;gBACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;gBAC/D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC9C,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,SAAS,GAAG,KAAK,CAAC;gBAElB,mDAAmD;gBACnD,MAAM,MAAM,GACV,KAAK,YAAY,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC9D,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC;oBAC9D,MAAM,KAAK,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;oBACnD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;oBAC3D,SAAS;gBACX,CAAC;gBAED,sBAAsB;gBACtB,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QAED,MAAM,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/embedder/openai.ts"],"names":[],"mappings":"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"}
|