@promptbook/node 0.110.0-4 → 0.110.0-5

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/umd/index.umd.js CHANGED
@@ -48,7 +48,7 @@
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.110.0-4';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.110.0-5';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -23270,18 +23270,6 @@
23270
23270
  get profile() {
23271
23271
  return OPENAI_PROVIDER_PROFILE;
23272
23272
  }
23273
- /*
23274
- Note: Commenting this out to avoid circular dependency
23275
- /**
23276
- * Create (sub)tools for calling OpenAI API Assistants
23277
- *
23278
- * @param assistantId Which assistant to use
23279
- * @returns Tools for calling OpenAI API Assistants with same token
23280
- * /
23281
- public createAssistantSubtools(assistantId: string_token): OpenAiAssistantExecutionTools {
23282
- return new OpenAiAssistantExecutionTools({ ...this.options, assistantId });
23283
- }
23284
- */
23285
23273
  /**
23286
23274
  * List all available models (non dynamically)
23287
23275
  *
@@ -23316,207 +23304,6 @@
23316
23304
  }
23317
23305
  }
23318
23306
 
23319
- /**
23320
- * Execution Tools for calling OpenAI API using the Responses API (Agents)
23321
- *
23322
- * @public exported from `@promptbook/openai`
23323
- */
23324
- class OpenAiAgentExecutionTools extends OpenAiExecutionTools {
23325
- constructor(options) {
23326
- super(options);
23327
- this.vectorStoreId = options.vectorStoreId;
23328
- }
23329
- get title() {
23330
- return 'OpenAI Agent';
23331
- }
23332
- get description() {
23333
- return 'Use OpenAI Responses API (Agentic)';
23334
- }
23335
- /**
23336
- * Calls OpenAI API to use a chat model with streaming.
23337
- */
23338
- async callChatModelStream(prompt, onProgress) {
23339
- if (this.options.isVerbose) {
23340
- console.info('💬 OpenAI Agent callChatModel call', { prompt });
23341
- }
23342
- const { content, parameters, modelRequirements } = prompt;
23343
- const client = await this.getClient();
23344
- if (modelRequirements.modelVariant !== 'CHAT') {
23345
- throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
23346
- }
23347
- const rawPromptContent = templateParameters(content, {
23348
- ...parameters,
23349
- modelName: 'agent',
23350
- });
23351
- // Build input items
23352
- const input = []; // TODO: Type properly when OpenAI types are updated
23353
- // Add previous messages from thread (if any)
23354
- if ('thread' in prompt && Array.isArray(prompt.thread)) {
23355
- const previousMessages = prompt.thread.map((msg) => ({
23356
- role: msg.sender === 'assistant' ? 'assistant' : 'user',
23357
- content: msg.content,
23358
- }));
23359
- input.push(...previousMessages);
23360
- }
23361
- // Add current user message
23362
- input.push({
23363
- role: 'user',
23364
- content: rawPromptContent,
23365
- });
23366
- // Prepare tools
23367
- const tools = modelRequirements.tools ? mapToolsToOpenAi(modelRequirements.tools) : undefined;
23368
- // Add file_search if vector store is present
23369
- const agentTools = tools ? [...tools] : [];
23370
- let toolResources = undefined;
23371
- if (this.vectorStoreId) {
23372
- agentTools.push({ type: 'file_search' });
23373
- toolResources = {
23374
- file_search: {
23375
- vector_store_ids: [this.vectorStoreId],
23376
- },
23377
- };
23378
- }
23379
- // Add file_search also if knowledgeSources are present in the prompt (passed via AgentLlmExecutionTools)
23380
- if (modelRequirements.knowledgeSources &&
23381
- modelRequirements.knowledgeSources.length > 0 &&
23382
- !this.vectorStoreId) {
23383
- // Note: Vector store should have been created by AgentLlmExecutionTools and passed via options.
23384
- // If we are here, it means we have knowledge sources but no vector store ID.
23385
- // We can't easily create one here without persisting it.
23386
- console.warn('Knowledge sources provided but no vector store ID. Creating temporary vector store is not implemented in callChatModelStream.');
23387
- }
23388
- const start = $getCurrentDate();
23389
- // Construct the request
23390
- const rawRequest = {
23391
- // TODO: Type properly as OpenAI.Responses.CreateResponseParams
23392
- model: modelRequirements.modelName || 'gpt-4o',
23393
- input,
23394
- instructions: modelRequirements.systemMessage,
23395
- tools: agentTools.length > 0 ? agentTools : undefined,
23396
- tool_resources: toolResources,
23397
- store: false, // Stateless by default as we pass full history
23398
- };
23399
- if (this.options.isVerbose) {
23400
- console.info(colors__default["default"].bgWhite('rawRequest (Responses API)'), JSON.stringify(rawRequest, null, 4));
23401
- }
23402
- // Call Responses API
23403
- // Note: Using any cast because types might not be updated yet
23404
- const response = await client.responses.create(rawRequest);
23405
- if (this.options.isVerbose) {
23406
- console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(response, null, 4));
23407
- }
23408
- const complete = $getCurrentDate();
23409
- let resultContent = '';
23410
- const toolCalls = [];
23411
- // Parse output items
23412
- if (response.output) {
23413
- for (const item of response.output) {
23414
- if (item.type === 'message' && item.role === 'assistant') {
23415
- for (const contentPart of item.content) {
23416
- if (contentPart.type === 'output_text') {
23417
- // "output_text" based on migration guide, or "text"? Guide says "output_text" in example.
23418
- resultContent += contentPart.text;
23419
- }
23420
- else if (contentPart.type === 'text') {
23421
- resultContent += contentPart.text.value || contentPart.text;
23422
- }
23423
- }
23424
- }
23425
- else if (item.type === 'function_call') ;
23426
- }
23427
- }
23428
- // Use output_text helper if available (mentioned in guide)
23429
- if (response.output_text) {
23430
- resultContent = response.output_text;
23431
- }
23432
- // TODO: Handle tool calls properly (Requires clearer docs or experimentation)
23433
- onProgress({
23434
- content: resultContent,
23435
- modelName: response.model || 'agent',
23436
- timing: { start, complete },
23437
- usage: UNCERTAIN_USAGE,
23438
- rawPromptContent,
23439
- rawRequest,
23440
- rawResponse: response,
23441
- });
23442
- return exportJson({
23443
- name: 'promptResult',
23444
- message: `Result of \`OpenAiAgentExecutionTools.callChatModelStream\``,
23445
- order: [],
23446
- value: {
23447
- content: resultContent,
23448
- modelName: response.model || 'agent',
23449
- timing: { start, complete },
23450
- usage: UNCERTAIN_USAGE,
23451
- rawPromptContent,
23452
- rawRequest,
23453
- rawResponse: response,
23454
- toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
23455
- },
23456
- });
23457
- }
23458
- /**
23459
- * Creates a vector store from knowledge sources
23460
- */
23461
- static async createVectorStore(client, name, knowledgeSources) {
23462
- // Create a vector store
23463
- const vectorStore = await client.beta.vectorStores.create({
23464
- name: `${name} Knowledge Base`,
23465
- });
23466
- const vectorStoreId = vectorStore.id;
23467
- // Upload files from knowledge sources to the vector store
23468
- const fileStreams = [];
23469
- for (const source of knowledgeSources) {
23470
- try {
23471
- // Check if it's a URL
23472
- if (source.startsWith('http://') || source.startsWith('https://')) {
23473
- // Download the file
23474
- const response = await fetch(source);
23475
- if (!response.ok) {
23476
- console.error(`Failed to download ${source}: ${response.statusText}`);
23477
- continue;
23478
- }
23479
- const buffer = await response.arrayBuffer();
23480
- const filename = source.split('/').pop() || 'downloaded-file';
23481
- const blob = new Blob([buffer]);
23482
- const file = new File([blob], filename);
23483
- fileStreams.push(file);
23484
- }
23485
- else {
23486
- // Local files not supported in browser env easily, same as before
23487
- }
23488
- }
23489
- catch (error) {
23490
- console.error(`Error processing knowledge source ${source}:`, error);
23491
- }
23492
- }
23493
- // Batch upload files to the vector store
23494
- if (fileStreams.length > 0) {
23495
- try {
23496
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
23497
- files: fileStreams,
23498
- });
23499
- }
23500
- catch (error) {
23501
- console.error('Error uploading files to vector store:', error);
23502
- }
23503
- }
23504
- return vectorStoreId;
23505
- }
23506
- /**
23507
- * Discriminant for type guards
23508
- */
23509
- get discriminant() {
23510
- return 'OPEN_AI_AGENT';
23511
- }
23512
- /**
23513
- * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentExecutionTools`
23514
- */
23515
- static isOpenAiAgentExecutionTools(llmExecutionTools) {
23516
- return llmExecutionTools.discriminant === 'OPEN_AI_AGENT';
23517
- }
23518
- }
23519
-
23520
23307
  /**
23521
23308
  * Uploads files to OpenAI and returns their IDs
23522
23309
  *
@@ -23538,6 +23325,10 @@
23538
23325
  return fileIds;
23539
23326
  }
23540
23327
 
23328
+ const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
23329
+ const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
23330
+ const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
23331
+ const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
23541
23332
  /**
23542
23333
  * Execution Tools for calling OpenAI API Assistants
23543
23334
  *
@@ -23551,7 +23342,6 @@
23551
23342
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
23552
23343
  *
23553
23344
  * @public exported from `@promptbook/openai`
23554
- * @deprecated Use `OpenAiAgentExecutionTools` instead which uses the new OpenAI Responses API
23555
23345
  */
