@utaba/deep-memory-embeddings-openai 0.6.0 → 0.8.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/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -92,10 +92,14 @@ var OpenAIEmbeddingProvider = class {
|
|
|
92
92
|
if (this._apiKey) {
|
|
93
93
|
headers["Authorization"] = `Bearer ${this._apiKey}`;
|
|
94
94
|
}
|
|
95
|
-
const
|
|
95
|
+
const requestBody = {
|
|
96
96
|
input,
|
|
97
97
|
model: this._model
|
|
98
|
-
}
|
|
98
|
+
};
|
|
99
|
+
if (this._dimensions !== void 0) {
|
|
100
|
+
requestBody.dimensions = this._dimensions;
|
|
101
|
+
}
|
|
102
|
+
const body = JSON.stringify(requestBody);
|
|
99
103
|
let response;
|
|
100
104
|
try {
|
|
101
105
|
response = await fetch(url, {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/OpenAIEmbeddingProvider.ts"],"sourcesContent":["// @utaba/deep-memory-embeddings-openai — OpenAI-compatible EmbeddingProvider\n\nexport {\n OpenAIEmbeddingProvider,\n type OpenAIEmbeddingProviderConfig,\n} from './OpenAIEmbeddingProvider.js';\n","// OpenAI-compatible EmbeddingProvider — works with vLLM, OpenAI, Azure, Ollama, etc.\n\nimport type { EmbeddingProvider } from '@utaba/deep-memory/providers';\nimport { ProviderError } from '@utaba/deep-memory';\n\n/** Configuration for the OpenAI-compatible embedding provider */\nexport interface OpenAIEmbeddingProviderConfig {\n /** Base URL of the embeddings API (e.g. \"http://localhost:8010\") */\n baseUrl: string;\n\n /** Model identifier sent in requests (e.g. \"Qwen/Qwen3-Embedding-8B\") */\n model: string;\n\n /** Dimensionality of the embedding vectors. Auto-detected on first call if omitted. */\n dimensions?: number;\n\n /** API key for authenticated endpoints. Optional for local servers. */\n apiKey?: string;\n\n /** Request timeout in milliseconds. Default: 30000 */\n timeoutMs?: number;\n\n /** Maximum texts per batch request. Default: 64 */\n maxBatchSize?: number;\n}\n\n/** Response shape from the OpenAI /v1/embeddings endpoint */\ninterface EmbeddingsResponse {\n object: string;\n data: Array<{ object: string; index: number; embedding: number[] }>;\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\nexport class OpenAIEmbeddingProvider implements EmbeddingProvider {\n private readonly _baseUrl: string;\n private readonly _model: string;\n private readonly _apiKey: string | undefined;\n private readonly _timeoutMs: number;\n private readonly _maxBatchSize: number;\n private _dimensions: number | undefined;\n\n constructor(config: OpenAIEmbeddingProviderConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this._model = config.model;\n this._apiKey = config.apiKey;\n this._timeoutMs = config.timeoutMs ?? 30_000;\n this._maxBatchSize = config.maxBatchSize ?? 64;\n this._dimensions = config.dimensions;\n }\n\n async embed(text: string): Promise<number[]> {\n const response = await this._request([text]);\n const vector = response.data[0]?.embedding;\n if (!vector) {\n throw new ProviderError(\n 'OpenAI embeddings API returned no data',\n 'Check that the model is loaded and the endpoint is responding correctly.',\n );\n }\n this._resolveDimensions(vector);\n return vector;\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const results: number[][] = [];\n\n // Chunk into maxBatchSize to avoid overloading the server\n for (let i = 0; i < texts.length; i += this._maxBatchSize) {\n const chunk = texts.slice(i, i + this._maxBatchSize);\n const response = await this._request(chunk);\n\n // Sort by index to guarantee order matches input\n const sorted = response.data.sort((a, b) => a.index - b.index);\n for (const item of sorted) {\n results.push(item.embedding);\n }\n }\n\n if (results.length > 0 && results[0]) {\n this._resolveDimensions(results[0]);\n }\n\n return results;\n }\n\n dimensions(): number {\n if (this._dimensions === undefined) {\n throw new ProviderError(\n 'Embedding dimensions not yet known. Call embed() first or provide dimensions in config.',\n 'Either set dimensions in OpenAIEmbeddingProviderConfig, or call embed() once before calling dimensions().',\n );\n }\n return this._dimensions;\n }\n\n modelId(): string {\n return this._model;\n }\n\n similarity(a: number[], b: number[]): number {\n return cosineSimilarity(a, b);\n }\n\n private async _request(input: string[]): Promise<EmbeddingsResponse> {\n const url = `${this._baseUrl}/v1/embeddings`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this._apiKey) {\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n\n const
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/OpenAIEmbeddingProvider.ts"],"sourcesContent":["// @utaba/deep-memory-embeddings-openai — OpenAI-compatible EmbeddingProvider\n\nexport {\n OpenAIEmbeddingProvider,\n type OpenAIEmbeddingProviderConfig,\n} from './OpenAIEmbeddingProvider.js';\n","// OpenAI-compatible EmbeddingProvider — works with vLLM, OpenAI, Azure, Ollama, etc.\n\nimport type { EmbeddingProvider } from '@utaba/deep-memory/providers';\nimport { ProviderError } from '@utaba/deep-memory';\n\n/** Configuration for the OpenAI-compatible embedding provider */\nexport interface OpenAIEmbeddingProviderConfig {\n /** Base URL of the embeddings API (e.g. \"http://localhost:8010\") */\n baseUrl: string;\n\n /** Model identifier sent in requests (e.g. \"Qwen/Qwen3-Embedding-8B\") */\n model: string;\n\n /** Dimensionality of the embedding vectors. Auto-detected on first call if omitted. */\n dimensions?: number;\n\n /** API key for authenticated endpoints. Optional for local servers. */\n apiKey?: string;\n\n /** Request timeout in milliseconds. Default: 30000 */\n timeoutMs?: number;\n\n /** Maximum texts per batch request. Default: 64 */\n maxBatchSize?: number;\n}\n\n/** Response shape from the OpenAI /v1/embeddings endpoint */\ninterface EmbeddingsResponse {\n object: string;\n data: Array<{ object: string; index: number; embedding: number[] }>;\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\nexport class OpenAIEmbeddingProvider implements EmbeddingProvider {\n private readonly _baseUrl: string;\n private readonly _model: string;\n private readonly _apiKey: string | undefined;\n private readonly _timeoutMs: number;\n private readonly _maxBatchSize: number;\n private _dimensions: number | undefined;\n\n constructor(config: OpenAIEmbeddingProviderConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this._model = config.model;\n this._apiKey = config.apiKey;\n this._timeoutMs = config.timeoutMs ?? 30_000;\n this._maxBatchSize = config.maxBatchSize ?? 64;\n this._dimensions = config.dimensions;\n }\n\n async embed(text: string): Promise<number[]> {\n const response = await this._request([text]);\n const vector = response.data[0]?.embedding;\n if (!vector) {\n throw new ProviderError(\n 'OpenAI embeddings API returned no data',\n 'Check that the model is loaded and the endpoint is responding correctly.',\n );\n }\n this._resolveDimensions(vector);\n return vector;\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const results: number[][] = [];\n\n // Chunk into maxBatchSize to avoid overloading the server\n for (let i = 0; i < texts.length; i += this._maxBatchSize) {\n const chunk = texts.slice(i, i + this._maxBatchSize);\n const response = await this._request(chunk);\n\n // Sort by index to guarantee order matches input\n const sorted = response.data.sort((a, b) => a.index - b.index);\n for (const item of sorted) {\n results.push(item.embedding);\n }\n }\n\n if (results.length > 0 && results[0]) {\n this._resolveDimensions(results[0]);\n }\n\n return results;\n }\n\n dimensions(): number {\n if (this._dimensions === undefined) {\n throw new ProviderError(\n 'Embedding dimensions not yet known. Call embed() first or provide dimensions in config.',\n 'Either set dimensions in OpenAIEmbeddingProviderConfig, or call embed() once before calling dimensions().',\n );\n }\n return this._dimensions;\n }\n\n modelId(): string {\n return this._model;\n }\n\n similarity(a: number[], b: number[]): number {\n return cosineSimilarity(a, b);\n }\n\n private async _request(input: string[]): Promise<EmbeddingsResponse> {\n const url = `${this._baseUrl}/v1/embeddings`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this._apiKey) {\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n\n const requestBody: { input: string[]; model: string; dimensions?: number } = {\n input,\n model: this._model,\n };\n if (this._dimensions !== undefined) {\n requestBody.dimensions = this._dimensions;\n }\n const body = JSON.stringify(requestBody);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: AbortSignal.timeout(this._timeoutMs),\n });\n } catch (error: unknown) {\n if (error instanceof DOMException && error.name === 'TimeoutError') {\n throw new ProviderError(\n `Embedding request timed out after ${this._timeoutMs}ms`,\n `Increase timeoutMs in config, reduce batch size, or check server health at ${this._baseUrl}/health`,\n );\n }\n throw new ProviderError(\n `Failed to connect to embedding server: ${error instanceof Error ? error.message : String(error)}`,\n `Check that the server is running at ${this._baseUrl}`,\n );\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new ProviderError(\n `Embedding API returned ${response.status}: ${errorBody}`,\n response.status === 401\n ? 'Check your API key in OpenAIEmbeddingProviderConfig.'\n : `Check the embedding server logs. URL: ${url}`,\n );\n }\n\n return (await response.json()) as EmbeddingsResponse;\n }\n\n private _resolveDimensions(vector: number[]): void {\n if (this._dimensions === undefined) {\n this._dimensions = vector.length;\n }\n }\n}\n\n/** Cosine similarity between two vectors */\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n const ai = a[i]!;\n const bi = b[i]!;\n dot += ai * bi;\n normA += ai * ai;\n normB += bi * bi;\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dot / denom;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -66,10 +66,14 @@ var OpenAIEmbeddingProvider = class {
|
|
|
66
66
|
if (this._apiKey) {
|
|
67
67
|
headers["Authorization"] = `Bearer ${this._apiKey}`;
|
|
68
68
|
}
|
|
69
|
-
const
|
|
69
|
+
const requestBody = {
|
|
70
70
|
input,
|
|
71
71
|
model: this._model
|
|
72
|
-
}
|
|
72
|
+
};
|
|
73
|
+
if (this._dimensions !== void 0) {
|
|
74
|
+
requestBody.dimensions = this._dimensions;
|
|
75
|
+
}
|
|
76
|
+
const body = JSON.stringify(requestBody);
|
|
73
77
|
let response;
|
|
74
78
|
try {
|
|
75
79
|
response = await fetch(url, {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/OpenAIEmbeddingProvider.ts"],"sourcesContent":["// OpenAI-compatible EmbeddingProvider — works with vLLM, OpenAI, Azure, Ollama, etc.\n\nimport type { EmbeddingProvider } from '@utaba/deep-memory/providers';\nimport { ProviderError } from '@utaba/deep-memory';\n\n/** Configuration for the OpenAI-compatible embedding provider */\nexport interface OpenAIEmbeddingProviderConfig {\n /** Base URL of the embeddings API (e.g. \"http://localhost:8010\") */\n baseUrl: string;\n\n /** Model identifier sent in requests (e.g. \"Qwen/Qwen3-Embedding-8B\") */\n model: string;\n\n /** Dimensionality of the embedding vectors. Auto-detected on first call if omitted. */\n dimensions?: number;\n\n /** API key for authenticated endpoints. Optional for local servers. */\n apiKey?: string;\n\n /** Request timeout in milliseconds. Default: 30000 */\n timeoutMs?: number;\n\n /** Maximum texts per batch request. Default: 64 */\n maxBatchSize?: number;\n}\n\n/** Response shape from the OpenAI /v1/embeddings endpoint */\ninterface EmbeddingsResponse {\n object: string;\n data: Array<{ object: string; index: number; embedding: number[] }>;\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\nexport class OpenAIEmbeddingProvider implements EmbeddingProvider {\n private readonly _baseUrl: string;\n private readonly _model: string;\n private readonly _apiKey: string | undefined;\n private readonly _timeoutMs: number;\n private readonly _maxBatchSize: number;\n private _dimensions: number | undefined;\n\n constructor(config: OpenAIEmbeddingProviderConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this._model = config.model;\n this._apiKey = config.apiKey;\n this._timeoutMs = config.timeoutMs ?? 30_000;\n this._maxBatchSize = config.maxBatchSize ?? 64;\n this._dimensions = config.dimensions;\n }\n\n async embed(text: string): Promise<number[]> {\n const response = await this._request([text]);\n const vector = response.data[0]?.embedding;\n if (!vector) {\n throw new ProviderError(\n 'OpenAI embeddings API returned no data',\n 'Check that the model is loaded and the endpoint is responding correctly.',\n );\n }\n this._resolveDimensions(vector);\n return vector;\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const results: number[][] = [];\n\n // Chunk into maxBatchSize to avoid overloading the server\n for (let i = 0; i < texts.length; i += this._maxBatchSize) {\n const chunk = texts.slice(i, i + this._maxBatchSize);\n const response = await this._request(chunk);\n\n // Sort by index to guarantee order matches input\n const sorted = response.data.sort((a, b) => a.index - b.index);\n for (const item of sorted) {\n results.push(item.embedding);\n }\n }\n\n if (results.length > 0 && results[0]) {\n this._resolveDimensions(results[0]);\n }\n\n return results;\n }\n\n dimensions(): number {\n if (this._dimensions === undefined) {\n throw new ProviderError(\n 'Embedding dimensions not yet known. Call embed() first or provide dimensions in config.',\n 'Either set dimensions in OpenAIEmbeddingProviderConfig, or call embed() once before calling dimensions().',\n );\n }\n return this._dimensions;\n }\n\n modelId(): string {\n return this._model;\n }\n\n similarity(a: number[], b: number[]): number {\n return cosineSimilarity(a, b);\n }\n\n private async _request(input: string[]): Promise<EmbeddingsResponse> {\n const url = `${this._baseUrl}/v1/embeddings`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this._apiKey) {\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n\n const
|
|
1
|
+
{"version":3,"sources":["../src/OpenAIEmbeddingProvider.ts"],"sourcesContent":["// OpenAI-compatible EmbeddingProvider — works with vLLM, OpenAI, Azure, Ollama, etc.\n\nimport type { EmbeddingProvider } from '@utaba/deep-memory/providers';\nimport { ProviderError } from '@utaba/deep-memory';\n\n/** Configuration for the OpenAI-compatible embedding provider */\nexport interface OpenAIEmbeddingProviderConfig {\n /** Base URL of the embeddings API (e.g. \"http://localhost:8010\") */\n baseUrl: string;\n\n /** Model identifier sent in requests (e.g. \"Qwen/Qwen3-Embedding-8B\") */\n model: string;\n\n /** Dimensionality of the embedding vectors. Auto-detected on first call if omitted. */\n dimensions?: number;\n\n /** API key for authenticated endpoints. Optional for local servers. */\n apiKey?: string;\n\n /** Request timeout in milliseconds. Default: 30000 */\n timeoutMs?: number;\n\n /** Maximum texts per batch request. Default: 64 */\n maxBatchSize?: number;\n}\n\n/** Response shape from the OpenAI /v1/embeddings endpoint */\ninterface EmbeddingsResponse {\n object: string;\n data: Array<{ object: string; index: number; embedding: number[] }>;\n model: string;\n usage: { prompt_tokens: number; total_tokens: number };\n}\n\nexport class OpenAIEmbeddingProvider implements EmbeddingProvider {\n private readonly _baseUrl: string;\n private readonly _model: string;\n private readonly _apiKey: string | undefined;\n private readonly _timeoutMs: number;\n private readonly _maxBatchSize: number;\n private _dimensions: number | undefined;\n\n constructor(config: OpenAIEmbeddingProviderConfig) {\n this._baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this._model = config.model;\n this._apiKey = config.apiKey;\n this._timeoutMs = config.timeoutMs ?? 30_000;\n this._maxBatchSize = config.maxBatchSize ?? 64;\n this._dimensions = config.dimensions;\n }\n\n async embed(text: string): Promise<number[]> {\n const response = await this._request([text]);\n const vector = response.data[0]?.embedding;\n if (!vector) {\n throw new ProviderError(\n 'OpenAI embeddings API returned no data',\n 'Check that the model is loaded and the endpoint is responding correctly.',\n );\n }\n this._resolveDimensions(vector);\n return vector;\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) return [];\n\n const results: number[][] = [];\n\n // Chunk into maxBatchSize to avoid overloading the server\n for (let i = 0; i < texts.length; i += this._maxBatchSize) {\n const chunk = texts.slice(i, i + this._maxBatchSize);\n const response = await this._request(chunk);\n\n // Sort by index to guarantee order matches input\n const sorted = response.data.sort((a, b) => a.index - b.index);\n for (const item of sorted) {\n results.push(item.embedding);\n }\n }\n\n if (results.length > 0 && results[0]) {\n this._resolveDimensions(results[0]);\n }\n\n return results;\n }\n\n dimensions(): number {\n if (this._dimensions === undefined) {\n throw new ProviderError(\n 'Embedding dimensions not yet known. Call embed() first or provide dimensions in config.',\n 'Either set dimensions in OpenAIEmbeddingProviderConfig, or call embed() once before calling dimensions().',\n );\n }\n return this._dimensions;\n }\n\n modelId(): string {\n return this._model;\n }\n\n similarity(a: number[], b: number[]): number {\n return cosineSimilarity(a, b);\n }\n\n private async _request(input: string[]): Promise<EmbeddingsResponse> {\n const url = `${this._baseUrl}/v1/embeddings`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (this._apiKey) {\n headers['Authorization'] = `Bearer ${this._apiKey}`;\n }\n\n const requestBody: { input: string[]; model: string; dimensions?: number } = {\n input,\n model: this._model,\n };\n if (this._dimensions !== undefined) {\n requestBody.dimensions = this._dimensions;\n }\n const body = JSON.stringify(requestBody);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: 'POST',\n headers,\n body,\n signal: AbortSignal.timeout(this._timeoutMs),\n });\n } catch (error: unknown) {\n if (error instanceof DOMException && error.name === 'TimeoutError') {\n throw new ProviderError(\n `Embedding request timed out after ${this._timeoutMs}ms`,\n `Increase timeoutMs in config, reduce batch size, or check server health at ${this._baseUrl}/health`,\n );\n }\n throw new ProviderError(\n `Failed to connect to embedding server: ${error instanceof Error ? error.message : String(error)}`,\n `Check that the server is running at ${this._baseUrl}`,\n );\n }\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => '');\n throw new ProviderError(\n `Embedding API returned ${response.status}: ${errorBody}`,\n response.status === 401\n ? 'Check your API key in OpenAIEmbeddingProviderConfig.'\n : `Check the embedding server logs. URL: ${url}`,\n );\n }\n\n return (await response.json()) as EmbeddingsResponse;\n }\n\n private _resolveDimensions(vector: number[]): void {\n if (this._dimensions === undefined) {\n this._dimensions = vector.length;\n }\n }\n}\n\n/** Cosine similarity between two vectors */\nfunction cosineSimilarity(a: number[], b: number[]): number {\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n const ai = a[i]!;\n const bi = b[i]!;\n dot += ai * bi;\n normA += ai * ai;\n normB += bi * bi;\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n return denom === 0 ? 0 : dot / denom;\n}\n"],"mappings":";AAGA,SAAS,qBAAqB;AA+BvB,IAAM,0BAAN,MAA2D;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EAER,YAAY,QAAuC;AACjD,SAAK,WAAW,OAAO,QAAQ,QAAQ,QAAQ,EAAE;AACjD,SAAK,SAAS,OAAO;AACrB,SAAK,UAAU,OAAO;AACtB,SAAK,aAAa,OAAO,aAAa;AACtC,SAAK,gBAAgB,OAAO,gBAAgB;AAC5C,SAAK,cAAc,OAAO;AAAA,EAC5B;AAAA,EAEA,MAAM,MAAM,MAAiC;AAC3C,UAAM,WAAW,MAAM,KAAK,SAAS,CAAC,IAAI,CAAC;AAC3C,UAAM,SAAS,SAAS,KAAK,CAAC,GAAG;AACjC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,SAAK,mBAAmB,MAAM;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,OAAsC;AACrD,QAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,UAAM,UAAsB,CAAC;AAG7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK,eAAe;AACzD,YAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,KAAK,aAAa;AACnD,YAAM,WAAW,MAAM,KAAK,SAAS,KAAK;AAG1C,YAAM,SAAS,SAAS,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC7D,iBAAW,QAAQ,QAAQ;AACzB,gBAAQ,KAAK,KAAK,SAAS;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,QAAQ,SAAS,KAAK,QAAQ,CAAC,GAAG;AACpC,WAAK,mBAAmB,QAAQ,CAAC,CAAC;AAAA,IACpC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAqB;AACnB,QAAI,KAAK,gBAAgB,QAAW;AAClC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAkB;AAChB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAW,GAAa,GAAqB;AAC3C,WAAO,iBAAiB,GAAG,CAAC;AAAA,EAC9B;AAAA,EAEA,MAAc,SAAS,OAA8C;AACnE,UAAM,MAAM,GAAG,KAAK,QAAQ;AAE5B,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AACA,QAAI,KAAK,SAAS;AAChB,cAAQ,eAAe,IAAI,UAAU,KAAK,OAAO;AAAA,IACnD;AAEA,UAAM,cAAuE;AAAA,MAC3E;AAAA,MACA,OAAO,KAAK;AAAA,IACd;AACA,QAAI,KAAK,gBAAgB,QAAW;AAClC,kBAAY,aAAa,KAAK;AAAA,IAChC;AACA,UAAM,OAAO,KAAK,UAAU,WAAW;AAEvC,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ,YAAY,QAAQ,KAAK,UAAU;AAAA,MAC7C,CAAC;AAAA,IACH,SAAS,OAAgB;AACvB,UAAI,iBAAiB,gBAAgB,MAAM,SAAS,gBAAgB;AAClE,cAAM,IAAI;AAAA,UACR,qCAAqC,KAAK,UAAU;AAAA,UACpD,8EAA8E,KAAK,QAAQ;AAAA,QAC7F;AAAA,MACF;AACA,YAAM,IAAI;AAAA,QACR,0CAA0C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,QAChG,uCAAuC,KAAK,QAAQ;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,YAAM,IAAI;AAAA,QACR,0BAA0B,SAAS,MAAM,KAAK,SAAS;AAAA,QACvD,SAAS,WAAW,MAChB,yDACA,yCAAyC,GAAG;AAAA,MAClD;AAAA,IACF;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEQ,mBAAmB,QAAwB;AACjD,QAAI,KAAK,gBAAgB,QAAW;AAClC,WAAK,cAAc,OAAO;AAAA,IAC5B;AAAA,EACF;AACF;AAGA,SAAS,iBAAiB,GAAa,GAAqB;AAC1D,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,UAAM,KAAK,EAAE,CAAC;AACd,UAAM,KAAK,EAAE,CAAC;AACd,WAAO,KAAK;AACZ,aAAS,KAAK;AACd,aAAS,KAAK;AAAA,EAChB;AACA,QAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;AAChD,SAAO,UAAU,IAAI,IAAI,MAAM;AACjC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utaba/deep-memory-embeddings-openai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "OpenAI-compatible embeddings provider for @utaba/deep-memory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"agents"
|
|
53
53
|
],
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@utaba/deep-memory": "0.
|
|
55
|
+
"@utaba/deep-memory": "0.8.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@types/node": "^22.0.0",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"vitest": "^4.1.2"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@utaba/deep-memory": "^0.
|
|
64
|
+
"@utaba/deep-memory": "^0.8.0"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsup",
|