@promptbook/remote-server 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/esm/index.es.js
CHANGED
|
@@ -22,6 +22,7 @@ import moment from 'moment';
|
|
|
22
22
|
import sha256 from 'crypto-js/sha256';
|
|
23
23
|
import { lookup, extension } from 'mime-types';
|
|
24
24
|
import { parse, unparse } from 'papaparse';
|
|
25
|
+
import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run } from '@openai/agents';
|
|
25
26
|
import Bottleneck from 'bottleneck';
|
|
26
27
|
import OpenAI from 'openai';
|
|
27
28
|
|
|
@@ -39,7 +40,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
|
|
|
39
40
|
* @generated
|
|
40
41
|
* @see https://github.com/webgptorg/promptbook
|
|
41
42
|
*/
|
|
42
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.110.0-
|
|
43
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.110.0-7';
|
|
43
44
|
/**
|
|
44
45
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
45
46
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -19462,16 +19463,11 @@ class OpenAiCompatibleExecutionTools {
|
|
|
19462
19463
|
const openAiOptions = { ...this.options };
|
|
19463
19464
|
delete openAiOptions.isVerbose;
|
|
19464
19465
|
delete openAiOptions.userId;
|
|
19465
|
-
// Enhanced configuration
|
|
19466
|
+
// Enhanced configuration with retries and timeouts.
|
|
19466
19467
|
const enhancedOptions = {
|
|
19467
19468
|
...openAiOptions,
|
|
19468
19469
|
timeout: API_REQUEST_TIMEOUT,
|
|
19469
19470
|
maxRetries: CONNECTION_RETRIES_LIMIT,
|
|
19470
|
-
defaultHeaders: {
|
|
19471
|
-
Connection: 'keep-alive',
|
|
19472
|
-
'Keep-Alive': 'timeout=30, max=100',
|
|
19473
|
-
...openAiOptions.defaultHeaders,
|
|
19474
|
-
},
|
|
19475
19471
|
};
|
|
19476
19472
|
this.client = new OpenAI(enhancedOptions);
|
|
19477
19473
|
}
|
|
@@ -20394,644 +20390,197 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
|
|
|
20394
20390
|
}
|
|
20395
20391
|
}
|
|
20396
20392
|
|
|
20397
|
-
/**
|
|
20398
|
-
* Uploads files to OpenAI and returns their IDs
|
|
20399
|
-
*
|
|
20400
|
-
* @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
|
|
20401
|
-
*/
|
|
20402
|
-
async function uploadFilesToOpenAi(client, files) {
|
|
20403
|
-
const fileIds = [];
|
|
20404
|
-
for (const file of files) {
|
|
20405
|
-
// Note: OpenAI API expects a File object or a ReadStream
|
|
20406
|
-
// In browser environment, we can pass the File object directly
|
|
20407
|
-
// In Node.js environment, we might need to convert it or use a different approach
|
|
20408
|
-
// But since `Prompt.files` already contains `File` objects, we try to pass them directly
|
|
20409
|
-
const uploadedFile = await client.files.create({
|
|
20410
|
-
file: file,
|
|
20411
|
-
purpose: 'assistants',
|
|
20412
|
-
});
|
|
20413
|
-
fileIds.push(uploadedFile.id);
|
|
20414
|
-
}
|
|
20415
|
-
return fileIds;
|
|
20416
|
-
}
|
|
20417
|
-
|
|
20418
20393
|
const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
|
|
20419
20394
|
const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
|
|
20420
20395
|
const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
|
|
20421
20396
|
const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
|
|
20422
20397
|
/**
|
|
20423
|
-
*
|
|
20424
|
-
*
|
|
20425
|
-
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
20426
|
-
*
|
|
20427
|
-
* Note: [🦖] There are several different things in Promptbook:
|
|
20428
|
-
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
20429
|
-
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
20430
|
-
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
20431
|
-
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
20432
|
-
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
20398
|
+
* Base class for OpenAI execution tools that need hosted vector stores.
|
|
20433
20399
|
*
|
|
20434
20400
|
* @public exported from `@promptbook/openai`
|
|
20435
20401
|
*/
|
|
20436
|
-
class
|
|
20402
|
+
class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
|
|
20437
20403
|
/**
|
|
20438
|
-
*
|
|
20439
|
-
*
|
|
20440
|
-
* @param options which are relevant are directly passed to the OpenAI client
|
|
20404
|
+
* Returns the per-knowledge-source download timeout in milliseconds.
|
|
20441
20405
|
*/
|
|
20442
|
-
|
|
20406
|
+
getKnowledgeSourceDownloadTimeoutMs() {
|
|
20443
20407
|
var _a;
|
|
20444
|
-
|
|
20445
|
-
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
|
|
20446
|
-
}
|
|
20447
|
-
super(options);
|
|
20448
|
-
this.isCreatingNewAssistantsAllowed = false;
|
|
20449
|
-
this.assistantId = options.assistantId;
|
|
20450
|
-
this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
|
|
20451
|
-
if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
|
|
20452
|
-
throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
|
|
20453
|
-
}
|
|
20454
|
-
// <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
|
|
20455
|
-
// TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
|
|
20408
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
|
|
20456
20409
|
}
|
|
20457
|
-
|
|
20458
|
-
|
|
20410
|
+
/**
|
|
20411
|
+
* Returns the max concurrency for knowledge source uploads.
|
|
20412
|
+
*/
|
|
20413
|
+
getKnowledgeSourceUploadMaxConcurrency() {
|
|
20414
|
+
var _a;
|
|
20415
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
|
|
20459
20416
|
}
|
|
20460
|
-
|
|
20461
|
-
|
|
20417
|
+
/**
|
|
20418
|
+
* Returns the polling interval in milliseconds for vector store uploads.
|
|
20419
|
+
*/
|
|
20420
|
+
getKnowledgeSourceUploadPollIntervalMs() {
|
|
20421
|
+
var _a;
|
|
20422
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
|
|
20462
20423
|
}
|
|
20463
20424
|
/**
|
|
20464
|
-
*
|
|
20425
|
+
* Returns the overall upload timeout in milliseconds for vector store uploads.
|
|
20465
20426
|
*/
|
|
20466
|
-
|
|
20467
|
-
|
|
20427
|
+
getKnowledgeSourceUploadTimeoutMs() {
|
|
20428
|
+
var _a;
|
|
20429
|
+
return (_a = this.vectorStoreOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
|
|
20468
20430
|
}
|
|
20469
20431
|
/**
|
|
20470
|
-
*
|
|
20432
|
+
* Returns true if we should continue even if vector store ingestion stalls.
|
|
20471
20433
|
*/
|
|
20472
|
-
|
|
20473
|
-
var _a
|
|
20474
|
-
|
|
20475
|
-
|
|
20434
|
+
shouldContinueOnVectorStoreStall() {
|
|
20435
|
+
var _a;
|
|
20436
|
+
return (_a = this.vectorStoreOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
|
|
20437
|
+
}
|
|
20438
|
+
/**
|
|
20439
|
+
* Returns vector-store-specific options with extended settings.
|
|
20440
|
+
*/
|
|
20441
|
+
get vectorStoreOptions() {
|
|
20442
|
+
return this.options;
|
|
20443
|
+
}
|
|
20444
|
+
/**
|
|
20445
|
+
* Returns the OpenAI vector stores API surface, supporting stable and beta SDKs.
|
|
20446
|
+
*/
|
|
20447
|
+
getVectorStoresApi(client) {
|
|
20448
|
+
var _a, _b;
|
|
20449
|
+
const vectorStores = (_a = client.vectorStores) !== null && _a !== void 0 ? _a : (_b = client.beta) === null || _b === void 0 ? void 0 : _b.vectorStores;
|
|
20450
|
+
if (!vectorStores) {
|
|
20451
|
+
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.');
|
|
20476
20452
|
}
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
|
|
20480
|
-
|
|
20481
|
-
|
|
20453
|
+
return vectorStores;
|
|
20454
|
+
}
|
|
20455
|
+
/**
|
|
20456
|
+
* Downloads a knowledge source URL into a File for vector store upload.
|
|
20457
|
+
*/
|
|
20458
|
+
async downloadKnowledgeSourceFile(options) {
|
|
20459
|
+
var _a;
|
|
20460
|
+
const { source, timeoutMs, logLabel } = options;
|
|
20461
|
+
const startedAtMs = Date.now();
|
|
20462
|
+
const controller = new AbortController();
|
|
20463
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
20464
|
+
if (this.options.isVerbose) {
|
|
20465
|
+
console.info('[🤰]', 'Downloading knowledge source', {
|
|
20466
|
+
source,
|
|
20467
|
+
timeoutMs,
|
|
20468
|
+
logLabel,
|
|
20469
|
+
});
|
|
20482
20470
|
}
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20471
|
+
try {
|
|
20472
|
+
const response = await fetch(source, { signal: controller.signal });
|
|
20473
|
+
const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
|
|
20474
|
+
if (!response.ok) {
|
|
20475
|
+
console.error('[🤰]', 'Failed to download knowledge source', {
|
|
20476
|
+
source,
|
|
20477
|
+
status: response.status,
|
|
20478
|
+
statusText: response.statusText,
|
|
20479
|
+
contentType,
|
|
20480
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
20481
|
+
logLabel,
|
|
20482
|
+
});
|
|
20483
|
+
return null;
|
|
20484
|
+
}
|
|
20485
|
+
const buffer = await response.arrayBuffer();
|
|
20486
|
+
let filename = source.split('/').pop() || 'downloaded-file';
|
|
20487
|
+
try {
|
|
20488
|
+
const url = new URL(source);
|
|
20489
|
+
filename = url.pathname.split('/').pop() || filename;
|
|
20490
|
+
}
|
|
20491
|
+
catch (error) {
|
|
20492
|
+
// Keep default filename
|
|
20493
|
+
}
|
|
20494
|
+
const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
|
|
20495
|
+
const elapsedMs = Date.now() - startedAtMs;
|
|
20496
|
+
const sizeBytes = buffer.byteLength;
|
|
20497
|
+
if (this.options.isVerbose) {
|
|
20498
|
+
console.info('[🤰]', 'Downloaded knowledge source', {
|
|
20499
|
+
source,
|
|
20500
|
+
filename,
|
|
20501
|
+
sizeBytes,
|
|
20502
|
+
contentType,
|
|
20503
|
+
elapsedMs,
|
|
20504
|
+
logLabel,
|
|
20505
|
+
});
|
|
20487
20506
|
}
|
|
20507
|
+
return { file, sizeBytes, filename, elapsedMs };
|
|
20488
20508
|
}
|
|
20489
|
-
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20498
|
-
// <- Note: [🧆]
|
|
20499
|
-
} as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
|
|
20500
|
-
|
|
20501
|
-
if (format === 'JSON') {
|
|
20502
|
-
modelSettings.response_format = {
|
|
20503
|
-
type: 'json_object',
|
|
20504
|
-
};
|
|
20509
|
+
catch (error) {
|
|
20510
|
+
assertsError(error);
|
|
20511
|
+
console.error('[🤰]', 'Error downloading knowledge source', {
|
|
20512
|
+
source,
|
|
20513
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
20514
|
+
logLabel,
|
|
20515
|
+
error: serializeError(error),
|
|
20516
|
+
});
|
|
20517
|
+
return null;
|
|
20505
20518
|
}
|
|
20506
|
-
|
|
20507
|
-
|
|
20508
|
-
// > 'response_format' of type 'json_object' is not supported with this model.
|
|
20509
|
-
const rawPromptContent = templateParameters(content, {
|
|
20510
|
-
...parameters,
|
|
20511
|
-
modelName: 'assistant',
|
|
20512
|
-
// <- [🧠] What is the best value here
|
|
20513
|
-
});
|
|
20514
|
-
// Build thread messages: include previous thread messages + current user message
|
|
20515
|
-
const threadMessages = [];
|
|
20516
|
-
// TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
|
|
20517
|
-
// Add previous messages from thread (if any)
|
|
20518
|
-
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
20519
|
-
const previousMessages = prompt.thread.map((msg) => ({
|
|
20520
|
-
role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
|
|
20521
|
-
content: msg.content,
|
|
20522
|
-
}));
|
|
20523
|
-
threadMessages.push(...previousMessages);
|
|
20519
|
+
finally {
|
|
20520
|
+
clearTimeout(timeoutId);
|
|
20524
20521
|
}
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
|
|
20535
|
-
}));
|
|
20522
|
+
}
|
|
20523
|
+
/**
|
|
20524
|
+
* Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
|
|
20525
|
+
*/
|
|
20526
|
+
async logVectorStoreFileBatchDiagnostics(options) {
|
|
20527
|
+
var _a, _b, _c, _d, _e;
|
|
20528
|
+
const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
|
|
20529
|
+
if (reason === 'stalled' && !this.options.isVerbose) {
|
|
20530
|
+
return;
|
|
20536
20531
|
}
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
// because streaming doesn't support tool execution flow properly
|
|
20544
|
-
if (hasTools) {
|
|
20545
|
-
onProgress({
|
|
20546
|
-
content: '',
|
|
20547
|
-
modelName: 'assistant',
|
|
20548
|
-
timing: { start, complete: $getCurrentDate() },
|
|
20549
|
-
usage: UNCERTAIN_USAGE,
|
|
20550
|
-
rawPromptContent,
|
|
20551
|
-
rawRequest: null,
|
|
20552
|
-
rawResponse: null,
|
|
20532
|
+
if (!batchId.startsWith('vsfb_')) {
|
|
20533
|
+
console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
|
|
20534
|
+
vectorStoreId,
|
|
20535
|
+
batchId,
|
|
20536
|
+
reason,
|
|
20537
|
+
logLabel,
|
|
20553
20538
|
});
|
|
20554
|
-
|
|
20555
|
-
|
|
20556
|
-
|
|
20557
|
-
|
|
20558
|
-
|
|
20559
|
-
|
|
20560
|
-
|
|
20561
|
-
|
|
20562
|
-
|
|
20563
|
-
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
20567
|
-
const
|
|
20568
|
-
const
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
20573
|
-
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
|
|
20574
|
-
const toolOutputs = [];
|
|
20575
|
-
for (const toolCall of toolCalls) {
|
|
20576
|
-
if (toolCall.type === 'function') {
|
|
20577
|
-
const functionName = toolCall.function.name;
|
|
20578
|
-
const functionArgs = JSON.parse(toolCall.function.arguments);
|
|
20579
|
-
const calledAt = $getCurrentDate();
|
|
20580
|
-
if (toolCall.id) {
|
|
20581
|
-
toolCallStartedAt.set(toolCall.id, calledAt);
|
|
20582
|
-
}
|
|
20583
|
-
onProgress({
|
|
20584
|
-
content: '',
|
|
20585
|
-
modelName: 'assistant',
|
|
20586
|
-
timing: { start, complete: $getCurrentDate() },
|
|
20587
|
-
usage: UNCERTAIN_USAGE,
|
|
20588
|
-
rawPromptContent,
|
|
20589
|
-
rawRequest: null,
|
|
20590
|
-
rawResponse: null,
|
|
20591
|
-
toolCalls: [
|
|
20592
|
-
{
|
|
20593
|
-
name: functionName,
|
|
20594
|
-
arguments: toolCall.function.arguments,
|
|
20595
|
-
result: '',
|
|
20596
|
-
rawToolCall: toolCall,
|
|
20597
|
-
createdAt: calledAt,
|
|
20598
|
-
},
|
|
20599
|
-
],
|
|
20600
|
-
});
|
|
20601
|
-
if (this.options.isVerbose) {
|
|
20602
|
-
console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
|
|
20603
|
-
}
|
|
20604
|
-
// Get execution tools for script execution
|
|
20605
|
-
const executionTools = this.options
|
|
20606
|
-
.executionTools;
|
|
20607
|
-
if (!executionTools || !executionTools.script) {
|
|
20608
|
-
throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
|
|
20609
|
-
}
|
|
20610
|
-
// TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
|
|
20611
|
-
const scriptTools = Array.isArray(executionTools.script)
|
|
20612
|
-
? executionTools.script
|
|
20613
|
-
: [executionTools.script];
|
|
20614
|
-
let functionResponse;
|
|
20615
|
-
let errors;
|
|
20616
|
-
try {
|
|
20617
|
-
const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
|
|
20618
|
-
functionResponse = await scriptTool.execute({
|
|
20619
|
-
scriptLanguage: 'javascript',
|
|
20620
|
-
script: `
|
|
20621
|
-
const args = ${JSON.stringify(functionArgs)};
|
|
20622
|
-
return await ${functionName}(args);
|
|
20623
|
-
`,
|
|
20624
|
-
parameters: prompt.parameters,
|
|
20625
|
-
});
|
|
20626
|
-
if (this.options.isVerbose) {
|
|
20627
|
-
console.info(`✅ Tool ${functionName} executed:`, functionResponse);
|
|
20628
|
-
}
|
|
20629
|
-
}
|
|
20630
|
-
catch (error) {
|
|
20631
|
-
assertsError(error);
|
|
20632
|
-
const serializedError = serializeError(error);
|
|
20633
|
-
errors = [serializedError];
|
|
20634
|
-
functionResponse = spaceTrim$2((block) => `
|
|
20635
|
-
|
|
20636
|
-
The invoked tool \`${functionName}\` failed with error:
|
|
20637
|
-
|
|
20638
|
-
\`\`\`json
|
|
20639
|
-
${block(JSON.stringify(serializedError, null, 4))}
|
|
20640
|
-
\`\`\`
|
|
20641
|
-
|
|
20642
|
-
`);
|
|
20643
|
-
console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
|
|
20644
|
-
console.error(error);
|
|
20645
|
-
}
|
|
20646
|
-
toolOutputs.push({
|
|
20647
|
-
tool_call_id: toolCall.id,
|
|
20648
|
-
output: functionResponse,
|
|
20649
|
-
});
|
|
20650
|
-
completedToolCalls.push({
|
|
20651
|
-
name: functionName,
|
|
20652
|
-
arguments: toolCall.function.arguments,
|
|
20653
|
-
result: functionResponse,
|
|
20654
|
-
rawToolCall: toolCall,
|
|
20655
|
-
createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
|
|
20656
|
-
errors,
|
|
20657
|
-
});
|
|
20658
|
-
}
|
|
20659
|
-
}
|
|
20660
|
-
// Submit tool outputs
|
|
20661
|
-
run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
|
|
20662
|
-
tool_outputs: toolOutputs,
|
|
20663
|
-
});
|
|
20664
|
-
}
|
|
20665
|
-
else {
|
|
20666
|
-
// Wait a bit before polling again
|
|
20667
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
20668
|
-
run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
|
|
20669
|
-
}
|
|
20670
|
-
}
|
|
20671
|
-
if (run.status !== 'completed') {
|
|
20672
|
-
throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
|
|
20673
|
-
}
|
|
20674
|
-
// Get messages from the thread
|
|
20675
|
-
const messages = await client.beta.threads.messages.list(run.thread_id);
|
|
20676
|
-
const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
|
|
20677
|
-
if (assistantMessages.length === 0) {
|
|
20678
|
-
throw new PipelineExecutionError('No assistant messages found after run completion');
|
|
20679
|
-
}
|
|
20680
|
-
const lastMessage = assistantMessages[0];
|
|
20681
|
-
const textContent = lastMessage.content.find((c) => c.type === 'text');
|
|
20682
|
-
if (!textContent || textContent.type !== 'text') {
|
|
20683
|
-
throw new PipelineExecutionError('No text content in assistant response');
|
|
20684
|
-
}
|
|
20685
|
-
complete = $getCurrentDate();
|
|
20686
|
-
const resultContent = textContent.text.value;
|
|
20687
|
-
const usage = UNCERTAIN_USAGE;
|
|
20688
|
-
// Progress callback with final result
|
|
20689
|
-
const finalChunk = {
|
|
20690
|
-
content: resultContent,
|
|
20691
|
-
modelName: 'assistant',
|
|
20692
|
-
timing: { start, complete },
|
|
20693
|
-
usage,
|
|
20694
|
-
rawPromptContent,
|
|
20695
|
-
rawRequest,
|
|
20696
|
-
rawResponse: { run, messages: messages.data },
|
|
20697
|
-
toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
|
|
20698
|
-
};
|
|
20699
|
-
onProgress(finalChunk);
|
|
20700
|
-
return exportJson({
|
|
20701
|
-
name: 'promptResult',
|
|
20702
|
-
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
|
|
20703
|
-
order: [],
|
|
20704
|
-
value: finalChunk,
|
|
20705
|
-
});
|
|
20706
|
-
}
|
|
20707
|
-
// Streaming mode (without tools)
|
|
20708
|
-
const rawRequest = {
|
|
20709
|
-
// TODO: [👨👨👧👧] ...modelSettings,
|
|
20710
|
-
// TODO: [👨👨👧👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
|
|
20711
|
-
assistant_id: this.assistantId,
|
|
20712
|
-
thread: {
|
|
20713
|
-
messages: threadMessages,
|
|
20714
|
-
},
|
|
20715
|
-
tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
|
|
20716
|
-
// <- TODO: Add user identification here> user: this.options.user,
|
|
20717
|
-
};
|
|
20718
|
-
if (this.options.isVerbose) {
|
|
20719
|
-
console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
|
|
20720
|
-
}
|
|
20721
|
-
const stream = await client.beta.threads.createAndRunStream(rawRequest);
|
|
20722
|
-
stream.on('connect', () => {
|
|
20723
|
-
if (this.options.isVerbose) {
|
|
20724
|
-
console.info('connect', stream.currentEvent);
|
|
20725
|
-
}
|
|
20726
|
-
});
|
|
20727
|
-
stream.on('textDelta', (textDelta, snapshot) => {
|
|
20728
|
-
if (this.options.isVerbose && textDelta.value) {
|
|
20729
|
-
console.info('textDelta', textDelta.value);
|
|
20730
|
-
}
|
|
20731
|
-
const chunk = {
|
|
20732
|
-
content: snapshot.value,
|
|
20733
|
-
modelName: 'assistant',
|
|
20734
|
-
timing: {
|
|
20735
|
-
start,
|
|
20736
|
-
complete: $getCurrentDate(),
|
|
20737
|
-
},
|
|
20738
|
-
usage: UNCERTAIN_USAGE,
|
|
20739
|
-
rawPromptContent,
|
|
20740
|
-
rawRequest,
|
|
20741
|
-
rawResponse: snapshot,
|
|
20742
|
-
};
|
|
20743
|
-
onProgress(chunk);
|
|
20744
|
-
});
|
|
20745
|
-
stream.on('messageCreated', (message) => {
|
|
20746
|
-
if (this.options.isVerbose) {
|
|
20747
|
-
console.info('messageCreated', message);
|
|
20748
|
-
}
|
|
20749
|
-
});
|
|
20750
|
-
stream.on('messageDone', (message) => {
|
|
20751
|
-
if (this.options.isVerbose) {
|
|
20752
|
-
console.info('messageDone', message);
|
|
20753
|
-
}
|
|
20754
|
-
});
|
|
20755
|
-
// TODO: [🐱🚀] Handle tool calls in assistants
|
|
20756
|
-
// Note: OpenAI Assistant streaming with tool calls requires special handling.
|
|
20757
|
-
// The stream will pause when a tool call is needed, and we need to:
|
|
20758
|
-
// 1. Wait for the run to reach 'requires_action' status
|
|
20759
|
-
// 2. Execute the tool calls
|
|
20760
|
-
// 3. Submit tool outputs via a separate API call (not on the stream)
|
|
20761
|
-
// 4. Continue the run
|
|
20762
|
-
// This requires switching to non-streaming mode or using the Runs API directly.
|
|
20763
|
-
// For now, tools with assistants should use the non-streaming chat completions API instead.
|
|
20764
|
-
const rawResponse = await stream.finalMessages();
|
|
20765
|
-
if (this.options.isVerbose) {
|
|
20766
|
-
console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
20767
|
-
}
|
|
20768
|
-
if (rawResponse.length !== 1) {
|
|
20769
|
-
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
|
|
20770
|
-
}
|
|
20771
|
-
if (rawResponse[0].content.length !== 1) {
|
|
20772
|
-
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
|
|
20773
|
-
}
|
|
20774
|
-
if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
|
|
20775
|
-
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`);
|
|
20776
|
-
}
|
|
20777
|
-
let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
|
|
20778
|
-
// Process annotations to replace file IDs with filenames
|
|
20779
|
-
if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
|
|
20780
|
-
const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
|
|
20781
|
-
// Map to store file ID -> filename to avoid duplicate requests
|
|
20782
|
-
const fileIdToName = new Map();
|
|
20783
|
-
for (const annotation of annotations) {
|
|
20784
|
-
if (annotation.type === 'file_citation') {
|
|
20785
|
-
const fileId = annotation.file_citation.file_id;
|
|
20786
|
-
let filename = fileIdToName.get(fileId);
|
|
20787
|
-
if (!filename) {
|
|
20788
|
-
try {
|
|
20789
|
-
const file = await client.files.retrieve(fileId);
|
|
20790
|
-
filename = file.filename;
|
|
20791
|
-
fileIdToName.set(fileId, filename);
|
|
20792
|
-
}
|
|
20793
|
-
catch (error) {
|
|
20794
|
-
console.error(`Failed to retrieve file info for ${fileId}`, error);
|
|
20795
|
-
// Fallback to "Source" or keep original if fetch fails
|
|
20796
|
-
filename = 'Source';
|
|
20797
|
-
}
|
|
20798
|
-
}
|
|
20799
|
-
if (filename && resultContent) {
|
|
20800
|
-
// Replace the citation marker with filename
|
|
20801
|
-
// Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
|
|
20802
|
-
// Note: annotation.text contains the exact marker like 【4:0†source】
|
|
20803
|
-
const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
|
|
20804
|
-
resultContent = resultContent.replace(annotation.text, newText);
|
|
20805
|
-
}
|
|
20806
|
-
}
|
|
20807
|
-
}
|
|
20808
|
-
}
|
|
20809
|
-
// eslint-disable-next-line prefer-const
|
|
20810
|
-
complete = $getCurrentDate();
|
|
20811
|
-
const usage = UNCERTAIN_USAGE;
|
|
20812
|
-
// <- TODO: [🥘] Compute real usage for assistant
|
|
20813
|
-
// ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
|
|
20814
|
-
if (resultContent === null) {
|
|
20815
|
-
throw new PipelineExecutionError('No response message from OpenAI');
|
|
20816
|
-
}
|
|
20817
|
-
return exportJson({
|
|
20818
|
-
name: 'promptResult',
|
|
20819
|
-
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
20820
|
-
order: [],
|
|
20821
|
-
value: {
|
|
20822
|
-
content: resultContent,
|
|
20823
|
-
modelName: 'assistant',
|
|
20824
|
-
// <- TODO: [🥘] Detect used model in assistant
|
|
20825
|
-
// ?> model: rawResponse.model || modelName,
|
|
20826
|
-
timing: {
|
|
20827
|
-
start,
|
|
20828
|
-
complete,
|
|
20829
|
-
},
|
|
20830
|
-
usage,
|
|
20831
|
-
rawPromptContent,
|
|
20832
|
-
rawRequest,
|
|
20833
|
-
rawResponse,
|
|
20834
|
-
// <- [🗯]
|
|
20835
|
-
},
|
|
20836
|
-
});
|
|
20837
|
-
}
|
|
20838
|
-
/*
|
|
20839
|
-
public async playground() {
|
|
20840
|
-
const client = await this.getClient();
|
|
20841
|
-
|
|
20842
|
-
// List all assistants
|
|
20843
|
-
const assistants = await client.beta.assistants.list();
|
|
20844
|
-
|
|
20845
|
-
// Get details of a specific assistant
|
|
20846
|
-
const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
|
|
20847
|
-
const assistant = await client.beta.assistants.retrieve(assistantId);
|
|
20848
|
-
|
|
20849
|
-
// Update an assistant
|
|
20850
|
-
const updatedAssistant = await client.beta.assistants.update(assistantId, {
|
|
20851
|
-
name: assistant.name + '(M)',
|
|
20852
|
-
description: 'Updated description via Promptbook',
|
|
20853
|
-
metadata: {
|
|
20854
|
-
[Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
|
|
20855
|
-
},
|
|
20856
|
-
});
|
|
20857
|
-
|
|
20858
|
-
await forEver();
|
|
20859
|
-
}
|
|
20860
|
-
*/
|
|
20861
|
-
/**
|
|
20862
|
-
* Get an existing assistant tool wrapper
|
|
20863
|
-
*/
|
|
20864
|
-
getAssistant(assistantId) {
|
|
20865
|
-
return new OpenAiAssistantExecutionTools({
|
|
20866
|
-
...this.options,
|
|
20867
|
-
isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
|
|
20868
|
-
assistantId,
|
|
20869
|
-
});
|
|
20870
|
-
}
|
|
20871
|
-
/**
|
|
20872
|
-
* Returns the per-knowledge-source download timeout in milliseconds.
|
|
20873
|
-
*/
|
|
20874
|
-
getKnowledgeSourceDownloadTimeoutMs() {
|
|
20875
|
-
var _a;
|
|
20876
|
-
return (_a = this.assistantOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
|
|
20877
|
-
}
|
|
20878
|
-
/**
|
|
20879
|
-
* Returns the max concurrency for knowledge source uploads.
|
|
20880
|
-
*/
|
|
20881
|
-
getKnowledgeSourceUploadMaxConcurrency() {
|
|
20882
|
-
var _a;
|
|
20883
|
-
return (_a = this.assistantOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
|
|
20884
|
-
}
|
|
20885
|
-
/**
|
|
20886
|
-
* Returns the polling interval in milliseconds for vector store uploads.
|
|
20887
|
-
*/
|
|
20888
|
-
getKnowledgeSourceUploadPollIntervalMs() {
|
|
20889
|
-
var _a;
|
|
20890
|
-
return (_a = this.assistantOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
|
|
20891
|
-
}
|
|
20892
|
-
/**
|
|
20893
|
-
* Returns the overall upload timeout in milliseconds for vector store uploads.
|
|
20894
|
-
*/
|
|
20895
|
-
getKnowledgeSourceUploadTimeoutMs() {
|
|
20896
|
-
var _a;
|
|
20897
|
-
return (_a = this.assistantOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
|
|
20898
|
-
}
|
|
20899
|
-
/**
|
|
20900
|
-
* Returns true if we should continue even if vector store ingestion stalls.
|
|
20901
|
-
*/
|
|
20902
|
-
shouldContinueOnVectorStoreStall() {
|
|
20903
|
-
var _a;
|
|
20904
|
-
return (_a = this.assistantOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
|
|
20905
|
-
}
|
|
20906
|
-
/**
|
|
20907
|
-
* Returns assistant-specific options with extended settings.
|
|
20908
|
-
*/
|
|
20909
|
-
get assistantOptions() {
|
|
20910
|
-
return this.options;
|
|
20911
|
-
}
|
|
20912
|
-
/**
|
|
20913
|
-
* Downloads a knowledge source URL into a File for vector store upload.
|
|
20914
|
-
*/
|
|
20915
|
-
async downloadKnowledgeSourceFile(options) {
|
|
20916
|
-
var _a;
|
|
20917
|
-
const { source, timeoutMs, logLabel } = options;
|
|
20918
|
-
const startedAtMs = Date.now();
|
|
20919
|
-
const controller = new AbortController();
|
|
20920
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
20921
|
-
if (this.options.isVerbose) {
|
|
20922
|
-
console.info('[🤰]', 'Downloading knowledge source', {
|
|
20923
|
-
source,
|
|
20924
|
-
timeoutMs,
|
|
20925
|
-
logLabel,
|
|
20926
|
-
});
|
|
20927
|
-
}
|
|
20928
|
-
try {
|
|
20929
|
-
const response = await fetch(source, { signal: controller.signal });
|
|
20930
|
-
const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
|
|
20931
|
-
if (!response.ok) {
|
|
20932
|
-
console.error('[🤰]', 'Failed to download knowledge source', {
|
|
20933
|
-
source,
|
|
20934
|
-
status: response.status,
|
|
20935
|
-
statusText: response.statusText,
|
|
20936
|
-
contentType,
|
|
20937
|
-
elapsedMs: Date.now() - startedAtMs,
|
|
20938
|
-
logLabel,
|
|
20939
|
-
});
|
|
20940
|
-
return null;
|
|
20941
|
-
}
|
|
20942
|
-
const buffer = await response.arrayBuffer();
|
|
20943
|
-
let filename = source.split('/').pop() || 'downloaded-file';
|
|
20944
|
-
try {
|
|
20945
|
-
const url = new URL(source);
|
|
20946
|
-
filename = url.pathname.split('/').pop() || filename;
|
|
20947
|
-
}
|
|
20948
|
-
catch (error) {
|
|
20949
|
-
// Keep default filename
|
|
20950
|
-
}
|
|
20951
|
-
const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
|
|
20952
|
-
const elapsedMs = Date.now() - startedAtMs;
|
|
20953
|
-
const sizeBytes = buffer.byteLength;
|
|
20954
|
-
if (this.options.isVerbose) {
|
|
20955
|
-
console.info('[🤰]', 'Downloaded knowledge source', {
|
|
20956
|
-
source,
|
|
20957
|
-
filename,
|
|
20958
|
-
sizeBytes,
|
|
20959
|
-
contentType,
|
|
20960
|
-
elapsedMs,
|
|
20961
|
-
logLabel,
|
|
20962
|
-
});
|
|
20963
|
-
}
|
|
20964
|
-
return { file, sizeBytes, filename, elapsedMs };
|
|
20965
|
-
}
|
|
20966
|
-
catch (error) {
|
|
20967
|
-
assertsError(error);
|
|
20968
|
-
console.error('[🤰]', 'Error downloading knowledge source', {
|
|
20969
|
-
source,
|
|
20970
|
-
elapsedMs: Date.now() - startedAtMs,
|
|
20971
|
-
logLabel,
|
|
20972
|
-
error: serializeError(error),
|
|
20973
|
-
});
|
|
20974
|
-
return null;
|
|
20975
|
-
}
|
|
20976
|
-
finally {
|
|
20977
|
-
clearTimeout(timeoutId);
|
|
20978
|
-
}
|
|
20979
|
-
}
|
|
20980
|
-
/**
|
|
20981
|
-
* Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
|
|
20982
|
-
*/
|
|
20983
|
-
async logVectorStoreFileBatchDiagnostics(options) {
|
|
20984
|
-
var _a, _b;
|
|
20985
|
-
const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
|
|
20986
|
-
if (reason === 'stalled' && !this.options.isVerbose) {
|
|
20987
|
-
return;
|
|
20988
|
-
}
|
|
20989
|
-
if (!batchId.startsWith('vsfb_')) {
|
|
20990
|
-
console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
|
|
20991
|
-
vectorStoreId,
|
|
20992
|
-
batchId,
|
|
20993
|
-
reason,
|
|
20994
|
-
logLabel,
|
|
20995
|
-
});
|
|
20996
|
-
return;
|
|
20997
|
-
}
|
|
20998
|
-
const fileIdToMetadata = new Map();
|
|
20999
|
-
for (const file of uploadedFiles) {
|
|
21000
|
-
fileIdToMetadata.set(file.fileId, file);
|
|
21001
|
-
}
|
|
21002
|
-
try {
|
|
21003
|
-
const limit = Math.min(100, Math.max(10, uploadedFiles.length));
|
|
21004
|
-
const batchFilesPage = await client.beta.vectorStores.fileBatches.listFiles(vectorStoreId, batchId, {
|
|
21005
|
-
limit,
|
|
21006
|
-
});
|
|
21007
|
-
const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
|
|
21008
|
-
const statusCounts = {
|
|
21009
|
-
in_progress: 0,
|
|
21010
|
-
completed: 0,
|
|
21011
|
-
failed: 0,
|
|
21012
|
-
cancelled: 0,
|
|
20539
|
+
return;
|
|
20540
|
+
}
|
|
20541
|
+
const fileIdToMetadata = new Map();
|
|
20542
|
+
for (const file of uploadedFiles) {
|
|
20543
|
+
fileIdToMetadata.set(file.fileId, file);
|
|
20544
|
+
}
|
|
20545
|
+
try {
|
|
20546
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
20547
|
+
const limit = Math.min(100, Math.max(10, uploadedFiles.length));
|
|
20548
|
+
const batchFilesPage = await vectorStores.fileBatches.listFiles(batchId, {
|
|
20549
|
+
vector_store_id: vectorStoreId,
|
|
20550
|
+
limit,
|
|
20551
|
+
});
|
|
20552
|
+
const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
|
|
20553
|
+
const statusCounts = {
|
|
20554
|
+
in_progress: 0,
|
|
20555
|
+
completed: 0,
|
|
20556
|
+
failed: 0,
|
|
20557
|
+
cancelled: 0,
|
|
21013
20558
|
};
|
|
21014
20559
|
const errorSamples = [];
|
|
21015
20560
|
const inProgressSamples = [];
|
|
21016
20561
|
const batchFileIds = new Set();
|
|
21017
20562
|
for (const file of batchFiles) {
|
|
21018
|
-
|
|
21019
|
-
statusCounts[
|
|
21020
|
-
const
|
|
21021
|
-
|
|
20563
|
+
const status = (_b = file.status) !== null && _b !== void 0 ? _b : 'unknown';
|
|
20564
|
+
statusCounts[status] = ((_c = statusCounts[status]) !== null && _c !== void 0 ? _c : 0) + 1;
|
|
20565
|
+
const vectorStoreFileId = file.id;
|
|
20566
|
+
const uploadedFileId = (_d = file.file_id) !== null && _d !== void 0 ? _d : file.fileId;
|
|
20567
|
+
const fileId = uploadedFileId !== null && uploadedFileId !== void 0 ? uploadedFileId : vectorStoreFileId;
|
|
20568
|
+
batchFileIds.add(fileId);
|
|
20569
|
+
const metadata = fileIdToMetadata.get(fileId);
|
|
20570
|
+
if (status === 'failed') {
|
|
21022
20571
|
errorSamples.push({
|
|
21023
|
-
fileId
|
|
20572
|
+
fileId,
|
|
20573
|
+
status,
|
|
20574
|
+
error: (_e = file.last_error) === null || _e === void 0 ? void 0 : _e.message,
|
|
21024
20575
|
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
21025
|
-
|
|
21026
|
-
status: file.status,
|
|
21027
|
-
lastError: file.last_error,
|
|
20576
|
+
vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
|
|
21028
20577
|
});
|
|
21029
20578
|
}
|
|
21030
|
-
|
|
20579
|
+
if (status === 'in_progress') {
|
|
21031
20580
|
inProgressSamples.push({
|
|
21032
|
-
fileId
|
|
20581
|
+
fileId,
|
|
21033
20582
|
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
21034
|
-
|
|
20583
|
+
vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
|
|
21035
20584
|
});
|
|
21036
20585
|
}
|
|
21037
20586
|
}
|
|
@@ -21043,7 +20592,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
21043
20592
|
filename: file.filename,
|
|
21044
20593
|
sizeBytes: file.sizeBytes,
|
|
21045
20594
|
}));
|
|
21046
|
-
const vectorStore = await
|
|
20595
|
+
const vectorStore = await vectorStores.retrieve(vectorStoreId);
|
|
21047
20596
|
const logPayload = {
|
|
21048
20597
|
vectorStoreId,
|
|
21049
20598
|
batchId,
|
|
@@ -21077,8 +20626,9 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
21077
20626
|
* Uploads knowledge source files to the vector store and polls until processing completes.
|
|
21078
20627
|
*/
|
|
21079
20628
|
async uploadKnowledgeSourceFilesToVectorStore(options) {
|
|
21080
|
-
var _a, _b, _c, _d;
|
|
20629
|
+
var _a, _b, _c, _d, _e, _f;
|
|
21081
20630
|
const { client, vectorStoreId, files, totalBytes, logLabel } = options;
|
|
20631
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
21082
20632
|
const uploadStartedAtMs = Date.now();
|
|
21083
20633
|
const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
|
|
21084
20634
|
const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
|
|
@@ -21195,373 +20745,1288 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
|
21195
20745
|
failedCount: failedUploads.length,
|
|
21196
20746
|
logLabel,
|
|
21197
20747
|
});
|
|
21198
|
-
return null;
|
|
20748
|
+
return null;
|
|
20749
|
+
}
|
|
20750
|
+
const batch = await vectorStores.fileBatches.create(vectorStoreId, {
|
|
20751
|
+
file_ids: fileIds,
|
|
20752
|
+
});
|
|
20753
|
+
const expectedBatchId = batch.id;
|
|
20754
|
+
const expectedBatchIdValid = expectedBatchId.startsWith('vsfb_');
|
|
20755
|
+
if (!expectedBatchIdValid) {
|
|
20756
|
+
console.error('[🤰]', 'Vector store file batch id looks invalid', {
|
|
20757
|
+
vectorStoreId,
|
|
20758
|
+
batchId: expectedBatchId,
|
|
20759
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
20760
|
+
logLabel,
|
|
20761
|
+
});
|
|
20762
|
+
}
|
|
20763
|
+
else if (batch.vector_store_id !== vectorStoreId) {
|
|
20764
|
+
console.error('[🤰]', 'Vector store file batch vector store id mismatch', {
|
|
20765
|
+
vectorStoreId,
|
|
20766
|
+
batchId: expectedBatchId,
|
|
20767
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
20768
|
+
logLabel,
|
|
20769
|
+
});
|
|
20770
|
+
}
|
|
20771
|
+
if (this.options.isVerbose) {
|
|
20772
|
+
console.info('[🤰]', 'Created vector store file batch', {
|
|
20773
|
+
vectorStoreId,
|
|
20774
|
+
batchId: expectedBatchId,
|
|
20775
|
+
fileCount: fileIds.length,
|
|
20776
|
+
logLabel,
|
|
20777
|
+
});
|
|
20778
|
+
}
|
|
20779
|
+
const pollStartedAtMs = Date.now();
|
|
20780
|
+
const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
|
|
20781
|
+
const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
|
|
20782
|
+
// let lastStatus: string | undefined;
|
|
20783
|
+
let lastCountsKey = '';
|
|
20784
|
+
let lastProgressKey = '';
|
|
20785
|
+
let lastLogAtMs = 0;
|
|
20786
|
+
let lastProgressAtMs = pollStartedAtMs;
|
|
20787
|
+
let lastDiagnosticsAtMs = pollStartedAtMs;
|
|
20788
|
+
let latestBatch = batch;
|
|
20789
|
+
let loggedBatchIdMismatch = false;
|
|
20790
|
+
let loggedBatchIdFallback = false;
|
|
20791
|
+
let loggedBatchIdInvalid = false;
|
|
20792
|
+
let shouldPoll = true;
|
|
20793
|
+
while (shouldPoll) {
|
|
20794
|
+
const nowMs = Date.now();
|
|
20795
|
+
// [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
|
|
20796
|
+
const rawBatchId = typeof latestBatch.id === 'string' ? latestBatch.id : '';
|
|
20797
|
+
const rawVectorStoreId = latestBatch.vector_store_id;
|
|
20798
|
+
let returnedBatchId = rawBatchId;
|
|
20799
|
+
let returnedBatchIdValid = typeof returnedBatchId === 'string' && returnedBatchId.startsWith('vsfb_');
|
|
20800
|
+
if (!returnedBatchIdValid && expectedBatchIdValid) {
|
|
20801
|
+
if (!loggedBatchIdFallback) {
|
|
20802
|
+
console.error('[🤰]', 'Vector store file batch id missing from response; falling back to expected', {
|
|
20803
|
+
vectorStoreId,
|
|
20804
|
+
expectedBatchId,
|
|
20805
|
+
returnedBatchId,
|
|
20806
|
+
rawVectorStoreId,
|
|
20807
|
+
logLabel,
|
|
20808
|
+
});
|
|
20809
|
+
loggedBatchIdFallback = true;
|
|
20810
|
+
}
|
|
20811
|
+
returnedBatchId = expectedBatchId;
|
|
20812
|
+
returnedBatchIdValid = true;
|
|
20813
|
+
}
|
|
20814
|
+
if (!returnedBatchIdValid && !loggedBatchIdInvalid) {
|
|
20815
|
+
console.error('[🤰]', 'Vector store file batch id is invalid; stopping polling', {
|
|
20816
|
+
vectorStoreId,
|
|
20817
|
+
expectedBatchId,
|
|
20818
|
+
returnedBatchId,
|
|
20819
|
+
rawVectorStoreId,
|
|
20820
|
+
logLabel,
|
|
20821
|
+
});
|
|
20822
|
+
loggedBatchIdInvalid = true;
|
|
20823
|
+
}
|
|
20824
|
+
const batchIdMismatch = expectedBatchIdValid && returnedBatchIdValid && returnedBatchId !== expectedBatchId;
|
|
20825
|
+
if (batchIdMismatch && !loggedBatchIdMismatch) {
|
|
20826
|
+
console.error('[🤰]', 'Vector store file batch id mismatch', {
|
|
20827
|
+
vectorStoreId,
|
|
20828
|
+
expectedBatchId,
|
|
20829
|
+
returnedBatchId,
|
|
20830
|
+
logLabel,
|
|
20831
|
+
});
|
|
20832
|
+
loggedBatchIdMismatch = true;
|
|
20833
|
+
}
|
|
20834
|
+
if (returnedBatchIdValid) {
|
|
20835
|
+
latestBatch = await vectorStores.fileBatches.retrieve(returnedBatchId, {
|
|
20836
|
+
vector_store_id: vectorStoreId,
|
|
20837
|
+
});
|
|
20838
|
+
}
|
|
20839
|
+
else {
|
|
20840
|
+
shouldPoll = false;
|
|
20841
|
+
continue;
|
|
20842
|
+
}
|
|
20843
|
+
const status = (_e = latestBatch.status) !== null && _e !== void 0 ? _e : 'unknown';
|
|
20844
|
+
const fileCounts = (_f = latestBatch.file_counts) !== null && _f !== void 0 ? _f : {};
|
|
20845
|
+
const progressKey = JSON.stringify(fileCounts);
|
|
20846
|
+
const statusCountsKey = `${status}-${progressKey}`;
|
|
20847
|
+
const isProgressing = progressKey !== lastProgressKey;
|
|
20848
|
+
if (isProgressing) {
|
|
20849
|
+
lastProgressAtMs = nowMs;
|
|
20850
|
+
lastProgressKey = progressKey;
|
|
20851
|
+
}
|
|
20852
|
+
if (this.options.isVerbose &&
|
|
20853
|
+
(statusCountsKey !== lastCountsKey || nowMs - lastLogAtMs >= progressLogIntervalMs)) {
|
|
20854
|
+
console.info('[🤰]', 'Vector store file batch status', {
|
|
20855
|
+
vectorStoreId,
|
|
20856
|
+
batchId: returnedBatchId,
|
|
20857
|
+
status,
|
|
20858
|
+
fileCounts,
|
|
20859
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
20860
|
+
logLabel,
|
|
20861
|
+
});
|
|
20862
|
+
lastCountsKey = statusCountsKey;
|
|
20863
|
+
lastLogAtMs = nowMs;
|
|
20864
|
+
}
|
|
20865
|
+
if (status === 'in_progress' &&
|
|
20866
|
+
nowMs - lastProgressAtMs >= VECTOR_STORE_STALL_LOG_THRESHOLD_MS &&
|
|
20867
|
+
nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
|
|
20868
|
+
lastDiagnosticsAtMs = nowMs;
|
|
20869
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
20870
|
+
client,
|
|
20871
|
+
vectorStoreId,
|
|
20872
|
+
batchId: returnedBatchId,
|
|
20873
|
+
uploadedFiles,
|
|
20874
|
+
logLabel,
|
|
20875
|
+
reason: 'stalled',
|
|
20876
|
+
});
|
|
20877
|
+
}
|
|
20878
|
+
if (status === 'completed') {
|
|
20879
|
+
if (this.options.isVerbose) {
|
|
20880
|
+
console.info('[🤰]', 'Vector store file batch completed', {
|
|
20881
|
+
vectorStoreId,
|
|
20882
|
+
batchId: returnedBatchId,
|
|
20883
|
+
fileCounts,
|
|
20884
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
20885
|
+
logLabel,
|
|
20886
|
+
});
|
|
20887
|
+
}
|
|
20888
|
+
shouldPoll = false;
|
|
20889
|
+
continue;
|
|
20890
|
+
}
|
|
20891
|
+
if (status === 'failed') {
|
|
20892
|
+
console.error('[🤰]', 'Vector store file batch completed with failures', {
|
|
20893
|
+
vectorStoreId,
|
|
20894
|
+
batchId: returnedBatchId,
|
|
20895
|
+
fileCounts,
|
|
20896
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
20897
|
+
logLabel,
|
|
20898
|
+
});
|
|
20899
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
20900
|
+
client,
|
|
20901
|
+
vectorStoreId,
|
|
20902
|
+
batchId: returnedBatchId,
|
|
20903
|
+
uploadedFiles,
|
|
20904
|
+
logLabel,
|
|
20905
|
+
reason: 'failed',
|
|
20906
|
+
});
|
|
20907
|
+
shouldPoll = false;
|
|
20908
|
+
continue;
|
|
20909
|
+
}
|
|
20910
|
+
if (status === 'cancelled') {
|
|
20911
|
+
console.error('[🤰]', 'Vector store file batch did not complete', {
|
|
20912
|
+
vectorStoreId,
|
|
20913
|
+
batchId: returnedBatchId,
|
|
20914
|
+
status,
|
|
20915
|
+
fileCounts,
|
|
20916
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
20917
|
+
logLabel,
|
|
20918
|
+
});
|
|
20919
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
20920
|
+
client,
|
|
20921
|
+
vectorStoreId,
|
|
20922
|
+
batchId: returnedBatchId,
|
|
20923
|
+
uploadedFiles,
|
|
20924
|
+
logLabel,
|
|
20925
|
+
reason: 'failed',
|
|
20926
|
+
});
|
|
20927
|
+
shouldPoll = false;
|
|
20928
|
+
continue;
|
|
20929
|
+
}
|
|
20930
|
+
if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
|
|
20931
|
+
console.error('[🤰]', 'Timed out waiting for vector store file batch', {
|
|
20932
|
+
vectorStoreId,
|
|
20933
|
+
batchId: returnedBatchId,
|
|
20934
|
+
fileCounts,
|
|
20935
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
20936
|
+
uploadTimeoutMs,
|
|
20937
|
+
logLabel,
|
|
20938
|
+
});
|
|
20939
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
20940
|
+
client,
|
|
20941
|
+
vectorStoreId,
|
|
20942
|
+
batchId: returnedBatchId,
|
|
20943
|
+
uploadedFiles,
|
|
20944
|
+
logLabel,
|
|
20945
|
+
reason: 'timeout',
|
|
20946
|
+
});
|
|
20947
|
+
if (this.shouldContinueOnVectorStoreStall()) {
|
|
20948
|
+
console.warn('[🤰]', 'Continuing despite vector store timeout as requested', {
|
|
20949
|
+
vectorStoreId,
|
|
20950
|
+
logLabel,
|
|
20951
|
+
});
|
|
20952
|
+
shouldPoll = false;
|
|
20953
|
+
continue;
|
|
20954
|
+
}
|
|
20955
|
+
try {
|
|
20956
|
+
const cancelBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
|
|
20957
|
+
if (!cancelBatchId.startsWith('vsfb_')) {
|
|
20958
|
+
console.error('[🤰]', 'Skipping vector store file batch cancel (invalid batch id)', {
|
|
20959
|
+
vectorStoreId,
|
|
20960
|
+
batchId: cancelBatchId,
|
|
20961
|
+
logLabel,
|
|
20962
|
+
});
|
|
20963
|
+
}
|
|
20964
|
+
else {
|
|
20965
|
+
await vectorStores.fileBatches.cancel(cancelBatchId, {
|
|
20966
|
+
vector_store_id: vectorStoreId,
|
|
20967
|
+
});
|
|
20968
|
+
}
|
|
20969
|
+
if (this.options.isVerbose) {
|
|
20970
|
+
console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
|
|
20971
|
+
vectorStoreId,
|
|
20972
|
+
batchId: batchIdMismatch && returnedBatchId.startsWith('vsfb_')
|
|
20973
|
+
? returnedBatchId
|
|
20974
|
+
: expectedBatchId,
|
|
20975
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
20976
|
+
logLabel,
|
|
20977
|
+
});
|
|
20978
|
+
}
|
|
20979
|
+
}
|
|
20980
|
+
catch (error) {
|
|
20981
|
+
assertsError(error);
|
|
20982
|
+
console.error('[🤰]', 'Failed to cancel vector store file batch after timeout', {
|
|
20983
|
+
vectorStoreId,
|
|
20984
|
+
batchId: expectedBatchId,
|
|
20985
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
20986
|
+
logLabel,
|
|
20987
|
+
error: serializeError(error),
|
|
20988
|
+
});
|
|
20989
|
+
}
|
|
20990
|
+
shouldPoll = false;
|
|
20991
|
+
continue;
|
|
20992
|
+
}
|
|
20993
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
20994
|
+
}
|
|
20995
|
+
return latestBatch;
|
|
20996
|
+
}
|
|
20997
|
+
/**
|
|
20998
|
+
* Creates a vector store and uploads knowledge sources, returning its ID.
|
|
20999
|
+
*/
|
|
21000
|
+
async createVectorStoreWithKnowledgeSources(options) {
|
|
21001
|
+
const { client, name, knowledgeSources, logLabel } = options;
|
|
21002
|
+
const vectorStores = this.getVectorStoresApi(client);
|
|
21003
|
+
const knowledgeSourcesCount = knowledgeSources.length;
|
|
21004
|
+
const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
|
|
21005
|
+
if (this.options.isVerbose) {
|
|
21006
|
+
console.info('[🤰]', 'Creating vector store with knowledge sources', {
|
|
21007
|
+
name,
|
|
21008
|
+
knowledgeSourcesCount,
|
|
21009
|
+
downloadTimeoutMs,
|
|
21010
|
+
logLabel,
|
|
21011
|
+
});
|
|
21199
21012
|
}
|
|
21200
|
-
const
|
|
21201
|
-
|
|
21013
|
+
const vectorStore = await vectorStores.create({
|
|
21014
|
+
name: `${name} Knowledge Base`,
|
|
21202
21015
|
});
|
|
21203
|
-
const
|
|
21204
|
-
|
|
21205
|
-
|
|
21206
|
-
console.error('[🤰]', 'Vector store file batch id looks invalid', {
|
|
21016
|
+
const vectorStoreId = vectorStore.id;
|
|
21017
|
+
if (this.options.isVerbose) {
|
|
21018
|
+
console.info('[🤰]', 'Vector store created', {
|
|
21207
21019
|
vectorStoreId,
|
|
21208
|
-
batchId: expectedBatchId,
|
|
21209
|
-
batchVectorStoreId: batch.vector_store_id,
|
|
21210
21020
|
logLabel,
|
|
21211
21021
|
});
|
|
21212
21022
|
}
|
|
21213
|
-
|
|
21214
|
-
|
|
21215
|
-
|
|
21216
|
-
|
|
21217
|
-
|
|
21218
|
-
|
|
21219
|
-
|
|
21023
|
+
const fileStreams = [];
|
|
21024
|
+
const skippedSources = [];
|
|
21025
|
+
let totalBytes = 0;
|
|
21026
|
+
const processingStartedAtMs = Date.now();
|
|
21027
|
+
for (const [index, source] of knowledgeSources.entries()) {
|
|
21028
|
+
try {
|
|
21029
|
+
const sourceType = source.startsWith('http') || source.startsWith('https') ? 'url' : 'file';
|
|
21030
|
+
if (this.options.isVerbose) {
|
|
21031
|
+
console.info('[🤰]', 'Processing knowledge source', {
|
|
21032
|
+
index: index + 1,
|
|
21033
|
+
total: knowledgeSourcesCount,
|
|
21034
|
+
source,
|
|
21035
|
+
sourceType,
|
|
21036
|
+
logLabel,
|
|
21037
|
+
});
|
|
21038
|
+
}
|
|
21039
|
+
// Check if it's a URL
|
|
21040
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
21041
|
+
const downloadResult = await this.downloadKnowledgeSourceFile({
|
|
21042
|
+
source,
|
|
21043
|
+
timeoutMs: downloadTimeoutMs,
|
|
21044
|
+
logLabel,
|
|
21045
|
+
});
|
|
21046
|
+
if (downloadResult) {
|
|
21047
|
+
fileStreams.push(downloadResult.file);
|
|
21048
|
+
totalBytes += downloadResult.sizeBytes;
|
|
21049
|
+
}
|
|
21050
|
+
else {
|
|
21051
|
+
skippedSources.push({ source, reason: 'download_failed' });
|
|
21052
|
+
}
|
|
21053
|
+
}
|
|
21054
|
+
else {
|
|
21055
|
+
skippedSources.push({ source, reason: 'unsupported_source_type' });
|
|
21056
|
+
if (this.options.isVerbose) {
|
|
21057
|
+
console.info('[🤰]', 'Skipping knowledge source (unsupported type)', {
|
|
21058
|
+
source,
|
|
21059
|
+
sourceType,
|
|
21060
|
+
logLabel,
|
|
21061
|
+
});
|
|
21062
|
+
}
|
|
21063
|
+
/*
|
|
21064
|
+
TODO: [🤰] Resolve problem with browser environment
|
|
21065
|
+
// Assume it's a local file path
|
|
21066
|
+
// Note: This will work in Node.js environment
|
|
21067
|
+
// For browser environments, this would need different handling
|
|
21068
|
+
const fs = await import('fs');
|
|
21069
|
+
const fileStream = fs.createReadStream(source);
|
|
21070
|
+
fileStreams.push(fileStream);
|
|
21071
|
+
*/
|
|
21072
|
+
}
|
|
21073
|
+
}
|
|
21074
|
+
catch (error) {
|
|
21075
|
+
assertsError(error);
|
|
21076
|
+
skippedSources.push({ source, reason: 'processing_error' });
|
|
21077
|
+
console.error('[🤰]', 'Error processing knowledge source', {
|
|
21078
|
+
source,
|
|
21079
|
+
logLabel,
|
|
21080
|
+
error: serializeError(error),
|
|
21081
|
+
});
|
|
21082
|
+
}
|
|
21220
21083
|
}
|
|
21221
21084
|
if (this.options.isVerbose) {
|
|
21222
|
-
console.info('[🤰]', '
|
|
21223
|
-
|
|
21224
|
-
|
|
21225
|
-
|
|
21085
|
+
console.info('[🤰]', 'Finished processing knowledge sources', {
|
|
21086
|
+
total: knowledgeSourcesCount,
|
|
21087
|
+
downloadedCount: fileStreams.length,
|
|
21088
|
+
skippedCount: skippedSources.length,
|
|
21089
|
+
totalBytes,
|
|
21090
|
+
elapsedMs: Date.now() - processingStartedAtMs,
|
|
21091
|
+
skippedSamples: skippedSources.slice(0, 3),
|
|
21226
21092
|
logLabel,
|
|
21227
21093
|
});
|
|
21228
21094
|
}
|
|
21229
|
-
|
|
21230
|
-
|
|
21231
|
-
|
|
21232
|
-
let lastStatus;
|
|
21233
|
-
let lastCountsKey = '';
|
|
21234
|
-
let lastProgressKey = '';
|
|
21235
|
-
let lastLogAtMs = 0;
|
|
21236
|
-
let lastProgressAtMs = pollStartedAtMs;
|
|
21237
|
-
let lastDiagnosticsAtMs = pollStartedAtMs;
|
|
21238
|
-
let latestBatch = batch;
|
|
21239
|
-
let loggedBatchIdMismatch = false;
|
|
21240
|
-
let shouldPoll = true;
|
|
21241
|
-
while (shouldPoll) {
|
|
21242
|
-
latestBatch = await client.beta.vectorStores.fileBatches.retrieve(vectorStoreId, expectedBatchId);
|
|
21243
|
-
const counts = latestBatch.file_counts;
|
|
21244
|
-
const countsKey = `${counts.completed}/${counts.failed}/${counts.in_progress}/${counts.cancelled}/${counts.total}`;
|
|
21245
|
-
const nowMs = Date.now();
|
|
21246
|
-
const returnedBatchId = latestBatch.id;
|
|
21247
|
-
// [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
|
|
21248
|
-
// We only consider it a mismatch if the returned ID looks like a Batch ID.
|
|
21249
|
-
const batchIdMismatch = returnedBatchId !== expectedBatchId && returnedBatchId.startsWith('vsfb_');
|
|
21250
|
-
const diagnosticsBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
|
|
21251
|
-
const shouldLog = this.options.isVerbose &&
|
|
21252
|
-
(latestBatch.status !== lastStatus ||
|
|
21253
|
-
countsKey !== lastCountsKey ||
|
|
21254
|
-
nowMs - lastLogAtMs >= progressLogIntervalMs);
|
|
21255
|
-
if (batchIdMismatch && !loggedBatchIdMismatch) {
|
|
21256
|
-
console.error('[🤰]', 'Vector store file batch id mismatch', {
|
|
21095
|
+
if (fileStreams.length > 0) {
|
|
21096
|
+
if (this.options.isVerbose) {
|
|
21097
|
+
console.info('[🤰]', 'Uploading files to vector store', {
|
|
21257
21098
|
vectorStoreId,
|
|
21258
|
-
|
|
21259
|
-
|
|
21260
|
-
|
|
21261
|
-
|
|
21099
|
+
fileCount: fileStreams.length,
|
|
21100
|
+
totalBytes,
|
|
21101
|
+
maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
|
|
21102
|
+
pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
|
|
21103
|
+
uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
|
|
21262
21104
|
logLabel,
|
|
21263
21105
|
});
|
|
21264
|
-
loggedBatchIdMismatch = true;
|
|
21265
|
-
}
|
|
21266
|
-
if (countsKey !== lastProgressKey) {
|
|
21267
|
-
lastProgressKey = countsKey;
|
|
21268
|
-
lastProgressAtMs = nowMs;
|
|
21269
21106
|
}
|
|
21270
|
-
|
|
21271
|
-
|
|
21107
|
+
try {
|
|
21108
|
+
await this.uploadKnowledgeSourceFilesToVectorStore({
|
|
21109
|
+
client,
|
|
21272
21110
|
vectorStoreId,
|
|
21273
|
-
|
|
21274
|
-
|
|
21275
|
-
status: latestBatch.status,
|
|
21276
|
-
fileCounts: counts,
|
|
21277
|
-
elapsedMs: nowMs - pollStartedAtMs,
|
|
21111
|
+
files: fileStreams,
|
|
21112
|
+
totalBytes,
|
|
21278
21113
|
logLabel,
|
|
21279
21114
|
});
|
|
21280
|
-
// [🤰] If there are in-progress files for a long time, log their details
|
|
21281
|
-
if (counts.in_progress > 0 && nowMs - lastProgressAtMs > VECTOR_STORE_STALL_LOG_THRESHOLD_MS) {
|
|
21282
|
-
await this.logVectorStoreFileBatchDiagnostics({
|
|
21283
|
-
client,
|
|
21284
|
-
vectorStoreId,
|
|
21285
|
-
batchId: diagnosticsBatchId,
|
|
21286
|
-
uploadedFiles,
|
|
21287
|
-
logLabel,
|
|
21288
|
-
reason: 'stalled',
|
|
21289
|
-
});
|
|
21290
|
-
}
|
|
21291
|
-
lastStatus = latestBatch.status;
|
|
21292
|
-
lastCountsKey = countsKey;
|
|
21293
|
-
lastLogAtMs = nowMs;
|
|
21294
21115
|
}
|
|
21295
|
-
|
|
21296
|
-
|
|
21297
|
-
|
|
21298
|
-
await this.logVectorStoreFileBatchDiagnostics({
|
|
21299
|
-
client,
|
|
21116
|
+
catch (error) {
|
|
21117
|
+
assertsError(error);
|
|
21118
|
+
console.error('[🤰]', 'Error uploading files to vector store', {
|
|
21300
21119
|
vectorStoreId,
|
|
21301
|
-
batchId: diagnosticsBatchId,
|
|
21302
|
-
uploadedFiles,
|
|
21303
21120
|
logLabel,
|
|
21304
|
-
|
|
21121
|
+
error: serializeError(error),
|
|
21305
21122
|
});
|
|
21306
21123
|
}
|
|
21307
|
-
|
|
21308
|
-
|
|
21309
|
-
|
|
21310
|
-
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
|
|
21314
|
-
|
|
21315
|
-
|
|
21316
|
-
|
|
21317
|
-
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21321
|
-
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
|
|
21325
|
-
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21329
|
-
|
|
21330
|
-
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
|
|
21334
|
-
|
|
21335
|
-
|
|
21336
|
-
|
|
21124
|
+
}
|
|
21125
|
+
else if (this.options.isVerbose) {
|
|
21126
|
+
console.info('[🤰]', 'No knowledge source files to upload', {
|
|
21127
|
+
vectorStoreId,
|
|
21128
|
+
skippedCount: skippedSources.length,
|
|
21129
|
+
logLabel,
|
|
21130
|
+
});
|
|
21131
|
+
}
|
|
21132
|
+
return {
|
|
21133
|
+
vectorStoreId,
|
|
21134
|
+
uploadedFileCount: fileStreams.length,
|
|
21135
|
+
skippedCount: skippedSources.length,
|
|
21136
|
+
totalBytes,
|
|
21137
|
+
};
|
|
21138
|
+
}
|
|
21139
|
+
}
|
|
21140
|
+
|
|
21141
|
+
const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.2';
|
|
21142
|
+
/**
|
|
21143
|
+
* Execution tools for OpenAI AgentKit (Agents SDK).
|
|
21144
|
+
*
|
|
21145
|
+
* @public exported from `@promptbook/openai`
|
|
21146
|
+
*/
|
|
21147
|
+
class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
|
|
21148
|
+
/**
|
|
21149
|
+
* Creates OpenAI AgentKit execution tools.
|
|
21150
|
+
*/
|
|
21151
|
+
constructor(options) {
|
|
21152
|
+
var _a;
|
|
21153
|
+
if (options.isProxied) {
|
|
21154
|
+
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI AgentKit`);
|
|
21155
|
+
}
|
|
21156
|
+
super(options);
|
|
21157
|
+
this.preparedAgentKitAgent = null;
|
|
21158
|
+
this.agentKitModelName = (_a = options.agentKitModelName) !== null && _a !== void 0 ? _a : DEFAULT_AGENT_KIT_MODEL_NAME;
|
|
21159
|
+
}
|
|
21160
|
+
get title() {
|
|
21161
|
+
return 'OpenAI AgentKit';
|
|
21162
|
+
}
|
|
21163
|
+
get description() {
|
|
21164
|
+
return 'Use OpenAI AgentKit for agent-style chat with tools and knowledge';
|
|
21165
|
+
}
|
|
21166
|
+
/**
|
|
21167
|
+
* Calls OpenAI AgentKit with a chat prompt (non-streaming).
|
|
21168
|
+
*/
|
|
21169
|
+
async callChatModel(prompt) {
|
|
21170
|
+
return this.callChatModelStream(prompt, () => { });
|
|
21171
|
+
}
|
|
21172
|
+
/**
|
|
21173
|
+
* Calls OpenAI AgentKit with a chat prompt (streaming).
|
|
21174
|
+
*/
|
|
21175
|
+
async callChatModelStream(prompt, onProgress) {
|
|
21176
|
+
const { content, parameters, modelRequirements } = prompt;
|
|
21177
|
+
if (modelRequirements.modelVariant !== 'CHAT') {
|
|
21178
|
+
throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
|
|
21179
|
+
}
|
|
21180
|
+
for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
|
|
21181
|
+
if (modelRequirements[key] !== undefined) {
|
|
21182
|
+
throw new NotYetImplementedError(`In \`OpenAiAgentKitExecutionTools\` you cannot specify \`${key}\``);
|
|
21183
|
+
}
|
|
21184
|
+
}
|
|
21185
|
+
const rawPromptContent = templateParameters(content, {
|
|
21186
|
+
...parameters,
|
|
21187
|
+
modelName: this.agentKitModelName,
|
|
21188
|
+
});
|
|
21189
|
+
const preparedAgentKitAgent = await this.prepareAgentKitAgent({
|
|
21190
|
+
name: (prompt.title || 'Agent'),
|
|
21191
|
+
instructions: modelRequirements.systemMessage || '',
|
|
21192
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
21193
|
+
tools: 'tools' in prompt && Array.isArray(prompt.tools) ? prompt.tools : modelRequirements.tools,
|
|
21194
|
+
});
|
|
21195
|
+
return this.callChatModelStreamWithPreparedAgent({
|
|
21196
|
+
openAiAgentKitAgent: preparedAgentKitAgent.agent,
|
|
21197
|
+
prompt,
|
|
21198
|
+
rawPromptContent,
|
|
21199
|
+
onProgress,
|
|
21200
|
+
});
|
|
21201
|
+
}
|
|
21202
|
+
/**
|
|
21203
|
+
* Returns a prepared AgentKit agent when the server wants to manage caching externally.
|
|
21204
|
+
*/
|
|
21205
|
+
getPreparedAgentKitAgent() {
|
|
21206
|
+
return this.preparedAgentKitAgent;
|
|
21207
|
+
}
|
|
21208
|
+
/**
|
|
21209
|
+
* Stores a prepared AgentKit agent for later reuse by external cache managers.
|
|
21210
|
+
*/
|
|
21211
|
+
setPreparedAgentKitAgent(preparedAgent) {
|
|
21212
|
+
this.preparedAgentKitAgent = preparedAgent;
|
|
21213
|
+
}
|
|
21214
|
+
/**
|
|
21215
|
+
* Creates a new tools instance bound to a prepared AgentKit agent.
|
|
21216
|
+
*/
|
|
21217
|
+
getPreparedAgentTools(preparedAgent) {
|
|
21218
|
+
const tools = new OpenAiAgentKitExecutionTools(this.agentKitOptions);
|
|
21219
|
+
tools.setPreparedAgentKitAgent(preparedAgent);
|
|
21220
|
+
return tools;
|
|
21221
|
+
}
|
|
21222
|
+
/**
|
|
21223
|
+
* Prepares an AgentKit agent with optional knowledge sources and tool definitions.
|
|
21224
|
+
*/
|
|
21225
|
+
async prepareAgentKitAgent(options) {
|
|
21226
|
+
var _a, _b;
|
|
21227
|
+
const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
|
|
21228
|
+
await this.ensureAgentKitDefaults();
|
|
21229
|
+
if (this.options.isVerbose) {
|
|
21230
|
+
console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
|
|
21231
|
+
name,
|
|
21232
|
+
instructionsLength: instructions.length,
|
|
21233
|
+
knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
|
|
21234
|
+
toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
|
|
21235
|
+
});
|
|
21236
|
+
}
|
|
21237
|
+
let vectorStoreId = cachedVectorStoreId;
|
|
21238
|
+
if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
|
|
21239
|
+
const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
|
|
21240
|
+
client: await this.getClient(),
|
|
21241
|
+
name,
|
|
21242
|
+
knowledgeSources,
|
|
21243
|
+
logLabel: 'agentkit preparation',
|
|
21244
|
+
});
|
|
21245
|
+
vectorStoreId = vectorStoreResult.vectorStoreId;
|
|
21246
|
+
}
|
|
21247
|
+
else if (vectorStoreId && this.options.isVerbose) {
|
|
21248
|
+
console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
|
|
21249
|
+
name,
|
|
21250
|
+
vectorStoreId,
|
|
21251
|
+
});
|
|
21252
|
+
}
|
|
21253
|
+
const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
|
|
21254
|
+
const openAiAgentKitAgent = new Agent$1({
|
|
21255
|
+
name,
|
|
21256
|
+
model: this.agentKitModelName,
|
|
21257
|
+
instructions: instructions || 'You are a helpful assistant.',
|
|
21258
|
+
tools: agentKitTools,
|
|
21259
|
+
});
|
|
21260
|
+
const preparedAgent = {
|
|
21261
|
+
agent: openAiAgentKitAgent,
|
|
21262
|
+
vectorStoreId,
|
|
21263
|
+
};
|
|
21264
|
+
if (storeAsPrepared) {
|
|
21265
|
+
this.setPreparedAgentKitAgent(preparedAgent);
|
|
21266
|
+
}
|
|
21267
|
+
if (this.options.isVerbose) {
|
|
21268
|
+
console.info('[🤰]', 'OpenAI AgentKit agent ready', {
|
|
21269
|
+
name,
|
|
21270
|
+
model: this.agentKitModelName,
|
|
21271
|
+
toolCount: agentKitTools.length,
|
|
21272
|
+
hasVectorStore: Boolean(vectorStoreId),
|
|
21273
|
+
});
|
|
21274
|
+
}
|
|
21275
|
+
return preparedAgent;
|
|
21276
|
+
}
|
|
21277
|
+
/**
|
|
21278
|
+
* Ensures the AgentKit SDK is wired to the OpenAI client and API key.
|
|
21279
|
+
*/
|
|
21280
|
+
async ensureAgentKitDefaults() {
|
|
21281
|
+
const client = await this.getClient();
|
|
21282
|
+
setDefaultOpenAIClient(client);
|
|
21283
|
+
const apiKey = this.agentKitOptions.apiKey;
|
|
21284
|
+
if (apiKey && typeof apiKey === 'string') {
|
|
21285
|
+
setDefaultOpenAIKey(apiKey);
|
|
21286
|
+
}
|
|
21287
|
+
}
|
|
21288
|
+
/**
|
|
21289
|
+
* Builds the tool list for AgentKit, including hosted file search when applicable.
|
|
21290
|
+
*/
|
|
21291
|
+
buildAgentKitTools(options) {
|
|
21292
|
+
var _a;
|
|
21293
|
+
const { tools, vectorStoreId } = options;
|
|
21294
|
+
const agentKitTools = [];
|
|
21295
|
+
if (vectorStoreId) {
|
|
21296
|
+
agentKitTools.push(fileSearchTool(vectorStoreId));
|
|
21297
|
+
}
|
|
21298
|
+
if (tools && tools.length > 0) {
|
|
21299
|
+
const scriptTools = this.resolveScriptTools();
|
|
21300
|
+
for (const toolDefinition of tools) {
|
|
21301
|
+
agentKitTools.push(tool({
|
|
21302
|
+
name: toolDefinition.name,
|
|
21303
|
+
description: toolDefinition.description,
|
|
21304
|
+
parameters: toolDefinition.parameters
|
|
21305
|
+
? {
|
|
21306
|
+
...toolDefinition.parameters,
|
|
21307
|
+
additionalProperties: false,
|
|
21308
|
+
required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
|
|
21309
|
+
}
|
|
21310
|
+
: undefined,
|
|
21311
|
+
strict: false,
|
|
21312
|
+
execute: async (input, runContext, details) => {
|
|
21313
|
+
var _a, _b, _c;
|
|
21314
|
+
const scriptTool = scriptTools[0];
|
|
21315
|
+
const functionName = toolDefinition.name;
|
|
21316
|
+
const calledAt = $getCurrentDate();
|
|
21317
|
+
const callId = (_a = details === null || details === void 0 ? void 0 : details.toolCall) === null || _a === void 0 ? void 0 : _a.callId;
|
|
21318
|
+
const functionArgs = input !== null && input !== void 0 ? input : {};
|
|
21319
|
+
if (this.options.isVerbose) {
|
|
21320
|
+
console.info('[🤰]', 'Executing AgentKit tool', {
|
|
21321
|
+
functionName,
|
|
21322
|
+
callId,
|
|
21323
|
+
calledAt,
|
|
21324
|
+
});
|
|
21325
|
+
}
|
|
21326
|
+
try {
|
|
21327
|
+
return await scriptTool.execute({
|
|
21328
|
+
scriptLanguage: 'javascript',
|
|
21329
|
+
script: `
|
|
21330
|
+
const args = ${JSON.stringify(functionArgs)};
|
|
21331
|
+
return await ${functionName}(args);
|
|
21332
|
+
`,
|
|
21333
|
+
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 : {},
|
|
21334
|
+
});
|
|
21335
|
+
}
|
|
21336
|
+
catch (error) {
|
|
21337
|
+
assertsError(error);
|
|
21338
|
+
const serializedError = serializeError(error);
|
|
21339
|
+
const errorMessage = spaceTrim$2((block) => `
|
|
21340
|
+
|
|
21341
|
+
The invoked tool \`${functionName}\` failed with error:
|
|
21342
|
+
|
|
21343
|
+
\`\`\`json
|
|
21344
|
+
${block(JSON.stringify(serializedError, null, 4))}
|
|
21345
|
+
\`\`\`
|
|
21346
|
+
|
|
21347
|
+
`);
|
|
21348
|
+
console.error('[🤰]', 'AgentKit tool execution failed', {
|
|
21349
|
+
functionName,
|
|
21350
|
+
callId,
|
|
21351
|
+
error: serializedError,
|
|
21352
|
+
});
|
|
21353
|
+
return errorMessage;
|
|
21354
|
+
}
|
|
21355
|
+
},
|
|
21356
|
+
}));
|
|
21337
21357
|
}
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21344
|
-
|
|
21345
|
-
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21358
|
+
}
|
|
21359
|
+
return agentKitTools;
|
|
21360
|
+
}
|
|
21361
|
+
/**
|
|
21362
|
+
* Resolves the configured script tools for tool execution.
|
|
21363
|
+
*/
|
|
21364
|
+
resolveScriptTools() {
|
|
21365
|
+
const executionTools = this.options.executionTools;
|
|
21366
|
+
if (!executionTools || !executionTools.script) {
|
|
21367
|
+
throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
|
|
21368
|
+
}
|
|
21369
|
+
return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
|
|
21370
|
+
}
|
|
21371
|
+
/**
|
|
21372
|
+
* Runs a prepared AgentKit agent and streams results back to the caller.
|
|
21373
|
+
*/
|
|
21374
|
+
async callChatModelStreamWithPreparedAgent(options) {
|
|
21375
|
+
var _a, _b, _c, _d;
|
|
21376
|
+
const { openAiAgentKitAgent, prompt, onProgress } = options;
|
|
21377
|
+
const rawPromptContent = (_a = options.rawPromptContent) !== null && _a !== void 0 ? _a : templateParameters(prompt.content, {
|
|
21378
|
+
...prompt.parameters,
|
|
21379
|
+
modelName: this.agentKitModelName,
|
|
21380
|
+
});
|
|
21381
|
+
const start = $getCurrentDate();
|
|
21382
|
+
let latestContent = '';
|
|
21383
|
+
const toolCalls = [];
|
|
21384
|
+
const toolCallIndexById = new Map();
|
|
21385
|
+
const inputItems = await this.buildAgentKitInputItems(prompt, rawPromptContent);
|
|
21386
|
+
const rawRequest = {
|
|
21387
|
+
agentName: openAiAgentKitAgent.name,
|
|
21388
|
+
input: inputItems,
|
|
21389
|
+
};
|
|
21390
|
+
const streamResult = await run(openAiAgentKitAgent, inputItems, {
|
|
21391
|
+
stream: true,
|
|
21392
|
+
context: { parameters: prompt.parameters },
|
|
21393
|
+
});
|
|
21394
|
+
for await (const event of streamResult) {
|
|
21395
|
+
if (event.type === 'raw_model_stream_event' && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'output_text_delta') {
|
|
21396
|
+
latestContent += event.data.delta;
|
|
21397
|
+
onProgress({
|
|
21398
|
+
content: latestContent,
|
|
21399
|
+
modelName: this.agentKitModelName,
|
|
21400
|
+
timing: { start, complete: $getCurrentDate() },
|
|
21401
|
+
usage: UNCERTAIN_USAGE,
|
|
21402
|
+
rawPromptContent: rawPromptContent,
|
|
21403
|
+
rawRequest: null,
|
|
21404
|
+
rawResponse: {},
|
|
21355
21405
|
});
|
|
21356
|
-
shouldPoll = false;
|
|
21357
21406
|
continue;
|
|
21358
21407
|
}
|
|
21359
|
-
if (
|
|
21360
|
-
|
|
21361
|
-
|
|
21362
|
-
|
|
21363
|
-
|
|
21364
|
-
|
|
21365
|
-
|
|
21366
|
-
|
|
21367
|
-
|
|
21368
|
-
|
|
21369
|
-
|
|
21370
|
-
|
|
21371
|
-
|
|
21372
|
-
|
|
21373
|
-
|
|
21374
|
-
|
|
21375
|
-
|
|
21376
|
-
|
|
21377
|
-
|
|
21378
|
-
|
|
21379
|
-
vectorStoreId,
|
|
21380
|
-
logLabel,
|
|
21408
|
+
if (event.type === 'run_item_stream_event') {
|
|
21409
|
+
const rawItem = (_c = event.item) === null || _c === void 0 ? void 0 : _c.rawItem;
|
|
21410
|
+
if (event.name === 'tool_called' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call') {
|
|
21411
|
+
const toolCall = {
|
|
21412
|
+
name: rawItem.name,
|
|
21413
|
+
arguments: rawItem.arguments,
|
|
21414
|
+
rawToolCall: rawItem,
|
|
21415
|
+
createdAt: $getCurrentDate(),
|
|
21416
|
+
};
|
|
21417
|
+
toolCallIndexById.set(rawItem.callId, toolCalls.length);
|
|
21418
|
+
toolCalls.push(toolCall);
|
|
21419
|
+
onProgress({
|
|
21420
|
+
content: latestContent,
|
|
21421
|
+
modelName: this.agentKitModelName,
|
|
21422
|
+
timing: { start, complete: $getCurrentDate() },
|
|
21423
|
+
usage: UNCERTAIN_USAGE,
|
|
21424
|
+
rawPromptContent: rawPromptContent,
|
|
21425
|
+
rawRequest: null,
|
|
21426
|
+
rawResponse: {},
|
|
21427
|
+
toolCalls: [toolCall],
|
|
21381
21428
|
});
|
|
21382
|
-
shouldPoll = false;
|
|
21383
|
-
continue;
|
|
21384
21429
|
}
|
|
21385
|
-
|
|
21386
|
-
const
|
|
21387
|
-
|
|
21388
|
-
|
|
21389
|
-
|
|
21390
|
-
|
|
21391
|
-
|
|
21392
|
-
|
|
21393
|
-
|
|
21394
|
-
|
|
21395
|
-
|
|
21396
|
-
|
|
21397
|
-
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21403
|
-
|
|
21404
|
-
|
|
21430
|
+
if (event.name === 'tool_output' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call_result') {
|
|
21431
|
+
const index = toolCallIndexById.get(rawItem.callId);
|
|
21432
|
+
const result = this.formatAgentKitToolOutput(rawItem.output);
|
|
21433
|
+
if (index !== undefined) {
|
|
21434
|
+
const existingToolCall = toolCalls[index];
|
|
21435
|
+
const completedToolCall = {
|
|
21436
|
+
...existingToolCall,
|
|
21437
|
+
result,
|
|
21438
|
+
rawToolCall: rawItem,
|
|
21439
|
+
};
|
|
21440
|
+
toolCalls[index] = completedToolCall;
|
|
21441
|
+
onProgress({
|
|
21442
|
+
content: latestContent,
|
|
21443
|
+
modelName: this.agentKitModelName,
|
|
21444
|
+
timing: { start, complete: $getCurrentDate() },
|
|
21445
|
+
usage: UNCERTAIN_USAGE,
|
|
21446
|
+
rawPromptContent: rawPromptContent,
|
|
21447
|
+
rawRequest: null,
|
|
21448
|
+
rawResponse: {},
|
|
21449
|
+
toolCalls: [completedToolCall],
|
|
21405
21450
|
});
|
|
21406
21451
|
}
|
|
21407
21452
|
}
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21412
|
-
|
|
21413
|
-
|
|
21414
|
-
|
|
21415
|
-
|
|
21453
|
+
}
|
|
21454
|
+
}
|
|
21455
|
+
await streamResult.completed;
|
|
21456
|
+
const complete = $getCurrentDate();
|
|
21457
|
+
const finalContent = ((_d = streamResult.finalOutput) !== null && _d !== void 0 ? _d : latestContent);
|
|
21458
|
+
const finalResult = {
|
|
21459
|
+
content: finalContent,
|
|
21460
|
+
modelName: this.agentKitModelName,
|
|
21461
|
+
timing: { start, complete },
|
|
21462
|
+
usage: UNCERTAIN_USAGE,
|
|
21463
|
+
rawPromptContent: rawPromptContent,
|
|
21464
|
+
rawRequest,
|
|
21465
|
+
rawResponse: { runResult: streamResult },
|
|
21466
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
21467
|
+
};
|
|
21468
|
+
onProgress(finalResult);
|
|
21469
|
+
return finalResult;
|
|
21470
|
+
}
|
|
21471
|
+
/**
|
|
21472
|
+
* Builds AgentKit input items from the prompt and optional thread.
|
|
21473
|
+
*/
|
|
21474
|
+
async buildAgentKitInputItems(prompt, rawPromptContent) {
|
|
21475
|
+
var _a;
|
|
21476
|
+
const inputItems = [];
|
|
21477
|
+
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
21478
|
+
for (const message of prompt.thread) {
|
|
21479
|
+
const sender = message.sender;
|
|
21480
|
+
const content = (_a = message.content) !== null && _a !== void 0 ? _a : '';
|
|
21481
|
+
if (sender === 'assistant' || sender === 'agent') {
|
|
21482
|
+
inputItems.push({
|
|
21483
|
+
role: 'assistant',
|
|
21484
|
+
status: 'completed',
|
|
21485
|
+
content: [{ type: 'output_text', text: content }],
|
|
21486
|
+
});
|
|
21487
|
+
}
|
|
21488
|
+
else {
|
|
21489
|
+
inputItems.push({
|
|
21490
|
+
role: 'user',
|
|
21491
|
+
content,
|
|
21416
21492
|
});
|
|
21417
21493
|
}
|
|
21418
|
-
shouldPoll = false;
|
|
21419
|
-
continue;
|
|
21420
21494
|
}
|
|
21421
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
21422
21495
|
}
|
|
21423
|
-
|
|
21496
|
+
const userContent = await this.buildAgentKitUserContent(prompt, rawPromptContent);
|
|
21497
|
+
inputItems.push({
|
|
21498
|
+
role: 'user',
|
|
21499
|
+
content: userContent,
|
|
21500
|
+
});
|
|
21501
|
+
return inputItems;
|
|
21424
21502
|
}
|
|
21425
21503
|
/**
|
|
21426
|
-
*
|
|
21504
|
+
* Builds the user message content for AgentKit runs, including file inputs when provided.
|
|
21427
21505
|
*/
|
|
21428
|
-
async
|
|
21429
|
-
|
|
21430
|
-
|
|
21431
|
-
|
|
21506
|
+
async buildAgentKitUserContent(prompt, rawPromptContent) {
|
|
21507
|
+
if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
|
|
21508
|
+
const fileItems = await Promise.all(prompt.files.map(async (file) => {
|
|
21509
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
21510
|
+
const base64 = Buffer.from(arrayBuffer).toString('base64');
|
|
21511
|
+
return {
|
|
21512
|
+
type: 'input_image',
|
|
21513
|
+
image: `data:${file.type};base64,${base64}`,
|
|
21514
|
+
};
|
|
21515
|
+
}));
|
|
21516
|
+
return [{ type: 'input_text', text: rawPromptContent }, ...fileItems];
|
|
21517
|
+
}
|
|
21518
|
+
return rawPromptContent;
|
|
21519
|
+
}
|
|
21520
|
+
/**
|
|
21521
|
+
* Normalizes AgentKit tool outputs into a string for Promptbook tool call results.
|
|
21522
|
+
*/
|
|
21523
|
+
formatAgentKitToolOutput(output) {
|
|
21524
|
+
if (typeof output === 'string') {
|
|
21525
|
+
return output;
|
|
21526
|
+
}
|
|
21527
|
+
if (output && typeof output === 'object') {
|
|
21528
|
+
const textOutput = output;
|
|
21529
|
+
if (textOutput.type === 'text' && typeof textOutput.text === 'string') {
|
|
21530
|
+
return textOutput.text;
|
|
21531
|
+
}
|
|
21532
|
+
}
|
|
21533
|
+
return JSON.stringify(output !== null && output !== void 0 ? output : null);
|
|
21534
|
+
}
|
|
21535
|
+
/**
|
|
21536
|
+
* Returns AgentKit-specific options.
|
|
21537
|
+
*/
|
|
21538
|
+
get agentKitOptions() {
|
|
21539
|
+
return this.options;
|
|
21540
|
+
}
|
|
21541
|
+
/**
|
|
21542
|
+
* Discriminant for type guards.
|
|
21543
|
+
*/
|
|
21544
|
+
get discriminant() {
|
|
21545
|
+
return DISCRIMINANT$1;
|
|
21546
|
+
}
|
|
21547
|
+
/**
|
|
21548
|
+
* Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentKitExecutionTools`.
|
|
21549
|
+
*/
|
|
21550
|
+
static isOpenAiAgentKitExecutionTools(llmExecutionTools) {
|
|
21551
|
+
return llmExecutionTools.discriminant === DISCRIMINANT$1;
|
|
21552
|
+
}
|
|
21553
|
+
}
|
|
21554
|
+
/**
|
|
21555
|
+
* Discriminant for type guards.
|
|
21556
|
+
*
|
|
21557
|
+
* @private const of `OpenAiAgentKitExecutionTools`
|
|
21558
|
+
*/
|
|
21559
|
+
const DISCRIMINANT$1 = 'OPEN_AI_AGENT_KIT_V1';
|
|
21560
|
+
|
|
21561
|
+
/**
|
|
21562
|
+
* Uploads files to OpenAI and returns their IDs
|
|
21563
|
+
*
|
|
21564
|
+
* @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
|
|
21565
|
+
*/
|
|
21566
|
+
async function uploadFilesToOpenAi(client, files) {
|
|
21567
|
+
const fileIds = [];
|
|
21568
|
+
for (const file of files) {
|
|
21569
|
+
// Note: OpenAI API expects a File object or a ReadStream
|
|
21570
|
+
// In browser environment, we can pass the File object directly
|
|
21571
|
+
// In Node.js environment, we might need to convert it or use a different approach
|
|
21572
|
+
// But since `Prompt.files` already contains `File` objects, we try to pass them directly
|
|
21573
|
+
const uploadedFile = await client.files.create({
|
|
21574
|
+
file: file,
|
|
21575
|
+
purpose: 'assistants',
|
|
21576
|
+
});
|
|
21577
|
+
fileIds.push(uploadedFile.id);
|
|
21578
|
+
}
|
|
21579
|
+
return fileIds;
|
|
21580
|
+
}
|
|
21581
|
+
|
|
21582
|
+
/**
|
|
21583
|
+
* Execution Tools for calling OpenAI API Assistants
|
|
21584
|
+
*
|
|
21585
|
+
* This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
|
|
21586
|
+
*
|
|
21587
|
+
* Note: [🦖] There are several different things in Promptbook:
|
|
21588
|
+
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
21589
|
+
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
21590
|
+
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
21591
|
+
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
21592
|
+
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
21593
|
+
*
|
|
21594
|
+
* @deprecated Use `OpenAiAgentKitExecutionTools` instead.
|
|
21595
|
+
* @public exported from `@promptbook/openai`
|
|
21596
|
+
*/
|
|
21597
|
+
class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
|
|
21598
|
+
/**
|
|
21599
|
+
* Creates OpenAI Execution Tools.
|
|
21600
|
+
*
|
|
21601
|
+
* @param options which are relevant are directly passed to the OpenAI client
|
|
21602
|
+
*/
|
|
21603
|
+
constructor(options) {
|
|
21604
|
+
var _a;
|
|
21605
|
+
if (options.isProxied) {
|
|
21606
|
+
throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
|
|
21607
|
+
}
|
|
21608
|
+
super(options);
|
|
21609
|
+
this.isCreatingNewAssistantsAllowed = false;
|
|
21610
|
+
this.assistantId = options.assistantId;
|
|
21611
|
+
this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
|
|
21612
|
+
if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
|
|
21613
|
+
throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
|
|
21614
|
+
}
|
|
21615
|
+
// <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
|
|
21616
|
+
// TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
|
|
21617
|
+
}
|
|
21618
|
+
get title() {
|
|
21619
|
+
return 'OpenAI Assistant';
|
|
21620
|
+
}
|
|
21621
|
+
get description() {
|
|
21622
|
+
return 'Use single assistant provided by OpenAI';
|
|
21623
|
+
}
|
|
21624
|
+
/**
|
|
21625
|
+
* Calls OpenAI API to use a chat model.
|
|
21626
|
+
*/
|
|
21627
|
+
async callChatModel(prompt) {
|
|
21628
|
+
return this.callChatModelStream(prompt, () => { });
|
|
21629
|
+
}
|
|
21630
|
+
/**
|
|
21631
|
+
* Calls OpenAI API to use a chat model with streaming.
|
|
21632
|
+
*/
|
|
21633
|
+
async callChatModelStream(prompt, onProgress) {
|
|
21634
|
+
var _a, _b, _c, _d, _e, _f;
|
|
21432
21635
|
if (this.options.isVerbose) {
|
|
21433
|
-
console.info('
|
|
21434
|
-
|
|
21435
|
-
|
|
21436
|
-
|
|
21437
|
-
|
|
21438
|
-
|
|
21636
|
+
console.info('💬 OpenAI callChatModel call', { prompt });
|
|
21637
|
+
}
|
|
21638
|
+
const { content, parameters, modelRequirements /*, format*/ } = prompt;
|
|
21639
|
+
const client = await this.getClient();
|
|
21640
|
+
// TODO: [☂] Use here more modelRequirements
|
|
21641
|
+
if (modelRequirements.modelVariant !== 'CHAT') {
|
|
21642
|
+
throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
|
|
21439
21643
|
}
|
|
21440
|
-
|
|
21441
|
-
|
|
21644
|
+
// TODO: [👨👨👧👧] Remove:
|
|
21645
|
+
for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
|
|
21646
|
+
if (modelRequirements[key] !== undefined) {
|
|
21647
|
+
throw new NotYetImplementedError(`In \`OpenAiAssistantExecutionTools\` you cannot specify \`${key}\``);
|
|
21648
|
+
}
|
|
21649
|
+
}
|
|
21650
|
+
/*
|
|
21651
|
+
TODO: [👨👨👧👧] Implement all of this for Assistants
|
|
21652
|
+
const modelName = modelRequirements.modelName || this.getDefaultChatModel().modelName;
|
|
21653
|
+
const modelSettings = {
|
|
21654
|
+
model: modelName,
|
|
21655
|
+
|
|
21656
|
+
temperature: modelRequirements.temperature,
|
|
21657
|
+
|
|
21658
|
+
// <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
|
|
21659
|
+
// <- Note: [🧆]
|
|
21660
|
+
} as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
|
|
21661
|
+
|
|
21662
|
+
if (format === 'JSON') {
|
|
21663
|
+
modelSettings.response_format = {
|
|
21664
|
+
type: 'json_object',
|
|
21665
|
+
};
|
|
21666
|
+
}
|
|
21667
|
+
*/
|
|
21668
|
+
// <- TODO: [🚸] Not all models are compatible with JSON mode
|
|
21669
|
+
// > 'response_format' of type 'json_object' is not supported with this model.
|
|
21670
|
+
const rawPromptContent = templateParameters(content, {
|
|
21671
|
+
...parameters,
|
|
21672
|
+
modelName: 'assistant',
|
|
21673
|
+
// <- [🧠] What is the best value here
|
|
21442
21674
|
});
|
|
21443
|
-
|
|
21444
|
-
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
|
|
21448
|
-
|
|
21675
|
+
// Build thread messages: include previous thread messages + current user message
|
|
21676
|
+
const threadMessages = [];
|
|
21677
|
+
// TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
|
|
21678
|
+
// Add previous messages from thread (if any)
|
|
21679
|
+
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
21680
|
+
const previousMessages = prompt.thread.map((msg) => ({
|
|
21681
|
+
role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
|
|
21682
|
+
content: msg.content,
|
|
21683
|
+
}));
|
|
21684
|
+
threadMessages.push(...previousMessages);
|
|
21449
21685
|
}
|
|
21450
|
-
|
|
21451
|
-
const
|
|
21452
|
-
|
|
21453
|
-
|
|
21454
|
-
|
|
21455
|
-
|
|
21456
|
-
|
|
21457
|
-
|
|
21458
|
-
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
|
|
21462
|
-
|
|
21463
|
-
|
|
21464
|
-
|
|
21465
|
-
|
|
21466
|
-
|
|
21467
|
-
|
|
21468
|
-
|
|
21469
|
-
|
|
21470
|
-
|
|
21471
|
-
|
|
21472
|
-
|
|
21473
|
-
|
|
21474
|
-
|
|
21475
|
-
|
|
21476
|
-
|
|
21477
|
-
|
|
21478
|
-
|
|
21686
|
+
// Always add the current user message
|
|
21687
|
+
const currentUserMessage = {
|
|
21688
|
+
role: 'user',
|
|
21689
|
+
content: rawPromptContent,
|
|
21690
|
+
};
|
|
21691
|
+
if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
|
|
21692
|
+
const fileIds = await uploadFilesToOpenAi(client, prompt.files);
|
|
21693
|
+
currentUserMessage.attachments = fileIds.map((fileId) => ({
|
|
21694
|
+
file_id: fileId,
|
|
21695
|
+
tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
|
|
21696
|
+
}));
|
|
21697
|
+
}
|
|
21698
|
+
threadMessages.push(currentUserMessage);
|
|
21699
|
+
// Check if tools are being used - if so, use non-streaming mode
|
|
21700
|
+
const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
|
|
21701
|
+
const start = $getCurrentDate();
|
|
21702
|
+
let complete;
|
|
21703
|
+
// [🐱🚀] When tools are present, we need to use the non-streaming Runs API
|
|
21704
|
+
// because streaming doesn't support tool execution flow properly
|
|
21705
|
+
if (hasTools) {
|
|
21706
|
+
onProgress({
|
|
21707
|
+
content: '',
|
|
21708
|
+
modelName: 'assistant',
|
|
21709
|
+
timing: { start, complete: $getCurrentDate() },
|
|
21710
|
+
usage: UNCERTAIN_USAGE,
|
|
21711
|
+
rawPromptContent,
|
|
21712
|
+
rawRequest: null,
|
|
21713
|
+
rawResponse: null,
|
|
21714
|
+
});
|
|
21715
|
+
const rawRequest = {
|
|
21716
|
+
assistant_id: this.assistantId,
|
|
21717
|
+
thread: {
|
|
21718
|
+
messages: threadMessages,
|
|
21719
|
+
},
|
|
21720
|
+
tools: mapToolsToOpenAi(modelRequirements.tools),
|
|
21721
|
+
};
|
|
21722
|
+
if (this.options.isVerbose) {
|
|
21723
|
+
console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
|
|
21724
|
+
}
|
|
21725
|
+
// Create thread and run
|
|
21726
|
+
let run = (await client.beta.threads.createAndRun(rawRequest));
|
|
21727
|
+
const completedToolCalls = [];
|
|
21728
|
+
const toolCallStartedAt = new Map();
|
|
21729
|
+
// Poll until run completes or requires action
|
|
21730
|
+
while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
|
|
21731
|
+
if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
|
|
21732
|
+
// Execute tools
|
|
21733
|
+
const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
|
|
21734
|
+
const toolOutputs = [];
|
|
21735
|
+
for (const toolCall of toolCalls) {
|
|
21736
|
+
if (toolCall.type === 'function') {
|
|
21737
|
+
const functionName = toolCall.function.name;
|
|
21738
|
+
const functionArgs = JSON.parse(toolCall.function.arguments);
|
|
21739
|
+
const calledAt = $getCurrentDate();
|
|
21740
|
+
if (toolCall.id) {
|
|
21741
|
+
toolCallStartedAt.set(toolCall.id, calledAt);
|
|
21742
|
+
}
|
|
21743
|
+
onProgress({
|
|
21744
|
+
content: '',
|
|
21745
|
+
modelName: 'assistant',
|
|
21746
|
+
timing: { start, complete: $getCurrentDate() },
|
|
21747
|
+
usage: UNCERTAIN_USAGE,
|
|
21748
|
+
rawPromptContent,
|
|
21749
|
+
rawRequest: null,
|
|
21750
|
+
rawResponse: null,
|
|
21751
|
+
toolCalls: [
|
|
21752
|
+
{
|
|
21753
|
+
name: functionName,
|
|
21754
|
+
arguments: toolCall.function.arguments,
|
|
21755
|
+
result: '',
|
|
21756
|
+
rawToolCall: toolCall,
|
|
21757
|
+
createdAt: calledAt,
|
|
21758
|
+
},
|
|
21759
|
+
],
|
|
21760
|
+
});
|
|
21761
|
+
if (this.options.isVerbose) {
|
|
21762
|
+
console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
|
|
21763
|
+
}
|
|
21764
|
+
// Get execution tools for script execution
|
|
21765
|
+
const executionTools = this.options
|
|
21766
|
+
.executionTools;
|
|
21767
|
+
if (!executionTools || !executionTools.script) {
|
|
21768
|
+
throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
|
|
21769
|
+
}
|
|
21770
|
+
// TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
|
|
21771
|
+
const scriptTools = Array.isArray(executionTools.script)
|
|
21772
|
+
? executionTools.script
|
|
21773
|
+
: [executionTools.script];
|
|
21774
|
+
let functionResponse;
|
|
21775
|
+
let errors;
|
|
21776
|
+
try {
|
|
21777
|
+
const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
|
|
21778
|
+
functionResponse = await scriptTool.execute({
|
|
21779
|
+
scriptLanguage: 'javascript',
|
|
21780
|
+
script: `
|
|
21781
|
+
const args = ${JSON.stringify(functionArgs)};
|
|
21782
|
+
return await ${functionName}(args);
|
|
21783
|
+
`,
|
|
21784
|
+
parameters: prompt.parameters,
|
|
21785
|
+
});
|
|
21786
|
+
if (this.options.isVerbose) {
|
|
21787
|
+
console.info(`✅ Tool ${functionName} executed:`, functionResponse);
|
|
21788
|
+
}
|
|
21789
|
+
}
|
|
21790
|
+
catch (error) {
|
|
21791
|
+
assertsError(error);
|
|
21792
|
+
const serializedError = serializeError(error);
|
|
21793
|
+
errors = [serializedError];
|
|
21794
|
+
functionResponse = spaceTrim$2((block) => `
|
|
21795
|
+
|
|
21796
|
+
The invoked tool \`${functionName}\` failed with error:
|
|
21797
|
+
|
|
21798
|
+
\`\`\`json
|
|
21799
|
+
${block(JSON.stringify(serializedError, null, 4))}
|
|
21800
|
+
\`\`\`
|
|
21801
|
+
|
|
21802
|
+
`);
|
|
21803
|
+
console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
|
|
21804
|
+
console.error(error);
|
|
21805
|
+
}
|
|
21806
|
+
toolOutputs.push({
|
|
21807
|
+
tool_call_id: toolCall.id,
|
|
21808
|
+
output: functionResponse,
|
|
21809
|
+
});
|
|
21810
|
+
completedToolCalls.push({
|
|
21811
|
+
name: functionName,
|
|
21812
|
+
arguments: toolCall.function.arguments,
|
|
21813
|
+
result: functionResponse,
|
|
21814
|
+
rawToolCall: toolCall,
|
|
21815
|
+
createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
|
|
21816
|
+
errors,
|
|
21817
|
+
});
|
|
21818
|
+
}
|
|
21479
21819
|
}
|
|
21820
|
+
// Submit tool outputs
|
|
21821
|
+
run = (await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
|
|
21822
|
+
tool_outputs: toolOutputs,
|
|
21823
|
+
}));
|
|
21480
21824
|
}
|
|
21481
21825
|
else {
|
|
21482
|
-
|
|
21483
|
-
|
|
21484
|
-
|
|
21485
|
-
source,
|
|
21486
|
-
sourceType,
|
|
21487
|
-
logLabel,
|
|
21488
|
-
});
|
|
21489
|
-
}
|
|
21490
|
-
/*
|
|
21491
|
-
TODO: [?????] Resolve problem with browser environment
|
|
21492
|
-
// Assume it's a local file path
|
|
21493
|
-
// Note: This will work in Node.js environment
|
|
21494
|
-
// For browser environments, this would need different handling
|
|
21495
|
-
const fs = await import('fs');
|
|
21496
|
-
const fileStream = fs.createReadStream(source);
|
|
21497
|
-
fileStreams.push(fileStream);
|
|
21498
|
-
*/
|
|
21826
|
+
// Wait a bit before polling again
|
|
21827
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
21828
|
+
run = (await client.beta.threads.runs.retrieve(run.thread_id, run.id));
|
|
21499
21829
|
}
|
|
21500
21830
|
}
|
|
21501
|
-
|
|
21502
|
-
|
|
21503
|
-
|
|
21504
|
-
|
|
21505
|
-
|
|
21506
|
-
|
|
21507
|
-
|
|
21508
|
-
|
|
21831
|
+
if (run.status !== 'completed') {
|
|
21832
|
+
throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
|
|
21833
|
+
}
|
|
21834
|
+
// Get messages from the thread
|
|
21835
|
+
const messages = await client.beta.threads.messages.list(run.thread_id);
|
|
21836
|
+
const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
|
|
21837
|
+
if (assistantMessages.length === 0) {
|
|
21838
|
+
throw new PipelineExecutionError('No assistant messages found after run completion');
|
|
21839
|
+
}
|
|
21840
|
+
const lastMessage = assistantMessages[0];
|
|
21841
|
+
const textContent = lastMessage.content.find((c) => c.type === 'text');
|
|
21842
|
+
if (!textContent || textContent.type !== 'text') {
|
|
21843
|
+
throw new PipelineExecutionError('No text content in assistant response');
|
|
21509
21844
|
}
|
|
21845
|
+
complete = $getCurrentDate();
|
|
21846
|
+
const resultContent = textContent.text.value;
|
|
21847
|
+
const usage = UNCERTAIN_USAGE;
|
|
21848
|
+
// Progress callback with final result
|
|
21849
|
+
const finalChunk = {
|
|
21850
|
+
content: resultContent,
|
|
21851
|
+
modelName: 'assistant',
|
|
21852
|
+
timing: { start, complete },
|
|
21853
|
+
usage,
|
|
21854
|
+
rawPromptContent,
|
|
21855
|
+
rawRequest,
|
|
21856
|
+
rawResponse: { run, messages: messages.data },
|
|
21857
|
+
toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
|
|
21858
|
+
};
|
|
21859
|
+
onProgress(finalChunk);
|
|
21860
|
+
return exportJson({
|
|
21861
|
+
name: 'promptResult',
|
|
21862
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
|
|
21863
|
+
order: [],
|
|
21864
|
+
value: finalChunk,
|
|
21865
|
+
});
|
|
21510
21866
|
}
|
|
21867
|
+
// Streaming mode (without tools)
|
|
21868
|
+
const rawRequest = {
|
|
21869
|
+
// TODO: [👨👨👧👧] ...modelSettings,
|
|
21870
|
+
// TODO: [👨👨👧👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
|
|
21871
|
+
assistant_id: this.assistantId,
|
|
21872
|
+
thread: {
|
|
21873
|
+
messages: threadMessages,
|
|
21874
|
+
},
|
|
21875
|
+
tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
|
|
21876
|
+
// <- TODO: Add user identification here> user: this.options.user,
|
|
21877
|
+
};
|
|
21511
21878
|
if (this.options.isVerbose) {
|
|
21512
|
-
console.info('
|
|
21513
|
-
total: knowledgeSourcesCount,
|
|
21514
|
-
downloadedCount: fileStreams.length,
|
|
21515
|
-
skippedCount: skippedSources.length,
|
|
21516
|
-
totalBytes,
|
|
21517
|
-
elapsedMs: Date.now() - processingStartedAtMs,
|
|
21518
|
-
skippedSamples: skippedSources.slice(0, 3),
|
|
21519
|
-
logLabel,
|
|
21520
|
-
});
|
|
21879
|
+
console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
|
|
21521
21880
|
}
|
|
21522
|
-
|
|
21881
|
+
const stream = await client.beta.threads.createAndRunStream(rawRequest);
|
|
21882
|
+
stream.on('connect', () => {
|
|
21523
21883
|
if (this.options.isVerbose) {
|
|
21524
|
-
console.info('
|
|
21525
|
-
vectorStoreId,
|
|
21526
|
-
fileCount: fileStreams.length,
|
|
21527
|
-
totalBytes,
|
|
21528
|
-
maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
|
|
21529
|
-
pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
|
|
21530
|
-
uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
|
|
21531
|
-
logLabel,
|
|
21532
|
-
});
|
|
21884
|
+
console.info('connect', stream.currentEvent);
|
|
21533
21885
|
}
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
21538
|
-
files: fileStreams,
|
|
21539
|
-
totalBytes,
|
|
21540
|
-
logLabel,
|
|
21541
|
-
});
|
|
21886
|
+
});
|
|
21887
|
+
stream.on('textDelta', (textDelta, snapshot) => {
|
|
21888
|
+
if (this.options.isVerbose && textDelta.value) {
|
|
21889
|
+
console.info('textDelta', textDelta.value);
|
|
21542
21890
|
}
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
}
|
|
21891
|
+
const chunk = {
|
|
21892
|
+
content: snapshot.value,
|
|
21893
|
+
modelName: 'assistant',
|
|
21894
|
+
timing: {
|
|
21895
|
+
start,
|
|
21896
|
+
complete: $getCurrentDate(),
|
|
21897
|
+
},
|
|
21898
|
+
usage: UNCERTAIN_USAGE,
|
|
21899
|
+
rawPromptContent,
|
|
21900
|
+
rawRequest,
|
|
21901
|
+
rawResponse: snapshot,
|
|
21902
|
+
};
|
|
21903
|
+
onProgress(chunk);
|
|
21904
|
+
});
|
|
21905
|
+
stream.on('messageCreated', (message) => {
|
|
21906
|
+
if (this.options.isVerbose) {
|
|
21907
|
+
console.info('messageCreated', message);
|
|
21908
|
+
}
|
|
21909
|
+
});
|
|
21910
|
+
stream.on('messageDone', (message) => {
|
|
21911
|
+
if (this.options.isVerbose) {
|
|
21912
|
+
console.info('messageDone', message);
|
|
21550
21913
|
}
|
|
21914
|
+
});
|
|
21915
|
+
// TODO: [🐱🚀] Handle tool calls in assistants
|
|
21916
|
+
// Note: OpenAI Assistant streaming with tool calls requires special handling.
|
|
21917
|
+
// The stream will pause when a tool call is needed, and we need to:
|
|
21918
|
+
// 1. Wait for the run to reach 'requires_action' status
|
|
21919
|
+
// 2. Execute the tool calls
|
|
21920
|
+
// 3. Submit tool outputs via a separate API call (not on the stream)
|
|
21921
|
+
// 4. Continue the run
|
|
21922
|
+
// This requires switching to non-streaming mode or using the Runs API directly.
|
|
21923
|
+
// For now, tools with assistants should use the non-streaming chat completions API instead.
|
|
21924
|
+
const rawResponse = await stream.finalMessages();
|
|
21925
|
+
if (this.options.isVerbose) {
|
|
21926
|
+
console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
|
|
21551
21927
|
}
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
vectorStoreId,
|
|
21555
|
-
skippedCount: skippedSources.length,
|
|
21556
|
-
logLabel,
|
|
21557
|
-
});
|
|
21928
|
+
if (rawResponse.length !== 1) {
|
|
21929
|
+
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
|
|
21558
21930
|
}
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
}
|
|
21931
|
+
if (rawResponse[0].content.length !== 1) {
|
|
21932
|
+
throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
|
|
21933
|
+
}
|
|
21934
|
+
if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
|
|
21935
|
+
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`);
|
|
21936
|
+
}
|
|
21937
|
+
let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
|
|
21938
|
+
// Process annotations to replace file IDs with filenames
|
|
21939
|
+
if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
|
|
21940
|
+
const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
|
|
21941
|
+
// Map to store file ID -> filename to avoid duplicate requests
|
|
21942
|
+
const fileIdToName = new Map();
|
|
21943
|
+
for (const annotation of annotations) {
|
|
21944
|
+
if (annotation.type === 'file_citation') {
|
|
21945
|
+
const fileId = annotation.file_citation.file_id;
|
|
21946
|
+
let filename = fileIdToName.get(fileId);
|
|
21947
|
+
if (!filename) {
|
|
21948
|
+
try {
|
|
21949
|
+
const file = await client.files.retrieve(fileId);
|
|
21950
|
+
filename = file.filename;
|
|
21951
|
+
fileIdToName.set(fileId, filename);
|
|
21952
|
+
}
|
|
21953
|
+
catch (error) {
|
|
21954
|
+
console.error(`Failed to retrieve file info for ${fileId}`, error);
|
|
21955
|
+
// Fallback to "Source" or keep original if fetch fails
|
|
21956
|
+
filename = 'Source';
|
|
21957
|
+
}
|
|
21958
|
+
}
|
|
21959
|
+
if (filename && resultContent) {
|
|
21960
|
+
// Replace the citation marker with filename
|
|
21961
|
+
// Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
|
|
21962
|
+
// Note: annotation.text contains the exact marker like 【4:0†source】
|
|
21963
|
+
const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
|
|
21964
|
+
resultContent = resultContent.replace(annotation.text, newText);
|
|
21965
|
+
}
|
|
21966
|
+
}
|
|
21967
|
+
}
|
|
21968
|
+
}
|
|
21969
|
+
// eslint-disable-next-line prefer-const
|
|
21970
|
+
complete = $getCurrentDate();
|
|
21971
|
+
const usage = UNCERTAIN_USAGE;
|
|
21972
|
+
// <- TODO: [🥘] Compute real usage for assistant
|
|
21973
|
+
// ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
|
|
21974
|
+
if (resultContent === null) {
|
|
21975
|
+
throw new PipelineExecutionError('No response message from OpenAI');
|
|
21976
|
+
}
|
|
21977
|
+
return exportJson({
|
|
21978
|
+
name: 'promptResult',
|
|
21979
|
+
message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
|
|
21980
|
+
order: [],
|
|
21981
|
+
value: {
|
|
21982
|
+
content: resultContent,
|
|
21983
|
+
modelName: 'assistant',
|
|
21984
|
+
// <- TODO: [🥘] Detect used model in assistant
|
|
21985
|
+
// ?> model: rawResponse.model || modelName,
|
|
21986
|
+
timing: {
|
|
21987
|
+
start,
|
|
21988
|
+
complete,
|
|
21989
|
+
},
|
|
21990
|
+
usage,
|
|
21991
|
+
rawPromptContent,
|
|
21992
|
+
rawRequest,
|
|
21993
|
+
rawResponse,
|
|
21994
|
+
// <- [🗯]
|
|
21995
|
+
},
|
|
21996
|
+
});
|
|
21997
|
+
}
|
|
21998
|
+
/*
|
|
21999
|
+
public async playground() {
|
|
22000
|
+
const client = await this.getClient();
|
|
22001
|
+
|
|
22002
|
+
// List all assistants
|
|
22003
|
+
const assistants = await client.beta.assistants.list();
|
|
22004
|
+
|
|
22005
|
+
// Get details of a specific assistant
|
|
22006
|
+
const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
|
|
22007
|
+
const assistant = await client.beta.assistants.retrieve(assistantId);
|
|
22008
|
+
|
|
22009
|
+
// Update an assistant
|
|
22010
|
+
const updatedAssistant = await client.beta.assistants.update(assistantId, {
|
|
22011
|
+
name: assistant.name + '(M)',
|
|
22012
|
+
description: 'Updated description via Promptbook',
|
|
22013
|
+
metadata: {
|
|
22014
|
+
[Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
|
|
22015
|
+
},
|
|
22016
|
+
});
|
|
22017
|
+
|
|
22018
|
+
await forEver();
|
|
22019
|
+
}
|
|
22020
|
+
*/
|
|
22021
|
+
/**
|
|
22022
|
+
* Get an existing assistant tool wrapper
|
|
22023
|
+
*/
|
|
22024
|
+
getAssistant(assistantId) {
|
|
22025
|
+
return new OpenAiAssistantExecutionTools({
|
|
22026
|
+
...this.options,
|
|
22027
|
+
isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
|
|
22028
|
+
assistantId,
|
|
22029
|
+
});
|
|
21565
22030
|
}
|
|
21566
22031
|
async createNewAssistant(options) {
|
|
21567
22032
|
var _a, _b, _c;
|
|
@@ -21770,6 +22235,7 @@ function emitAssistantPreparationProgress(options) {
|
|
|
21770
22235
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
21771
22236
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
21772
22237
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
22238
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
21773
22239
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
21774
22240
|
*
|
|
21775
22241
|
* @public exported from `@promptbook/core`
|
|
@@ -21904,6 +22370,7 @@ class AgentLlmExecutionTools {
|
|
|
21904
22370
|
* Calls the chat model with agent-specific system prompt and requirements with streaming
|
|
21905
22371
|
*/
|
|
21906
22372
|
async callChatModelStream(prompt, onProgress) {
|
|
22373
|
+
var _a, _b;
|
|
21907
22374
|
// Ensure we're working with a chat prompt
|
|
21908
22375
|
if (prompt.modelRequirements.modelVariant !== 'CHAT') {
|
|
21909
22376
|
throw new Error('AgentLlmExecutionTools only supports chat prompts');
|
|
@@ -21931,7 +22398,75 @@ class AgentLlmExecutionTools {
|
|
|
21931
22398
|
}, // Cast to avoid readonly mismatch from spread
|
|
21932
22399
|
};
|
|
21933
22400
|
console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
|
|
21934
|
-
if (
|
|
22401
|
+
if (OpenAiAgentKitExecutionTools.isOpenAiAgentKitExecutionTools(this.options.llmTools)) {
|
|
22402
|
+
const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
|
|
22403
|
+
const vectorStoreHash = SHA256(JSON.stringify((_a = modelRequirements.knowledgeSources) !== null && _a !== void 0 ? _a : [])).toString();
|
|
22404
|
+
const cachedVectorStore = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
|
|
22405
|
+
const cachedAgentKit = AgentLlmExecutionTools.agentKitAgentCache.get(this.title);
|
|
22406
|
+
let preparedAgentKit = this.options.assistantPreparationMode === 'external'
|
|
22407
|
+
? this.options.llmTools.getPreparedAgentKitAgent()
|
|
22408
|
+
: null;
|
|
22409
|
+
const vectorStoreId = (preparedAgentKit === null || preparedAgentKit === void 0 ? void 0 : preparedAgentKit.vectorStoreId) ||
|
|
22410
|
+
(cachedVectorStore && cachedVectorStore.requirementsHash === vectorStoreHash
|
|
22411
|
+
? cachedVectorStore.vectorStoreId
|
|
22412
|
+
: undefined);
|
|
22413
|
+
if (!preparedAgentKit && cachedAgentKit && cachedAgentKit.requirementsHash === requirementsHash) {
|
|
22414
|
+
if (this.options.isVerbose) {
|
|
22415
|
+
console.info('[🤰]', 'Using cached OpenAI AgentKit agent', {
|
|
22416
|
+
agent: this.title,
|
|
22417
|
+
});
|
|
22418
|
+
}
|
|
22419
|
+
preparedAgentKit = {
|
|
22420
|
+
agent: cachedAgentKit.agent,
|
|
22421
|
+
vectorStoreId: cachedAgentKit.vectorStoreId,
|
|
22422
|
+
};
|
|
22423
|
+
}
|
|
22424
|
+
if (!preparedAgentKit) {
|
|
22425
|
+
if (this.options.isVerbose) {
|
|
22426
|
+
console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
|
|
22427
|
+
agent: this.title,
|
|
22428
|
+
});
|
|
22429
|
+
}
|
|
22430
|
+
if (!vectorStoreId && ((_b = modelRequirements.knowledgeSources) === null || _b === void 0 ? void 0 : _b.length)) {
|
|
22431
|
+
emitAssistantPreparationProgress({
|
|
22432
|
+
onProgress,
|
|
22433
|
+
prompt,
|
|
22434
|
+
modelName: this.modelName,
|
|
22435
|
+
phase: 'Creating knowledge base',
|
|
22436
|
+
});
|
|
22437
|
+
}
|
|
22438
|
+
emitAssistantPreparationProgress({
|
|
22439
|
+
onProgress,
|
|
22440
|
+
prompt,
|
|
22441
|
+
modelName: this.modelName,
|
|
22442
|
+
phase: 'Preparing AgentKit agent',
|
|
22443
|
+
});
|
|
22444
|
+
preparedAgentKit = await this.options.llmTools.prepareAgentKitAgent({
|
|
22445
|
+
name: this.title,
|
|
22446
|
+
instructions: modelRequirements.systemMessage || '',
|
|
22447
|
+
knowledgeSources: modelRequirements.knowledgeSources,
|
|
22448
|
+
tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
|
|
22449
|
+
vectorStoreId,
|
|
22450
|
+
});
|
|
22451
|
+
}
|
|
22452
|
+
if (preparedAgentKit.vectorStoreId) {
|
|
22453
|
+
AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
|
|
22454
|
+
vectorStoreId: preparedAgentKit.vectorStoreId,
|
|
22455
|
+
requirementsHash: vectorStoreHash,
|
|
22456
|
+
});
|
|
22457
|
+
}
|
|
22458
|
+
AgentLlmExecutionTools.agentKitAgentCache.set(this.title, {
|
|
22459
|
+
agent: preparedAgentKit.agent,
|
|
22460
|
+
requirementsHash,
|
|
22461
|
+
vectorStoreId: preparedAgentKit.vectorStoreId,
|
|
22462
|
+
});
|
|
22463
|
+
underlyingLlmResult = await this.options.llmTools.callChatModelStreamWithPreparedAgent({
|
|
22464
|
+
openAiAgentKitAgent: preparedAgentKit.agent,
|
|
22465
|
+
prompt: promptWithAgentModelRequirements,
|
|
22466
|
+
onProgress,
|
|
22467
|
+
});
|
|
22468
|
+
}
|
|
22469
|
+
else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
|
|
21935
22470
|
// ... deprecated path ...
|
|
21936
22471
|
const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
|
|
21937
22472
|
const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
|
|
@@ -22056,6 +22591,10 @@ class AgentLlmExecutionTools {
|
|
|
22056
22591
|
return agentResult;
|
|
22057
22592
|
}
|
|
22058
22593
|
}
|
|
22594
|
+
/**
|
|
22595
|
+
* Cached AgentKit agents to avoid rebuilding identical instances.
|
|
22596
|
+
*/
|
|
22597
|
+
AgentLlmExecutionTools.agentKitAgentCache = new Map();
|
|
22059
22598
|
/**
|
|
22060
22599
|
* Cache of OpenAI assistants to avoid creating duplicates
|
|
22061
22600
|
*/
|
|
@@ -22137,6 +22676,7 @@ function buildTeacherSummary(commitments, used) {
|
|
|
22137
22676
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
22138
22677
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
22139
22678
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
22679
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
22140
22680
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
22141
22681
|
*
|
|
22142
22682
|
* @public exported from `@promptbook/core`
|
|
@@ -22507,7 +23047,8 @@ function buildRemoteAgentSource(profile, meta) {
|
|
|
22507
23047
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
22508
23048
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
22509
23049
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
22510
|
-
* - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
23050
|
+
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
23051
|
+
* - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
|
|
22511
23052
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
22512
23053
|
*
|
|
22513
23054
|
* @public exported from `@promptbook/core`
|