23556
23346
  class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
23557
23347
  /**
@@ -23988,111 +23778,731 @@
23988
23778
  assistantId,
23989
23779
  });
23990
23780
  }
23991
- async createNewAssistant(options) {
23992
- var _a, _b, _c;
23993
- if (!this.isCreatingNewAssistantsAllowed) {
23994
- throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
23995
- }
23996
- // await this.playground();
23997
- const { name, instructions, knowledgeSources, tools } = options;
23998
- const preparationStartedAtMs = Date.now();
23999
- const knowledgeSourcesCount = (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0;
24000
- const toolsCount = (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0;
23781
+ /**
23782
+ * Returns the per-knowledge-source download timeout in milliseconds.
23783
+ */
23784
+ getKnowledgeSourceDownloadTimeoutMs() {
23785
+ var _a;
23786
+ return (_a = this.assistantOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
23787
+ }
23788
+ /**
23789
+ * Returns the max concurrency for knowledge source uploads.
23790
+ */
23791
+ getKnowledgeSourceUploadMaxConcurrency() {
23792
+ var _a;
23793
+ return (_a = this.assistantOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
23794
+ }
23795
+ /**
23796
+ * Returns the polling interval in milliseconds for vector store uploads.
23797
+ */
23798
+ getKnowledgeSourceUploadPollIntervalMs() {
23799
+ var _a;
23800
+ return (_a = this.assistantOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
23801
+ }
23802
+ /**
23803
+ * Returns the overall upload timeout in milliseconds for vector store uploads.
23804
+ */
23805
+ getKnowledgeSourceUploadTimeoutMs() {
23806
+ var _a;
23807
+ return (_a = this.assistantOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
23808
+ }
23809
+ /**
23810
+ * Returns true if we should continue even if vector store ingestion stalls.
23811
+ */
23812
+ shouldContinueOnVectorStoreStall() {
23813
+ var _a;
23814
+ return (_a = this.assistantOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
23815
+ }
23816
+ /**
23817
+ * Returns assistant-specific options with extended settings.
23818
+ */
23819
+ get assistantOptions() {
23820
+ return this.options;
23821
+ }
23822
+ /**
23823
+ * Downloads a knowledge source URL into a File for vector store upload.
23824
+ */
23825
+ async downloadKnowledgeSourceFile(options) {
23826
+ var _a;
23827
+ const { source, timeoutMs, logLabel } = options;
23828
+ const startedAtMs = Date.now();
23829
+ const controller = new AbortController();
23830
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
24001
23831
  if (this.options.isVerbose) {
24002
- console.info('[🤰]', 'Starting OpenAI assistant creation', {
24003
- name,
24004
- knowledgeSourcesCount,
24005
- toolsCount,
24006
- instructionsLength: instructions.length,
23832
+ console.info('[🤰]', 'Downloading knowledge source', {
23833
+ source,
23834
+ timeoutMs,
23835
+ logLabel,
24007
23836
  });
24008
23837
  }
24009
- const client = await this.getClient();
24010
- let vectorStoreId;
24011
- // If knowledge sources are provided, create a vector store with them
24012
- if (knowledgeSources && knowledgeSources.length > 0) {
24013
- if (this.options.isVerbose) {
24014
- console.info('[🤰]', 'Creating vector store with knowledge sources', {
24015
- name,
24016
- knowledgeSourcesCount,
23838
+ try {
23839
+ const response = await fetch(source, { signal: controller.signal });
23840
+ const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
23841
+ if (!response.ok) {
23842
+ console.error('[🤰]', 'Failed to download knowledge source', {
23843
+ source,
23844
+ status: response.status,
23845
+ statusText: response.statusText,
23846
+ contentType,
23847
+ elapsedMs: Date.now() - startedAtMs,
23848
+ logLabel,
24017
23849
  });
23850
+ return null;
24018
23851
  }
24019
- // Create a vector store
24020
- const vectorStore = await client.beta.vectorStores.create({
24021
- name: `${name} Knowledge Base`,
24022
- });
24023
- vectorStoreId = vectorStore.id;
23852
+ const buffer = await response.arrayBuffer();
23853
+ let filename = source.split('/').pop() || 'downloaded-file';
23854
+ try {
23855
+ const url = new URL(source);
23856
+ filename = url.pathname.split('/').pop() || filename;
23857
+ }
23858
+ catch (error) {
23859
+ // Keep default filename
23860
+ }
23861
+ const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
23862
+ const elapsedMs = Date.now() - startedAtMs;
23863
+ const sizeBytes = buffer.byteLength;
24024
23864
  if (this.options.isVerbose) {
24025
- console.info('[🤰]', 'Vector store created', {
24026
- vectorStoreId,
23865
+ console.info('[🤰]', 'Downloaded knowledge source', {
23866
+ source,
23867
+ filename,
23868
+ sizeBytes,
23869
+ contentType,
23870
+ elapsedMs,
23871
+ logLabel,
24027
23872
  });
24028
23873
  }
24029
- // Upload files from knowledge sources to the vector store
24030
- const fileStreams = [];
24031
- for (const [index, source] of knowledgeSources.entries()) {
23874
+ return { file, sizeBytes, filename, elapsedMs };
23875
+ }
23876
+ catch (error) {
23877
+ assertsError(error);
23878
+ console.error('[🤰]', 'Error downloading knowledge source', {
23879
+ source,
23880
+ elapsedMs: Date.now() - startedAtMs,
23881
+ logLabel,
23882
+ error: serializeError(error),
23883
+ });
23884
+ return null;
23885
+ }
23886
+ finally {
23887
+ clearTimeout(timeoutId);
23888
+ }
23889
+ }
23890
+ /**
23891
+ * Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
23892
+ */
23893
+ async logVectorStoreFileBatchDiagnostics(options) {
23894
+ var _a, _b;
23895
+ const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
23896
+ if (reason === 'stalled' && !this.options.isVerbose) {
23897
+ return;
23898
+ }
23899
+ if (!batchId.startsWith('vsfb_')) {
23900
+ console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
23901
+ vectorStoreId,
23902
+ batchId,
23903
+ reason,
23904
+ logLabel,
23905
+ });
23906
+ return;
23907
+ }
23908
+ const fileIdToMetadata = new Map();
23909
+ for (const file of uploadedFiles) {
23910
+ fileIdToMetadata.set(file.fileId, file);
23911
+ }
23912
+ try {
23913
+ const limit = Math.min(100, Math.max(10, uploadedFiles.length));
23914
+ const batchFilesPage = await client.beta.vectorStores.fileBatches.listFiles(vectorStoreId, batchId, {
23915
+ limit,
23916
+ });
23917
+ const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
23918
+ const statusCounts = {
23919
+ in_progress: 0,
23920
+ completed: 0,
23921
+ failed: 0,
23922
+ cancelled: 0,
23923
+ };
23924
+ const errorSamples = [];
23925
+ const inProgressSamples = [];
23926
+ const batchFileIds = new Set();
23927
+ for (const file of batchFiles) {
23928
+ batchFileIds.add(file.id);
23929
+ statusCounts[file.status] = ((_b = statusCounts[file.status]) !== null && _b !== void 0 ? _b : 0) + 1;
23930
+ const metadata = fileIdToMetadata.get(file.id);
23931
+ if (file.last_error) {
23932
+ errorSamples.push({
23933
+ fileId: file.id,
23934
+ filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
23935
+ sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
23936
+ status: file.status,
23937
+ lastError: file.last_error,
23938
+ });
23939
+ }
23940
+ else if (file.status === 'in_progress' && inProgressSamples.length < 5) {
23941
+ inProgressSamples.push({
23942
+ fileId: file.id,
23943
+ filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
23944
+ sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
23945
+ });
23946
+ }
23947
+ }
23948
+ const missingSamples = uploadedFiles
23949
+ .filter((file) => !batchFileIds.has(file.fileId))
23950
+ .slice(0, 5)
23951
+ .map((file) => ({
23952
+ fileId: file.fileId,
23953
+ filename: file.filename,
23954
+ sizeBytes: file.sizeBytes,
23955
+ }));
23956
+ const vectorStore = await client.beta.vectorStores.retrieve(vectorStoreId);
23957
+ const logPayload = {
23958
+ vectorStoreId,
23959
+ batchId,
23960
+ reason,
23961
+ vectorStoreStatus: vectorStore.status,
23962
+ vectorStoreFileCounts: vectorStore.file_counts,
23963
+ vectorStoreUsageBytes: vectorStore.usage_bytes,
23964
+ batchFileCount: batchFiles.length,
23965
+ statusCounts,
23966
+ errorSamples: errorSamples.slice(0, 5),
23967
+ inProgressSamples,
23968
+ missingFileCount: uploadedFiles.length - batchFileIds.size,
23969
+ missingSamples,
23970
+ logLabel,
23971
+ };
23972
+ const logFunction = reason === 'stalled' ? console.info : console.error;
23973
+ logFunction('[🤰]', 'Vector store file batch diagnostics', logPayload);
23974
+ }
23975
+ catch (error) {
23976
+ assertsError(error);
23977
+ console.error('[🤰]', 'Vector store file batch diagnostics failed', {
23978
+ vectorStoreId,
23979
+ batchId,
23980
+ reason,
23981
+ logLabel,
23982
+ error: serializeError(error),
23983
+ });
23984
+ }
23985
+ }
23986
+ /**
23987
+ * Uploads knowledge source files to the vector store and polls until processing completes.
23988
+ */
23989
+ async uploadKnowledgeSourceFilesToVectorStore(options) {
23990
+ var _a, _b, _c, _d;
23991
+ const { client, vectorStoreId, files, totalBytes, logLabel } = options;
23992
+ const uploadStartedAtMs = Date.now();
23993
+ const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
23994
+ const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
23995
+ const uploadTimeoutMs = Math.max(1000, this.getKnowledgeSourceUploadTimeoutMs());
23996
+ if (this.options.isVerbose) {
23997
+ console.info('[🤰]', 'Uploading knowledge source files to OpenAI', {
23998
+ vectorStoreId,
23999
+ fileCount: files.length,
24000
+ totalBytes,
24001
+ maxConcurrency,
24002
+ pollIntervalMs,
24003
+ uploadTimeoutMs,
24004
+ logLabel,
24005
+ });
24006
+ }
24007
+ const fileTypeSummary = {};
24008
+ for (const file of files) {
24009
+ const filename = (_a = file.name) !== null && _a !== void 0 ? _a : '';
24010
+ const extension = filename.includes('.')
24011
+ ? (_c = (_b = filename.split('.').pop()) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : 'unknown'
24012
+ : 'unknown';
24013
+ const sizeBytes = typeof file.size === 'number' ? file.size : 0;
24014
+ const summary = (_d = fileTypeSummary[extension]) !== null && _d !== void 0 ? _d : { count: 0, totalBytes: 0 };
24015
+ summary.count += 1;
24016
+ summary.totalBytes += sizeBytes;
24017
+ fileTypeSummary[extension] = summary;
24018
+ }
24019
+ if (this.options.isVerbose) {
24020
+ console.info('[🤰]', 'Knowledge source file summary', {
24021
+ vectorStoreId,
24022
+ fileCount: files.length,
24023
+ totalBytes,
24024
+ fileTypeSummary,
24025
+ logLabel,
24026
+ });
24027
+ }
24028
+ const fileEntries = files.map((file, index) => ({ file, index }));
24029
+ const fileIterator = fileEntries.values();
24030
+ const fileIds = [];
24031
+ const uploadedFiles = [];
24032
+ const failedUploads = [];
24033
+ let uploadedCount = 0;
24034
+ const processFiles = async (iterator) => {
24035
+ var _a, _b;
24036
+ for (const { file, index } of iterator) {
24037
+ const uploadIndex = index + 1;
24038
+ const filename = file.name || `knowledge-source-${uploadIndex}`;
24039
+ const extension = filename.includes('.')
24040
+ ? (_b = (_a = filename.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'unknown'
24041
+ : 'unknown';
24042
+ const sizeBytes = typeof file.size === 'number' ? file.size : undefined;
24043
+ const fileUploadStartedAtMs = Date.now();
24044
+ if (this.options.isVerbose) {
24045
+ console.info('[🤰]', 'Uploading knowledge source file', {
24046
+ index: uploadIndex,
24047
+ total: files.length,
24048
+ filename,
24049
+ extension,
24050
+ sizeBytes,
24051
+ logLabel,
24052
+ });
24053
+ }
24032
24054
  try {
24055
+ const uploaded = await client.files.create({ file, purpose: 'assistants' });
24056
+ fileIds.push(uploaded.id);
24057
+ uploadedFiles.push({ fileId: uploaded.id, filename, sizeBytes });
24058
+ uploadedCount += 1;
24033
24059
  if (this.options.isVerbose) {
24034
- console.info('[🤰]', 'Processing knowledge source', {
24035
- index: index + 1,
24036
- total: knowledgeSources.length,
24037
- source,
24038
- sourceType: source.startsWith('http') || source.startsWith('https') ? 'url' : 'file',
24060
+ console.info('[🤰]', 'Uploaded knowledge source file', {
24061
+ index: uploadIndex,
24062
+ total: files.length,
24063
+ filename,
24064
+ sizeBytes,
24065
+ fileId: uploaded.id,
24066
+ elapsedMs: Date.now() - fileUploadStartedAtMs,
24067
+ logLabel,
24039
24068
  });
24040
24069
  }
24041
- // Check if it's a URL
24042
- if (source.startsWith('http://') || source.startsWith('https://')) {
24043
- // Download the file
24044
- const response = await fetch(source);
24045
- if (!response.ok) {
24046
- console.error(`Failed to download ${source}: ${response.statusText}`);
24047
- continue;
24048
- }
24049
- const buffer = await response.arrayBuffer();
24050
- let filename = source.split('/').pop() || 'downloaded-file';
24051
- try {
24052
- const url = new URL(source);
24053
- filename = url.pathname.split('/').pop() || filename;
24054
- }
24055
- catch (error) {
24056
- // Keep default filename
24057
- }
24058
- const blob = new Blob([buffer]);
24059
- const file = new File([blob], filename);
24060
- fileStreams.push(file);
24061
- }
24062
- else {
24063
- /*
24064
- TODO: [🐱‍🚀] Resolve problem with browser environment
24065
- // Assume it's a local file path
24066
- // Note: This will work in Node.js environment
24067
- // For browser environments, this would need different handling
24068
- const fs = await import('fs');
24069
- const fileStream = fs.createReadStream(source);
24070
- fileStreams.push(fileStream);
24071
- */
24072
- }
24073
24070
  }
24074
24071
  catch (error) {
24075
- console.error(`Error processing knowledge source ${source}:`, error);
24072
+ assertsError(error);
24073
+ const serializedError = serializeError(error);
24074
+ failedUploads.push({ index: uploadIndex, filename, error: serializedError });
24075
+ console.error('[🤰]', 'Failed to upload knowledge source file', {
24076
+ index: uploadIndex,
24077
+ total: files.length,
24078
+ filename,
24079
+ sizeBytes,
24080
+ elapsedMs: Date.now() - fileUploadStartedAtMs,
24081
+ logLabel,
24082
+ error: serializedError,
24083
+ });
24076
24084
  }
24077
24085
  }
24078
- // Batch upload files to the vector store
24079
- if (fileStreams.length > 0) {
24080
- try {
24081
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
24082
- files: fileStreams,
24086
+ };
24087
+ const workerCount = Math.min(maxConcurrency, files.length);
24088
+ const workers = Array.from({ length: workerCount }, () => processFiles(fileIterator));
24089
+ await Promise.all(workers);
24090
+ if (this.options.isVerbose) {
24091
+ console.info('[🤰]', 'Finished uploading knowledge source files', {
24092
+ vectorStoreId,
24093
+ fileCount: files.length,
24094
+ uploadedCount,
24095
+ failedCount: failedUploads.length,
24096
+ elapsedMs: Date.now() - uploadStartedAtMs,
24097
+ failedSamples: failedUploads.slice(0, 3),
24098
+ logLabel,
24099
+ });
24100
+ }
24101
+ if (fileIds.length === 0) {
24102
+ console.error('[🤰]', 'No knowledge source files were uploaded', {
24103
+ vectorStoreId,
24104
+ fileCount: files.length,
24105
+ failedCount: failedUploads.length,
24106
+ logLabel,
24107
+ });
24108
+ return null;
24109
+ }
24110
+ const batch = await client.beta.vectorStores.fileBatches.create(vectorStoreId, {
24111
+ file_ids: fileIds,
24112
+ });
24113
+ const expectedBatchId = batch.id;
24114
+ const expectedBatchIdValid = expectedBatchId.startsWith('vsfb_');
24115
+ if (!expectedBatchIdValid) {
24116
+ console.error('[🤰]', 'Vector store file batch id looks invalid', {
24117
+ vectorStoreId,
24118
+ batchId: expectedBatchId,
24119
+ batchVectorStoreId: batch.vector_store_id,
24120
+ logLabel,
24121
+ });
24122
+ }
24123
+ else if (batch.vector_store_id !== vectorStoreId) {
24124
+ console.error('[🤰]', 'Vector store file batch vector store id mismatch', {
24125
+ vectorStoreId,
24126
+ batchId: expectedBatchId,
24127
+ batchVectorStoreId: batch.vector_store_id,
24128
+ logLabel,
24129
+ });
24130
+ }
24131
+ if (this.options.isVerbose) {
24132
+ console.info('[🤰]', 'Created vector store file batch', {
24133
+ vectorStoreId,
24134
+ batchId: expectedBatchId,
24135
+ fileCount: fileIds.length,
24136
+ logLabel,
24137
+ });
24138
+ }
24139
+ const pollStartedAtMs = Date.now();
24140
+ const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
24141
+ const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
24142
+ let lastStatus;
24143
+ let lastCountsKey = '';
24144
+ let lastProgressKey = '';
24145
+ let lastLogAtMs = 0;
24146
+ let lastProgressAtMs = pollStartedAtMs;
24147
+ let lastDiagnosticsAtMs = pollStartedAtMs;
24148
+ let latestBatch = batch;
24149
+ let loggedBatchIdMismatch = false;
24150
+ let shouldPoll = true;
24151
+ while (shouldPoll) {
24152
+ latestBatch = await client.beta.vectorStores.fileBatches.retrieve(vectorStoreId, expectedBatchId);
24153
+ const counts = latestBatch.file_counts;
24154
+ const countsKey = `${counts.completed}/${counts.failed}/${counts.in_progress}/${counts.cancelled}/${counts.total}`;
24155
+ const nowMs = Date.now();
24156
+ const returnedBatchId = latestBatch.id;
24157
+ // [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
24158
+ // We only consider it a mismatch if the returned ID looks like a Batch ID.
24159
+ const batchIdMismatch = returnedBatchId !== expectedBatchId && returnedBatchId.startsWith('vsfb_');
24160
+ const diagnosticsBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
24161
+ const shouldLog = this.options.isVerbose &&
24162
+ (latestBatch.status !== lastStatus ||
24163
+ countsKey !== lastCountsKey ||
24164
+ nowMs - lastLogAtMs >= progressLogIntervalMs);
24165
+ if (batchIdMismatch && !loggedBatchIdMismatch) {
24166
+ console.error('[🤰]', 'Vector store file batch id mismatch', {
24167
+ vectorStoreId,
24168
+ expectedBatchId,
24169
+ returnedBatchId,
24170
+ status: latestBatch.status,
24171
+ fileCounts: counts,
24172
+ logLabel,
24173
+ });
24174
+ loggedBatchIdMismatch = true;
24175
+ }
24176
+ if (countsKey !== lastProgressKey) {
24177
+ lastProgressKey = countsKey;
24178
+ lastProgressAtMs = nowMs;
24179
+ }
24180
+ if (shouldLog) {
24181
+ console.info('[🤰]', 'Vector store file batch status', {
24182
+ vectorStoreId,
24183
+ batchId: expectedBatchId,
24184
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24185
+ status: latestBatch.status,
24186
+ fileCounts: counts,
24187
+ elapsedMs: nowMs - pollStartedAtMs,
24188
+ logLabel,
24189
+ });
24190
+ // [🤰] If there are in-progress files for a long time, log their details
24191
+ if (counts.in_progress > 0 && nowMs - lastProgressAtMs > VECTOR_STORE_STALL_LOG_THRESHOLD_MS) {
24192
+ await this.logVectorStoreFileBatchDiagnostics({
24193
+ client,
24194
+ vectorStoreId,
24195
+ batchId: diagnosticsBatchId,
24196
+ uploadedFiles,
24197
+ logLabel,
24198
+ reason: 'stalled',
24199
+ });
24200
+ }
24201
+ lastStatus = latestBatch.status;
24202
+ lastCountsKey = countsKey;
24203
+ lastLogAtMs = nowMs;
24204
+ }
24205
+ if (nowMs - lastProgressAtMs >= diagnosticsIntervalMs &&
24206
+ nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
24207
+ lastDiagnosticsAtMs = nowMs;
24208
+ await this.logVectorStoreFileBatchDiagnostics({
24209
+ client,
24210
+ vectorStoreId,
24211
+ batchId: diagnosticsBatchId,
24212
+ uploadedFiles,
24213
+ logLabel,
24214
+ reason: 'stalled',
24215
+ });
24216
+ }
24217
+ if (latestBatch.status === 'completed') {
24218
+ if (this.options.isVerbose) {
24219
+ console.info('[🤰]', 'Vector store file batch completed', {
24220
+ vectorStoreId,
24221
+ batchId: expectedBatchId,
24222
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24223
+ fileCounts: latestBatch.file_counts,
24224
+ elapsedMs: Date.now() - uploadStartedAtMs,
24225
+ logLabel,
24083
24226
  });
24227
+ }
24228
+ if (latestBatch.file_counts.failed > 0) {
24229
+ console.error('[🤰]', 'Vector store file batch completed with failures', {
24230
+ vectorStoreId,
24231
+ batchId: expectedBatchId,
24232
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24233
+ fileCounts: latestBatch.file_counts,
24234
+ logLabel,
24235
+ });
24236
+ await this.logVectorStoreFileBatchDiagnostics({
24237
+ client,
24238
+ vectorStoreId,
24239
+ batchId: diagnosticsBatchId,
24240
+ uploadedFiles,
24241
+ logLabel,
24242
+ reason: 'failed',
24243
+ });
24244
+ }
24245
+ shouldPoll = false;
24246
+ continue;
24247
+ }
24248
+ if (latestBatch.status === 'failed' || latestBatch.status === 'cancelled') {
24249
+ console.error('[🤰]', 'Vector store file batch did not complete', {
24250
+ vectorStoreId,
24251
+ batchId: expectedBatchId,
24252
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24253
+ status: latestBatch.status,
24254
+ fileCounts: latestBatch.file_counts,
24255
+ elapsedMs: Date.now() - uploadStartedAtMs,
24256
+ logLabel,
24257
+ });
24258
+ await this.logVectorStoreFileBatchDiagnostics({
24259
+ client,
24260
+ vectorStoreId,
24261
+ batchId: diagnosticsBatchId,
24262
+ uploadedFiles,
24263
+ logLabel,
24264
+ reason: 'failed',
24265
+ });
24266
+ shouldPoll = false;
24267
+ continue;
24268
+ }
24269
+ if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
24270
+ console.error('[🤰]', 'Timed out waiting for vector store file batch', {
24271
+ vectorStoreId,
24272
+ batchId: expectedBatchId,
24273
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24274
+ fileCounts: latestBatch.file_counts,
24275
+ elapsedMs: nowMs - pollStartedAtMs,
24276
+ uploadTimeoutMs,
24277
+ logLabel,
24278
+ });
24279
+ await this.logVectorStoreFileBatchDiagnostics({
24280
+ client,
24281
+ vectorStoreId,
24282
+ batchId: diagnosticsBatchId,
24283
+ uploadedFiles,
24284
+ logLabel,
24285
+ reason: 'timeout',
24286
+ });
24287
+ if (this.shouldContinueOnVectorStoreStall()) {
24288
+ console.warn('[🤰]', 'Continuing despite vector store timeout as requested', {
24289
+ vectorStoreId,
24290
+ logLabel,
24291
+ });
24292
+ shouldPoll = false;
24293
+ continue;
24294
+ }
24295
+ try {
24296
+ const cancelBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
24297
+ if (!cancelBatchId.startsWith('vsfb_')) {
24298
+ console.error('[🤰]', 'Skipping vector store file batch cancel (invalid batch id)', {
24299
+ vectorStoreId,
24300
+ batchId: cancelBatchId,
24301
+ logLabel,
24302
+ });
24303
+ }
24304
+ else {
24305
+ await client.beta.vectorStores.fileBatches.cancel(vectorStoreId, cancelBatchId);
24306
+ }
24084
24307
  if (this.options.isVerbose) {
24085
- console.info('[🤰]', 'Uploaded files to vector store', {
24308
+ console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
24086
24309
  vectorStoreId,
24087
- fileCount: fileStreams.length,
24310
+ batchId: batchIdMismatch && returnedBatchId.startsWith('vsfb_')
24311
+ ? returnedBatchId
24312
+ : expectedBatchId,
24313
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24314
+ logLabel,
24088
24315
  });
24089
24316
  }
24090
24317
  }
24091
24318
  catch (error) {
24092
- console.error('Error uploading files to vector store:', error);
24319
+ assertsError(error);
24320
+ console.error('[🤰]', 'Failed to cancel vector store file batch after timeout', {
24321
+ vectorStoreId,
24322
+ batchId: expectedBatchId,
24323
+ ...(batchIdMismatch ? { returnedBatchId } : {}),
24324
+ logLabel,
24325
+ error: serializeError(error),
24326
+ });
24327
+ }
24328
+ shouldPoll = false;
24329
+ continue;
24330
+ }
24331
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
24332
+ }
24333
+ return latestBatch;
24334
+ }
24335
+ /**
24336
+ * Creates a vector store and uploads knowledge sources, returning its ID.
24337
+ */
24338
+ async createVectorStoreWithKnowledgeSources(options) {
24339
+ const { client, name, knowledgeSources, logLabel } = options;
24340
+ const knowledgeSourcesCount = knowledgeSources.length;
24341
+ const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
24342
+ if (this.options.isVerbose) {
24343
+ console.info('[🤰]', 'Creating vector store with knowledge sources', {
24344
+ name,
24345
+ knowledgeSourcesCount,
24346
+ downloadTimeoutMs,
24347
+ logLabel,
24348
+ });
24349
+ }
24350
+ const vectorStore = await client.beta.vectorStores.create({
24351
+ name: `${name} Knowledge Base`,
24352
+ });
24353
+ const vectorStoreId = vectorStore.id;
24354
+ if (this.options.isVerbose) {
24355
+ console.info('[🤰]', 'Vector store created', {
24356
+ vectorStoreId,
24357
+ logLabel,
24358
+ });
24359
+ }
24360
+ const fileStreams = [];
24361
+ const skippedSources = [];
24362
+ let totalBytes = 0;
24363
+ const processingStartedAtMs = Date.now();
24364
+ for (const [index, source] of knowledgeSources.entries()) {
24365
+ try {
24366
+ const sourceType = source.startsWith('http') || source.startsWith('https') ? 'url' : 'file';
24367
+ if (this.options.isVerbose) {
24368
+ console.info('[🤰]', 'Processing knowledge source', {
24369
+ index: index + 1,
24370
+ total: knowledgeSourcesCount,
24371
+ source,
24372
+ sourceType,
24373
+ logLabel,
24374
+ });
24093
24375
  }
24376
+ // Check if it's a URL
24377
+ if (source.startsWith('http://') || source.startsWith('https://')) {
24378
+ const downloadResult = await this.downloadKnowledgeSourceFile({
24379
+ source,
24380
+ timeoutMs: downloadTimeoutMs,
24381
+ logLabel,
24382
+ });
24383
+ if (downloadResult) {
24384
+ fileStreams.push(downloadResult.file);
24385
+ totalBytes += downloadResult.sizeBytes;
24386
+ }
24387
+ else {
24388
+ skippedSources.push({ source, reason: 'download_failed' });
24389
+ }
24390
+ }
24391
+ else {
24392
+ skippedSources.push({ source, reason: 'unsupported_source_type' });
24393
+ if (this.options.isVerbose) {
24394
+ console.info('[🤰]', 'Skipping knowledge source (unsupported type)', {
24395
+ source,
24396
+ sourceType,
24397
+ logLabel,
24398
+ });
24399
+ }
24400
+ /*
24401
+ TODO: [?????] Resolve problem with browser environment
24402
+ // Assume it's a local file path
24403
+ // Note: This will work in Node.js environment
24404
+ // For browser environments, this would need different handling
24405
+ const fs = await import('fs');
24406
+ const fileStream = fs.createReadStream(source);
24407
+ fileStreams.push(fileStream);
24408
+ */
24409
+ }
24410
+ }
24411
+ catch (error) {
24412
+ assertsError(error);
24413
+ skippedSources.push({ source, reason: 'processing_error' });
24414
+ console.error('[🤰]', 'Error processing knowledge source', {
24415
+ source,
24416
+ logLabel,
24417
+ error: serializeError(error),
24418
+ });
24419
+ }
24420
+ }
24421
+ if (this.options.isVerbose) {
24422
+ console.info('[🤰]', 'Finished processing knowledge sources', {
24423
+ total: knowledgeSourcesCount,
24424
+ downloadedCount: fileStreams.length,
24425
+ skippedCount: skippedSources.length,
24426
+ totalBytes,
24427
+ elapsedMs: Date.now() - processingStartedAtMs,
24428
+ skippedSamples: skippedSources.slice(0, 3),
24429
+ logLabel,
24430
+ });
24431
+ }
24432
+ if (fileStreams.length > 0) {
24433
+ if (this.options.isVerbose) {
24434
+ console.info('[🤰]', 'Uploading files to vector store', {
24435
+ vectorStoreId,
24436
+ fileCount: fileStreams.length,
24437
+ totalBytes,
24438
+ maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
24439
+ pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
24440
+ uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
24441
+ logLabel,
24442
+ });
24443
+ }
24444
+ try {
24445
+ await this.uploadKnowledgeSourceFilesToVectorStore({
24446
+ client,
24447
+ vectorStoreId,
24448
+ files: fileStreams,
24449
+ totalBytes,
24450
+ logLabel,
24451
+ });
24452
+ }
24453
+ catch (error) {
24454
+ assertsError(error);
24455
+ console.error('[🤰]', 'Error uploading files to vector store', {
24456
+ vectorStoreId,
24457
+ logLabel,
24458
+ error: serializeError(error),
24459
+ });
24094
24460
  }
24095
24461
  }
24462
+ else if (this.options.isVerbose) {
24463
+ console.info('[🤰]', 'No knowledge source files to upload', {
24464
+ vectorStoreId,
24465
+ skippedCount: skippedSources.length,
24466
+ logLabel,
24467
+ });
24468
+ }
24469
+ return {
24470
+ vectorStoreId,
24471
+ uploadedFileCount: fileStreams.length,
24472
+ skippedCount: skippedSources.length,
24473
+ totalBytes,
24474
+ };
24475
+ }
24476
+ async createNewAssistant(options) {
24477
+ var _a, _b, _c;
24478
+ if (!this.isCreatingNewAssistantsAllowed) {
24479
+ throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
24480
+ }
24481
+ // await this.playground();
24482
+ const { name, instructions, knowledgeSources, tools } = options;
24483
+ const preparationStartedAtMs = Date.now();
24484
+ const knowledgeSourcesCount = (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0;
24485
+ const toolsCount = (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0;
24486
+ if (this.options.isVerbose) {
24487
+ console.info('[🤰]', 'Starting OpenAI assistant creation', {
24488
+ name,
24489
+ knowledgeSourcesCount,
24490
+ toolsCount,
24491
+ instructionsLength: instructions.length,
24492
+ });
24493
+ }
24494
+ const client = await this.getClient();
24495
+ let vectorStoreId;
24496
+ // If knowledge sources are provided, create a vector store with them
24497
+ if (knowledgeSources && knowledgeSources.length > 0) {
24498
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
24499
+ client,
24500
+ name,
24501
+ knowledgeSources,
24502
+ logLabel: 'assistant creation',
24503
+ });
24504
+ vectorStoreId = vectorStoreResult.vectorStoreId;
24505
+ }
24096
24506
  // Create assistant with vector store attached
24097
24507
  const assistantConfig = {
24098
24508
  name,
@@ -24158,91 +24568,14 @@
24158
24568
  const client = await this.getClient();
24159
24569
  let vectorStoreId;
24160
24570
  // If knowledge sources are provided, create a vector store with them
24161
- // TODO: [🧠] Reuse vector store creation logic from createNewAssistant
24162
24571
  if (knowledgeSources && knowledgeSources.length > 0) {
24163
- if (this.options.isVerbose) {
24164
- console.info('[🤰]', 'Creating vector store for assistant update', {
24165
- assistantId,
24166
- name,
24167
- knowledgeSourcesCount,
24168
- });
24169
- }
24170
- // Create a vector store
24171
- const vectorStore = await client.beta.vectorStores.create({
24172
- name: `${name} Knowledge Base`,
24572
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
24573
+ client,
24574
+ name: name !== null && name !== void 0 ? name : assistantId,
24575
+ knowledgeSources,
24576
+ logLabel: 'assistant update',
24173
24577
  });
24174
- vectorStoreId = vectorStore.id;
24175
- if (this.options.isVerbose) {
24176
- console.info('[🤰]', 'Vector store created for assistant update', {
24177
- vectorStoreId,
24178
- });
24179
- }
24180
- // Upload files from knowledge sources to the vector store
24181
- const fileStreams = [];
24182
- for (const [index, source] of knowledgeSources.entries()) {
24183
- try {
24184
- if (this.options.isVerbose) {
24185
- console.info('[🤰]', 'Processing knowledge source for update', {
24186
- index: index + 1,
24187
- total: knowledgeSources.length,
24188
- source,
24189
- sourceType: source.startsWith('http') || source.startsWith('https') ? 'url' : 'file',
24190
- });
24191
- }
24192
- // Check if it's a URL
24193
- if (source.startsWith('http://') || source.startsWith('https://')) {
24194
- // Download the file
24195
- const response = await fetch(source);
24196
- if (!response.ok) {
24197
- console.error(`Failed to download ${source}: ${response.statusText}`);
24198
- continue;
24199
- }
24200
- const buffer = await response.arrayBuffer();
24201
- let filename = source.split('/').pop() || 'downloaded-file';
24202
- try {
24203
- const url = new URL(source);
24204
- filename = url.pathname.split('/').pop() || filename;
24205
- }
24206
- catch (error) {
24207
- // Keep default filename
24208
- }
24209
- const blob = new Blob([buffer]);
24210
- const file = new File([blob], filename);
24211
- fileStreams.push(file);
24212
- }
24213
- else {
24214
- /*
24215
- TODO: [🐱‍🚀] Resolve problem with browser environment
24216
- // Assume it's a local file path
24217
- // Note: This will work in Node.js environment
24218
- // For browser environments, this would need different handling
24219
- const fs = await import('fs');
24220
- const fileStream = fs.createReadStream(source);
24221
- fileStreams.push(fileStream);
24222
- */
24223
- }
24224
- }
24225
- catch (error) {
24226
- console.error(`Error processing knowledge source ${source}:`, error);
24227
- }
24228
- }
24229
- // Batch upload files to the vector store
24230
- if (fileStreams.length > 0) {
24231
- try {
24232
- await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
24233
- files: fileStreams,
24234
- });
24235
- if (this.options.isVerbose) {
24236
- console.info('[🤰]', 'Uploaded files to vector store for update', {
24237
- vectorStoreId,
24238
- fileCount: fileStreams.length,
24239
- });
24240
- }
24241
- }
24242
- catch (error) {
24243
- console.error('Error uploading files to vector store:', error);
24244
- }
24245
- }
24578
+ vectorStoreId = vectorStoreResult.vectorStoreId;
24246
24579
  }
24247
24580
  const assistantUpdate = {
24248
24581
  name,
@@ -24346,7 +24679,6 @@
24346
24679
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
24347
24680
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
24348
24681
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
24349
- * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
24350
24682
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
24351
24683
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
24352
24684
  *
@@ -24509,65 +24841,7 @@
24509
24841
  }, // Cast to avoid readonly mismatch from spread
24510
24842
  };
24511
24843
  console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
24512
- if (OpenAiAgentExecutionTools.isOpenAiAgentExecutionTools(this.options.llmTools)) {
24513
- const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
24514
- const cached = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
24515
- let agentTools;
24516
- if (cached && cached.requirementsHash === requirementsHash) {
24517
- if (this.options.isVerbose) {
24518
- console.log(`1️⃣ Using cached OpenAI Agent Vector Store for agent ${this.title}...`);
24519
- }
24520
- // Create new instance with cached vectorStoreId
24521
- // We need to access options from the original tool.
24522
- // We assume isOpenAiAgentExecutionTools implies it has options we can clone.
24523
- // But protected options are not accessible.
24524
- // We can cast to access options if they were public, or use a method to clone.
24525
- // OpenAiAgentExecutionTools doesn't have a clone method.
24526
- // However, we can just assume the passed tool *might* not have the vector store yet, or we are replacing it.
24527
- // Actually, if the passed tool IS OpenAiAgentExecutionTools, we should use it as a base.
24528
- // TODO: [🧠] This is a bit hacky, accessing protected options or recreating tools.
24529
- // Ideally OpenAiAgentExecutionTools should have a method `withVectorStoreId`.
24530
- agentTools = new OpenAiAgentExecutionTools({
24531
- ...this.options.llmTools.options,
24532
- vectorStoreId: cached.vectorStoreId,
24533
- });
24534
- }
24535
- else {
24536
- if (this.options.isVerbose) {
24537
- console.log(`1️⃣ Creating/Updating OpenAI Agent Vector Store for agent ${this.title}...`);
24538
- }
24539
- let vectorStoreId;
24540
- if (modelRequirements.knowledgeSources && modelRequirements.knowledgeSources.length > 0) {
24541
- const client = await this.options.llmTools.getClient();
24542
- vectorStoreId = await OpenAiAgentExecutionTools.createVectorStore(client, this.title, modelRequirements.knowledgeSources);
24543
- }
24544
- if (vectorStoreId) {
24545
- AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
24546
- vectorStoreId,
24547
- requirementsHash,
24548
- });
24549
- }
24550
- agentTools = new OpenAiAgentExecutionTools({
24551
- ...this.options.llmTools.options,
24552
- vectorStoreId,
24553
- });
24554
- }
24555
- // Create modified chat prompt with agent system message specific to OpenAI Agent
24556
- // Note: Unlike Assistants API, Responses API expects instructions (system message) to be passed in the call.
24557
- // So we use promptWithAgentModelRequirements which has the system message prepended.
24558
- // But we need to make sure we pass knowledgeSources in modelRequirements so OpenAiAgentExecutionTools can fallback to warning if vectorStoreId is missing (though we just handled it).
24559
- const promptForAgent = {
24560
- ...promptWithAgentModelRequirements,
24561
- modelRequirements: {
24562
- ...promptWithAgentModelRequirements.modelRequirements,
24563
- knowledgeSources: modelRequirements.knowledgeSources
24564
- ? [...modelRequirements.knowledgeSources]
24565
- : undefined, // Pass knowledge sources explicitly
24566
- },
24567
- };
24568
- underlyingLlmResult = await agentTools.callChatModelStream(promptForAgent, onProgress);
24569
- }
24570
- else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
24844
+ if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
24571
24845
  // ... deprecated path ...
24572
24846
  const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
24573
24847
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
@@ -24772,7 +25046,6 @@
24772
25046
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
24773
25047
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
24774
25048
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
24775
- * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
24776
25049
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
24777
25050
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
24778
25051
  *