viberag 0.3.2 → 0.4.0
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/README.md +2 -2
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.js +100 -102
- package/dist/cli/commands/handlers.d.ts +8 -6
- package/dist/cli/commands/handlers.js +90 -32
- package/dist/cli/commands/useCommands.d.ts +20 -0
- package/dist/cli/commands/useCommands.js +189 -0
- package/dist/cli/commands/useRagCommands.d.ts +2 -5
- package/dist/cli/commands/useRagCommands.js +11 -18
- package/dist/cli/components/InitWizard.js +66 -27
- package/dist/cli/components/McpSetupWizard.js +23 -4
- package/dist/cli/components/SlotRow.d.ts +22 -0
- package/dist/cli/components/SlotRow.js +55 -0
- package/dist/cli/components/StatusBar.d.ts +14 -0
- package/dist/cli/components/StatusBar.js +156 -0
- package/dist/cli/contexts/DaemonStatusContext.d.ts +38 -0
- package/dist/cli/contexts/DaemonStatusContext.js +106 -0
- package/dist/cli/hooks/useStatusPolling.d.ts +34 -0
- package/dist/cli/hooks/useStatusPolling.js +121 -0
- package/dist/cli/store/app/selectors.d.ts +87 -0
- package/dist/cli/store/app/selectors.js +28 -0
- package/dist/cli/store/app/slice.d.ts +1013 -0
- package/dist/cli/store/app/slice.js +112 -0
- package/dist/cli/store/hooks.d.ts +22 -0
- package/dist/cli/store/hooks.js +17 -0
- package/dist/cli/store/store.d.ts +17 -0
- package/dist/cli/store/store.js +18 -0
- package/dist/cli/store/wizard/selectors.d.ts +115 -0
- package/dist/cli/store/wizard/selectors.js +36 -0
- package/dist/cli/store/wizard/slice.d.ts +523 -0
- package/dist/cli/store/wizard/slice.js +119 -0
- package/dist/cli/utils/error-handler.d.ts +55 -0
- package/dist/cli/utils/error-handler.js +92 -0
- package/dist/client/auto-start.d.ts +42 -0
- package/dist/client/auto-start.js +250 -0
- package/dist/client/connection.d.ts +48 -0
- package/dist/client/connection.js +200 -0
- package/dist/client/index.d.ts +93 -0
- package/dist/client/index.js +209 -0
- package/dist/client/types.d.ts +105 -0
- package/dist/client/types.js +7 -0
- package/dist/common/components/SlotRow.d.ts +22 -0
- package/dist/common/components/SlotRow.js +53 -0
- package/dist/common/components/StatusBar.js +82 -31
- package/dist/common/types.d.ts +12 -13
- package/dist/daemon/handlers.d.ts +15 -0
- package/dist/daemon/handlers.js +157 -0
- package/dist/daemon/index.d.ts +21 -0
- package/dist/daemon/index.js +123 -0
- package/dist/daemon/lib/chunker/bounded-channel.d.ts +51 -0
- package/dist/daemon/lib/chunker/bounded-channel.js +138 -0
- package/dist/daemon/lib/chunker/index.d.ts +135 -0
- package/dist/daemon/lib/chunker/index.js +1370 -0
- package/dist/daemon/lib/chunker/types.d.ts +77 -0
- package/dist/daemon/lib/chunker/types.js +50 -0
- package/dist/daemon/lib/config.d.ts +73 -0
- package/dist/daemon/lib/config.js +149 -0
- package/dist/daemon/lib/constants.d.ts +75 -0
- package/dist/daemon/lib/constants.js +114 -0
- package/dist/daemon/lib/gitignore.d.ts +57 -0
- package/dist/daemon/lib/gitignore.js +246 -0
- package/dist/daemon/lib/logger.d.ts +51 -0
- package/dist/daemon/lib/logger.js +167 -0
- package/dist/daemon/lib/manifest.d.ts +58 -0
- package/dist/daemon/lib/manifest.js +116 -0
- package/dist/daemon/lib/merkle/diff.d.ts +32 -0
- package/dist/daemon/lib/merkle/diff.js +107 -0
- package/dist/daemon/lib/merkle/hash.d.ts +40 -0
- package/dist/daemon/lib/merkle/hash.js +180 -0
- package/dist/daemon/lib/merkle/index.d.ts +71 -0
- package/dist/daemon/lib/merkle/index.js +309 -0
- package/dist/daemon/lib/merkle/node.d.ts +55 -0
- package/dist/daemon/lib/merkle/node.js +82 -0
- package/dist/daemon/lifecycle.d.ts +50 -0
- package/dist/daemon/lifecycle.js +142 -0
- package/dist/daemon/owner.d.ts +175 -0
- package/dist/daemon/owner.js +609 -0
- package/dist/daemon/protocol.d.ts +100 -0
- package/dist/daemon/protocol.js +163 -0
- package/dist/daemon/providers/api-utils.d.ts +130 -0
- package/dist/daemon/providers/api-utils.js +248 -0
- package/dist/daemon/providers/gemini.d.ts +39 -0
- package/dist/daemon/providers/gemini.js +205 -0
- package/dist/daemon/providers/index.d.ts +14 -0
- package/dist/daemon/providers/index.js +14 -0
- package/dist/daemon/providers/local-4b.d.ts +28 -0
- package/dist/daemon/providers/local-4b.js +51 -0
- package/dist/daemon/providers/local.d.ts +36 -0
- package/dist/daemon/providers/local.js +166 -0
- package/dist/daemon/providers/mistral.d.ts +35 -0
- package/dist/daemon/providers/mistral.js +160 -0
- package/dist/daemon/providers/mock.d.ts +35 -0
- package/dist/daemon/providers/mock.js +69 -0
- package/dist/daemon/providers/openai.d.ts +41 -0
- package/dist/daemon/providers/openai.js +190 -0
- package/dist/daemon/providers/types.d.ts +68 -0
- package/dist/daemon/providers/types.js +6 -0
- package/dist/daemon/providers/validate.d.ts +30 -0
- package/dist/daemon/providers/validate.js +162 -0
- package/dist/daemon/server.d.ts +79 -0
- package/dist/daemon/server.js +293 -0
- package/dist/daemon/services/index.d.ts +11 -0
- package/dist/daemon/services/index.js +16 -0
- package/dist/daemon/services/indexing.d.ts +117 -0
- package/dist/daemon/services/indexing.js +573 -0
- package/dist/daemon/services/search/filters.d.ts +21 -0
- package/dist/daemon/services/search/filters.js +106 -0
- package/dist/daemon/services/search/fts.d.ts +32 -0
- package/dist/daemon/services/search/fts.js +61 -0
- package/dist/daemon/services/search/hybrid.d.ts +17 -0
- package/dist/daemon/services/search/hybrid.js +58 -0
- package/dist/daemon/services/search/index.d.ts +108 -0
- package/dist/daemon/services/search/index.js +417 -0
- package/dist/daemon/services/search/types.d.ts +126 -0
- package/dist/daemon/services/search/types.js +4 -0
- package/dist/daemon/services/search/vector.d.ts +25 -0
- package/dist/daemon/services/search/vector.js +44 -0
- package/dist/daemon/services/storage/index.d.ts +110 -0
- package/dist/daemon/services/storage/index.js +378 -0
- package/dist/daemon/services/storage/schema.d.ts +24 -0
- package/dist/daemon/services/storage/schema.js +51 -0
- package/dist/daemon/services/storage/types.d.ts +105 -0
- package/dist/daemon/services/storage/types.js +71 -0
- package/dist/daemon/services/types.d.ts +192 -0
- package/dist/daemon/services/types.js +53 -0
- package/dist/daemon/services/watcher.d.ts +98 -0
- package/dist/daemon/services/watcher.js +386 -0
- package/dist/daemon/state.d.ts +119 -0
- package/dist/daemon/state.js +161 -0
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +44 -60
- package/dist/mcp/server.d.ts +10 -14
- package/dist/mcp/server.js +75 -74
- package/dist/mcp/services/lazy-loader.d.ts +23 -0
- package/dist/mcp/services/lazy-loader.js +34 -0
- package/dist/mcp/warmup.d.ts +3 -3
- package/dist/mcp/warmup.js +39 -40
- package/dist/mcp/watcher.d.ts +5 -7
- package/dist/mcp/watcher.js +73 -64
- package/dist/rag/config/index.d.ts +2 -0
- package/dist/rag/constants.d.ts +30 -0
- package/dist/rag/constants.js +38 -0
- package/dist/rag/embeddings/api-utils.d.ts +121 -0
- package/dist/rag/embeddings/api-utils.js +259 -0
- package/dist/rag/embeddings/gemini.d.ts +4 -12
- package/dist/rag/embeddings/gemini.js +22 -72
- package/dist/rag/embeddings/index.d.ts +5 -3
- package/dist/rag/embeddings/index.js +5 -2
- package/dist/rag/embeddings/local-4b.d.ts +2 -2
- package/dist/rag/embeddings/local-4b.js +1 -1
- package/dist/rag/embeddings/local.d.ts +10 -3
- package/dist/rag/embeddings/local.js +58 -12
- package/dist/rag/embeddings/mistral.d.ts +4 -12
- package/dist/rag/embeddings/mistral.js +22 -72
- package/dist/rag/embeddings/mock.d.ts +35 -0
- package/dist/rag/embeddings/mock.js +69 -0
- package/dist/rag/embeddings/openai.d.ts +11 -13
- package/dist/rag/embeddings/openai.js +47 -75
- package/dist/rag/embeddings/types.d.ts +27 -1
- package/dist/rag/embeddings/validate.d.ts +9 -1
- package/dist/rag/embeddings/validate.js +17 -4
- package/dist/rag/index.d.ts +2 -2
- package/dist/rag/index.js +1 -1
- package/dist/rag/indexer/bounded-channel.d.ts +51 -0
- package/dist/rag/indexer/bounded-channel.js +138 -0
- package/dist/rag/indexer/indexer.d.ts +4 -14
- package/dist/rag/indexer/indexer.js +246 -169
- package/dist/rag/indexer/types.d.ts +1 -0
- package/dist/rag/logger/index.d.ts +22 -0
- package/dist/rag/logger/index.js +78 -1
- package/dist/rag/manifest/index.js +1 -2
- package/dist/rag/search/index.js +1 -1
- package/dist/rag/storage/schema.d.ts +2 -4
- package/dist/rag/storage/schema.js +3 -5
- package/dist/store/app/selectors.d.ts +87 -0
- package/dist/store/app/selectors.js +28 -0
- package/dist/store/app/slice.d.ts +1013 -0
- package/dist/store/app/slice.js +112 -0
- package/dist/store/hooks.d.ts +22 -0
- package/dist/store/hooks.js +17 -0
- package/dist/store/index.d.ts +12 -0
- package/dist/store/index.js +18 -0
- package/dist/store/indexing/listeners.d.ts +25 -0
- package/dist/store/indexing/listeners.js +46 -0
- package/dist/store/indexing/selectors.d.ts +195 -0
- package/dist/store/indexing/selectors.js +69 -0
- package/dist/store/indexing/slice.d.ts +309 -0
- package/dist/store/indexing/slice.js +113 -0
- package/dist/store/slot-progress/listeners.d.ts +23 -0
- package/dist/store/slot-progress/listeners.js +33 -0
- package/dist/store/slot-progress/selectors.d.ts +67 -0
- package/dist/store/slot-progress/selectors.js +36 -0
- package/dist/store/slot-progress/slice.d.ts +246 -0
- package/dist/store/slot-progress/slice.js +70 -0
- package/dist/store/store.d.ts +17 -0
- package/dist/store/store.js +18 -0
- package/dist/store/warmup/selectors.d.ts +109 -0
- package/dist/store/warmup/selectors.js +44 -0
- package/dist/store/warmup/slice.d.ts +137 -0
- package/dist/store/warmup/slice.js +72 -0
- package/dist/store/watcher/selectors.d.ts +115 -0
- package/dist/store/watcher/selectors.js +52 -0
- package/dist/store/watcher/slice.d.ts +269 -0
- package/dist/store/watcher/slice.js +100 -0
- package/dist/store/wizard/selectors.d.ts +115 -0
- package/dist/store/wizard/selectors.js +36 -0
- package/dist/store/wizard/slice.d.ts +523 -0
- package/dist/store/wizard/slice.js +119 -0
- package/package.json +10 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for API-based embedding providers.
|
|
3
|
+
* Provides common retry logic, rate limiting, and concurrency patterns.
|
|
4
|
+
*
|
|
5
|
+
* Slot progress is dispatched directly to the Redux store, eliminating
|
|
6
|
+
* the callback chain and providing a single source of truth for UI state.
|
|
7
|
+
*/
|
|
8
|
+
import type { Logger } from '../logger/index.js';
|
|
9
|
+
import { CONCURRENCY } from '../constants.js';
|
|
10
|
+
export { CONCURRENCY };
|
|
11
|
+
/** Delay (ms) between batch completion and next batch start (per slot) */
|
|
12
|
+
export declare const BATCH_DELAY_MS = 200;
|
|
13
|
+
/** Max retry attempts on rate limit */
|
|
14
|
+
export declare const MAX_RETRIES = 12;
|
|
15
|
+
/** Initial backoff (ms) */
|
|
16
|
+
export declare const INITIAL_BACKOFF_MS = 1000;
|
|
17
|
+
/** Maximum backoff (ms) */
|
|
18
|
+
export declare const MAX_BACKOFF_MS = 60000;
|
|
19
|
+
/**
|
|
20
|
+
* Sleep for a specified duration.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sleep(ms: number): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if an error is a rate limit error (429 or quota exceeded).
|
|
25
|
+
*/
|
|
26
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Check if an error is a known transient API error that should be retried.
|
|
29
|
+
*
|
|
30
|
+
* GEMINI TRANSIENT BUG:
|
|
31
|
+
* The Gemini API has a known server-side bug where it intermittently returns
|
|
32
|
+
* a 400 "API key expired" error even when the key is valid. This is NOT an
|
|
33
|
+
* actual authentication failure - it's a transient error that resolves on retry.
|
|
34
|
+
*
|
|
35
|
+
* Evidence:
|
|
36
|
+
* - Users report: "if I try the same request again a few times, it usually works fine"
|
|
37
|
+
* - New API keys don't fix it
|
|
38
|
+
* - Same key works in curl but fails randomly via API clients
|
|
39
|
+
* - Google has acknowledged this as a P1/P2 bug
|
|
40
|
+
*
|
|
41
|
+
* GitHub issues documenting this bug:
|
|
42
|
+
* - https://github.com/google-gemini/gemini-cli/issues/4430
|
|
43
|
+
* - https://github.com/google-gemini/gemini-cli/issues/1712
|
|
44
|
+
* - https://github.com/google-gemini/gemini-cli/issues/8675
|
|
45
|
+
*
|
|
46
|
+
* We detect this specific error and retry it rather than failing immediately.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isTransientApiError(error: unknown): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Check if an error should trigger a retry (rate limit OR transient error).
|
|
51
|
+
*/
|
|
52
|
+
export declare function isRetriableError(error: unknown): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Callbacks for rate limiting and progress reporting.
|
|
55
|
+
*
|
|
56
|
+
* Note: Slot progress is now handled via Redux store dispatch,
|
|
57
|
+
* not callbacks. Only throttle and batch progress use callbacks.
|
|
58
|
+
*/
|
|
59
|
+
export interface ApiProviderCallbacks {
|
|
60
|
+
onThrottle?: (message: string | null) => void;
|
|
61
|
+
onBatchProgress?: (processed: number, total: number) => void;
|
|
62
|
+
/**
|
|
63
|
+
* When set to true, callbacks will be skipped.
|
|
64
|
+
* Used to prevent stale progress updates after an error occurs
|
|
65
|
+
* while other concurrent batches are still completing.
|
|
66
|
+
*/
|
|
67
|
+
aborted?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Metadata for a batch of chunks, used for detailed failure logging.
|
|
71
|
+
*/
|
|
72
|
+
export interface BatchMetadata {
|
|
73
|
+
/** File paths for chunks in this batch */
|
|
74
|
+
filepaths: string[];
|
|
75
|
+
/** Start/end lines per chunk */
|
|
76
|
+
lineRanges: Array<{
|
|
77
|
+
start: number;
|
|
78
|
+
end: number;
|
|
79
|
+
}>;
|
|
80
|
+
/** Text sizes per chunk (in characters) */
|
|
81
|
+
sizes: number[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute an async function with exponential backoff retry on retriable errors.
|
|
85
|
+
*
|
|
86
|
+
* Retries on:
|
|
87
|
+
* - Rate limit errors (429, quota exceeded)
|
|
88
|
+
* - Transient API errors (e.g., Gemini's spurious "API key expired" bug)
|
|
89
|
+
*
|
|
90
|
+
* @param fn - The async function to execute
|
|
91
|
+
* @param callbacks - Optional callbacks for throttle notifications
|
|
92
|
+
* @param onRetrying - Optional callback when entering retry state
|
|
93
|
+
* @returns The result of the function
|
|
94
|
+
*/
|
|
95
|
+
export declare function withRetry<T>(fn: () => Promise<T>, callbacks?: ApiProviderCallbacks, onRetrying?: (retryInfo: string | null) => void): Promise<T>;
|
|
96
|
+
/**
|
|
97
|
+
* Process batches with p-limit sliding window concurrency and inter-batch delay.
|
|
98
|
+
* Reports progress per-batch (more granular than group-based).
|
|
99
|
+
*
|
|
100
|
+
* Slot progress is dispatched directly to the Redux store, providing a single
|
|
101
|
+
* source of truth for UI state. Each slot index (0 to CONCURRENCY-1) is reused
|
|
102
|
+
* as batches complete.
|
|
103
|
+
*
|
|
104
|
+
* When an error occurs, sets callbacks.aborted = true to prevent stale progress
|
|
105
|
+
* updates from concurrent batches that are still completing. Failures are logged
|
|
106
|
+
* with detailed chunk metadata if provided.
|
|
107
|
+
*
|
|
108
|
+
* @param batches - Array of batches to process
|
|
109
|
+
* @param processBatch - Function to process a single batch
|
|
110
|
+
* @param callbacks - Optional callbacks for progress reporting
|
|
111
|
+
* @param batchSize - Optional batch size for calculating chunk indices
|
|
112
|
+
* @param batchMetadata - Optional metadata per batch for detailed failure logging
|
|
113
|
+
* @param logger - Optional logger for debug output
|
|
114
|
+
* @param chunkOffset - Optional offset for cumulative chunk numbering (default: 0)
|
|
115
|
+
* @returns Flattened array of results
|
|
116
|
+
*/
|
|
117
|
+
export declare function processBatchesWithLimit<T>(batches: T[][], processBatch: (batch: T[], onRetrying?: (retryInfo: string | null) => void) => Promise<number[][]>, callbacks?: ApiProviderCallbacks, batchSize?: number, batchMetadata?: BatchMetadata[], logger?: Logger, chunkOffset?: number): Promise<number[][]>;
|
|
118
|
+
/**
|
|
119
|
+
* Split an array into batches of a specified size.
|
|
120
|
+
*/
|
|
121
|
+
export declare function chunk<T>(array: T[], size: number): T[][];
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for API-based embedding providers.
|
|
3
|
+
* Provides common retry logic, rate limiting, and concurrency patterns.
|
|
4
|
+
*
|
|
5
|
+
* Slot progress is dispatched directly to the Redux store, eliminating
|
|
6
|
+
* the callback chain and providing a single source of truth for UI state.
|
|
7
|
+
*/
|
|
8
|
+
import pLimit from 'p-limit';
|
|
9
|
+
import { store, SlotProgressActions } from '../../store/index.js';
|
|
10
|
+
import { CONCURRENCY } from '../constants.js';
|
|
11
|
+
// Re-export for backward compatibility
|
|
12
|
+
export { CONCURRENCY };
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Constants
|
|
15
|
+
// ============================================================================
|
|
16
|
+
/** Delay (ms) between batch completion and next batch start (per slot) */
|
|
17
|
+
export const BATCH_DELAY_MS = 200;
|
|
18
|
+
/** Max retry attempts on rate limit */
|
|
19
|
+
export const MAX_RETRIES = 12;
|
|
20
|
+
/** Initial backoff (ms) */
|
|
21
|
+
export const INITIAL_BACKOFF_MS = 1000;
|
|
22
|
+
/** Maximum backoff (ms) */
|
|
23
|
+
export const MAX_BACKOFF_MS = 60000;
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Utility Functions
|
|
26
|
+
// ============================================================================
|
|
27
|
+
/**
|
|
28
|
+
* Sleep for a specified duration.
|
|
29
|
+
*/
|
|
30
|
+
export function sleep(ms) {
|
|
31
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if an error is a rate limit error (429 or quota exceeded).
|
|
35
|
+
*/
|
|
36
|
+
export function isRateLimitError(error) {
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
const msg = error.message.toLowerCase();
|
|
39
|
+
return msg.includes('429') || msg.includes('rate') || msg.includes('quota');
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if an error is a known transient API error that should be retried.
|
|
45
|
+
*
|
|
46
|
+
* GEMINI TRANSIENT BUG:
|
|
47
|
+
* The Gemini API has a known server-side bug where it intermittently returns
|
|
48
|
+
* a 400 "API key expired" error even when the key is valid. This is NOT an
|
|
49
|
+
* actual authentication failure - it's a transient error that resolves on retry.
|
|
50
|
+
*
|
|
51
|
+
* Evidence:
|
|
52
|
+
* - Users report: "if I try the same request again a few times, it usually works fine"
|
|
53
|
+
* - New API keys don't fix it
|
|
54
|
+
* - Same key works in curl but fails randomly via API clients
|
|
55
|
+
* - Google has acknowledged this as a P1/P2 bug
|
|
56
|
+
*
|
|
57
|
+
* GitHub issues documenting this bug:
|
|
58
|
+
* - https://github.com/google-gemini/gemini-cli/issues/4430
|
|
59
|
+
* - https://github.com/google-gemini/gemini-cli/issues/1712
|
|
60
|
+
* - https://github.com/google-gemini/gemini-cli/issues/8675
|
|
61
|
+
*
|
|
62
|
+
* We detect this specific error and retry it rather than failing immediately.
|
|
63
|
+
*/
|
|
64
|
+
export function isTransientApiError(error) {
|
|
65
|
+
if (error instanceof Error) {
|
|
66
|
+
const msg = error.message.toLowerCase();
|
|
67
|
+
// Gemini transient "API key expired" bug (400 status)
|
|
68
|
+
// The specific message is: "API key expired. Please renew the API key."
|
|
69
|
+
// We check for this specific pattern to avoid retrying actual auth failures
|
|
70
|
+
if (msg.includes('api key expired') &&
|
|
71
|
+
(msg.includes('400') || msg.includes('invalid_argument'))) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Check if an error should trigger a retry (rate limit OR transient error).
|
|
79
|
+
*/
|
|
80
|
+
export function isRetriableError(error) {
|
|
81
|
+
return isRateLimitError(error) || isTransientApiError(error);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute an async function with exponential backoff retry on retriable errors.
|
|
85
|
+
*
|
|
86
|
+
* Retries on:
|
|
87
|
+
* - Rate limit errors (429, quota exceeded)
|
|
88
|
+
* - Transient API errors (e.g., Gemini's spurious "API key expired" bug)
|
|
89
|
+
*
|
|
90
|
+
* @param fn - The async function to execute
|
|
91
|
+
* @param callbacks - Optional callbacks for throttle notifications
|
|
92
|
+
* @param onRetrying - Optional callback when entering retry state
|
|
93
|
+
* @returns The result of the function
|
|
94
|
+
*/
|
|
95
|
+
export async function withRetry(fn, callbacks, onRetrying) {
|
|
96
|
+
let attempt = 0;
|
|
97
|
+
let backoffMs = INITIAL_BACKOFF_MS;
|
|
98
|
+
while (true) {
|
|
99
|
+
try {
|
|
100
|
+
const result = await fn();
|
|
101
|
+
// Clear throttle message on success (if was throttling)
|
|
102
|
+
// Skip if aborted (another batch failed)
|
|
103
|
+
if (attempt > 0 && !callbacks?.aborted) {
|
|
104
|
+
callbacks?.onThrottle?.(null);
|
|
105
|
+
onRetrying?.(null);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (isRetriableError(error) && attempt < MAX_RETRIES) {
|
|
111
|
+
attempt++;
|
|
112
|
+
const secs = Math.round(backoffMs / 1000);
|
|
113
|
+
const retryInfo = `retry ${attempt}/${MAX_RETRIES} in ${secs}s`;
|
|
114
|
+
// Provide context-appropriate message
|
|
115
|
+
// Skip if aborted (another batch failed)
|
|
116
|
+
if (!callbacks?.aborted) {
|
|
117
|
+
const isTransient = isTransientApiError(error);
|
|
118
|
+
const reason = isTransient ? 'Transient API error' : 'Rate limited';
|
|
119
|
+
callbacks?.onThrottle?.(`${reason} - ${retryInfo}`);
|
|
120
|
+
onRetrying?.(retryInfo);
|
|
121
|
+
}
|
|
122
|
+
await sleep(backoffMs);
|
|
123
|
+
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Process batches with p-limit sliding window concurrency and inter-batch delay.
|
|
133
|
+
* Reports progress per-batch (more granular than group-based).
|
|
134
|
+
*
|
|
135
|
+
* Slot progress is dispatched directly to the Redux store, providing a single
|
|
136
|
+
* source of truth for UI state. Each slot index (0 to CONCURRENCY-1) is reused
|
|
137
|
+
* as batches complete.
|
|
138
|
+
*
|
|
139
|
+
* When an error occurs, sets callbacks.aborted = true to prevent stale progress
|
|
140
|
+
* updates from concurrent batches that are still completing. Failures are logged
|
|
141
|
+
* with detailed chunk metadata if provided.
|
|
142
|
+
*
|
|
143
|
+
* @param batches - Array of batches to process
|
|
144
|
+
* @param processBatch - Function to process a single batch
|
|
145
|
+
* @param callbacks - Optional callbacks for progress reporting
|
|
146
|
+
* @param batchSize - Optional batch size for calculating chunk indices
|
|
147
|
+
* @param batchMetadata - Optional metadata per batch for detailed failure logging
|
|
148
|
+
* @param logger - Optional logger for debug output
|
|
149
|
+
* @param chunkOffset - Optional offset for cumulative chunk numbering (default: 0)
|
|
150
|
+
* @returns Flattened array of results
|
|
151
|
+
*/
|
|
152
|
+
export async function processBatchesWithLimit(batches, processBatch, callbacks, batchSize, batchMetadata, logger, chunkOffset = 0) {
|
|
153
|
+
const limit = pLimit(CONCURRENCY);
|
|
154
|
+
let processedItems = 0;
|
|
155
|
+
const totalItems = batches.reduce((sum, batch) => sum + batch.length, 0);
|
|
156
|
+
// Track which slot index to assign next (wraps around CONCURRENCY)
|
|
157
|
+
let nextSlotIndex = 0;
|
|
158
|
+
try {
|
|
159
|
+
const batchResults = await Promise.all(batches.map((batch, batchIndex) => limit(async () => {
|
|
160
|
+
// Assign slot index (reuse slots as batches complete)
|
|
161
|
+
const slotIndex = nextSlotIndex++ % CONCURRENCY;
|
|
162
|
+
// Calculate cumulative chunk positions (with offset from prior batches)
|
|
163
|
+
const startChunk = chunkOffset + batchIndex * (batchSize ?? batch.length) + 1;
|
|
164
|
+
const endChunk = startChunk + batch.length - 1;
|
|
165
|
+
const batchInfo = `chunks ${startChunk}-${endChunk}`;
|
|
166
|
+
// Dispatch to Redux: mark slot as processing
|
|
167
|
+
if (!callbacks?.aborted) {
|
|
168
|
+
store.dispatch(SlotProgressActions.setSlotProcessing({
|
|
169
|
+
index: slotIndex,
|
|
170
|
+
batchInfo,
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
// Callback for when this slot enters retry state
|
|
174
|
+
const onRetrying = (retryInfo) => {
|
|
175
|
+
if (callbacks?.aborted)
|
|
176
|
+
return;
|
|
177
|
+
if (retryInfo) {
|
|
178
|
+
store.dispatch(SlotProgressActions.setSlotRateLimited({
|
|
179
|
+
index: slotIndex,
|
|
180
|
+
batchInfo,
|
|
181
|
+
retryInfo,
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// Cleared - back to processing
|
|
186
|
+
store.dispatch(SlotProgressActions.setSlotProcessing({
|
|
187
|
+
index: slotIndex,
|
|
188
|
+
batchInfo,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
let result;
|
|
193
|
+
try {
|
|
194
|
+
result = await processBatch(batch, onRetrying);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// Log detailed failure info before re-throwing
|
|
198
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
199
|
+
const batchMeta = batchMetadata?.[batchIndex];
|
|
200
|
+
// Log to debug.log with full chunk context
|
|
201
|
+
if (logger) {
|
|
202
|
+
logger.error('api-utils', 'Batch failed after retries', {
|
|
203
|
+
batchIndex,
|
|
204
|
+
batchInfo,
|
|
205
|
+
chunkCount: batch.length,
|
|
206
|
+
files: batchMeta?.filepaths ?? [],
|
|
207
|
+
lineRanges: batchMeta?.lineRanges ?? [],
|
|
208
|
+
sizes: batchMeta?.sizes ?? [],
|
|
209
|
+
error: errorMsg,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
// Dispatch failure to Redux for UI visibility
|
|
213
|
+
store.dispatch(SlotProgressActions.addFailure({
|
|
214
|
+
batchInfo,
|
|
215
|
+
error: errorMsg,
|
|
216
|
+
timestamp: new Date().toISOString(),
|
|
217
|
+
}));
|
|
218
|
+
// Re-throw to trigger outer catch (abort and cleanup)
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
// Skip updates if aborted (another batch failed)
|
|
222
|
+
if (callbacks?.aborted) {
|
|
223
|
+
store.dispatch(SlotProgressActions.setSlotIdle(slotIndex));
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
// Delay before releasing the slot (rate limit protection)
|
|
227
|
+
await sleep(BATCH_DELAY_MS);
|
|
228
|
+
// Dispatch to Redux: mark slot as idle
|
|
229
|
+
store.dispatch(SlotProgressActions.setSlotIdle(slotIndex));
|
|
230
|
+
// Report progress per-batch
|
|
231
|
+
processedItems += batch.length;
|
|
232
|
+
callbacks?.onBatchProgress?.(processedItems, totalItems);
|
|
233
|
+
return result;
|
|
234
|
+
})));
|
|
235
|
+
// Reset all slots when complete
|
|
236
|
+
store.dispatch(SlotProgressActions.resetSlots());
|
|
237
|
+
return batchResults.flat();
|
|
238
|
+
}
|
|
239
|
+
catch (error) {
|
|
240
|
+
// Set aborted flag to stop progress updates from other concurrent batches
|
|
241
|
+
// that are still completing in the background
|
|
242
|
+
if (callbacks) {
|
|
243
|
+
callbacks.aborted = true;
|
|
244
|
+
}
|
|
245
|
+
// Reset slots on error
|
|
246
|
+
store.dispatch(SlotProgressActions.resetSlots());
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Split an array into batches of a specified size.
|
|
252
|
+
*/
|
|
253
|
+
export function chunk(array, size) {
|
|
254
|
+
const batches = [];
|
|
255
|
+
for (let i = 0; i < array.length; i += size) {
|
|
256
|
+
batches.push(array.slice(i, i + size));
|
|
257
|
+
}
|
|
258
|
+
return batches;
|
|
259
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Free tier available with generous limits.
|
|
10
10
|
*/
|
|
11
|
-
import type { EmbeddingProvider, ModelProgressCallback } from './types.js';
|
|
11
|
+
import type { EmbeddingProvider, ModelProgressCallback, EmbedOptions } from './types.js';
|
|
12
12
|
/**
|
|
13
13
|
* Gemini embedding provider.
|
|
14
14
|
* Uses gemini-embedding-001 model via Google's Generative AI API.
|
|
@@ -17,19 +17,11 @@ export declare class GeminiEmbeddingProvider implements EmbeddingProvider {
|
|
|
17
17
|
readonly dimensions = 1536;
|
|
18
18
|
private apiKey;
|
|
19
19
|
private initialized;
|
|
20
|
-
onThrottle
|
|
21
|
-
onBatchProgress
|
|
20
|
+
onThrottle: ((message: string | null) => void) | undefined;
|
|
21
|
+
onBatchProgress: ((processed: number, total: number) => void) | undefined;
|
|
22
22
|
constructor(apiKey?: string);
|
|
23
23
|
initialize(_onProgress?: ModelProgressCallback): Promise<void>;
|
|
24
|
-
embed(texts: string[]): Promise<number[][]>;
|
|
25
|
-
/**
|
|
26
|
-
* Embed a batch with exponential backoff retry on rate limit errors.
|
|
27
|
-
*/
|
|
28
|
-
private embedBatchWithRetry;
|
|
29
|
-
/**
|
|
30
|
-
* Check if an error is a rate limit error (429 or quota exceeded).
|
|
31
|
-
*/
|
|
32
|
-
private isRateLimitError;
|
|
24
|
+
embed(texts: string[], options?: EmbedOptions): Promise<number[][]>;
|
|
33
25
|
private embedBatch;
|
|
34
26
|
embedSingle(text: string): Promise<number[]>;
|
|
35
27
|
close(): void;
|
|
@@ -8,19 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Free tier available with generous limits.
|
|
10
10
|
*/
|
|
11
|
+
import { chunk, processBatchesWithLimit, withRetry, } from './api-utils.js';
|
|
11
12
|
const GEMINI_API_BASE = 'https://generativelanguage.googleapis.com/v1beta/models';
|
|
12
13
|
const MODEL = 'gemini-embedding-001';
|
|
13
14
|
// Gemini limits: 2,048 tokens/text, 20,000 tokens/batch, 100-250 texts/batch
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const CONCURRENCY = 5; // Max concurrent API requests
|
|
18
|
-
const MAX_RETRIES = 12; // Max retry attempts on rate limit
|
|
19
|
-
const INITIAL_BACKOFF_MS = 1000; // Start at 1s
|
|
20
|
-
const MAX_BACKOFF_MS = 60000; // Cap at 60s (1 min)
|
|
21
|
-
function sleep(ms) {
|
|
22
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
23
|
-
}
|
|
15
|
+
// Chunks are ~2000 chars + context header ≈ 800-1000 tokens each
|
|
16
|
+
// 16 chunks × 1000 tokens = 16,000 tokens (safe margin under 20k limit)
|
|
17
|
+
const BATCH_SIZE = 16;
|
|
24
18
|
/**
|
|
25
19
|
* Gemini embedding provider.
|
|
26
20
|
* Uses gemini-embedding-001 model via Google's Generative AI API.
|
|
@@ -50,14 +44,14 @@ export class GeminiEmbeddingProvider {
|
|
|
50
44
|
enumerable: true,
|
|
51
45
|
configurable: true,
|
|
52
46
|
writable: true,
|
|
53
|
-
value:
|
|
47
|
+
value: undefined
|
|
54
48
|
});
|
|
55
49
|
// Callback for batch progress - (processed, total) chunks
|
|
56
50
|
Object.defineProperty(this, "onBatchProgress", {
|
|
57
51
|
enumerable: true,
|
|
58
52
|
configurable: true,
|
|
59
53
|
writable: true,
|
|
60
|
-
value:
|
|
54
|
+
value: undefined
|
|
61
55
|
});
|
|
62
56
|
// Trim the key to remove any accidental whitespace
|
|
63
57
|
this.apiKey = (apiKey ?? '').trim();
|
|
@@ -68,73 +62,29 @@ export class GeminiEmbeddingProvider {
|
|
|
68
62
|
}
|
|
69
63
|
this.initialized = true;
|
|
70
64
|
}
|
|
71
|
-
async embed(texts) {
|
|
65
|
+
async embed(texts, options) {
|
|
72
66
|
if (!this.initialized) {
|
|
73
67
|
await this.initialize();
|
|
74
68
|
}
|
|
75
69
|
if (texts.length === 0) {
|
|
76
70
|
return [];
|
|
77
71
|
}
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
results.push(...result);
|
|
93
|
-
}
|
|
94
|
-
// Report progress after concurrent group completes
|
|
95
|
-
completed += concurrentBatches.length;
|
|
96
|
-
const processed = Math.min(completed * BATCH_SIZE, texts.length);
|
|
97
|
-
this.onBatchProgress?.(processed, texts.length);
|
|
98
|
-
}
|
|
99
|
-
return results;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Embed a batch with exponential backoff retry on rate limit errors.
|
|
103
|
-
*/
|
|
104
|
-
async embedBatchWithRetry(batch) {
|
|
105
|
-
let attempt = 0;
|
|
106
|
-
let backoffMs = INITIAL_BACKOFF_MS;
|
|
107
|
-
while (true) {
|
|
108
|
-
try {
|
|
109
|
-
const result = await this.embedBatch(batch);
|
|
110
|
-
// Clear throttle message on success (if was throttling)
|
|
111
|
-
if (attempt > 0)
|
|
112
|
-
this.onThrottle?.(null);
|
|
113
|
-
return result;
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
if (this.isRateLimitError(error) && attempt < MAX_RETRIES) {
|
|
117
|
-
attempt++;
|
|
118
|
-
const secs = Math.round(backoffMs / 1000);
|
|
119
|
-
this.onThrottle?.(`Rate limited - retry ${attempt}/${MAX_RETRIES} in ${secs}s`);
|
|
120
|
-
await sleep(backoffMs);
|
|
121
|
-
backoffMs = Math.min(backoffMs * 2, MAX_BACKOFF_MS);
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Check if an error is a rate limit error (429 or quota exceeded).
|
|
131
|
-
*/
|
|
132
|
-
isRateLimitError(error) {
|
|
133
|
-
if (error instanceof Error) {
|
|
134
|
-
const msg = error.message.toLowerCase();
|
|
135
|
-
return (msg.includes('429') || msg.includes('rate') || msg.includes('quota'));
|
|
72
|
+
const batches = chunk(texts, BATCH_SIZE);
|
|
73
|
+
const callbacks = {
|
|
74
|
+
onThrottle: this.onThrottle,
|
|
75
|
+
onBatchProgress: this.onBatchProgress,
|
|
76
|
+
};
|
|
77
|
+
// Convert chunk metadata to batch metadata if provided
|
|
78
|
+
let batchMetadata;
|
|
79
|
+
if (options?.chunkMetadata) {
|
|
80
|
+
const metaBatches = chunk(options.chunkMetadata, BATCH_SIZE);
|
|
81
|
+
batchMetadata = metaBatches.map(metaBatch => ({
|
|
82
|
+
filepaths: metaBatch.map(m => m.filepath),
|
|
83
|
+
lineRanges: metaBatch.map(m => ({ start: m.startLine, end: m.endLine })),
|
|
84
|
+
sizes: metaBatch.map(m => m.size),
|
|
85
|
+
}));
|
|
136
86
|
}
|
|
137
|
-
return
|
|
87
|
+
return processBatchesWithLimit(batches, (batch, onRetrying) => withRetry(() => this.embedBatch(batch), callbacks, onRetrying), callbacks, BATCH_SIZE, batchMetadata, options?.logger, options?.chunkOffset ?? 0);
|
|
138
88
|
}
|
|
139
89
|
async embedBatch(texts) {
|
|
140
90
|
const url = `${GEMINI_API_BASE}/${MODEL}:batchEmbedContents`;
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { GeminiEmbeddingProvider } from './gemini.js';
|
|
6
6
|
export { Local4BEmbeddingProvider } from './local-4b.js';
|
|
7
|
-
export { LocalEmbeddingProvider } from './local.js';
|
|
7
|
+
export { LocalEmbeddingProvider, clearCachedPipeline } from './local.js';
|
|
8
8
|
export { MistralEmbeddingProvider } from './mistral.js';
|
|
9
|
+
export { MockEmbeddingProvider } from './mock.js';
|
|
9
10
|
export { OpenAIEmbeddingProvider } from './openai.js';
|
|
10
|
-
export { validateApiKey, type ValidationResult } from './validate.js';
|
|
11
|
-
export type { EmbeddingProvider, ModelProgressCallback } from './types.js';
|
|
11
|
+
export { validateApiKey, type ValidationResult, type ValidateApiKeyOptions, } from './validate.js';
|
|
12
|
+
export type { EmbeddingProvider, ModelProgressCallback, ChunkMetadata, EmbedOptions, } from './types.js';
|
|
13
|
+
export { CONCURRENCY, BATCH_DELAY_MS, MAX_RETRIES, INITIAL_BACKOFF_MS, MAX_BACKOFF_MS, sleep, isRateLimitError, isTransientApiError, isRetriableError, withRetry, processBatchesWithLimit, chunk, type ApiProviderCallbacks, type BatchMetadata, } from './api-utils.js';
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { GeminiEmbeddingProvider } from './gemini.js';
|
|
6
6
|
export { Local4BEmbeddingProvider } from './local-4b.js';
|
|
7
|
-
export { LocalEmbeddingProvider } from './local.js';
|
|
7
|
+
export { LocalEmbeddingProvider, clearCachedPipeline } from './local.js';
|
|
8
8
|
export { MistralEmbeddingProvider } from './mistral.js';
|
|
9
|
+
export { MockEmbeddingProvider } from './mock.js';
|
|
9
10
|
export { OpenAIEmbeddingProvider } from './openai.js';
|
|
10
|
-
export { validateApiKey } from './validate.js';
|
|
11
|
+
export { validateApiKey, } from './validate.js';
|
|
12
|
+
// Shared utilities for API-based providers
|
|
13
|
+
export { CONCURRENCY, BATCH_DELAY_MS, MAX_RETRIES, INITIAL_BACKOFF_MS, MAX_BACKOFF_MS, sleep, isRateLimitError, isTransientApiError, isRetriableError, withRetry, processBatchesWithLimit, chunk, } from './api-utils.js';
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - 32K context window
|
|
15
15
|
* - +5 MTEB points over 0.6B (69.45 vs 64.33)
|
|
16
16
|
*/
|
|
17
|
-
import type { EmbeddingProvider, ModelProgressCallback } from './types.js';
|
|
17
|
+
import type { EmbeddingProvider, ModelProgressCallback, EmbedOptions } from './types.js';
|
|
18
18
|
/**
|
|
19
19
|
* Local embedding provider using Qwen3-Embedding-4B FP32.
|
|
20
20
|
* Currently throws an error - no compatible ONNX model available.
|
|
@@ -22,7 +22,7 @@ import type { EmbeddingProvider, ModelProgressCallback } from './types.js';
|
|
|
22
22
|
export declare class Local4BEmbeddingProvider implements EmbeddingProvider {
|
|
23
23
|
readonly dimensions = 2560;
|
|
24
24
|
initialize(_onProgress?: ModelProgressCallback): Promise<void>;
|
|
25
|
-
embed(_texts: string[]): Promise<number[][]>;
|
|
25
|
+
embed(_texts: string[], _options?: EmbedOptions): Promise<number[][]>;
|
|
26
26
|
embedSingle(_text: string): Promise<number[]>;
|
|
27
27
|
close(): void;
|
|
28
28
|
}
|
|
@@ -13,16 +13,23 @@
|
|
|
13
13
|
* - No per-token costs
|
|
14
14
|
* - Data never leaves your machine
|
|
15
15
|
*/
|
|
16
|
-
import type { EmbeddingProvider, ModelProgressCallback } from './types.js';
|
|
16
|
+
import type { EmbeddingProvider, ModelProgressCallback, EmbedOptions } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* Clear the cached pipeline.
|
|
19
|
+
* Useful for tests that need to reset state between runs.
|
|
20
|
+
*/
|
|
21
|
+
export declare function clearCachedPipeline(): void;
|
|
17
22
|
/**
|
|
18
23
|
* Local embedding provider using Qwen3-Embedding-0.6B Q8.
|
|
19
24
|
*/
|
|
20
25
|
export declare class LocalEmbeddingProvider implements EmbeddingProvider {
|
|
21
26
|
readonly dimensions = 1024;
|
|
22
|
-
private extractor;
|
|
23
27
|
private initialized;
|
|
28
|
+
/** Progress callback for per-chunk updates during embedding */
|
|
29
|
+
onBatchProgress: ((processed: number, total: number) => void) | undefined;
|
|
24
30
|
initialize(onProgress?: ModelProgressCallback): Promise<void>;
|
|
25
|
-
|
|
31
|
+
private loadModel;
|
|
32
|
+
embed(texts: string[], _options?: EmbedOptions): Promise<number[][]>;
|
|
26
33
|
private embedBatch;
|
|
27
34
|
embedSingle(text: string): Promise<number[]>;
|
|
28
35
|
close(): void;
|