@promptbook/node 0.110.0-5 → 0.110.0-7
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/esm/index.es.js +1461 -920
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/openai.index.d.ts +8 -0
- package/esm/typings/src/_packages/types.index.d.ts +4 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +0 -3
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +2 -1
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +1 -0
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +5 -0
- package/esm/typings/src/llm-providers/agent/AgentOptions.d.ts +4 -3
- package/esm/typings/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +7 -5
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +2 -1
- package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +111 -0
- package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOptions.d.ts +15 -0
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +3 -42
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +2 -33
- package/esm/typings/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +135 -0
- package/esm/typings/src/llm-providers/openai/utils/mapToolsToOpenAi.d.ts +1 -1
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +7 -3
- package/umd/index.umd.js +1464 -924
- package/umd/index.umd.js.map +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('jszip'), require('crypto'), require('crypto-js'), require('crypto-js/enc-hex'), require('@mozilla/readability'), require('jsdom'), require('showdown'), require('child_process'), require('waitasecond'), require('dotenv'), require('crypto-js/sha256'), require('rxjs'), require('moment'), require('mime-types'), require('papaparse'), require('bottleneck'), require('openai')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'jszip', 'crypto', 'crypto-js', 'crypto-js/enc-hex', '@mozilla/readability', 'jsdom', 'showdown', 'child_process', 'waitasecond', 'dotenv', 'crypto-js/sha256', 'rxjs', 'moment', 'mime-types', 'papaparse', 'bottleneck', 'openai'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim$1, global.JSZip, global.crypto, global.cryptoJs, global.hexEncoder, global.readability, global.jsdom, global.showdown, global.child_process, global.waitasecond, global.dotenv, global.sha256, global.rxjs, global.moment, global.mimeTypes, global.papaparse, global.Bottleneck, global.OpenAI));
|
|
5
|
-
})(this, (function (exports, colors, promises, path, spaceTrim$1, JSZip, crypto, cryptoJs, hexEncoder, readability, jsdom, showdown, child_process, waitasecond, dotenv, sha256, rxjs, moment, mimeTypes, papaparse, Bottleneck, OpenAI) { 'use strict';
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('colors'), require('fs/promises'), require('path'), require('spacetrim'), require('jszip'), require('crypto'), require('crypto-js'), require('crypto-js/enc-hex'), require('@mozilla/readability'), require('jsdom'), require('showdown'), require('child_process'), require('waitasecond'), require('dotenv'), require('crypto-js/sha256'), require('rxjs'), require('moment'), require('mime-types'), require('papaparse'), require('@openai/agents'), require('bottleneck'), require('openai')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'colors', 'fs/promises', 'path', 'spacetrim', 'jszip', 'crypto', 'crypto-js', 'crypto-js/enc-hex', '@mozilla/readability', 'jsdom', 'showdown', 'child_process', 'waitasecond', 'dotenv', 'crypto-js/sha256', 'rxjs', 'moment', 'mime-types', 'papaparse', '@openai/agents', 'bottleneck', 'openai'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["promptbook-node"] = {}, global.colors, global.promises, global.path, global.spaceTrim$1, global.JSZip, global.crypto, global.cryptoJs, global.hexEncoder, global.readability, global.jsdom, global.showdown, global.child_process, global.waitasecond, global.dotenv, global.sha256, global.rxjs, global.moment, global.mimeTypes, global.papaparse, global.agents, global.Bottleneck, global.OpenAI));
|
|
5
|
+
})(this, (function (exports, colors, promises, path, spaceTrim$1, JSZip, crypto, cryptoJs, hexEncoder, readability, jsdom, showdown, child_process, waitasecond, dotenv, sha256, rxjs, moment, mimeTypes, papaparse, agents, Bottleneck, OpenAI) { 'use strict';
|
|
6
6
|
|
|
7
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
8
8
|
|
|
@@ -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-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.110.0-7';
|
|
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
|
|
@@ -22372,16 +22372,11 @@
|
|
|
22372
22372
|
const openAiOptions = { ...this.options };
|
|
22373
22373
|
delete openAiOptions.isVerbose;
|
|
22374
22374
|
delete openAiOptions.userId;
|
|
22375
|
-
// Enhanced configuration
|
|
22375
|
+
// Enhanced configuration with retries and timeouts.
|
|
22376
22376
|
const enhancedOptions = {
|
|
22377
22377
|
...openAiOptions,
|
|
22378
22378
|
timeout: API_REQUEST_TIMEOUT,
|
|
22379
22379
|
maxRetries: CONNECTION_RETRIES_LIMIT,
|
|
22380
|
-
defaultHeaders: {
|
|
22381
|
-
Connection: 'keep-alive',
|
|
22382
|
-
'Keep-Alive': 'timeout=30, max=100',
|
|
22383
|
-
...openAiOptions.defaultHeaders,
|
|
22384
|
-
},
|
|
22385
22380
|
};
|
|
22386
22381
|
this.client = new OpenAI__default["default"](enhancedOptions);
|
|
22387
22382
|
}
|
|
@@ -23304,644 +23299,197 @@
|
|
|
23304
23299
|
}
|
|
23305
23300
|
}
|
|
23306
23301
|
|
|
23307
|
-
/**
|
|
23308
|
-
* Uploads files to OpenAI and returns their IDs
|
|
23309
|
-
*
|
|
23310
|
-
* @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
|
|
23311
|
-
*/
|
|
23312
|
-
async function uploadFilesToOpenAi(client, files) {
|
|
23313
|
-
const fileIds = [];
|
|
23314
|
-
for (const file of files) {
|
|
23315
|
-
// Note: OpenAI API expects a File object or a ReadStream
|
|
23316
|
-
// In browser environment, we can pass the File object directly
|
|
23317
|
-
// In Node.js environment, we might need to convert it or use a different approach
|
|
23318
|
-
// But since `Prompt.files` already contains `File` objects, we try to pass them directly
|
|
23319
|
-
const uploadedFile = await client.files.create({
|
|
23320
|
-
file: file,
|
|
23321
|
-
purpose: 'assistants',
|
|
23322
|
-
});
|
|
23323
|
-
fileIds.push(uploadedFile.id);
|
|
23324
|
-
}
|
|
23325
|
-
return fileIds;
|
|
23326
|
-
}
|
|
23327
|
-
|
|
23328
23302
|
const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
|
|
23329
23303
|
const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
|
|
23330
23304
|
const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
|
|
23331
23305
|
const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
|
|
23332
23306
|
/**
|
|
23333
|
-
*
|
|
23334
|
-
*
|
|
23335
|
-
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
23336
|
-
*
|
|
23337
|
-
* Note: [🦖] There are several different things in Promptbook:
|
|
23338
|
-
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
23339
|
-
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
23340
|
-
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
23341
|
-
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
23342
|
-
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
23307
|
+
* Base class for OpenAI execution tools that need hosted vector stores.
|
|
23343
23308
|
*
|
|
23344
23309
|
* @public exported from `@promptbook/openai`
|
|
23345
23310
|
*/
|
|
23346
|
-
class
|
|
23311
|
+
class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
|
|
23347
23312
|
/**
|
|
23348
|
-
*
|
|
23349
|
-
*
|
|
23350
|
-
* @param options which are relevant are directly passed to the OpenAI client
|
|
23313
|
+
* Returns the per-knowledge-source download timeout in milliseconds.
|
|
23351
23314
|
*/
|
|
23352
|
-
|
|
23315
|
+
getKnowledgeSourceDownloadTimeoutMs() {
|
|
23353
23316
|
var _a;
|
|
23354
|
-
|
|
23355
|
-
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
|
|
23356
|
-
}
|
|
23357
|
-
super(options);
|
|
23358
|
-
this.isCreatingNewAssistantsAllowed = false;
|
|
23359
|
-
this.assistantId = options.assistantId;
|
|
23360
|
-
this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
|
|
23361
|
-
if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
|
|
23362
|
-
throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
|
|
23363
|
-
}
|
|
23364
|
-
// <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
|
|
23365
|
-
// TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
|
|
23317
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
|
|
23366
23318
|
}
|
|
23367
|
-
|
|
23368
|
-
|
|
23319
|
+
/**
|
|
23320
|
+
* Returns the max concurrency for knowledge source uploads.
|
|
23321
|
+
*/
|
|
23322
|
+
getKnowledgeSourceUploadMaxConcurrency() {
|
|
23323
|
+
var _a;
|
|
23324
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
|
|
23369
23325
|
}
|
|
23370
|
-
|
|
23371
|
-
|
|
23326
|
+
/**
|
|
23327
|
+
* Returns the polling interval in milliseconds for vector store uploads.
|
|
23328
|
+
*/
|
|
23329
|
+
getKnowledgeSourceUploadPollIntervalMs() {
|
|
23330
|
+
var _a;
|
|
23331
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
|
|
23372
23332
|
}
|
|
23373
23333
|
/**
|
|
23374
|
-
*
|
|
23334
|
+
* Returns the overall upload timeout in milliseconds for vector store uploads.
|
|
23375
23335
|
*/
|
|
23376
|
-
|
|
23377
|
-
|
|
23336
|
+
getKnowledgeSourceUploadTimeoutMs() {
|
|
23337
|
+
var _a;
|
|
23338
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
|
|
23378
23339
|
}
|
|
23379
23340
|
/**
|
|
23380
|
-
*
|
|
23341
|
+
* Returns true if we should continue even if vector store ingestion stalls.
|
|
23381
23342
|
*/
|
|
23382
|
-
|
|
23383
|
-
var _a
|
|
23384
|
-
|
|
23385
|
-
|
|
23343
|
+
shouldContinueOnVectorStoreStall() {
|
|
23344
|
+
var _a;
|
|
23345
|
+
return (_a = this.vectorStoreOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
|
|
23346
|
+
}
|
|
23347
|
+
/**
|
|
23348
|
+
* Returns vector-store-specific options with extended settings.
|
|
23349
|
+
*/
|
|
23350
|
+
get vectorStoreOptions() {
|
|
23351
|
+
return this.options;
|
|
23352
|
+
}
|
|
23353
|
+
/**
|
|
23354
|
+
* Returns the OpenAI vector stores API surface, supporting stable and beta SDKs.
|
|
23355
|
+
*/
|
|
23356
|
+
getVectorStoresApi(client) {
|
|
23357
|
+
var _a, _b;
|
|
23358
|
+
const vectorStores = (_a = client.vectorStores) !== null && _a !== void 0 ? _a : (_b = client.beta) === null || _b === void 0 ? void 0 : _b.vectorStores;
|
|
23359
|
+
if (!vectorStores) {
|
|
23360
|
+
throw new Error('OpenAI client does not support vector stores. Please ensure you are using a compatible version of the OpenAI SDK with vector store support.');
|
|
23386
23361
|
}
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
|
|
23390
|
-
|
|
23391
|
-
|
|
23362
|
+
return vectorStores;
|
|
23363
|
+
}
|
|
23364
|
+
/**
|
|
23365
|
+
* Downloads a knowledge source URL into a File for vector store upload.
|
|
23366
|
+
*/
|
|
23367
|
+
async downloadKnowledgeSourceFile(options) {
|
|
23368
|
+
var _a;
|
|
23369
|
+
const { source, timeoutMs, logLabel } = options;
|
|
23370
|
+
const startedAtMs = Date.now();
|
|
23371
|
+
const controller = new AbortController();
|
|
23372
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
23373
|
+
if (this.options.isVerbose) {
|
|
23374
|
+
console.info('[🤰]', 'Downloading knowledge source', {
|
|
23375
|
+
source,
|
|
23376
|
+
timeoutMs,
|
|
23377
|
+
logLabel,
|
|
23378
|
+
});
|
|
23392
23379
|
}
|
|
23393
|
-
|
|
23394
|
-
|
|
23395
|
-
|
|
23396
|
-
|
|
23380
|
+
try {
|
|
23381
|
+
const response = await fetch(source, { signal: controller.signal });
|
|
23382
|
+
const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
|
|
23383
|
+
if (!response.ok) {
|
|
23384
|
+
console.error('[🤰]', 'Failed to download knowledge source', {
|
|
23385
|
+
source,
|
|
23386
|
+
status: response.status,
|
|
23387
|
+
statusText: response.statusText,
|
|
23388
|
+
contentType,
|
|
23389
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
23390
|
+
logLabel,
|
|
23391
|
+
});
|
|
23392
|
+
return null;
|
|
23393
|
+
}
|
|
23394
|
+
const buffer = await response.arrayBuffer();
|
|
23395
|
+
let filename = source.split('/').pop() || 'downloaded-file';
|
|
23396
|
+
try {
|
|
23397
|
+
const url = new URL(source);
|
|
23398
|
+
filename = url.pathname.split('/').pop() || filename;
|
|
23399
|
+
}
|
|
23400
|
+
catch (error) {
|
|
23401
|
+
// Keep default filename
|
|
23402
|
+
}
|
|
23403
|
+
const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
|
|
23404
|
+
const elapsedMs = Date.now() - startedAtMs;
|
|
23405
|
+
const sizeBytes = buffer.byteLength;
|
|
23406
|
+
if (this.options.isVerbose) {
|
|
23407
|
+
console.info('[🤰]', 'Downloaded knowledge source', {
|
|
23408
|
+
source,
|
|
23409
|
+
filename,
|
|
23410
|
+
sizeBytes,
|
|
23411
|
+
contentType,
|
|
23412
|
+
elapsedMs,
|
|
23413
|
+
logLabel,
|
|
23414
|
+
});
|
|
23397
23415
|
}
|
|
23416
|
+
return { file, sizeBytes, filename, elapsedMs };
|
|
23398
23417
|
}
|
|
23399
|
-
|
|
23400
|
-
|
|
23401
|
-
|
|
23402
|
-
|
|
23403
|
-
|
|
23404
|
-
|
|
23405
|
-
|
|
23406
|
-
|
|
23407
|
-
|
|
23408
|
-
// <- Note: [🧆]
|
|
23409
|
-
} as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
|
|
23410
|
-
|
|
23411
|
-
if (format === 'JSON') {
|
|
23412
|
-
modelSettings.response_format = {
|
|
23413
|
-
type: 'json_object',
|
|
23414
|
-
};
|
|
23418
|
+
catch (error) {
|
|
23419
|
+
assertsError(error);
|
|
23420
|
+
console.error('[🤰]', 'Error downloading knowledge source', {
|
|
23421
|
+
source,
|
|
23422
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
23423
|
+
logLabel,
|
|
23424
|
+
error: serializeError(error),
|
|
23425
|
+
});
|
|
23426
|
+
return null;
|
|
23415
23427
|
}
|
|
23416
|
-
|
|
23417
|
-
|
|
23418
|
-
// > 'response_format' of type 'json_object' is not supported with this model.
|
|
23419
|
-
const rawPromptContent = templateParameters(content, {
|
|
23420
|
-
...parameters,
|
|
23421
|
-
modelName: 'assistant',
|
|
23422
|
-
// <- [🧠] What is the best value here
|
|
23423
|
-
});
|
|
23424
|
-
// Build thread messages: include previous thread messages + current user message
|
|
23425
|
-
const threadMessages = [];
|
|
23426
|
-
// TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
|
|
23427
|
-
// Add previous messages from thread (if any)
|
|
23428
|
-
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
23429
|
-
const previousMessages = prompt.thread.map((msg) => ({
|
|
23430
|
-
role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
|
|
23431
|
-
content: msg.content,
|
|
23432
|
-
}));
|
|
23433
|
-
threadMessages.push(...previousMessages);
|
|
23428
|
+
finally {
|
|
23429
|
+
clearTimeout(timeoutId);
|
|
23434
23430
|
}
|
|
23435
|
-
|
|
23436
|
-
|
|
23437
|
-
|
|
23438
|
-
|
|
23439
|
-
|
|
23440
|
-
|
|
23441
|
-
|
|
23442
|
-
|
|
23443
|
-
|
|
23444
|
-
tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
|
|
23445
|
-
}));
|
|
23431
|
+
}
|
|
23432
|
+
/**
|
|
23433
|
+
* Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
|
|
23434
|
+
*/
|
|
23435
|
+
async logVectorStoreFileBatchDiagnostics(options) {
|
|
23436
|
+
var _a, _b, _c, _d, _e;
|
|
23437
|
+
const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
|
|
23438
|
+
if (reason === 'stalled' && !this.options.isVerbose) {
|
|
23439
|
+
return;
|
|
23446
23440
|
}
|
|
23447
|
-
|
|
23448
|
-
|
|
23449
|
-
|
|
23450
|
-
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
// because streaming doesn't support tool execution flow properly
|
|
23454
|
-
if (hasTools) {
|
|
23455
|
-
onProgress({
|
|
23456
|
-
content: '',
|
|
23457
|
-
modelName: 'assistant',
|
|
23458
|
-
timing: { start, complete: $getCurrentDate() },
|
|
23459
|
-
usage: UNCERTAIN_USAGE,
|
|
23460
|
-
rawPromptContent,
|
|
23461
|
-
rawRequest: null,
|
|
23462
|
-
rawResponse: null,
|
|
23441
|
+
if (!batchId.startsWith('vsfb_')) {
|
|
23442
|
+
console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
|
|
23443
|
+
vectorStoreId,
|
|
23444
|
+
batchId,
|
|
23445
|
+
reason,
|
|
23446
|
+
logLabel,
|
|
23463
23447
|
});
|
|
23464
|
-
|
|
23465
|
-
|
|
23466
|
-
|
|
23467
|
-
|
|
23468
|
-
|
|
23469
|
-
|
|
23470
|
-
|
|
23471
|
-
|
|
23472
|
-
|
|
23473
|
-
|
|
23474
|
-
|
|
23475
|
-
|
|
23476
|
-
|
|
23477
|
-
const
|
|
23478
|
-
const
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
|
|
23484
|
-
const toolOutputs = [];
|
|
23485
|
-
for (const toolCall of toolCalls) {
|
|
23486
|
-
if (toolCall.type === 'function') {
|
|
23487
|
-
const functionName = toolCall.function.name;
|
|
23488
|
-
const functionArgs = JSON.parse(toolCall.function.arguments);
|
|
23489
|
-
const calledAt = $getCurrentDate();
|
|
23490
|
-
if (toolCall.id) {
|
|
23491
|
-
toolCallStartedAt.set(toolCall.id, calledAt);
|
|
23492
|
-
}
|
|
23493
|
-
onProgress({
|
|
23494
|
-
content: '',
|
|
23495
|
-
modelName: 'assistant',
|
|
23496
|
-
timing: { start, complete: $getCurrentDate() },
|
|
23497
|
-
usage: UNCERTAIN_USAGE,
|
|
23498
|
-
rawPromptContent,
|
|
23499
|
-
rawRequest: null,
|
|
23500
|
-
rawResponse: null,
|
|
23501
|
-
toolCalls: [
|
|
23502
|
-
{
|
|
23503
|
-
name: functionName,
|
|
23504
|
-
arguments: toolCall.function.arguments,
|
|
23505
|
-
result: '',
|
|
23506
|
-
rawToolCall: toolCall,
|
|
23507
|
-
createdAt: calledAt,
|
|
23508
|
-
},
|
|
23509
|
-
],
|
|
23510
|
-
});
|
|
23511
|
-
if (this.options.isVerbose) {
|
|
23512
|
-
console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
|
|
23513
|
-
}
|
|
23514
|
-
// Get execution tools for script execution
|
|
23515
|
-
const executionTools = this.options
|
|
23516
|
-
.executionTools;
|
|
23517
|
-
if (!executionTools || !executionTools.script) {
|
|
23518
|
-
throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
|
|
23519
|
-
}
|
|
23520
|
-
// TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
|
|
23521
|
-
const scriptTools = Array.isArray(executionTools.script)
|
|
23522
|
-
? executionTools.script
|
|
23523
|
-
: [executionTools.script];
|
|
23524
|
-
let functionResponse;
|
|
23525
|
-
let errors;
|
|
23526
|
-
try {
|
|
23527
|
-
const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
|
|
23528
|
-
functionResponse = await scriptTool.execute({
|
|
23529
|
-
scriptLanguage: 'javascript',
|
|
23530
|
-
script: `
|
|
23531
|
-
const args = ${JSON.stringify(functionArgs)};
|
|
23532
|
-
return await ${functionName}(args);
|
|
23533
|
-
`,
|
|
23534
|
-
parameters: prompt.parameters,
|
|
23535
|
-
});
|
|
23536
|
-
if (this.options.isVerbose) {
|
|
23537
|
-
console.info(`✅ Tool ${functionName} executed:`, functionResponse);
|
|
23538
|
-
}
|
|
23539
|
-
}
|
|
23540
|
-
catch (error) {
|
|
23541
|
-
assertsError(error);
|
|
23542
|
-
const serializedError = serializeError(error);
|
|
23543
|
-
errors = [serializedError];
|
|
23544
|
-
functionResponse = spaceTrim__default["default"]((block) => `
|
|
23545
|
-
|
|
23546
|
-
The invoked tool \`${functionName}\` failed with error:
|
|
23547
|
-
|
|
23548
|
-
\`\`\`json
|
|
23549
|
-
${block(JSON.stringify(serializedError, null, 4))}
|
|
23550
|
-
\`\`\`
|
|
23551
|
-
|
|
23552
|
-
`);
|
|
23553
|
-
console.error(colors__default["default"].bgRed(`❌ Error executing tool ${functionName}:`));
|
|
23554
|
-
console.error(error);
|
|
23555
|
-
}
|
|
23556
|
-
toolOutputs.push({
|
|
23557
|
-
tool_call_id: toolCall.id,
|
|
23558
|
-
output: functionResponse,
|
|
23559
|
-
});
|
|
23560
|
-
completedToolCalls.push({
|
|
23561
|
-
name: functionName,
|
|
23562
|
-
arguments: toolCall.function.arguments,
|
|
23563
|
-
result: functionResponse,
|
|
23564
|
-
rawToolCall: toolCall,
|
|
23565
|
-
createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
|
|
23566
|
-
errors,
|
|
23567
|
-
});
|
|
23568
|
-
}
|
|
23569
|
-
}
|
|
23570
|
-
// Submit tool outputs
|
|
23571
|
-
run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
|
|
23572
|
-
tool_outputs: toolOutputs,
|
|
23573
|
-
});
|
|
23574
|
-
}
|
|
23575
|
-
else {
|
|
23576
|
-
// Wait a bit before polling again
|
|
23577
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
23578
|
-
run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
|
|
23579
|
-
}
|
|
23580
|
-
}
|
|
23581
|
-
if (run.status !== 'completed') {
|
|
23582
|
-
throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
|
|
23583
|
-
}
|
|
23584
|
-
// Get messages from the thread
|
|
23585
|
-
const messages = await client.beta.threads.messages.list(run.thread_id);
|
|
23586
|
-
const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
|
|
23587
|
-
if (assistantMessages.length === 0) {
|
|
23588
|
-
throw new PipelineExecutionError('No assistant messages found after run completion');
|
|
23589
|
-
}
|
|
23590
|
-
const lastMessage = assistantMessages[0];
|
|
23591
|
-
const textContent = lastMessage.content.find((c) => c.type === 'text');
|
|
23592
|
-
if (!textContent || textContent.type !== 'text') {
|
|
23593
|
-
throw new PipelineExecutionError('No text content in assistant response');
|
|
23594
|
-
}
|
|
23595
|
-
complete = $getCurrentDate();
|
|
23596
|
-
const resultContent = textContent.text.value;
|
|
23597
|
-
const usage = UNCERTAIN_USAGE;
|
|
23598
|
-
// Progress callback with final result
|
|
23599
|
-
const finalChunk = {
|
|
23600
|
-
content: resultContent,
|
|
23601
|
-
modelName: 'assistant',
|
|
23602
|
-
timing: { start, complete },
|
|
23603
|
-
usage,
|
|
23604
|
-
rawPromptContent,
|
|
23605
|
-
rawRequest,
|
|
23606
|
-
rawResponse: { run, messages: messages.data },
|
|
23607
|
-
toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
|
|
23608
|
-
};
|
|
23609
|
-
onProgress(finalChunk);
|
|
23610
|
-
return exportJson({
|
|
23611
|
-
name: 'promptResult',
|
|
23612
|
-
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
|
|
23613
|
-
order: [],
|
|
23614
|
-
value: finalChunk,
|
|
23615
|
-
});
|
|
23616
|
-
}
|
|
23617
|
-
// Streaming mode (without tools)
|
|
23618
|
-
const rawRequest = {
|
|
23619
|
-
// TODO: [👨👨👧👧] ...modelSettings,
|
|
23620
|
-
// TODO: [👨👨👧👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
|
|
23621
|
-
assistant_id: this.assistantId,
|
|
23622
|
-
thread: {
|
|
23623
|
-
messages: threadMessages,
|
|
23624
|
-
},
|
|
23625
|
-
tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
|
|
23626
|
-
// <- TODO: Add user identification here> user: this.options.user,
|
|
23627
|
-
};
|
|
23628
|
-
if (this.options.isVerbose) {
|
|
23629
|
-
console.info(colors__default["default"].bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
|
|
23630
|
-
}
|
|
23631
|
-
const stream = await client.beta.threads.createAndRunStream(rawRequest);
|
|
23632
|
-
stream.on('connect', () => {
|
|
23633
|
-
if (this.options.isVerbose) {
|
|
23634
|
-
console.info('connect', stream.currentEvent);
|
|
23635
|
-
}
|
|
23636
|
-
});
|
|
23637
|
-
stream.on('textDelta', (textDelta, snapshot) => {
|
|
23638
|
-
if (this.options.isVerbose && textDelta.value) {
|
|
23639
|
-
console.info('textDelta', textDelta.value);
|
|
23640
|
-
}
|
|
23641
|
-
const chunk = {
|
|
23642
|
-
content: snapshot.value,
|
|
23643
|
-
modelName: 'assistant',
|
|
23644
|
-
timing: {
|
|
23645
|
-
start,
|
|
23646
|
-
complete: $getCurrentDate(),
|
|
23647
|
-
},
|
|
23648
|
-
usage: UNCERTAIN_USAGE,
|
|
23649
|
-
rawPromptContent,
|
|
23650
|
-
rawRequest,
|
|
23651
|
-
rawResponse: snapshot,
|
|
23652
|
-
};
|
|
23653
|
-
onProgress(chunk);
|
|
23654
|
-
});
|
|
23655
|
-
stream.on('messageCreated', (message) => {
|
|
23656
|
-
if (this.options.isVerbose) {
|
|
23657
|
-
console.info('messageCreated', message);
|
|
23658
|
-
}
|
|
23659
|
-
});
|
|
23660
|
-
stream.on('messageDone', (message) => {
|
|
23661
|
-
if (this.options.isVerbose) {
|
|
23662
|
-
console.info('messageDone', message);
|
|
23663
|
-
}
|
|
23664
|
-
});
|
|
23665
|
-
// TODO: [🐱🚀] Handle tool calls in assistants
|
|
23666
|
-
// Note: OpenAI Assistant streaming with tool calls requires special handling.
|
|
23667
|
-
// The stream will pause when a tool call is needed, and we need to:
|
|
23668
|
-
// 1. Wait for the run to reach 'requires_action' status
|
|
23669
|
-
// 2. Execute the tool calls
|
|
23670
|
-
// 3. Submit tool outputs via a separate API call (not on the stream)
|
|
23671
|
-
// 4. Continue the run
|
|
23672
|
-
// This requires switching to non-streaming mode or using the Runs API directly.
|
|
23673
|
-
// For now, tools with assistants should use the non-streaming chat completions API instead.
|
|
23674
|
-
const rawResponse = await stream.finalMessages();
|
|
23675
|
-
if (this.options.isVerbose) {
|
|
23676
|
-
console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
23677
|
-
}
|
|
23678
|
-
if (rawResponse.length !== 1) {
|
|
23679
|
-
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
|
|
23680
|
-
}
|
|
23681
|
-
if (rawResponse[0].content.length !== 1) {
|
|
23682
|
-
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
|
|
23683
|
-
}
|
|
23684
|
-
if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
|
|
23685
|
-
throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.type} finalMessages content type from OpenAI`);
|
|
23686
|
-
}
|
|
23687
|
-
let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
|
|
23688
|
-
// Process annotations to replace file IDs with filenames
|
|
23689
|
-
if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
|
|
23690
|
-
const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
|
|
23691
|
-
// Map to store file ID -> filename to avoid duplicate requests
|
|
23692
|
-
const fileIdToName = new Map();
|
|
23693
|
-
for (const annotation of annotations) {
|
|
23694
|
-
if (annotation.type === 'file_citation') {
|
|
23695
|
-
const fileId = annotation.file_citation.file_id;
|
|
23696
|
-
let filename = fileIdToName.get(fileId);
|
|
23697
|
-
if (!filename) {
|
|
23698
|
-
try {
|
|
23699
|
-
const file = await client.files.retrieve(fileId);
|
|
23700
|
-
filename = file.filename;
|
|
23701
|
-
fileIdToName.set(fileId, filename);
|
|
23702
|
-
}
|
|
23703
|
-
catch (error) {
|
|
23704
|
-
console.error(`Failed to retrieve file info for ${fileId}`, error);
|
|
23705
|
-
// Fallback to "Source" or keep original if fetch fails
|
|
23706
|
-
filename = 'Source';
|
|
23707
|
-
}
|
|
23708
|
-
}
|
|
23709
|
-
if (filename && resultContent) {
|
|
23710
|
-
// Replace the citation marker with filename
|
|
23711
|
-
// Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
|
|
23712
|
-
// Note: annotation.text contains the exact marker like 【4:0†source】
|
|
23713
|
-
const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
|
|
23714
|
-
resultContent = resultContent.replace(annotation.text, newText);
|
|
23715
|
-
}
|
|
23716
|
-
}
|
|
23717
|
-
}
|
|
23718
|
-
}
|
|
23719
|
-
// eslint-disable-next-line prefer-const
|
|
23720
|
-
complete = $getCurrentDate();
|
|
23721
|
-
const usage = UNCERTAIN_USAGE;
|
|
23722
|
-
// <- TODO: [🥘] Compute real usage for assistant
|
|
23723
|
-
// ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
|
|
23724
|
-
if (resultContent === null) {
|
|
23725
|
-
throw new PipelineExecutionError('No response message from OpenAI');
|
|
23726
|
-
}
|
|
23727
|
-
return exportJson({
|
|
23728
|
-
name: 'promptResult',
|
|
23729
|
-
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
23730
|
-
order: [],
|
|
23731
|
-
value: {
|
|
23732
|
-
content: resultContent,
|
|
23733
|
-
modelName: 'assistant',
|
|
23734
|
-
// <- TODO: [🥘] Detect used model in assistant
|
|
23735
|
-
// ?> model: rawResponse.model || modelName,
|
|
23736
|
-
timing: {
|
|
23737
|
-
start,
|
|
23738
|
-
complete,
|
|
23739
|
-
},
|
|
23740
|
-
usage,
|
|
23741
|
-
rawPromptContent,
|
|
23742
|
-
rawRequest,
|
|
23743
|
-
rawResponse,
|
|
23744
|
-
// <- [🗯]
|
|
23745
|
-
},
|
|
23746
|
-
});
|
|
23747
|
-
}
|
|
23748
|
-
/*
|
|
23749
|
-
public async playground() {
|
|
23750
|
-
const client = await this.getClient();
|
|
23751
|
-
|
|
23752
|
-
// List all assistants
|
|
23753
|
-
const assistants = await client.beta.assistants.list();
|
|
23754
|
-
|
|
23755
|
-
// Get details of a specific assistant
|
|
23756
|
-
const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
|
|
23757
|
-
const assistant = await client.beta.assistants.retrieve(assistantId);
|
|
23758
|
-
|
|
23759
|
-
// Update an assistant
|
|
23760
|
-
const updatedAssistant = await client.beta.assistants.update(assistantId, {
|
|
23761
|
-
name: assistant.name + '(M)',
|
|
23762
|
-
description: 'Updated description via Promptbook',
|
|
23763
|
-
metadata: {
|
|
23764
|
-
[Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
|
|
23765
|
-
},
|
|
23766
|
-
});
|
|
23767
|
-
|
|
23768
|
-
await forEver();
|
|
23769
|
-
}
|
|
23770
|
-
*/
|
|
23771
|
-
/**
|
|
23772
|
-
* Get an existing assistant tool wrapper
|
|
23773
|
-
*/
|
|
23774
|
-
getAssistant(assistantId) {
|
|
23775
|
-
return new OpenAiAssistantExecutionTools({
|
|
23776
|
-
...this.options,
|
|
23777
|
-
isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
|
|
23778
|
-
assistantId,
|
|
23779
|
-
});
|
|
23780
|
-
}
|
|
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);
|
|
23831
|
-
if (this.options.isVerbose) {
|
|
23832
|
-
console.info('[🤰]', 'Downloading knowledge source', {
|
|
23833
|
-
source,
|
|
23834
|
-
timeoutMs,
|
|
23835
|
-
logLabel,
|
|
23836
|
-
});
|
|
23837
|
-
}
|
|
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,
|
|
23849
|
-
});
|
|
23850
|
-
return null;
|
|
23851
|
-
}
|
|
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;
|
|
23864
|
-
if (this.options.isVerbose) {
|
|
23865
|
-
console.info('[🤰]', 'Downloaded knowledge source', {
|
|
23866
|
-
source,
|
|
23867
|
-
filename,
|
|
23868
|
-
sizeBytes,
|
|
23869
|
-
contentType,
|
|
23870
|
-
elapsedMs,
|
|
23871
|
-
logLabel,
|
|
23872
|
-
});
|
|
23873
|
-
}
|
|
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,
|
|
23448
|
+
return;
|
|
23449
|
+
}
|
|
23450
|
+
const fileIdToMetadata = new Map();
|
|
23451
|
+
for (const file of uploadedFiles) {
|
|
23452
|
+
fileIdToMetadata.set(file.fileId, file);
|
|
23453
|
+
}
|
|
23454
|
+
try {
|
|
23455
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
23456
|
+
const limit = Math.min(100, Math.max(10, uploadedFiles.length));
|
|
23457
|
+
const batchFilesPage = await vectorStores.fileBatches.listFiles(batchId, {
|
|
23458
|
+
vector_store_id: vectorStoreId,
|
|
23459
|
+
limit,
|
|
23460
|
+
});
|
|
23461
|
+
const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
|
|
23462
|
+
const statusCounts = {
|
|
23463
|
+
in_progress: 0,
|
|
23464
|
+
completed: 0,
|
|
23465
|
+
failed: 0,
|
|
23466
|
+
cancelled: 0,
|
|
23923
23467
|
};
|
|
23924
23468
|
const errorSamples = [];
|
|
23925
23469
|
const inProgressSamples = [];
|
|
23926
23470
|
const batchFileIds = new Set();
|
|
23927
23471
|
for (const file of batchFiles) {
|
|
23928
|
-
|
|
23929
|
-
statusCounts[
|
|
23930
|
-
const
|
|
23931
|
-
|
|
23472
|
+
const status = (_b = file.status) !== null && _b !== void 0 ? _b : 'unknown';
|
|
23473
|
+
statusCounts[status] = ((_c = statusCounts[status]) !== null && _c !== void 0 ? _c : 0) + 1;
|
|
23474
|
+
const vectorStoreFileId = file.id;
|
|
23475
|
+
const uploadedFileId = (_d = file.file_id) !== null && _d !== void 0 ? _d : file.fileId;
|
|
23476
|
+
const fileId = uploadedFileId !== null && uploadedFileId !== void 0 ? uploadedFileId : vectorStoreFileId;
|
|
23477
|
+
batchFileIds.add(fileId);
|
|
23478
|
+
const metadata = fileIdToMetadata.get(fileId);
|
|
23479
|
+
if (status === 'failed') {
|
|
23932
23480
|
errorSamples.push({
|
|
23933
|
-
fileId
|
|
23481
|
+
fileId,
|
|
23482
|
+
status,
|
|
23483
|
+
error: (_e = file.last_error) === null || _e === void 0 ? void 0 : _e.message,
|
|
23934
23484
|
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
23935
|
-
|
|
23936
|
-
status: file.status,
|
|
23937
|
-
lastError: file.last_error,
|
|
23485
|
+
vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
|
|
23938
23486
|
});
|
|
23939
23487
|
}
|
|
23940
|
-
|
|
23488
|
+
if (status === 'in_progress') {
|
|
23941
23489
|
inProgressSamples.push({
|
|
23942
|
-
fileId
|
|
23490
|
+
fileId,
|
|
23943
23491
|
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
23944
|
-
|
|
23492
|
+
vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
|
|
23945
23493
|
});
|
|
23946
23494
|
}
|
|
23947
23495
|
}
|
|
@@ -23953,7 +23501,7 @@
|
|
|
23953
23501
|
filename: file.filename,
|
|
23954
23502
|
sizeBytes: file.sizeBytes,
|
|
23955
23503
|
}));
|
|
23956
|
-
const vectorStore = await
|
|
23504
|
+
const vectorStore = await vectorStores.retrieve(vectorStoreId);
|
|
23957
23505
|
const logPayload = {
|
|
23958
23506
|
vectorStoreId,
|
|
23959
23507
|
batchId,
|
|
@@ -23987,8 +23535,9 @@
|
|
|
23987
23535
|
* Uploads knowledge source files to the vector store and polls until processing completes.
|
|
23988
23536
|
*/
|
|
23989
23537
|
async uploadKnowledgeSourceFilesToVectorStore(options) {
|
|
23990
|
-
var _a, _b, _c, _d;
|
|
23538
|
+
var _a, _b, _c, _d, _e, _f;
|
|
23991
23539
|
const { client, vectorStoreId, files, totalBytes, logLabel } = options;
|
|
23540
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
23992
23541
|
const uploadStartedAtMs = Date.now();
|
|
23993
23542
|
const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
|
|
23994
23543
|
const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
|
|
@@ -24105,373 +23654,1288 @@
|
|
|
24105
23654
|
failedCount: failedUploads.length,
|
|
24106
23655
|
logLabel,
|
|
24107
23656
|
});
|
|
24108
|
-
return null;
|
|
23657
|
+
return null;
|
|
23658
|
+
}
|
|
23659
|
+
const batch = await vectorStores.fileBatches.create(vectorStoreId, {
|
|
23660
|
+
file_ids: fileIds,
|
|
23661
|
+
});
|
|
23662
|
+
const expectedBatchId = batch.id;
|
|
23663
|
+
const expectedBatchIdValid = expectedBatchId.startsWith('vsfb_');
|
|
23664
|
+
if (!expectedBatchIdValid) {
|
|
23665
|
+
console.error('[🤰]', 'Vector store file batch id looks invalid', {
|
|
23666
|
+
vectorStoreId,
|
|
23667
|
+
batchId: expectedBatchId,
|
|
23668
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
23669
|
+
logLabel,
|
|
23670
|
+
});
|
|
23671
|
+
}
|
|
23672
|
+
else if (batch.vector_store_id !== vectorStoreId) {
|
|
23673
|
+
console.error('[🤰]', 'Vector store file batch vector store id mismatch', {
|
|
23674
|
+
vectorStoreId,
|
|
23675
|
+
batchId: expectedBatchId,
|
|
23676
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
23677
|
+
logLabel,
|
|
23678
|
+
});
|
|
23679
|
+
}
|
|
23680
|
+
if (this.options.isVerbose) {
|
|
23681
|
+
console.info('[🤰]', 'Created vector store file batch', {
|
|
23682
|
+
vectorStoreId,
|
|
23683
|
+
batchId: expectedBatchId,
|
|
23684
|
+
fileCount: fileIds.length,
|
|
23685
|
+
logLabel,
|
|
23686
|
+
});
|
|
23687
|
+
}
|
|
23688
|
+
const pollStartedAtMs = Date.now();
|
|
23689
|
+
const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
|
|
23690
|
+
const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
|
|
23691
|
+
// let lastStatus: string | undefined;
|
|
23692
|
+
let lastCountsKey = '';
|
|
23693
|
+
let lastProgressKey = '';
|
|
23694
|
+
let lastLogAtMs = 0;
|
|
23695
|
+
let lastProgressAtMs = pollStartedAtMs;
|
|
23696
|
+
let lastDiagnosticsAtMs = pollStartedAtMs;
|
|
23697
|
+
let latestBatch = batch;
|
|
23698
|
+
let loggedBatchIdMismatch = false;
|
|
23699
|
+
let loggedBatchIdFallback = false;
|
|
23700
|
+
let loggedBatchIdInvalid = false;
|
|
23701
|
+
let shouldPoll = true;
|
|
23702
|
+
while (shouldPoll) {
|
|
23703
|
+
const nowMs = Date.now();
|
|
23704
|
+
// [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
|
|
23705
|
+
const rawBatchId = typeof latestBatch.id === 'string' ? latestBatch.id : '';
|
|
23706
|
+
const rawVectorStoreId = latestBatch.vector_store_id;
|
|
23707
|
+
let returnedBatchId = rawBatchId;
|
|
23708
|
+
let returnedBatchIdValid = typeof returnedBatchId === 'string' && returnedBatchId.startsWith('vsfb_');
|
|
23709
|
+
if (!returnedBatchIdValid && expectedBatchIdValid) {
|
|
23710
|
+
if (!loggedBatchIdFallback) {
|
|
23711
|
+
console.error('[🤰]', 'Vector store file batch id missing from response; falling back to expected', {
|
|
23712
|
+
vectorStoreId,
|
|
23713
|
+
expectedBatchId,
|
|
23714
|
+
returnedBatchId,
|
|
23715
|
+
rawVectorStoreId,
|
|
23716
|
+
logLabel,
|
|
23717
|
+
});
|
|
23718
|
+
loggedBatchIdFallback = true;
|
|
23719
|
+
}
|
|
23720
|
+
returnedBatchId = expectedBatchId;
|
|
23721
|
+
returnedBatchIdValid = true;
|
|
23722
|
+
}
|
|
23723
|
+
if (!returnedBatchIdValid && !loggedBatchIdInvalid) {
|
|
23724
|
+
console.error('[🤰]', 'Vector store file batch id is invalid; stopping polling', {
|
|
23725
|
+
vectorStoreId,
|
|
23726
|
+
expectedBatchId,
|
|
23727
|
+
returnedBatchId,
|
|
23728
|
+
rawVectorStoreId,
|
|
23729
|
+
logLabel,
|
|
23730
|
+
});
|
|
23731
|
+
loggedBatchIdInvalid = true;
|
|
23732
|
+
}
|
|
23733
|
+
const batchIdMismatch = expectedBatchIdValid && returnedBatchIdValid && returnedBatchId !== expectedBatchId;
|
|
23734
|
+
if (batchIdMismatch && !loggedBatchIdMismatch) {
|
|
23735
|
+
console.error('[🤰]', 'Vector store file batch id mismatch', {
|
|
23736
|
+
vectorStoreId,
|
|
23737
|
+
expectedBatchId,
|
|
23738
|
+
returnedBatchId,
|
|
23739
|
+
logLabel,
|
|
23740
|
+
});
|
|
23741
|
+
loggedBatchIdMismatch = true;
|
|
23742
|
+
}
|
|
23743
|
+
if (returnedBatchIdValid) {
|
|
23744
|
+
latestBatch = await vectorStores.fileBatches.retrieve(returnedBatchId, {
|
|
23745
|
+
vector_store_id: vectorStoreId,
|
|
23746
|
+
});
|
|
23747
|
+
}
|
|
23748
|
+
else {
|
|
23749
|
+
shouldPoll = false;
|
|
23750
|
+
continue;
|
|
23751
|
+
}
|
|
23752
|
+
const status = (_e = latestBatch.status) !== null && _e !== void 0 ? _e : 'unknown';
|
|
23753
|
+
const fileCounts = (_f = latestBatch.file_counts) !== null && _f !== void 0 ? _f : {};
|
|
23754
|
+
const progressKey = JSON.stringify(fileCounts);
|
|
23755
|
+
const statusCountsKey = `${status}-${progressKey}`;
|
|
23756
|
+
const isProgressing = progressKey !== lastProgressKey;
|
|
23757
|
+
if (isProgressing) {
|
|
23758
|
+
lastProgressAtMs = nowMs;
|
|
23759
|
+
lastProgressKey = progressKey;
|
|
23760
|
+
}
|
|
23761
|
+
if (this.options.isVerbose &&
|
|
23762
|
+
(statusCountsKey !== lastCountsKey || nowMs - lastLogAtMs >= progressLogIntervalMs)) {
|
|
23763
|
+
console.info('[🤰]', 'Vector store file batch status', {
|
|
23764
|
+
vectorStoreId,
|
|
23765
|
+
batchId: returnedBatchId,
|
|
23766
|
+
status,
|
|
23767
|
+
fileCounts,
|
|
23768
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
23769
|
+
logLabel,
|
|
23770
|
+
});
|
|
23771
|
+
lastCountsKey = statusCountsKey;
|
|
23772
|
+
lastLogAtMs = nowMs;
|
|
23773
|
+
}
|
|
23774
|
+
if (status === 'in_progress' &&
|
|
23775
|
+
nowMs - lastProgressAtMs >= VECTOR_STORE_STALL_LOG_THRESHOLD_MS &&
|
|
23776
|
+
nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
|
|
23777
|
+
lastDiagnosticsAtMs = nowMs;
|
|
23778
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
23779
|
+
client,
|
|
23780
|
+
vectorStoreId,
|
|
23781
|
+
batchId: returnedBatchId,
|
|
23782
|
+
uploadedFiles,
|
|
23783
|
+
logLabel,
|
|
23784
|
+
reason: 'stalled',
|
|
23785
|
+
});
|
|
23786
|
+
}
|
|
23787
|
+
if (status === 'completed') {
|
|
23788
|
+
if (this.options.isVerbose) {
|
|
23789
|
+
console.info('[🤰]', 'Vector store file batch completed', {
|
|
23790
|
+
vectorStoreId,
|
|
23791
|
+
batchId: returnedBatchId,
|
|
23792
|
+
fileCounts,
|
|
23793
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
23794
|
+
logLabel,
|
|
23795
|
+
});
|
|
23796
|
+
}
|
|
23797
|
+
shouldPoll = false;
|
|
23798
|
+
continue;
|
|
23799
|
+
}
|
|
23800
|
+
if (status === 'failed') {
|
|
23801
|
+
console.error('[🤰]', 'Vector store file batch completed with failures', {
|
|
23802
|
+
vectorStoreId,
|
|
23803
|
+
batchId: returnedBatchId,
|
|
23804
|
+
fileCounts,
|
|
23805
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
23806
|
+
logLabel,
|
|
23807
|
+
});
|
|
23808
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
23809
|
+
client,
|
|
23810
|
+
vectorStoreId,
|
|
23811
|
+
batchId: returnedBatchId,
|
|
23812
|
+
uploadedFiles,
|
|
23813
|
+
logLabel,
|
|
23814
|
+
reason: 'failed',
|
|
23815
|
+
});
|
|
23816
|
+
shouldPoll = false;
|
|
23817
|
+
continue;
|
|
23818
|
+
}
|
|
23819
|
+
if (status === 'cancelled') {
|
|
23820
|
+
console.error('[🤰]', 'Vector store file batch did not complete', {
|
|
23821
|
+
vectorStoreId,
|
|
23822
|
+
batchId: returnedBatchId,
|
|
23823
|
+
status,
|
|
23824
|
+
fileCounts,
|
|
23825
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
23826
|
+
logLabel,
|
|
23827
|
+
});
|
|
23828
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
23829
|
+
client,
|
|
23830
|
+
vectorStoreId,
|
|
23831
|
+
batchId: returnedBatchId,
|
|
23832
|
+
uploadedFiles,
|
|
23833
|
+
logLabel,
|
|
23834
|
+
reason: 'failed',
|
|
23835
|
+
});
|
|
23836
|
+
shouldPoll = false;
|
|
23837
|
+
continue;
|
|
23838
|
+
}
|
|
23839
|
+
if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
|
|
23840
|
+
console.error('[🤰]', 'Timed out waiting for vector store file batch', {
|
|
23841
|
+
vectorStoreId,
|
|
23842
|
+
batchId: returnedBatchId,
|
|
23843
|
+
fileCounts,
|
|
23844
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
23845
|
+
uploadTimeoutMs,
|
|
23846
|
+
logLabel,
|
|
23847
|
+
});
|
|
23848
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
23849
|
+
client,
|
|
23850
|
+
vectorStoreId,
|
|
23851
|
+
batchId: returnedBatchId,
|
|
23852
|
+
uploadedFiles,
|
|
23853
|
+
logLabel,
|
|
23854
|
+
reason: 'timeout',
|
|
23855
|
+
});
|
|
23856
|
+
if (this.shouldContinueOnVectorStoreStall()) {
|
|
23857
|
+
console.warn('[🤰]', 'Continuing despite vector store timeout as requested', {
|
|
23858
|
+
vectorStoreId,
|
|
23859
|
+
logLabel,
|
|
23860
|
+
});
|
|
23861
|
+
shouldPoll = false;
|
|
23862
|
+
continue;
|
|
23863
|
+
}
|
|
23864
|
+
try {
|
|
23865
|
+
const cancelBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
|
|
23866
|
+
if (!cancelBatchId.startsWith('vsfb_')) {
|
|
23867
|
+
console.error('[🤰]', 'Skipping vector store file batch cancel (invalid batch id)', {
|
|
23868
|
+
vectorStoreId,
|
|
23869
|
+
batchId: cancelBatchId,
|
|
23870
|
+
logLabel,
|
|
23871
|
+
});
|
|
23872
|
+
}
|
|
23873
|
+
else {
|
|
23874
|
+
await vectorStores.fileBatches.cancel(cancelBatchId, {
|
|
23875
|
+
vector_store_id: vectorStoreId,
|
|
23876
|
+
});
|
|
23877
|
+
}
|
|
23878
|
+
if (this.options.isVerbose) {
|
|
23879
|
+
console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
|
|
23880
|
+
vectorStoreId,
|
|
23881
|
+
batchId: batchIdMismatch && returnedBatchId.startsWith('vsfb_')
|
|
23882
|
+
? returnedBatchId
|
|
23883
|
+
: expectedBatchId,
|
|
23884
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
23885
|
+
logLabel,
|
|
23886
|
+
});
|
|
23887
|
+
}
|
|
23888
|
+
}
|
|
23889
|
+
catch (error) {
|
|
23890
|
+
assertsError(error);
|
|
23891
|
+
console.error('[🤰]', 'Failed to cancel vector store file batch after timeout', {
|
|
23892
|
+
vectorStoreId,
|
|
23893
|
+
batchId: expectedBatchId,
|
|
23894
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
23895
|
+
logLabel,
|
|
23896
|
+
error: serializeError(error),
|
|
23897
|
+
});
|
|
23898
|
+
}
|
|
23899
|
+
shouldPoll = false;
|
|
23900
|
+
continue;
|
|
23901
|
+
}
|
|
23902
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
23903
|
+
}
|
|
23904
|
+
return latestBatch;
|
|
23905
|
+
}
|
|
23906
|
+
/**
|
|
23907
|
+
* Creates a vector store and uploads knowledge sources, returning its ID.
|
|
23908
|
+
*/
|
|
23909
|
+
async createVectorStoreWithKnowledgeSources(options) {
|
|
23910
|
+
const { client, name, knowledgeSources, logLabel } = options;
|
|
23911
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
23912
|
+
const knowledgeSourcesCount = knowledgeSources.length;
|
|
23913
|
+
const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
|
|
23914
|
+
if (this.options.isVerbose) {
|
|
23915
|
+
console.info('[🤰]', 'Creating vector store with knowledge sources', {
|
|
23916
|
+
name,
|
|
23917
|
+
knowledgeSourcesCount,
|
|
23918
|
+
downloadTimeoutMs,
|
|
23919
|
+
logLabel,
|
|
23920
|
+
});
|
|
24109
23921
|
}
|
|
24110
|
-
const
|
|
24111
|
-
|
|
23922
|
+
const vectorStore = await vectorStores.create({
|
|
23923
|
+
name: `${name} Knowledge Base`,
|
|
24112
23924
|
});
|
|
24113
|
-
const
|
|
24114
|
-
|
|
24115
|
-
|
|
24116
|
-
console.error('[🤰]', 'Vector store file batch id looks invalid', {
|
|
23925
|
+
const vectorStoreId = vectorStore.id;
|
|
23926
|
+
if (this.options.isVerbose) {
|
|
23927
|
+
console.info('[🤰]', 'Vector store created', {
|
|
24117
23928
|
vectorStoreId,
|
|
24118
|
-
batchId: expectedBatchId,
|
|
24119
|
-
batchVectorStoreId: batch.vector_store_id,
|
|
24120
23929
|
logLabel,
|
|
24121
23930
|
});
|
|
24122
23931
|
}
|
|
24123
|
-
|
|
24124
|
-
|
|
24125
|
-
|
|
24126
|
-
|
|
24127
|
-
|
|
24128
|
-
|
|
24129
|
-
|
|
23932
|
+
const fileStreams = [];
|
|
23933
|
+
const skippedSources = [];
|
|
23934
|
+
let totalBytes = 0;
|
|
23935
|
+
const processingStartedAtMs = Date.now();
|
|
23936
|
+
for (const [index, source] of knowledgeSources.entries()) {
|
|
23937
|
+
try {
|
|
23938
|
+
const sourceType = source.startsWith('http') || source.startsWith('https') ? 'url' : 'file';
|
|
23939
|
+
if (this.options.isVerbose) {
|
|
23940
|
+
console.info('[🤰]', 'Processing knowledge source', {
|
|
23941
|
+
index: index + 1,
|
|
23942
|
+
total: knowledgeSourcesCount,
|
|
23943
|
+
source,
|
|
23944
|
+
sourceType,
|
|
23945
|
+
logLabel,
|
|
23946
|
+
});
|
|
23947
|
+
}
|
|
23948
|
+
// Check if it's a URL
|
|
23949
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
23950
|
+
const downloadResult = await this.downloadKnowledgeSourceFile({
|
|
23951
|
+
source,
|
|
23952
|
+
timeoutMs: downloadTimeoutMs,
|
|
23953
|
+
logLabel,
|
|
23954
|
+
});
|
|
23955
|
+
if (downloadResult) {
|
|
23956
|
+
fileStreams.push(downloadResult.file);
|
|
23957
|
+
totalBytes += downloadResult.sizeBytes;
|
|
23958
|
+
}
|
|
23959
|
+
else {
|
|
23960
|
+
skippedSources.push({ source, reason: 'download_failed' });
|
|
23961
|
+
}
|
|
23962
|
+
}
|
|
23963
|
+
else {
|
|
23964
|
+
skippedSources.push({ source, reason: 'unsupported_source_type' });
|
|
23965
|
+
if (this.options.isVerbose) {
|
|
23966
|
+
console.info('[🤰]', 'Skipping knowledge source (unsupported type)', {
|
|
23967
|
+
source,
|
|
23968
|
+
sourceType,
|
|
23969
|
+
logLabel,
|
|
23970
|
+
});
|
|
23971
|
+
}
|
|
23972
|
+
/*
|
|
23973
|
+
TODO: [🤰] Resolve problem with browser environment
|
|
23974
|
+
// Assume it's a local file path
|
|
23975
|
+
// Note: This will work in Node.js environment
|
|
23976
|
+
// For browser environments, this would need different handling
|
|
23977
|
+
const fs = await import('fs');
|
|
23978
|
+
const fileStream = fs.createReadStream(source);
|
|
23979
|
+
fileStreams.push(fileStream);
|
|
23980
|
+
*/
|
|
23981
|
+
}
|
|
23982
|
+
}
|
|
23983
|
+
catch (error) {
|
|
23984
|
+
assertsError(error);
|
|
23985
|
+
skippedSources.push({ source, reason: 'processing_error' });
|
|
23986
|
+
console.error('[🤰]', 'Error processing knowledge source', {
|
|
23987
|
+
source,
|
|
23988
|
+
logLabel,
|
|
23989
|
+
error: serializeError(error),
|
|
23990
|
+
});
|
|
23991
|
+
}
|
|
24130
23992
|
}
|
|
24131
23993
|
if (this.options.isVerbose) {
|
|
24132
|
-
console.info('[🤰]', '
|
|
24133
|
-
|
|
24134
|
-
|
|
24135
|
-
|
|
23994
|
+
console.info('[🤰]', 'Finished processing knowledge sources', {
|
|
23995
|
+
total: knowledgeSourcesCount,
|
|
23996
|
+
downloadedCount: fileStreams.length,
|
|
23997
|
+
skippedCount: skippedSources.length,
|
|
23998
|
+
totalBytes,
|
|
23999
|
+
elapsedMs: Date.now() - processingStartedAtMs,
|
|
24000
|
+
skippedSamples: skippedSources.slice(0, 3),
|
|
24136
24001
|
logLabel,
|
|
24137
24002
|
});
|
|
24138
24003
|
}
|
|
24139
|
-
|
|
24140
|
-
|
|
24141
|
-
|
|
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', {
|
|
24004
|
+
if (fileStreams.length > 0) {
|
|
24005
|
+
if (this.options.isVerbose) {
|
|
24006
|
+
console.info('[🤰]', 'Uploading files to vector store', {
|
|
24167
24007
|
vectorStoreId,
|
|
24168
|
-
|
|
24169
|
-
|
|
24170
|
-
|
|
24171
|
-
|
|
24008
|
+
fileCount: fileStreams.length,
|
|
24009
|
+
totalBytes,
|
|
24010
|
+
maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
|
|
24011
|
+
pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
|
|
24012
|
+
uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
|
|
24172
24013
|
logLabel,
|
|
24173
24014
|
});
|
|
24174
|
-
loggedBatchIdMismatch = true;
|
|
24175
|
-
}
|
|
24176
|
-
if (countsKey !== lastProgressKey) {
|
|
24177
|
-
lastProgressKey = countsKey;
|
|
24178
|
-
lastProgressAtMs = nowMs;
|
|
24179
24015
|
}
|
|
24180
|
-
|
|
24181
|
-
|
|
24016
|
+
try {
|
|
24017
|
+
await this.uploadKnowledgeSourceFilesToVectorStore({
|
|
24018
|
+
client,
|
|
24182
24019
|
vectorStoreId,
|
|
24183
|
-
|
|
24184
|
-
|
|
24185
|
-
status: latestBatch.status,
|
|
24186
|
-
fileCounts: counts,
|
|
24187
|
-
elapsedMs: nowMs - pollStartedAtMs,
|
|
24020
|
+
files: fileStreams,
|
|
24021
|
+
totalBytes,
|
|
24188
24022
|
logLabel,
|
|
24189
24023
|
});
|
|
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
24024
|
}
|
|
24205
|
-
|
|
24206
|
-
|
|
24207
|
-
|
|
24208
|
-
await this.logVectorStoreFileBatchDiagnostics({
|
|
24209
|
-
client,
|
|
24025
|
+
catch (error) {
|
|
24026
|
+
assertsError(error);
|
|
24027
|
+
console.error('[🤰]', 'Error uploading files to vector store', {
|
|
24210
24028
|
vectorStoreId,
|
|
24211
|
-
batchId: diagnosticsBatchId,
|
|
24212
|
-
uploadedFiles,
|
|
24213
24029
|
logLabel,
|
|
24214
|
-
|
|
24030
|
+
error: serializeError(error),
|
|
24215
24031
|
});
|
|
24216
24032
|
}
|
|
24217
|
-
|
|
24218
|
-
|
|
24219
|
-
|
|
24220
|
-
|
|
24221
|
-
|
|
24222
|
-
|
|
24223
|
-
|
|
24224
|
-
|
|
24225
|
-
|
|
24226
|
-
|
|
24227
|
-
|
|
24228
|
-
|
|
24229
|
-
|
|
24230
|
-
|
|
24231
|
-
|
|
24232
|
-
|
|
24233
|
-
|
|
24234
|
-
|
|
24235
|
-
|
|
24236
|
-
|
|
24237
|
-
|
|
24238
|
-
|
|
24239
|
-
|
|
24240
|
-
|
|
24241
|
-
|
|
24242
|
-
|
|
24243
|
-
|
|
24244
|
-
|
|
24245
|
-
|
|
24246
|
-
|
|
24033
|
+
}
|
|
24034
|
+
else if (this.options.isVerbose) {
|
|
24035
|
+
console.info('[🤰]', 'No knowledge source files to upload', {
|
|
24036
|
+
vectorStoreId,
|
|
24037
|
+
skippedCount: skippedSources.length,
|
|
24038
|
+
logLabel,
|
|
24039
|
+
});
|
|
24040
|
+
}
|
|
24041
|
+
return {
|
|
24042
|
+
vectorStoreId,
|
|
24043
|
+
uploadedFileCount: fileStreams.length,
|
|
24044
|
+
skippedCount: skippedSources.length,
|
|
24045
|
+
totalBytes,
|
|
24046
|
+
};
|
|
24047
|
+
}
|
|
24048
|
+
}
|
|
24049
|
+
|
|
24050
|
+
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.2';
|
|
24051
|
+
/**
|
|
24052
|
+
* Execution tools for OpenAI AgentKit (Agents SDK).
|
|
24053
|
+
*
|
|
24054
|
+
* @public exported from `@promptbook/openai`
|
|
24055
|
+
*/
|
|
24056
|
+
class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
|
|
24057
|
+
/**
|
|
24058
|
+
* Creates OpenAI AgentKit execution tools.
|
|
24059
|
+
*/
|
|
24060
|
+
constructor(options) {
|
|
24061
|
+
var _a;
|
|
24062
|
+
if (options.isProxied) {
|
|
24063
|
+
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI AgentKit`);
|
|
24064
|
+
}
|
|
24065
|
+
super(options);
|
|
24066
|
+
this.preparedAgentKitAgent = null;
|
|
24067
|
+
this.agentKitModelName = (_a = options.agentKitModelName) !== null && _a !== void 0 ? _a : DEFAULT_AGENT_KIT_MODEL_NAME;
|
|
24068
|
+
}
|
|
24069
|
+
get title() {
|
|
24070
|
+
return 'OpenAI AgentKit';
|
|
24071
|
+
}
|
|
24072
|
+
get description() {
|
|
24073
|
+
return 'Use OpenAI AgentKit for agent-style chat with tools and knowledge';
|
|
24074
|
+
}
|
|
24075
|
+
/**
|
|
24076
|
+
* Calls OpenAI AgentKit with a chat prompt (non-streaming).
|
|
24077
|
+
*/
|
|
24078
|
+
async callChatModel(prompt) {
|
|
24079
|
+
return this.callChatModelStream(prompt, () => { });
|
|
24080
|
+
}
|
|
24081
|
+
/**
|
|
24082
|
+
* Calls OpenAI AgentKit with a chat prompt (streaming).
|
|
24083
|
+
*/
|
|
24084
|
+
async callChatModelStream(prompt, onProgress) {
|
|
24085
|
+
const { content, parameters, modelRequirements } = prompt;
|
|
24086
|
+
if (modelRequirements.modelVariant !== 'CHAT') {
|
|
24087
|
+
throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
|
|
24088
|
+
}
|
|
24089
|
+
for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
|
|
24090
|
+
if (modelRequirements[key] !== undefined) {
|
|
24091
|
+
throw new NotYetImplementedError(`In \`OpenAiAgentKitExecutionTools\` you cannot specify \`${key}\``);
|
|
24092
|
+
}
|
|
24093
|
+
}
|
|
24094
|
+
const rawPromptContent = templateParameters(content, {
|
|
24095
|
+
...parameters,
|
|
24096
|
+
modelName: this.agentKitModelName,
|
|
24097
|
+
});
|
|
24098
|
+
const preparedAgentKitAgent = await this.prepareAgentKitAgent({
|
|
24099
|
+
name: (prompt.title || 'Agent'),
|
|
24100
|
+
instructions: modelRequirements.systemMessage || '',
|
|
24101
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
24102
|
+
tools: 'tools' in prompt && Array.isArray(prompt.tools) ? prompt.tools : modelRequirements.tools,
|
|
24103
|
+
});
|
|
24104
|
+
return this.callChatModelStreamWithPreparedAgent({
|
|
24105
|
+
openAiAgentKitAgent: preparedAgentKitAgent.agent,
|
|
24106
|
+
prompt,
|
|
24107
|
+
rawPromptContent,
|
|
24108
|
+
onProgress,
|
|
24109
|
+
});
|
|
24110
|
+
}
|
|
24111
|
+
/**
|
|
24112
|
+
* Returns a prepared AgentKit agent when the server wants to manage caching externally.
|
|
24113
|
+
*/
|
|
24114
|
+
getPreparedAgentKitAgent() {
|
|
24115
|
+
return this.preparedAgentKitAgent;
|
|
24116
|
+
}
|
|
24117
|
+
/**
|
|
24118
|
+
* Stores a prepared AgentKit agent for later reuse by external cache managers.
|
|
24119
|
+
*/
|
|
24120
|
+
setPreparedAgentKitAgent(preparedAgent) {
|
|
24121
|
+
this.preparedAgentKitAgent = preparedAgent;
|
|
24122
|
+
}
|
|
24123
|
+
/**
|
|
24124
|
+
* Creates a new tools instance bound to a prepared AgentKit agent.
|
|
24125
|
+
*/
|
|
24126
|
+
getPreparedAgentTools(preparedAgent) {
|
|
24127
|
+
const tools = new OpenAiAgentKitExecutionTools(this.agentKitOptions);
|
|
24128
|
+
tools.setPreparedAgentKitAgent(preparedAgent);
|
|
24129
|
+
return tools;
|
|
24130
|
+
}
|
|
24131
|
+
/**
|
|
24132
|
+
* Prepares an AgentKit agent with optional knowledge sources and tool definitions.
|
|
24133
|
+
*/
|
|
24134
|
+
async prepareAgentKitAgent(options) {
|
|
24135
|
+
var _a, _b;
|
|
24136
|
+
const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
|
|
24137
|
+
await this.ensureAgentKitDefaults();
|
|
24138
|
+
if (this.options.isVerbose) {
|
|
24139
|
+
console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
|
|
24140
|
+
name,
|
|
24141
|
+
instructionsLength: instructions.length,
|
|
24142
|
+
knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
|
|
24143
|
+
toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
|
|
24144
|
+
});
|
|
24145
|
+
}
|
|
24146
|
+
let vectorStoreId = cachedVectorStoreId;
|
|
24147
|
+
if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
|
|
24148
|
+
const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
|
|
24149
|
+
client: await this.getClient(),
|
|
24150
|
+
name,
|
|
24151
|
+
knowledgeSources,
|
|
24152
|
+
logLabel: 'agentkit preparation',
|
|
24153
|
+
});
|
|
24154
|
+
vectorStoreId = vectorStoreResult.vectorStoreId;
|
|
24155
|
+
}
|
|
24156
|
+
else if (vectorStoreId && this.options.isVerbose) {
|
|
24157
|
+
console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
|
|
24158
|
+
name,
|
|
24159
|
+
vectorStoreId,
|
|
24160
|
+
});
|
|
24161
|
+
}
|
|
24162
|
+
const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
|
|
24163
|
+
const openAiAgentKitAgent = new agents.Agent({
|
|
24164
|
+
name,
|
|
24165
|
+
model: this.agentKitModelName,
|
|
24166
|
+
instructions: instructions || 'You are a helpful assistant.',
|
|
24167
|
+
tools: agentKitTools,
|
|
24168
|
+
});
|
|
24169
|
+
const preparedAgent = {
|
|
24170
|
+
agent: openAiAgentKitAgent,
|
|
24171
|
+
vectorStoreId,
|
|
24172
|
+
};
|
|
24173
|
+
if (storeAsPrepared) {
|
|
24174
|
+
this.setPreparedAgentKitAgent(preparedAgent);
|
|
24175
|
+
}
|
|
24176
|
+
if (this.options.isVerbose) {
|
|
24177
|
+
console.info('[🤰]', 'OpenAI AgentKit agent ready', {
|
|
24178
|
+
name,
|
|
24179
|
+
model: this.agentKitModelName,
|
|
24180
|
+
toolCount: agentKitTools.length,
|
|
24181
|
+
hasVectorStore: Boolean(vectorStoreId),
|
|
24182
|
+
});
|
|
24183
|
+
}
|
|
24184
|
+
return preparedAgent;
|
|
24185
|
+
}
|
|
24186
|
+
/**
|
|
24187
|
+
* Ensures the AgentKit SDK is wired to the OpenAI client and API key.
|
|
24188
|
+
*/
|
|
24189
|
+
async ensureAgentKitDefaults() {
|
|
24190
|
+
const client = await this.getClient();
|
|
24191
|
+
agents.setDefaultOpenAIClient(client);
|
|
24192
|
+
const apiKey = this.agentKitOptions.apiKey;
|
|
24193
|
+
if (apiKey && typeof apiKey === 'string') {
|
|
24194
|
+
agents.setDefaultOpenAIKey(apiKey);
|
|
24195
|
+
}
|
|
24196
|
+
}
|
|
24197
|
+
/**
|
|
24198
|
+
* Builds the tool list for AgentKit, including hosted file search when applicable.
|
|
24199
|
+
*/
|
|
24200
|
+
buildAgentKitTools(options) {
|
|
24201
|
+
var _a;
|
|
24202
|
+
const { tools, vectorStoreId } = options;
|
|
24203
|
+
const agentKitTools = [];
|
|
24204
|
+
if (vectorStoreId) {
|
|
24205
|
+
agentKitTools.push(agents.fileSearchTool(vectorStoreId));
|
|
24206
|
+
}
|
|
24207
|
+
if (tools && tools.length > 0) {
|
|
24208
|
+
const scriptTools = this.resolveScriptTools();
|
|
24209
|
+
for (const toolDefinition of tools) {
|
|
24210
|
+
agentKitTools.push(agents.tool({
|
|
24211
|
+
name: toolDefinition.name,
|
|
24212
|
+
description: toolDefinition.description,
|
|
24213
|
+
parameters: toolDefinition.parameters
|
|
24214
|
+
? {
|
|
24215
|
+
...toolDefinition.parameters,
|
|
24216
|
+
additionalProperties: false,
|
|
24217
|
+
required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
|
|
24218
|
+
}
|
|
24219
|
+
: undefined,
|
|
24220
|
+
strict: false,
|
|
24221
|
+
execute: async (input, runContext, details) => {
|
|
24222
|
+
var _a, _b, _c;
|
|
24223
|
+
const scriptTool = scriptTools[0];
|
|
24224
|
+
const functionName = toolDefinition.name;
|
|
24225
|
+
const calledAt = $getCurrentDate();
|
|
24226
|
+
const callId = (_a = details === null || details === void 0 ? void 0 : details.toolCall) === null || _a === void 0 ? void 0 : _a.callId;
|
|
24227
|
+
const functionArgs = input !== null && input !== void 0 ? input : {};
|
|
24228
|
+
if (this.options.isVerbose) {
|
|
24229
|
+
console.info('[🤰]', 'Executing AgentKit tool', {
|
|
24230
|
+
functionName,
|
|
24231
|
+
callId,
|
|
24232
|
+
calledAt,
|
|
24233
|
+
});
|
|
24234
|
+
}
|
|
24235
|
+
try {
|
|
24236
|
+
return await scriptTool.execute({
|
|
24237
|
+
scriptLanguage: 'javascript',
|
|
24238
|
+
script: `
|
|
24239
|
+
const args = ${JSON.stringify(functionArgs)};
|
|
24240
|
+
return await ${functionName}(args);
|
|
24241
|
+
`,
|
|
24242
|
+
parameters: (_c = (_b = runContext === null || runContext === void 0 ? void 0 : runContext.context) === null || _b === void 0 ? void 0 : _b.parameters) !== null && _c !== void 0 ? _c : {},
|
|
24243
|
+
});
|
|
24244
|
+
}
|
|
24245
|
+
catch (error) {
|
|
24246
|
+
assertsError(error);
|
|
24247
|
+
const serializedError = serializeError(error);
|
|
24248
|
+
const errorMessage = spaceTrim__default["default"]((block) => `
|
|
24249
|
+
|
|
24250
|
+
The invoked tool \`${functionName}\` failed with error:
|
|
24251
|
+
|
|
24252
|
+
\`\`\`json
|
|
24253
|
+
${block(JSON.stringify(serializedError, null, 4))}
|
|
24254
|
+
\`\`\`
|
|
24255
|
+
|
|
24256
|
+
`);
|
|
24257
|
+
console.error('[🤰]', 'AgentKit tool execution failed', {
|
|
24258
|
+
functionName,
|
|
24259
|
+
callId,
|
|
24260
|
+
error: serializedError,
|
|
24261
|
+
});
|
|
24262
|
+
return errorMessage;
|
|
24263
|
+
}
|
|
24264
|
+
},
|
|
24265
|
+
}));
|
|
24247
24266
|
}
|
|
24248
|
-
|
|
24249
|
-
|
|
24250
|
-
|
|
24251
|
-
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24267
|
+
}
|
|
24268
|
+
return agentKitTools;
|
|
24269
|
+
}
|
|
24270
|
+
/**
|
|
24271
|
+
* Resolves the configured script tools for tool execution.
|
|
24272
|
+
*/
|
|
24273
|
+
resolveScriptTools() {
|
|
24274
|
+
const executionTools = this.options.executionTools;
|
|
24275
|
+
if (!executionTools || !executionTools.script) {
|
|
24276
|
+
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
24277
|
+
}
|
|
24278
|
+
return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
|
|
24279
|
+
}
|
|
24280
|
+
/**
|
|
24281
|
+
* Runs a prepared AgentKit agent and streams results back to the caller.
|
|
24282
|
+
*/
|
|
24283
|
+
async callChatModelStreamWithPreparedAgent(options) {
|
|
24284
|
+
var _a, _b, _c, _d;
|
|
24285
|
+
const { openAiAgentKitAgent, prompt, onProgress } = options;
|
|
24286
|
+
const rawPromptContent = (_a = options.rawPromptContent) !== null && _a !== void 0 ? _a : templateParameters(prompt.content, {
|
|
24287
|
+
...prompt.parameters,
|
|
24288
|
+
modelName: this.agentKitModelName,
|
|
24289
|
+
});
|
|
24290
|
+
const start = $getCurrentDate();
|
|
24291
|
+
let latestContent = '';
|
|
24292
|
+
const toolCalls = [];
|
|
24293
|
+
const toolCallIndexById = new Map();
|
|
24294
|
+
const inputItems = await this.buildAgentKitInputItems(prompt, rawPromptContent);
|
|
24295
|
+
const rawRequest = {
|
|
24296
|
+
agentName: openAiAgentKitAgent.name,
|
|
24297
|
+
input: inputItems,
|
|
24298
|
+
};
|
|
24299
|
+
const streamResult = await agents.run(openAiAgentKitAgent, inputItems, {
|
|
24300
|
+
stream: true,
|
|
24301
|
+
context: { parameters: prompt.parameters },
|
|
24302
|
+
});
|
|
24303
|
+
for await (const event of streamResult) {
|
|
24304
|
+
if (event.type === 'raw_model_stream_event' && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'output_text_delta') {
|
|
24305
|
+
latestContent += event.data.delta;
|
|
24306
|
+
onProgress({
|
|
24307
|
+
content: latestContent,
|
|
24308
|
+
modelName: this.agentKitModelName,
|
|
24309
|
+
timing: { start, complete: $getCurrentDate() },
|
|
24310
|
+
usage: UNCERTAIN_USAGE,
|
|
24311
|
+
rawPromptContent: rawPromptContent,
|
|
24312
|
+
rawRequest: null,
|
|
24313
|
+
rawResponse: {},
|
|
24265
24314
|
});
|
|
24266
|
-
shouldPoll = false;
|
|
24267
24315
|
continue;
|
|
24268
24316
|
}
|
|
24269
|
-
if (
|
|
24270
|
-
|
|
24271
|
-
|
|
24272
|
-
|
|
24273
|
-
|
|
24274
|
-
|
|
24275
|
-
|
|
24276
|
-
|
|
24277
|
-
|
|
24278
|
-
|
|
24279
|
-
|
|
24280
|
-
|
|
24281
|
-
|
|
24282
|
-
|
|
24283
|
-
|
|
24284
|
-
|
|
24285
|
-
|
|
24286
|
-
|
|
24287
|
-
|
|
24288
|
-
|
|
24289
|
-
vectorStoreId,
|
|
24290
|
-
logLabel,
|
|
24317
|
+
if (event.type === 'run_item_stream_event') {
|
|
24318
|
+
const rawItem = (_c = event.item) === null || _c === void 0 ? void 0 : _c.rawItem;
|
|
24319
|
+
if (event.name === 'tool_called' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call') {
|
|
24320
|
+
const toolCall = {
|
|
24321
|
+
name: rawItem.name,
|
|
24322
|
+
arguments: rawItem.arguments,
|
|
24323
|
+
rawToolCall: rawItem,
|
|
24324
|
+
createdAt: $getCurrentDate(),
|
|
24325
|
+
};
|
|
24326
|
+
toolCallIndexById.set(rawItem.callId, toolCalls.length);
|
|
24327
|
+
toolCalls.push(toolCall);
|
|
24328
|
+
onProgress({
|
|
24329
|
+
content: latestContent,
|
|
24330
|
+
modelName: this.agentKitModelName,
|
|
24331
|
+
timing: { start, complete: $getCurrentDate() },
|
|
24332
|
+
usage: UNCERTAIN_USAGE,
|
|
24333
|
+
rawPromptContent: rawPromptContent,
|
|
24334
|
+
rawRequest: null,
|
|
24335
|
+
rawResponse: {},
|
|
24336
|
+
toolCalls: [toolCall],
|
|
24291
24337
|
});
|
|
24292
|
-
shouldPoll = false;
|
|
24293
|
-
continue;
|
|
24294
24338
|
}
|
|
24295
|
-
|
|
24296
|
-
const
|
|
24297
|
-
|
|
24298
|
-
|
|
24299
|
-
|
|
24300
|
-
|
|
24301
|
-
|
|
24302
|
-
|
|
24303
|
-
|
|
24304
|
-
|
|
24305
|
-
|
|
24306
|
-
|
|
24307
|
-
|
|
24308
|
-
|
|
24309
|
-
|
|
24310
|
-
|
|
24311
|
-
|
|
24312
|
-
|
|
24313
|
-
|
|
24314
|
-
|
|
24339
|
+
if (event.name === 'tool_output' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call_result') {
|
|
24340
|
+
const index = toolCallIndexById.get(rawItem.callId);
|
|
24341
|
+
const result = this.formatAgentKitToolOutput(rawItem.output);
|
|
24342
|
+
if (index !== undefined) {
|
|
24343
|
+
const existingToolCall = toolCalls[index];
|
|
24344
|
+
const completedToolCall = {
|
|
24345
|
+
...existingToolCall,
|
|
24346
|
+
result,
|
|
24347
|
+
rawToolCall: rawItem,
|
|
24348
|
+
};
|
|
24349
|
+
toolCalls[index] = completedToolCall;
|
|
24350
|
+
onProgress({
|
|
24351
|
+
content: latestContent,
|
|
24352
|
+
modelName: this.agentKitModelName,
|
|
24353
|
+
timing: { start, complete: $getCurrentDate() },
|
|
24354
|
+
usage: UNCERTAIN_USAGE,
|
|
24355
|
+
rawPromptContent: rawPromptContent,
|
|
24356
|
+
rawRequest: null,
|
|
24357
|
+
rawResponse: {},
|
|
24358
|
+
toolCalls: [completedToolCall],
|
|
24315
24359
|
});
|
|
24316
24360
|
}
|
|
24317
24361
|
}
|
|
24318
|
-
|
|
24319
|
-
|
|
24320
|
-
|
|
24321
|
-
|
|
24322
|
-
|
|
24323
|
-
|
|
24324
|
-
|
|
24325
|
-
|
|
24362
|
+
}
|
|
24363
|
+
}
|
|
24364
|
+
await streamResult.completed;
|
|
24365
|
+
const complete = $getCurrentDate();
|
|
24366
|
+
const finalContent = ((_d = streamResult.finalOutput) !== null && _d !== void 0 ? _d : latestContent);
|
|
24367
|
+
const finalResult = {
|
|
24368
|
+
content: finalContent,
|
|
24369
|
+
modelName: this.agentKitModelName,
|
|
24370
|
+
timing: { start, complete },
|
|
24371
|
+
usage: UNCERTAIN_USAGE,
|
|
24372
|
+
rawPromptContent: rawPromptContent,
|
|
24373
|
+
rawRequest,
|
|
24374
|
+
rawResponse: { runResult: streamResult },
|
|
24375
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
24376
|
+
};
|
|
24377
|
+
onProgress(finalResult);
|
|
24378
|
+
return finalResult;
|
|
24379
|
+
}
|
|
24380
|
+
/**
|
|
24381
|
+
* Builds AgentKit input items from the prompt and optional thread.
|
|
24382
|
+
*/
|
|
24383
|
+
async buildAgentKitInputItems(prompt, rawPromptContent) {
|
|
24384
|
+
var _a;
|
|
24385
|
+
const inputItems = [];
|
|
24386
|
+
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
24387
|
+
for (const message of prompt.thread) {
|
|
24388
|
+
const sender = message.sender;
|
|
24389
|
+
const content = (_a = message.content) !== null && _a !== void 0 ? _a : '';
|
|
24390
|
+
if (sender === 'assistant' || sender === 'agent') {
|
|
24391
|
+
inputItems.push({
|
|
24392
|
+
role: 'assistant',
|
|
24393
|
+
status: 'completed',
|
|
24394
|
+
content: [{ type: 'output_text', text: content }],
|
|
24395
|
+
});
|
|
24396
|
+
}
|
|
24397
|
+
else {
|
|
24398
|
+
inputItems.push({
|
|
24399
|
+
role: 'user',
|
|
24400
|
+
content,
|
|
24326
24401
|
});
|
|
24327
24402
|
}
|
|
24328
|
-
shouldPoll = false;
|
|
24329
|
-
continue;
|
|
24330
24403
|
}
|
|
24331
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
24332
24404
|
}
|
|
24333
|
-
|
|
24405
|
+
const userContent = await this.buildAgentKitUserContent(prompt, rawPromptContent);
|
|
24406
|
+
inputItems.push({
|
|
24407
|
+
role: 'user',
|
|
24408
|
+
content: userContent,
|
|
24409
|
+
});
|
|
24410
|
+
return inputItems;
|
|
24334
24411
|
}
|
|
24335
24412
|
/**
|
|
24336
|
-
*
|
|
24413
|
+
* Builds the user message content for AgentKit runs, including file inputs when provided.
|
|
24337
24414
|
*/
|
|
24338
|
-
async
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24415
|
+
async buildAgentKitUserContent(prompt, rawPromptContent) {
|
|
24416
|
+
if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
|
|
24417
|
+
const fileItems = await Promise.all(prompt.files.map(async (file) => {
|
|
24418
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
24419
|
+
const base64 = Buffer.from(arrayBuffer).toString('base64');
|
|
24420
|
+
return {
|
|
24421
|
+
type: 'input_image',
|
|
24422
|
+
image: `data:${file.type};base64,${base64}`,
|
|
24423
|
+
};
|
|
24424
|
+
}));
|
|
24425
|
+
return [{ type: 'input_text', text: rawPromptContent }, ...fileItems];
|
|
24426
|
+
}
|
|
24427
|
+
return rawPromptContent;
|
|
24428
|
+
}
|
|
24429
|
+
/**
|
|
24430
|
+
* Normalizes AgentKit tool outputs into a string for Promptbook tool call results.
|
|
24431
|
+
*/
|
|
24432
|
+
formatAgentKitToolOutput(output) {
|
|
24433
|
+
if (typeof output === 'string') {
|
|
24434
|
+
return output;
|
|
24435
|
+
}
|
|
24436
|
+
if (output && typeof output === 'object') {
|
|
24437
|
+
const textOutput = output;
|
|
24438
|
+
if (textOutput.type === 'text' && typeof textOutput.text === 'string') {
|
|
24439
|
+
return textOutput.text;
|
|
24440
|
+
}
|
|
24441
|
+
}
|
|
24442
|
+
return JSON.stringify(output !== null && output !== void 0 ? output : null);
|
|
24443
|
+
}
|
|
24444
|
+
/**
|
|
24445
|
+
* Returns AgentKit-specific options.
|
|
24446
|
+
*/
|
|
24447
|
+
get agentKitOptions() {
|
|
24448
|
+
return this.options;
|
|
24449
|
+
}
|
|
24450
|
+
/**
|
|
24451
|
+
* Discriminant for type guards.
|
|
24452
|
+
*/
|
|
24453
|
+
get discriminant() {
|
|
24454
|
+
return DISCRIMINANT$1;
|
|
24455
|
+
}
|
|
24456
|
+
/**
|
|
24457
|
+
* Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentKitExecutionTools`.
|
|
24458
|
+
*/
|
|
24459
|
+
static isOpenAiAgentKitExecutionTools(llmExecutionTools) {
|
|
24460
|
+
return llmExecutionTools.discriminant === DISCRIMINANT$1;
|
|
24461
|
+
}
|
|
24462
|
+
}
|
|
24463
|
+
/**
|
|
24464
|
+
* Discriminant for type guards.
|
|
24465
|
+
*
|
|
24466
|
+
* @private const of `OpenAiAgentKitExecutionTools`
|
|
24467
|
+
*/
|
|
24468
|
+
const DISCRIMINANT$1 = 'OPEN_AI_AGENT_KIT_V1';
|
|
24469
|
+
|
|
24470
|
+
/**
|
|
24471
|
+
* Uploads files to OpenAI and returns their IDs
|
|
24472
|
+
*
|
|
24473
|
+
* @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
|
|
24474
|
+
*/
|
|
24475
|
+
async function uploadFilesToOpenAi(client, files) {
|
|
24476
|
+
const fileIds = [];
|
|
24477
|
+
for (const file of files) {
|
|
24478
|
+
// Note: OpenAI API expects a File object or a ReadStream
|
|
24479
|
+
// In browser environment, we can pass the File object directly
|
|
24480
|
+
// In Node.js environment, we might need to convert it or use a different approach
|
|
24481
|
+
// But since `Prompt.files` already contains `File` objects, we try to pass them directly
|
|
24482
|
+
const uploadedFile = await client.files.create({
|
|
24483
|
+
file: file,
|
|
24484
|
+
purpose: 'assistants',
|
|
24485
|
+
});
|
|
24486
|
+
fileIds.push(uploadedFile.id);
|
|
24487
|
+
}
|
|
24488
|
+
return fileIds;
|
|
24489
|
+
}
|
|
24490
|
+
|
|
24491
|
+
/**
|
|
24492
|
+
* Execution Tools for calling OpenAI API Assistants
|
|
24493
|
+
*
|
|
24494
|
+
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
24495
|
+
*
|
|
24496
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
24497
|
+
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
24498
|
+
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
24499
|
+
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
24500
|
+
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
24501
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
24502
|
+
*
|
|
24503
|
+
* @deprecated Use `OpenAiAgentKitExecutionTools` instead.
|
|
24504
|
+
* @public exported from `@promptbook/openai`
|
|
24505
|
+
*/
|
|
24506
|
+
class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
|
|
24507
|
+
/**
|
|
24508
|
+
* Creates OpenAI Execution Tools.
|
|
24509
|
+
*
|
|
24510
|
+
* @param options which are relevant are directly passed to the OpenAI client
|
|
24511
|
+
*/
|
|
24512
|
+
constructor(options) {
|
|
24513
|
+
var _a;
|
|
24514
|
+
if (options.isProxied) {
|
|
24515
|
+
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
|
|
24516
|
+
}
|
|
24517
|
+
super(options);
|
|
24518
|
+
this.isCreatingNewAssistantsAllowed = false;
|
|
24519
|
+
this.assistantId = options.assistantId;
|
|
24520
|
+
this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
|
|
24521
|
+
if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
|
|
24522
|
+
throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
|
|
24523
|
+
}
|
|
24524
|
+
// <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
|
|
24525
|
+
// TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
|
|
24526
|
+
}
|
|
24527
|
+
get title() {
|
|
24528
|
+
return 'OpenAI Assistant';
|
|
24529
|
+
}
|
|
24530
|
+
get description() {
|
|
24531
|
+
return 'Use single assistant provided by OpenAI';
|
|
24532
|
+
}
|
|
24533
|
+
/**
|
|
24534
|
+
* Calls OpenAI API to use a chat model.
|
|
24535
|
+
*/
|
|
24536
|
+
async callChatModel(prompt) {
|
|
24537
|
+
return this.callChatModelStream(prompt, () => { });
|
|
24538
|
+
}
|
|
24539
|
+
/**
|
|
24540
|
+
* Calls OpenAI API to use a chat model with streaming.
|
|
24541
|
+
*/
|
|
24542
|
+
async callChatModelStream(prompt, onProgress) {
|
|
24543
|
+
var _a, _b, _c, _d, _e, _f;
|
|
24342
24544
|
if (this.options.isVerbose) {
|
|
24343
|
-
console.info('
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24545
|
+
console.info('💬 OpenAI callChatModel call', { prompt });
|
|
24546
|
+
}
|
|
24547
|
+
const { content, parameters, modelRequirements /*, format*/ } = prompt;
|
|
24548
|
+
const client = await this.getClient();
|
|
24549
|
+
// TODO: [☂] Use here more modelRequirements
|
|
24550
|
+
if (modelRequirements.modelVariant !== 'CHAT') {
|
|
24551
|
+
throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
|
|
24349
24552
|
}
|
|
24350
|
-
|
|
24351
|
-
|
|
24553
|
+
// TODO: [👨👨👧👧] Remove:
|
|
24554
|
+
for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
|
|
24555
|
+
if (modelRequirements[key] !== undefined) {
|
|
24556
|
+
throw new NotYetImplementedError(`In \`OpenAiAssistantExecutionTools\` you cannot specify \`${key}\``);
|
|
24557
|
+
}
|
|
24558
|
+
}
|
|
24559
|
+
/*
|
|
24560
|
+
TODO: [👨👨👧👧] Implement all of this for Assistants
|
|
24561
|
+
const modelName = modelRequirements.modelName || this.getDefaultChatModel().modelName;
|
|
24562
|
+
const modelSettings = {
|
|
24563
|
+
model: modelName,
|
|
24564
|
+
|
|
24565
|
+
temperature: modelRequirements.temperature,
|
|
24566
|
+
|
|
24567
|
+
// <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
|
|
24568
|
+
// <- Note: [🧆]
|
|
24569
|
+
} as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
|
|
24570
|
+
|
|
24571
|
+
if (format === 'JSON') {
|
|
24572
|
+
modelSettings.response_format = {
|
|
24573
|
+
type: 'json_object',
|
|
24574
|
+
};
|
|
24575
|
+
}
|
|
24576
|
+
*/
|
|
24577
|
+
// <- TODO: [🚸] Not all models are compatible with JSON mode
|
|
24578
|
+
// > 'response_format' of type 'json_object' is not supported with this model.
|
|
24579
|
+
const rawPromptContent = templateParameters(content, {
|
|
24580
|
+
...parameters,
|
|
24581
|
+
modelName: 'assistant',
|
|
24582
|
+
// <- [🧠] What is the best value here
|
|
24352
24583
|
});
|
|
24353
|
-
|
|
24354
|
-
|
|
24355
|
-
|
|
24356
|
-
|
|
24357
|
-
|
|
24358
|
-
|
|
24584
|
+
// Build thread messages: include previous thread messages + current user message
|
|
24585
|
+
const threadMessages = [];
|
|
24586
|
+
// TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
|
|
24587
|
+
// Add previous messages from thread (if any)
|
|
24588
|
+
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
24589
|
+
const previousMessages = prompt.thread.map((msg) => ({
|
|
24590
|
+
role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
|
|
24591
|
+
content: msg.content,
|
|
24592
|
+
}));
|
|
24593
|
+
threadMessages.push(...previousMessages);
|
|
24359
24594
|
}
|
|
24360
|
-
|
|
24361
|
-
const
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24365
|
-
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
|
|
24369
|
-
|
|
24370
|
-
|
|
24371
|
-
|
|
24372
|
-
|
|
24373
|
-
|
|
24374
|
-
|
|
24375
|
-
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
|
|
24379
|
-
|
|
24380
|
-
|
|
24381
|
-
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24595
|
+
// Always add the current user message
|
|
24596
|
+
const currentUserMessage = {
|
|
24597
|
+
role: 'user',
|
|
24598
|
+
content: rawPromptContent,
|
|
24599
|
+
};
|
|
24600
|
+
if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
|
|
24601
|
+
const fileIds = await uploadFilesToOpenAi(client, prompt.files);
|
|
24602
|
+
currentUserMessage.attachments = fileIds.map((fileId) => ({
|
|
24603
|
+
file_id: fileId,
|
|
24604
|
+
tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
|
|
24605
|
+
}));
|
|
24606
|
+
}
|
|
24607
|
+
threadMessages.push(currentUserMessage);
|
|
24608
|
+
// Check if tools are being used - if so, use non-streaming mode
|
|
24609
|
+
const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
|
|
24610
|
+
const start = $getCurrentDate();
|
|
24611
|
+
let complete;
|
|
24612
|
+
// [🐱🚀] When tools are present, we need to use the non-streaming Runs API
|
|
24613
|
+
// because streaming doesn't support tool execution flow properly
|
|
24614
|
+
if (hasTools) {
|
|
24615
|
+
onProgress({
|
|
24616
|
+
content: '',
|
|
24617
|
+
modelName: 'assistant',
|
|
24618
|
+
timing: { start, complete: $getCurrentDate() },
|
|
24619
|
+
usage: UNCERTAIN_USAGE,
|
|
24620
|
+
rawPromptContent,
|
|
24621
|
+
rawRequest: null,
|
|
24622
|
+
rawResponse: null,
|
|
24623
|
+
});
|
|
24624
|
+
const rawRequest = {
|
|
24625
|
+
assistant_id: this.assistantId,
|
|
24626
|
+
thread: {
|
|
24627
|
+
messages: threadMessages,
|
|
24628
|
+
},
|
|
24629
|
+
tools: mapToolsToOpenAi(modelRequirements.tools),
|
|
24630
|
+
};
|
|
24631
|
+
if (this.options.isVerbose) {
|
|
24632
|
+
console.info(colors__default["default"].bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
|
|
24633
|
+
}
|
|
24634
|
+
// Create thread and run
|
|
24635
|
+
let run = (await client.beta.threads.createAndRun(rawRequest));
|
|
24636
|
+
const completedToolCalls = [];
|
|
24637
|
+
const toolCallStartedAt = new Map();
|
|
24638
|
+
// Poll until run completes or requires action
|
|
24639
|
+
while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
|
|
24640
|
+
if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
|
|
24641
|
+
// Execute tools
|
|
24642
|
+
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
|
|
24643
|
+
const toolOutputs = [];
|
|
24644
|
+
for (const toolCall of toolCalls) {
|
|
24645
|
+
if (toolCall.type === 'function') {
|
|
24646
|
+
const functionName = toolCall.function.name;
|
|
24647
|
+
const functionArgs = JSON.parse(toolCall.function.arguments);
|
|
24648
|
+
const calledAt = $getCurrentDate();
|
|
24649
|
+
if (toolCall.id) {
|
|
24650
|
+
toolCallStartedAt.set(toolCall.id, calledAt);
|
|
24651
|
+
}
|
|
24652
|
+
onProgress({
|
|
24653
|
+
content: '',
|
|
24654
|
+
modelName: 'assistant',
|
|
24655
|
+
timing: { start, complete: $getCurrentDate() },
|
|
24656
|
+
usage: UNCERTAIN_USAGE,
|
|
24657
|
+
rawPromptContent,
|
|
24658
|
+
rawRequest: null,
|
|
24659
|
+
rawResponse: null,
|
|
24660
|
+
toolCalls: [
|
|
24661
|
+
{
|
|
24662
|
+
name: functionName,
|
|
24663
|
+
arguments: toolCall.function.arguments,
|
|
24664
|
+
result: '',
|
|
24665
|
+
rawToolCall: toolCall,
|
|
24666
|
+
createdAt: calledAt,
|
|
24667
|
+
},
|
|
24668
|
+
],
|
|
24669
|
+
});
|
|
24670
|
+
if (this.options.isVerbose) {
|
|
24671
|
+
console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
|
|
24672
|
+
}
|
|
24673
|
+
// Get execution tools for script execution
|
|
24674
|
+
const executionTools = this.options
|
|
24675
|
+
.executionTools;
|
|
24676
|
+
if (!executionTools || !executionTools.script) {
|
|
24677
|
+
throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
|
|
24678
|
+
}
|
|
24679
|
+
// TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
|
|
24680
|
+
const scriptTools = Array.isArray(executionTools.script)
|
|
24681
|
+
? executionTools.script
|
|
24682
|
+
: [executionTools.script];
|
|
24683
|
+
let functionResponse;
|
|
24684
|
+
let errors;
|
|
24685
|
+
try {
|
|
24686
|
+
const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
|
|
24687
|
+
functionResponse = await scriptTool.execute({
|
|
24688
|
+
scriptLanguage: 'javascript',
|
|
24689
|
+
script: `
|
|
24690
|
+
const args = ${JSON.stringify(functionArgs)};
|
|
24691
|
+
return await ${functionName}(args);
|
|
24692
|
+
`,
|
|
24693
|
+
parameters: prompt.parameters,
|
|
24694
|
+
});
|
|
24695
|
+
if (this.options.isVerbose) {
|
|
24696
|
+
console.info(`✅ Tool ${functionName} executed:`, functionResponse);
|
|
24697
|
+
}
|
|
24698
|
+
}
|
|
24699
|
+
catch (error) {
|
|
24700
|
+
assertsError(error);
|
|
24701
|
+
const serializedError = serializeError(error);
|
|
24702
|
+
errors = [serializedError];
|
|
24703
|
+
functionResponse = spaceTrim__default["default"]((block) => `
|
|
24704
|
+
|
|
24705
|
+
The invoked tool \`${functionName}\` failed with error:
|
|
24706
|
+
|
|
24707
|
+
\`\`\`json
|
|
24708
|
+
${block(JSON.stringify(serializedError, null, 4))}
|
|
24709
|
+
\`\`\`
|
|
24710
|
+
|
|
24711
|
+
`);
|
|
24712
|
+
console.error(colors__default["default"].bgRed(`❌ Error executing tool ${functionName}:`));
|
|
24713
|
+
console.error(error);
|
|
24714
|
+
}
|
|
24715
|
+
toolOutputs.push({
|
|
24716
|
+
tool_call_id: toolCall.id,
|
|
24717
|
+
output: functionResponse,
|
|
24718
|
+
});
|
|
24719
|
+
completedToolCalls.push({
|
|
24720
|
+
name: functionName,
|
|
24721
|
+
arguments: toolCall.function.arguments,
|
|
24722
|
+
result: functionResponse,
|
|
24723
|
+
rawToolCall: toolCall,
|
|
24724
|
+
createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
|
|
24725
|
+
errors,
|
|
24726
|
+
});
|
|
24727
|
+
}
|
|
24389
24728
|
}
|
|
24729
|
+
// Submit tool outputs
|
|
24730
|
+
run = (await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
|
|
24731
|
+
tool_outputs: toolOutputs,
|
|
24732
|
+
}));
|
|
24390
24733
|
}
|
|
24391
24734
|
else {
|
|
24392
|
-
|
|
24393
|
-
|
|
24394
|
-
|
|
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
|
-
*/
|
|
24735
|
+
// Wait a bit before polling again
|
|
24736
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
24737
|
+
run = (await client.beta.threads.runs.retrieve(run.thread_id, run.id));
|
|
24409
24738
|
}
|
|
24410
24739
|
}
|
|
24411
|
-
|
|
24412
|
-
|
|
24413
|
-
|
|
24414
|
-
|
|
24415
|
-
|
|
24416
|
-
|
|
24417
|
-
|
|
24418
|
-
|
|
24740
|
+
if (run.status !== 'completed') {
|
|
24741
|
+
throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
|
|
24742
|
+
}
|
|
24743
|
+
// Get messages from the thread
|
|
24744
|
+
const messages = await client.beta.threads.messages.list(run.thread_id);
|
|
24745
|
+
const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
|
|
24746
|
+
if (assistantMessages.length === 0) {
|
|
24747
|
+
throw new PipelineExecutionError('No assistant messages found after run completion');
|
|
24748
|
+
}
|
|
24749
|
+
const lastMessage = assistantMessages[0];
|
|
24750
|
+
const textContent = lastMessage.content.find((c) => c.type === 'text');
|
|
24751
|
+
if (!textContent || textContent.type !== 'text') {
|
|
24752
|
+
throw new PipelineExecutionError('No text content in assistant response');
|
|
24419
24753
|
}
|
|
24754
|
+
complete = $getCurrentDate();
|
|
24755
|
+
const resultContent = textContent.text.value;
|
|
24756
|
+
const usage = UNCERTAIN_USAGE;
|
|
24757
|
+
// Progress callback with final result
|
|
24758
|
+
const finalChunk = {
|
|
24759
|
+
content: resultContent,
|
|
24760
|
+
modelName: 'assistant',
|
|
24761
|
+
timing: { start, complete },
|
|
24762
|
+
usage,
|
|
24763
|
+
rawPromptContent,
|
|
24764
|
+
rawRequest,
|
|
24765
|
+
rawResponse: { run, messages: messages.data },
|
|
24766
|
+
toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
|
|
24767
|
+
};
|
|
24768
|
+
onProgress(finalChunk);
|
|
24769
|
+
return exportJson({
|
|
24770
|
+
name: 'promptResult',
|
|
24771
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
|
|
24772
|
+
order: [],
|
|
24773
|
+
value: finalChunk,
|
|
24774
|
+
});
|
|
24420
24775
|
}
|
|
24776
|
+
// Streaming mode (without tools)
|
|
24777
|
+
const rawRequest = {
|
|
24778
|
+
// TODO: [👨👨👧👧] ...modelSettings,
|
|
24779
|
+
// TODO: [👨👨👧👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
|
|
24780
|
+
assistant_id: this.assistantId,
|
|
24781
|
+
thread: {
|
|
24782
|
+
messages: threadMessages,
|
|
24783
|
+
},
|
|
24784
|
+
tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
|
|
24785
|
+
// <- TODO: Add user identification here> user: this.options.user,
|
|
24786
|
+
};
|
|
24421
24787
|
if (this.options.isVerbose) {
|
|
24422
|
-
console.info(
|
|
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
|
-
});
|
|
24788
|
+
console.info(colors__default["default"].bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
|
|
24431
24789
|
}
|
|
24432
|
-
|
|
24790
|
+
const stream = await client.beta.threads.createAndRunStream(rawRequest);
|
|
24791
|
+
stream.on('connect', () => {
|
|
24433
24792
|
if (this.options.isVerbose) {
|
|
24434
|
-
console.info('
|
|
24435
|
-
vectorStoreId,
|
|
24436
|
-
fileCount: fileStreams.length,
|
|
24437
|
-
totalBytes,
|
|
24438
|
-
maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
|
|
24439
|
-
pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
|
|
24440
|
-
uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
|
|
24441
|
-
logLabel,
|
|
24442
|
-
});
|
|
24793
|
+
console.info('connect', stream.currentEvent);
|
|
24443
24794
|
}
|
|
24444
|
-
|
|
24445
|
-
|
|
24446
|
-
|
|
24447
|
-
|
|
24448
|
-
files: fileStreams,
|
|
24449
|
-
totalBytes,
|
|
24450
|
-
logLabel,
|
|
24451
|
-
});
|
|
24795
|
+
});
|
|
24796
|
+
stream.on('textDelta', (textDelta, snapshot) => {
|
|
24797
|
+
if (this.options.isVerbose && textDelta.value) {
|
|
24798
|
+
console.info('textDelta', textDelta.value);
|
|
24452
24799
|
}
|
|
24453
|
-
|
|
24454
|
-
|
|
24455
|
-
|
|
24456
|
-
|
|
24457
|
-
|
|
24458
|
-
|
|
24459
|
-
}
|
|
24800
|
+
const chunk = {
|
|
24801
|
+
content: snapshot.value,
|
|
24802
|
+
modelName: 'assistant',
|
|
24803
|
+
timing: {
|
|
24804
|
+
start,
|
|
24805
|
+
complete: $getCurrentDate(),
|
|
24806
|
+
},
|
|
24807
|
+
usage: UNCERTAIN_USAGE,
|
|
24808
|
+
rawPromptContent,
|
|
24809
|
+
rawRequest,
|
|
24810
|
+
rawResponse: snapshot,
|
|
24811
|
+
};
|
|
24812
|
+
onProgress(chunk);
|
|
24813
|
+
});
|
|
24814
|
+
stream.on('messageCreated', (message) => {
|
|
24815
|
+
if (this.options.isVerbose) {
|
|
24816
|
+
console.info('messageCreated', message);
|
|
24817
|
+
}
|
|
24818
|
+
});
|
|
24819
|
+
stream.on('messageDone', (message) => {
|
|
24820
|
+
if (this.options.isVerbose) {
|
|
24821
|
+
console.info('messageDone', message);
|
|
24460
24822
|
}
|
|
24823
|
+
});
|
|
24824
|
+
// TODO: [🐱🚀] Handle tool calls in assistants
|
|
24825
|
+
// Note: OpenAI Assistant streaming with tool calls requires special handling.
|
|
24826
|
+
// The stream will pause when a tool call is needed, and we need to:
|
|
24827
|
+
// 1. Wait for the run to reach 'requires_action' status
|
|
24828
|
+
// 2. Execute the tool calls
|
|
24829
|
+
// 3. Submit tool outputs via a separate API call (not on the stream)
|
|
24830
|
+
// 4. Continue the run
|
|
24831
|
+
// This requires switching to non-streaming mode or using the Runs API directly.
|
|
24832
|
+
// For now, tools with assistants should use the non-streaming chat completions API instead.
|
|
24833
|
+
const rawResponse = await stream.finalMessages();
|
|
24834
|
+
if (this.options.isVerbose) {
|
|
24835
|
+
console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
24461
24836
|
}
|
|
24462
|
-
|
|
24463
|
-
|
|
24464
|
-
vectorStoreId,
|
|
24465
|
-
skippedCount: skippedSources.length,
|
|
24466
|
-
logLabel,
|
|
24467
|
-
});
|
|
24837
|
+
if (rawResponse.length !== 1) {
|
|
24838
|
+
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
|
|
24468
24839
|
}
|
|
24469
|
-
|
|
24470
|
-
|
|
24471
|
-
|
|
24472
|
-
|
|
24473
|
-
|
|
24474
|
-
}
|
|
24840
|
+
if (rawResponse[0].content.length !== 1) {
|
|
24841
|
+
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
|
|
24842
|
+
}
|
|
24843
|
+
if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
|
|
24844
|
+
throw new PipelineExecutionError(`There is NOT 'text' BUT ${(_c = rawResponse[0].content[0]) === null || _c === void 0 ? void 0 : _c.type} finalMessages content type from OpenAI`);
|
|
24845
|
+
}
|
|
24846
|
+
let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
|
|
24847
|
+
// Process annotations to replace file IDs with filenames
|
|
24848
|
+
if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
|
|
24849
|
+
const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
|
|
24850
|
+
// Map to store file ID -> filename to avoid duplicate requests
|
|
24851
|
+
const fileIdToName = new Map();
|
|
24852
|
+
for (const annotation of annotations) {
|
|
24853
|
+
if (annotation.type === 'file_citation') {
|
|
24854
|
+
const fileId = annotation.file_citation.file_id;
|
|
24855
|
+
let filename = fileIdToName.get(fileId);
|
|
24856
|
+
if (!filename) {
|
|
24857
|
+
try {
|
|
24858
|
+
const file = await client.files.retrieve(fileId);
|
|
24859
|
+
filename = file.filename;
|
|
24860
|
+
fileIdToName.set(fileId, filename);
|
|
24861
|
+
}
|
|
24862
|
+
catch (error) {
|
|
24863
|
+
console.error(`Failed to retrieve file info for ${fileId}`, error);
|
|
24864
|
+
// Fallback to "Source" or keep original if fetch fails
|
|
24865
|
+
filename = 'Source';
|
|
24866
|
+
}
|
|
24867
|
+
}
|
|
24868
|
+
if (filename && resultContent) {
|
|
24869
|
+
// Replace the citation marker with filename
|
|
24870
|
+
// Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
|
|
24871
|
+
// Note: annotation.text contains the exact marker like 【4:0†source】
|
|
24872
|
+
const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
|
|
24873
|
+
resultContent = resultContent.replace(annotation.text, newText);
|
|
24874
|
+
}
|
|
24875
|
+
}
|
|
24876
|
+
}
|
|
24877
|
+
}
|
|
24878
|
+
// eslint-disable-next-line prefer-const
|
|
24879
|
+
complete = $getCurrentDate();
|
|
24880
|
+
const usage = UNCERTAIN_USAGE;
|
|
24881
|
+
// <- TODO: [🥘] Compute real usage for assistant
|
|
24882
|
+
// ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
|
|
24883
|
+
if (resultContent === null) {
|
|
24884
|
+
throw new PipelineExecutionError('No response message from OpenAI');
|
|
24885
|
+
}
|
|
24886
|
+
return exportJson({
|
|
24887
|
+
name: 'promptResult',
|
|
24888
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
24889
|
+
order: [],
|
|
24890
|
+
value: {
|
|
24891
|
+
content: resultContent,
|
|
24892
|
+
modelName: 'assistant',
|
|
24893
|
+
// <- TODO: [🥘] Detect used model in assistant
|
|
24894
|
+
// ?> model: rawResponse.model || modelName,
|
|
24895
|
+
timing: {
|
|
24896
|
+
start,
|
|
24897
|
+
complete,
|
|
24898
|
+
},
|
|
24899
|
+
usage,
|
|
24900
|
+
rawPromptContent,
|
|
24901
|
+
rawRequest,
|
|
24902
|
+
rawResponse,
|
|
24903
|
+
// <- [🗯]
|
|
24904
|
+
},
|
|
24905
|
+
});
|
|
24906
|
+
}
|
|
24907
|
+
/*
|
|
24908
|
+
public async playground() {
|
|
24909
|
+
const client = await this.getClient();
|
|
24910
|
+
|
|
24911
|
+
// List all assistants
|
|
24912
|
+
const assistants = await client.beta.assistants.list();
|
|
24913
|
+
|
|
24914
|
+
// Get details of a specific assistant
|
|
24915
|
+
const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
|
|
24916
|
+
const assistant = await client.beta.assistants.retrieve(assistantId);
|
|
24917
|
+
|
|
24918
|
+
// Update an assistant
|
|
24919
|
+
const updatedAssistant = await client.beta.assistants.update(assistantId, {
|
|
24920
|
+
name: assistant.name + '(M)',
|
|
24921
|
+
description: 'Updated description via Promptbook',
|
|
24922
|
+
metadata: {
|
|
24923
|
+
[Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
|
|
24924
|
+
},
|
|
24925
|
+
});
|
|
24926
|
+
|
|
24927
|
+
await forEver();
|
|
24928
|
+
}
|
|
24929
|
+
*/
|
|
24930
|
+
/**
|
|
24931
|
+
* Get an existing assistant tool wrapper
|
|
24932
|
+
*/
|
|
24933
|
+
getAssistant(assistantId) {
|
|
24934
|
+
return new OpenAiAssistantExecutionTools({
|
|
24935
|
+
...this.options,
|
|
24936
|
+
isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
|
|
24937
|
+
assistantId,
|
|
24938
|
+
});
|
|
24475
24939
|
}
|
|
24476
24940
|
async createNewAssistant(options) {
|
|
24477
24941
|
var _a, _b, _c;
|
|
@@ -24680,6 +25144,7 @@
|
|
|
24680
25144
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
24681
25145
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
24682
25146
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
25147
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
24683
25148
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
24684
25149
|
*
|
|
24685
25150
|
* @public exported from `@promptbook/core`
|
|
@@ -24814,6 +25279,7 @@
|
|
|
24814
25279
|
* Calls the chat model with agent-specific system prompt and requirements with streaming
|
|
24815
25280
|
*/
|
|
24816
25281
|
async callChatModelStream(prompt, onProgress) {
|
|
25282
|
+
var _a, _b;
|
|
24817
25283
|
// Ensure we're working with a chat prompt
|
|
24818
25284
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
24819
25285
|
throw new Error('AgentLlmExecutionTools only supports chat prompts');
|
|
@@ -24841,7 +25307,75 @@
|
|
|
24841
25307
|
}, // Cast to avoid readonly mismatch from spread
|
|
24842
25308
|
};
|
|
24843
25309
|
console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
|
|
24844
|
-
if (
|
|
25310
|
+
if (OpenAiAgentKitExecutionTools.isOpenAiAgentKitExecutionTools(this.options.llmTools)) {
|
|
25311
|
+
const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
|
|
25312
|
+
const vectorStoreHash = cryptoJs.SHA256(JSON.stringify((_a = modelRequirements.knowledgeSources) !== null && _a !== void 0 ? _a : [])).toString();
|
|
25313
|
+
const cachedVectorStore = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
|
|
25314
|
+
const cachedAgentKit = AgentLlmExecutionTools.agentKitAgentCache.get(this.title);
|
|
25315
|
+
let preparedAgentKit = this.options.assistantPreparationMode === 'external'
|
|
25316
|
+
? this.options.llmTools.getPreparedAgentKitAgent()
|
|
25317
|
+
: null;
|
|
25318
|
+
const vectorStoreId = (preparedAgentKit === null || preparedAgentKit === void 0 ? void 0 : preparedAgentKit.vectorStoreId) ||
|
|
25319
|
+
(cachedVectorStore && cachedVectorStore.requirementsHash === vectorStoreHash
|
|
25320
|
+
? cachedVectorStore.vectorStoreId
|
|
25321
|
+
: undefined);
|
|
25322
|
+
if (!preparedAgentKit && cachedAgentKit && cachedAgentKit.requirementsHash === requirementsHash) {
|
|
25323
|
+
if (this.options.isVerbose) {
|
|
25324
|
+
console.info('[🤰]', 'Using cached OpenAI AgentKit agent', {
|
|
25325
|
+
agent: this.title,
|
|
25326
|
+
});
|
|
25327
|
+
}
|
|
25328
|
+
preparedAgentKit = {
|
|
25329
|
+
agent: cachedAgentKit.agent,
|
|
25330
|
+
vectorStoreId: cachedAgentKit.vectorStoreId,
|
|
25331
|
+
};
|
|
25332
|
+
}
|
|
25333
|
+
if (!preparedAgentKit) {
|
|
25334
|
+
if (this.options.isVerbose) {
|
|
25335
|
+
console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
|
|
25336
|
+
agent: this.title,
|
|
25337
|
+
});
|
|
25338
|
+
}
|
|
25339
|
+
if (!vectorStoreId && ((_b = modelRequirements.knowledgeSources) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
25340
|
+
emitAssistantPreparationProgress({
|
|
25341
|
+
onProgress,
|
|
25342
|
+
prompt,
|
|
25343
|
+
modelName: this.modelName,
|
|
25344
|
+
phase: 'Creating knowledge base',
|
|
25345
|
+
});
|
|
25346
|
+
}
|
|
25347
|
+
emitAssistantPreparationProgress({
|
|
25348
|
+
onProgress,
|
|
25349
|
+
prompt,
|
|
25350
|
+
modelName: this.modelName,
|
|
25351
|
+
phase: 'Preparing AgentKit agent',
|
|
25352
|
+
});
|
|
25353
|
+
preparedAgentKit = await this.options.llmTools.prepareAgentKitAgent({
|
|
25354
|
+
name: this.title,
|
|
25355
|
+
instructions: modelRequirements.systemMessage || '',
|
|
25356
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
25357
|
+
tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
|
|
25358
|
+
vectorStoreId,
|
|
25359
|
+
});
|
|
25360
|
+
}
|
|
25361
|
+
if (preparedAgentKit.vectorStoreId) {
|
|
25362
|
+
AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
|
|
25363
|
+
vectorStoreId: preparedAgentKit.vectorStoreId,
|
|
25364
|
+
requirementsHash: vectorStoreHash,
|
|
25365
|
+
});
|
|
25366
|
+
}
|
|
25367
|
+
AgentLlmExecutionTools.agentKitAgentCache.set(this.title, {
|
|
25368
|
+
agent: preparedAgentKit.agent,
|
|
25369
|
+
requirementsHash,
|
|
25370
|
+
vectorStoreId: preparedAgentKit.vectorStoreId,
|
|
25371
|
+
});
|
|
25372
|
+
underlyingLlmResult = await this.options.llmTools.callChatModelStreamWithPreparedAgent({
|
|
25373
|
+
openAiAgentKitAgent: preparedAgentKit.agent,
|
|
25374
|
+
prompt: promptWithAgentModelRequirements,
|
|
25375
|
+
onProgress,
|
|
25376
|
+
});
|
|
25377
|
+
}
|
|
25378
|
+
else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
|
|
24845
25379
|
// ... deprecated path ...
|
|
24846
25380
|
const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
|
|
24847
25381
|
const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
|
|
@@ -24966,6 +25500,10 @@
|
|
|
24966
25500
|
return agentResult;
|
|
24967
25501
|
}
|
|
24968
25502
|
}
|
|
25503
|
+
/**
|
|
25504
|
+
* Cached AgentKit agents to avoid rebuilding identical instances.
|
|
25505
|
+
*/
|
|
25506
|
+
AgentLlmExecutionTools.agentKitAgentCache = new Map();
|
|
24969
25507
|
/**
|
|
24970
25508
|
* Cache of OpenAI assistants to avoid creating duplicates
|
|
24971
25509
|
*/
|
|
@@ -25047,6 +25585,7 @@
|
|
|
25047
25585
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
25048
25586
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
25049
25587
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
25588
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
25050
25589
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
25051
25590
|
*
|
|
25052
25591
|
* @public exported from `@promptbook/core`
|
|
@@ -25417,7 +25956,8 @@
|
|
|
25417
25956
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
25418
25957
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
25419
25958
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
25420
|
-
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
25959
|
+
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
25960
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
25421
25961
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
25422
25962
|
*
|
|
25423
25963
|
* @public exported from `@promptbook/core`
|