@promptbook/node 0.110.0-3 → 0.110.0-5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.es.js +798 -456
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/openai.index.d.ts +0 -4
- package/esm/typings/src/_packages/types.index.d.ts +0 -4
- package/esm/typings/src/book-components/Chat/Chat/ChatActionsBar.d.ts +4 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageList.d.ts +0 -2
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +0 -7
- package/esm/typings/src/book-components/Chat/SourceChip/SourceChip.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/hooks/useChatActionsOverlap.d.ts +6 -3
- package/esm/typings/src/book-components/Chat/utils/collectTeamToolCallSummary.d.ts +69 -0
- package/esm/typings/src/book-components/Chat/utils/getToolCallChipletInfo.d.ts +13 -6
- package/esm/typings/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +9 -0
- package/esm/typings/src/book-components/Chat/utils/toolCallParsing.d.ts +4 -0
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +0 -7
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +0 -1
- package/esm/typings/src/llm-providers/agent/AgentOptions.d.ts +9 -0
- package/esm/typings/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +9 -0
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +40 -1
- package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +30 -0
- package/esm/typings/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +29 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +3 -5
- package/umd/index.umd.js +798 -456
- package/umd/index.umd.js.map +1 -1
- package/esm/typings/src/llm-providers/openai/OpenAiAgentExecutionTools.d.ts +0 -43
- package/esm/typings/src/llm-providers/openai/createOpenAiAgentExecutionTools.d.ts +0 -11
package/umd/index.umd.js
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* @generated
|
|
49
49
|
* @see https://github.com/webgptorg/promptbook
|
|
50
50
|
*/
|
|
51
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.110.0-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.110.0-5';
|
|
52
52
|
/**
|
|
53
53
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
54
54
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -16988,11 +16988,16 @@
|
|
|
16988
16988
|
const request = buildTeammateRequest(message, args.context);
|
|
16989
16989
|
let response = '';
|
|
16990
16990
|
let error = null;
|
|
16991
|
+
let toolCalls;
|
|
16991
16992
|
try {
|
|
16992
16993
|
const remoteAgent = await getRemoteTeammateAgent(entry.teammate.url);
|
|
16993
16994
|
const prompt = buildTeammatePrompt(request);
|
|
16994
16995
|
const teammateResult = await remoteAgent.callChatModel(prompt);
|
|
16995
16996
|
response = teammateResult.content || '';
|
|
16997
|
+
toolCalls =
|
|
16998
|
+
'toolCalls' in teammateResult && Array.isArray(teammateResult.toolCalls)
|
|
16999
|
+
? teammateResult.toolCalls
|
|
17000
|
+
: undefined;
|
|
16996
17001
|
}
|
|
16997
17002
|
catch (err) {
|
|
16998
17003
|
error = err instanceof Error ? err.message : String(err);
|
|
@@ -17002,6 +17007,7 @@
|
|
|
17002
17007
|
teammate: teammateMetadata,
|
|
17003
17008
|
request,
|
|
17004
17009
|
response: teammateReply,
|
|
17010
|
+
toolCalls: toolCalls && toolCalls.length > 0 ? toolCalls : undefined,
|
|
17005
17011
|
error,
|
|
17006
17012
|
conversation: [
|
|
17007
17013
|
{
|
|
@@ -23264,18 +23270,6 @@
|
|
|
23264
23270
|
get profile() {
|
|
23265
23271
|
return OPENAI_PROVIDER_PROFILE;
|
|
23266
23272
|
}
|
|
23267
|
-
/*
|
|
23268
|
-
Note: Commenting this out to avoid circular dependency
|
|
23269
|
-
/**
|
|
23270
|
-
* Create (sub)tools for calling OpenAI API Assistants
|
|
23271
|
-
*
|
|
23272
|
-
* @param assistantId Which assistant to use
|
|
23273
|
-
* @returns Tools for calling OpenAI API Assistants with same token
|
|
23274
|
-
* /
|
|
23275
|
-
public createAssistantSubtools(assistantId: string_token): OpenAiAssistantExecutionTools {
|
|
23276
|
-
return new OpenAiAssistantExecutionTools({ ...this.options, assistantId });
|
|
23277
|
-
}
|
|
23278
|
-
*/
|
|
23279
23273
|
/**
|
|
23280
23274
|
* List all available models (non dynamically)
|
|
23281
23275
|
*
|
|
@@ -23310,207 +23304,6 @@
|
|
|
23310
23304
|
}
|
|
23311
23305
|
}
|
|
23312
23306
|
|
|
23313
|
-
/**
|
|
23314
|
-
* Execution Tools for calling OpenAI API using the Responses API (Agents)
|
|
23315
|
-
*
|
|
23316
|
-
* @public exported from `@promptbook/openai`
|
|
23317
|
-
*/
|
|
23318
|
-
class OpenAiAgentExecutionTools extends OpenAiExecutionTools {
|
|
23319
|
-
constructor(options) {
|
|
23320
|
-
super(options);
|
|
23321
|
-
this.vectorStoreId = options.vectorStoreId;
|
|
23322
|
-
}
|
|
23323
|
-
get title() {
|
|
23324
|
-
return 'OpenAI Agent';
|
|
23325
|
-
}
|
|
23326
|
-
get description() {
|
|
23327
|
-
return 'Use OpenAI Responses API (Agentic)';
|
|
23328
|
-
}
|
|
23329
|
-
/**
|
|
23330
|
-
* Calls OpenAI API to use a chat model with streaming.
|
|
23331
|
-
*/
|
|
23332
|
-
async callChatModelStream(prompt, onProgress) {
|
|
23333
|
-
if (this.options.isVerbose) {
|
|
23334
|
-
console.info('💬 OpenAI Agent callChatModel call', { prompt });
|
|
23335
|
-
}
|
|
23336
|
-
const { content, parameters, modelRequirements } = prompt;
|
|
23337
|
-
const client = await this.getClient();
|
|
23338
|
-
if (modelRequirements.modelVariant !== 'CHAT') {
|
|
23339
|
-
throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
|
|
23340
|
-
}
|
|
23341
|
-
const rawPromptContent = templateParameters(content, {
|
|
23342
|
-
...parameters,
|
|
23343
|
-
modelName: 'agent',
|
|
23344
|
-
});
|
|
23345
|
-
// Build input items
|
|
23346
|
-
const input = []; // TODO: Type properly when OpenAI types are updated
|
|
23347
|
-
// Add previous messages from thread (if any)
|
|
23348
|
-
if ('thread' in prompt && Array.isArray(prompt.thread)) {
|
|
23349
|
-
const previousMessages = prompt.thread.map((msg) => ({
|
|
23350
|
-
role: msg.sender === 'assistant' ? 'assistant' : 'user',
|
|
23351
|
-
content: msg.content,
|
|
23352
|
-
}));
|
|
23353
|
-
input.push(...previousMessages);
|
|
23354
|
-
}
|
|
23355
|
-
// Add current user message
|
|
23356
|
-
input.push({
|
|
23357
|
-
role: 'user',
|
|
23358
|
-
content: rawPromptContent,
|
|
23359
|
-
});
|
|
23360
|
-
// Prepare tools
|
|
23361
|
-
const tools = modelRequirements.tools ? mapToolsToOpenAi(modelRequirements.tools) : undefined;
|
|
23362
|
-
// Add file_search if vector store is present
|
|
23363
|
-
const agentTools = tools ? [...tools] : [];
|
|
23364
|
-
let toolResources = undefined;
|
|
23365
|
-
if (this.vectorStoreId) {
|
|
23366
|
-
agentTools.push({ type: 'file_search' });
|
|
23367
|
-
toolResources = {
|
|
23368
|
-
file_search: {
|
|
23369
|
-
vector_store_ids: [this.vectorStoreId],
|
|
23370
|
-
},
|
|
23371
|
-
};
|
|
23372
|
-
}
|
|
23373
|
-
// Add file_search also if knowledgeSources are present in the prompt (passed via AgentLlmExecutionTools)
|
|
23374
|
-
if (modelRequirements.knowledgeSources &&
|
|
23375
|
-
modelRequirements.knowledgeSources.length > 0 &&
|
|
23376
|
-
!this.vectorStoreId) {
|
|
23377
|
-
// Note: Vector store should have been created by AgentLlmExecutionTools and passed via options.
|
|
23378
|
-
// If we are here, it means we have knowledge sources but no vector store ID.
|
|
23379
|
-
// We can't easily create one here without persisting it.
|
|
23380
|
-
console.warn('Knowledge sources provided but no vector store ID. Creating temporary vector store is not implemented in callChatModelStream.');
|
|
23381
|
-
}
|
|
23382
|
-
const start = $getCurrentDate();
|
|
23383
|
-
// Construct the request
|
|
23384
|
-
const rawRequest = {
|
|
23385
|
-
// TODO: Type properly as OpenAI.Responses.CreateResponseParams
|
|
23386
|
-
model: modelRequirements.modelName || 'gpt-4o',
|
|
23387
|
-
input,
|
|
23388
|
-
instructions: modelRequirements.systemMessage,
|
|
23389
|
-
tools: agentTools.length > 0 ? agentTools : undefined,
|
|
23390
|
-
tool_resources: toolResources,
|
|
23391
|
-
store: false, // Stateless by default as we pass full history
|
|
23392
|
-
};
|
|
23393
|
-
if (this.options.isVerbose) {
|
|
23394
|
-
console.info(colors__default["default"].bgWhite('rawRequest (Responses API)'), JSON.stringify(rawRequest, null, 4));
|
|
23395
|
-
}
|
|
23396
|
-
// Call Responses API
|
|
23397
|
-
// Note: Using any cast because types might not be updated yet
|
|
23398
|
-
const response = await client.responses.create(rawRequest);
|
|
23399
|
-
if (this.options.isVerbose) {
|
|
23400
|
-
console.info(colors__default["default"].bgWhite('rawResponse'), JSON.stringify(response, null, 4));
|
|
23401
|
-
}
|
|
23402
|
-
const complete = $getCurrentDate();
|
|
23403
|
-
let resultContent = '';
|
|
23404
|
-
const toolCalls = [];
|
|
23405
|
-
// Parse output items
|
|
23406
|
-
if (response.output) {
|
|
23407
|
-
for (const item of response.output) {
|
|
23408
|
-
if (item.type === 'message' && item.role === 'assistant') {
|
|
23409
|
-
for (const contentPart of item.content) {
|
|
23410
|
-
if (contentPart.type === 'output_text') {
|
|
23411
|
-
// "output_text" based on migration guide, or "text"? Guide says "output_text" in example.
|
|
23412
|
-
resultContent += contentPart.text;
|
|
23413
|
-
}
|
|
23414
|
-
else if (contentPart.type === 'text') {
|
|
23415
|
-
resultContent += contentPart.text.value || contentPart.text;
|
|
23416
|
-
}
|
|
23417
|
-
}
|
|
23418
|
-
}
|
|
23419
|
-
else if (item.type === 'function_call') ;
|
|
23420
|
-
}
|
|
23421
|
-
}
|
|
23422
|
-
// Use output_text helper if available (mentioned in guide)
|
|
23423
|
-
if (response.output_text) {
|
|
23424
|
-
resultContent = response.output_text;
|
|
23425
|
-
}
|
|
23426
|
-
// TODO: Handle tool calls properly (Requires clearer docs or experimentation)
|
|
23427
|
-
onProgress({
|
|
23428
|
-
content: resultContent,
|
|
23429
|
-
modelName: response.model || 'agent',
|
|
23430
|
-
timing: { start, complete },
|
|
23431
|
-
usage: UNCERTAIN_USAGE,
|
|
23432
|
-
rawPromptContent,
|
|
23433
|
-
rawRequest,
|
|
23434
|
-
rawResponse: response,
|
|
23435
|
-
});
|
|
23436
|
-
return exportJson({
|
|
23437
|
-
name: 'promptResult',
|
|
23438
|
-
message: `Result of \`OpenAiAgentExecutionTools.callChatModelStream\``,
|
|
23439
|
-
order: [],
|
|
23440
|
-
value: {
|
|
23441
|
-
content: resultContent,
|
|
23442
|
-
modelName: response.model || 'agent',
|
|
23443
|
-
timing: { start, complete },
|
|
23444
|
-
usage: UNCERTAIN_USAGE,
|
|
23445
|
-
rawPromptContent,
|
|
23446
|
-
rawRequest,
|
|
23447
|
-
rawResponse: response,
|
|
23448
|
-
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
23449
|
-
},
|
|
23450
|
-
});
|
|
23451
|
-
}
|
|
23452
|
-
/**
|
|
23453
|
-
* Creates a vector store from knowledge sources
|
|
23454
|
-
*/
|
|
23455
|
-
static async createVectorStore(client, name, knowledgeSources) {
|
|
23456
|
-
// Create a vector store
|
|
23457
|
-
const vectorStore = await client.beta.vectorStores.create({
|
|
23458
|
-
name: `${name} Knowledge Base`,
|
|
23459
|
-
});
|
|
23460
|
-
const vectorStoreId = vectorStore.id;
|
|
23461
|
-
// Upload files from knowledge sources to the vector store
|
|
23462
|
-
const fileStreams = [];
|
|
23463
|
-
for (const source of knowledgeSources) {
|
|
23464
|
-
try {
|
|
23465
|
-
// Check if it's a URL
|
|
23466
|
-
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
23467
|
-
// Download the file
|
|
23468
|
-
const response = await fetch(source);
|
|
23469
|
-
if (!response.ok) {
|
|
23470
|
-
console.error(`Failed to download ${source}: ${response.statusText}`);
|
|
23471
|
-
continue;
|
|
23472
|
-
}
|
|
23473
|
-
const buffer = await response.arrayBuffer();
|
|
23474
|
-
const filename = source.split('/').pop() || 'downloaded-file';
|
|
23475
|
-
const blob = new Blob([buffer]);
|
|
23476
|
-
const file = new File([blob], filename);
|
|
23477
|
-
fileStreams.push(file);
|
|
23478
|
-
}
|
|
23479
|
-
else {
|
|
23480
|
-
// Local files not supported in browser env easily, same as before
|
|
23481
|
-
}
|
|
23482
|
-
}
|
|
23483
|
-
catch (error) {
|
|
23484
|
-
console.error(`Error processing knowledge source ${source}:`, error);
|
|
23485
|
-
}
|
|
23486
|
-
}
|
|
23487
|
-
// Batch upload files to the vector store
|
|
23488
|
-
if (fileStreams.length > 0) {
|
|
23489
|
-
try {
|
|
23490
|
-
await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
|
|
23491
|
-
files: fileStreams,
|
|
23492
|
-
});
|
|
23493
|
-
}
|
|
23494
|
-
catch (error) {
|
|
23495
|
-
console.error('Error uploading files to vector store:', error);
|
|
23496
|
-
}
|
|
23497
|
-
}
|
|
23498
|
-
return vectorStoreId;
|
|
23499
|
-
}
|
|
23500
|
-
/**
|
|
23501
|
-
* Discriminant for type guards
|
|
23502
|
-
*/
|
|
23503
|
-
get discriminant() {
|
|
23504
|
-
return 'OPEN_AI_AGENT';
|
|
23505
|
-
}
|
|
23506
|
-
/**
|
|
23507
|
-
* Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentExecutionTools`
|
|
23508
|
-
*/
|
|
23509
|
-
static isOpenAiAgentExecutionTools(llmExecutionTools) {
|
|
23510
|
-
return llmExecutionTools.discriminant === 'OPEN_AI_AGENT';
|
|
23511
|
-
}
|
|
23512
|
-
}
|
|
23513
|
-
|
|
23514
23307
|
/**
|
|
23515
23308
|
* Uploads files to OpenAI and returns their IDs
|
|
23516
23309
|
*
|
|
@@ -23532,6 +23325,10 @@
|
|
|
23532
23325
|
return fileIds;
|
|
23533
23326
|
}
|
|
23534
23327
|
|
|
23328
|
+
const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
|
|
23329
|
+
const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
|
|
23330
|
+
const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
|
|
23331
|
+
const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
|
|
23535
23332
|
/**
|
|
23536
23333
|
* Execution Tools for calling OpenAI API Assistants
|
|
23537
23334
|
*
|
|
@@ -23545,7 +23342,6 @@
|
|
|
23545
23342
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
23546
23343
|
*
|
|
23547
23344
|
* @public exported from `@promptbook/openai`
|
|
23548
|
-
* @deprecated Use `OpenAiAgentExecutionTools` instead which uses the new OpenAI Responses API
|
|
23549
23345
|
*/
|
|
23550
23346
|
class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
|
|
23551
23347
|
/**
|
|
@@ -23982,111 +23778,731 @@
|
|
|
23982
23778
|
assistantId,
|
|
23983
23779
|
});
|
|
23984
23780
|
}
|
|
23985
|
-
|
|
23986
|
-
|
|
23987
|
-
|
|
23988
|
-
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
23992
|
-
|
|
23993
|
-
|
|
23994
|
-
|
|
23781
|
+
/**
|
|
23782
|
+
* Returns the per-knowledge-source download timeout in milliseconds.
|
|
23783
|
+
*/
|
|
23784
|
+
getKnowledgeSourceDownloadTimeoutMs() {
|
|
23785
|
+
var _a;
|
|
23786
|
+
return (_a = this.assistantOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
|
|
23787
|
+
}
|
|
23788
|
+
/**
|
|
23789
|
+
* Returns the max concurrency for knowledge source uploads.
|
|
23790
|
+
*/
|
|
23791
|
+
getKnowledgeSourceUploadMaxConcurrency() {
|
|
23792
|
+
var _a;
|
|
23793
|
+
return (_a = this.assistantOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
|
|
23794
|
+
}
|
|
23795
|
+
/**
|
|
23796
|
+
* Returns the polling interval in milliseconds for vector store uploads.
|
|
23797
|
+
*/
|
|
23798
|
+
getKnowledgeSourceUploadPollIntervalMs() {
|
|
23799
|
+
var _a;
|
|
23800
|
+
return (_a = this.assistantOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
|
|
23801
|
+
}
|
|
23802
|
+
/**
|
|
23803
|
+
* Returns the overall upload timeout in milliseconds for vector store uploads.
|
|
23804
|
+
*/
|
|
23805
|
+
getKnowledgeSourceUploadTimeoutMs() {
|
|
23806
|
+
var _a;
|
|
23807
|
+
return (_a = this.assistantOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
|
|
23808
|
+
}
|
|
23809
|
+
/**
|
|
23810
|
+
* Returns true if we should continue even if vector store ingestion stalls.
|
|
23811
|
+
*/
|
|
23812
|
+
shouldContinueOnVectorStoreStall() {
|
|
23813
|
+
var _a;
|
|
23814
|
+
return (_a = this.assistantOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
|
|
23815
|
+
}
|
|
23816
|
+
/**
|
|
23817
|
+
* Returns assistant-specific options with extended settings.
|
|
23818
|
+
*/
|
|
23819
|
+
get assistantOptions() {
|
|
23820
|
+
return this.options;
|
|
23821
|
+
}
|
|
23822
|
+
/**
|
|
23823
|
+
* Downloads a knowledge source URL into a File for vector store upload.
|
|
23824
|
+
*/
|
|
23825
|
+
async downloadKnowledgeSourceFile(options) {
|
|
23826
|
+
var _a;
|
|
23827
|
+
const { source, timeoutMs, logLabel } = options;
|
|
23828
|
+
const startedAtMs = Date.now();
|
|
23829
|
+
const controller = new AbortController();
|
|
23830
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
23995
23831
|
if (this.options.isVerbose) {
|
|
23996
|
-
console.info('[🤰]', '
|
|
23997
|
-
|
|
23998
|
-
|
|
23999
|
-
|
|
24000
|
-
instructionsLength: instructions.length,
|
|
23832
|
+
console.info('[🤰]', 'Downloading knowledge source', {
|
|
23833
|
+
source,
|
|
23834
|
+
timeoutMs,
|
|
23835
|
+
logLabel,
|
|
24001
23836
|
});
|
|
24002
23837
|
}
|
|
24003
|
-
|
|
24004
|
-
|
|
24005
|
-
|
|
24006
|
-
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
|
|
24010
|
-
|
|
23838
|
+
try {
|
|
23839
|
+
const response = await fetch(source, { signal: controller.signal });
|
|
23840
|
+
const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : undefined;
|
|
23841
|
+
if (!response.ok) {
|
|
23842
|
+
console.error('[🤰]', 'Failed to download knowledge source', {
|
|
23843
|
+
source,
|
|
23844
|
+
status: response.status,
|
|
23845
|
+
statusText: response.statusText,
|
|
23846
|
+
contentType,
|
|
23847
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
23848
|
+
logLabel,
|
|
24011
23849
|
});
|
|
23850
|
+
return null;
|
|
24012
23851
|
}
|
|
24013
|
-
|
|
24014
|
-
|
|
24015
|
-
|
|
24016
|
-
|
|
24017
|
-
|
|
23852
|
+
const buffer = await response.arrayBuffer();
|
|
23853
|
+
let filename = source.split('/').pop() || 'downloaded-file';
|
|
23854
|
+
try {
|
|
23855
|
+
const url = new URL(source);
|
|
23856
|
+
filename = url.pathname.split('/').pop() || filename;
|
|
23857
|
+
}
|
|
23858
|
+
catch (error) {
|
|
23859
|
+
// Keep default filename
|
|
23860
|
+
}
|
|
23861
|
+
const file = new File([buffer], filename, contentType ? { type: contentType } : undefined);
|
|
23862
|
+
const elapsedMs = Date.now() - startedAtMs;
|
|
23863
|
+
const sizeBytes = buffer.byteLength;
|
|
24018
23864
|
if (this.options.isVerbose) {
|
|
24019
|
-
console.info('[🤰]', '
|
|
24020
|
-
|
|
23865
|
+
console.info('[🤰]', 'Downloaded knowledge source', {
|
|
23866
|
+
source,
|
|
23867
|
+
filename,
|
|
23868
|
+
sizeBytes,
|
|
23869
|
+
contentType,
|
|
23870
|
+
elapsedMs,
|
|
23871
|
+
logLabel,
|
|
24021
23872
|
});
|
|
24022
23873
|
}
|
|
24023
|
-
|
|
24024
|
-
|
|
24025
|
-
|
|
23874
|
+
return { file, sizeBytes, filename, elapsedMs };
|
|
23875
|
+
}
|
|
23876
|
+
catch (error) {
|
|
23877
|
+
assertsError(error);
|
|
23878
|
+
console.error('[🤰]', 'Error downloading knowledge source', {
|
|
23879
|
+
source,
|
|
23880
|
+
elapsedMs: Date.now() - startedAtMs,
|
|
23881
|
+
logLabel,
|
|
23882
|
+
error: serializeError(error),
|
|
23883
|
+
});
|
|
23884
|
+
return null;
|
|
23885
|
+
}
|
|
23886
|
+
finally {
|
|
23887
|
+
clearTimeout(timeoutId);
|
|
23888
|
+
}
|
|
23889
|
+
}
|
|
23890
|
+
/**
|
|
23891
|
+
* Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
|
|
23892
|
+
*/
|
|
23893
|
+
async logVectorStoreFileBatchDiagnostics(options) {
|
|
23894
|
+
var _a, _b;
|
|
23895
|
+
const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
|
|
23896
|
+
if (reason === 'stalled' && !this.options.isVerbose) {
|
|
23897
|
+
return;
|
|
23898
|
+
}
|
|
23899
|
+
if (!batchId.startsWith('vsfb_')) {
|
|
23900
|
+
console.error('[🤰]', 'Vector store file batch diagnostics skipped (invalid batch id)', {
|
|
23901
|
+
vectorStoreId,
|
|
23902
|
+
batchId,
|
|
23903
|
+
reason,
|
|
23904
|
+
logLabel,
|
|
23905
|
+
});
|
|
23906
|
+
return;
|
|
23907
|
+
}
|
|
23908
|
+
const fileIdToMetadata = new Map();
|
|
23909
|
+
for (const file of uploadedFiles) {
|
|
23910
|
+
fileIdToMetadata.set(file.fileId, file);
|
|
23911
|
+
}
|
|
23912
|
+
try {
|
|
23913
|
+
const limit = Math.min(100, Math.max(10, uploadedFiles.length));
|
|
23914
|
+
const batchFilesPage = await client.beta.vectorStores.fileBatches.listFiles(vectorStoreId, batchId, {
|
|
23915
|
+
limit,
|
|
23916
|
+
});
|
|
23917
|
+
const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
|
|
23918
|
+
const statusCounts = {
|
|
23919
|
+
in_progress: 0,
|
|
23920
|
+
completed: 0,
|
|
23921
|
+
failed: 0,
|
|
23922
|
+
cancelled: 0,
|
|
23923
|
+
};
|
|
23924
|
+
const errorSamples = [];
|
|
23925
|
+
const inProgressSamples = [];
|
|
23926
|
+
const batchFileIds = new Set();
|
|
23927
|
+
for (const file of batchFiles) {
|
|
23928
|
+
batchFileIds.add(file.id);
|
|
23929
|
+
statusCounts[file.status] = ((_b = statusCounts[file.status]) !== null && _b !== void 0 ? _b : 0) + 1;
|
|
23930
|
+
const metadata = fileIdToMetadata.get(file.id);
|
|
23931
|
+
if (file.last_error) {
|
|
23932
|
+
errorSamples.push({
|
|
23933
|
+
fileId: file.id,
|
|
23934
|
+
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
23935
|
+
sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
|
|
23936
|
+
status: file.status,
|
|
23937
|
+
lastError: file.last_error,
|
|
23938
|
+
});
|
|
23939
|
+
}
|
|
23940
|
+
else if (file.status === 'in_progress' && inProgressSamples.length < 5) {
|
|
23941
|
+
inProgressSamples.push({
|
|
23942
|
+
fileId: file.id,
|
|
23943
|
+
filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
|
|
23944
|
+
sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
|
|
23945
|
+
});
|
|
23946
|
+
}
|
|
23947
|
+
}
|
|
23948
|
+
const missingSamples = uploadedFiles
|
|
23949
|
+
.filter((file) => !batchFileIds.has(file.fileId))
|
|
23950
|
+
.slice(0, 5)
|
|
23951
|
+
.map((file) => ({
|
|
23952
|
+
fileId: file.fileId,
|
|
23953
|
+
filename: file.filename,
|
|
23954
|
+
sizeBytes: file.sizeBytes,
|
|
23955
|
+
}));
|
|
23956
|
+
const vectorStore = await client.beta.vectorStores.retrieve(vectorStoreId);
|
|
23957
|
+
const logPayload = {
|
|
23958
|
+
vectorStoreId,
|
|
23959
|
+
batchId,
|
|
23960
|
+
reason,
|
|
23961
|
+
vectorStoreStatus: vectorStore.status,
|
|
23962
|
+
vectorStoreFileCounts: vectorStore.file_counts,
|
|
23963
|
+
vectorStoreUsageBytes: vectorStore.usage_bytes,
|
|
23964
|
+
batchFileCount: batchFiles.length,
|
|
23965
|
+
statusCounts,
|
|
23966
|
+
errorSamples: errorSamples.slice(0, 5),
|
|
23967
|
+
inProgressSamples,
|
|
23968
|
+
missingFileCount: uploadedFiles.length - batchFileIds.size,
|
|
23969
|
+
missingSamples,
|
|
23970
|
+
logLabel,
|
|
23971
|
+
};
|
|
23972
|
+
const logFunction = reason === 'stalled' ? console.info : console.error;
|
|
23973
|
+
logFunction('[🤰]', 'Vector store file batch diagnostics', logPayload);
|
|
23974
|
+
}
|
|
23975
|
+
catch (error) {
|
|
23976
|
+
assertsError(error);
|
|
23977
|
+
console.error('[🤰]', 'Vector store file batch diagnostics failed', {
|
|
23978
|
+
vectorStoreId,
|
|
23979
|
+
batchId,
|
|
23980
|
+
reason,
|
|
23981
|
+
logLabel,
|
|
23982
|
+
error: serializeError(error),
|
|
23983
|
+
});
|
|
23984
|
+
}
|
|
23985
|
+
}
|
|
23986
|
+
/**
|
|
23987
|
+
* Uploads knowledge source files to the vector store and polls until processing completes.
|
|
23988
|
+
*/
|
|
23989
|
+
async uploadKnowledgeSourceFilesToVectorStore(options) {
|
|
23990
|
+
var _a, _b, _c, _d;
|
|
23991
|
+
const { client, vectorStoreId, files, totalBytes, logLabel } = options;
|
|
23992
|
+
const uploadStartedAtMs = Date.now();
|
|
23993
|
+
const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
|
|
23994
|
+
const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
|
|
23995
|
+
const uploadTimeoutMs = Math.max(1000, this.getKnowledgeSourceUploadTimeoutMs());
|
|
23996
|
+
if (this.options.isVerbose) {
|
|
23997
|
+
console.info('[🤰]', 'Uploading knowledge source files to OpenAI', {
|
|
23998
|
+
vectorStoreId,
|
|
23999
|
+
fileCount: files.length,
|
|
24000
|
+
totalBytes,
|
|
24001
|
+
maxConcurrency,
|
|
24002
|
+
pollIntervalMs,
|
|
24003
|
+
uploadTimeoutMs,
|
|
24004
|
+
logLabel,
|
|
24005
|
+
});
|
|
24006
|
+
}
|
|
24007
|
+
const fileTypeSummary = {};
|
|
24008
|
+
for (const file of files) {
|
|
24009
|
+
const filename = (_a = file.name) !== null && _a !== void 0 ? _a : '';
|
|
24010
|
+
const extension = filename.includes('.')
|
|
24011
|
+
? (_c = (_b = filename.split('.').pop()) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : 'unknown'
|
|
24012
|
+
: 'unknown';
|
|
24013
|
+
const sizeBytes = typeof file.size === 'number' ? file.size : 0;
|
|
24014
|
+
const summary = (_d = fileTypeSummary[extension]) !== null && _d !== void 0 ? _d : { count: 0, totalBytes: 0 };
|
|
24015
|
+
summary.count += 1;
|
|
24016
|
+
summary.totalBytes += sizeBytes;
|
|
24017
|
+
fileTypeSummary[extension] = summary;
|
|
24018
|
+
}
|
|
24019
|
+
if (this.options.isVerbose) {
|
|
24020
|
+
console.info('[🤰]', 'Knowledge source file summary', {
|
|
24021
|
+
vectorStoreId,
|
|
24022
|
+
fileCount: files.length,
|
|
24023
|
+
totalBytes,
|
|
24024
|
+
fileTypeSummary,
|
|
24025
|
+
logLabel,
|
|
24026
|
+
});
|
|
24027
|
+
}
|
|
24028
|
+
const fileEntries = files.map((file, index) => ({ file, index }));
|
|
24029
|
+
const fileIterator = fileEntries.values();
|
|
24030
|
+
const fileIds = [];
|
|
24031
|
+
const uploadedFiles = [];
|
|
24032
|
+
const failedUploads = [];
|
|
24033
|
+
let uploadedCount = 0;
|
|
24034
|
+
const processFiles = async (iterator) => {
|
|
24035
|
+
var _a, _b;
|
|
24036
|
+
for (const { file, index } of iterator) {
|
|
24037
|
+
const uploadIndex = index + 1;
|
|
24038
|
+
const filename = file.name || `knowledge-source-${uploadIndex}`;
|
|
24039
|
+
const extension = filename.includes('.')
|
|
24040
|
+
? (_b = (_a = filename.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : 'unknown'
|
|
24041
|
+
: 'unknown';
|
|
24042
|
+
const sizeBytes = typeof file.size === 'number' ? file.size : undefined;
|
|
24043
|
+
const fileUploadStartedAtMs = Date.now();
|
|
24044
|
+
if (this.options.isVerbose) {
|
|
24045
|
+
console.info('[🤰]', 'Uploading knowledge source file', {
|
|
24046
|
+
index: uploadIndex,
|
|
24047
|
+
total: files.length,
|
|
24048
|
+
filename,
|
|
24049
|
+
extension,
|
|
24050
|
+
sizeBytes,
|
|
24051
|
+
logLabel,
|
|
24052
|
+
});
|
|
24053
|
+
}
|
|
24026
24054
|
try {
|
|
24055
|
+
const uploaded = await client.files.create({ file, purpose: 'assistants' });
|
|
24056
|
+
fileIds.push(uploaded.id);
|
|
24057
|
+
uploadedFiles.push({ fileId: uploaded.id, filename, sizeBytes });
|
|
24058
|
+
uploadedCount += 1;
|
|
24027
24059
|
if (this.options.isVerbose) {
|
|
24028
|
-
console.info('[🤰]', '
|
|
24029
|
-
index:
|
|
24030
|
-
total:
|
|
24031
|
-
|
|
24032
|
-
|
|
24060
|
+
console.info('[🤰]', 'Uploaded knowledge source file', {
|
|
24061
|
+
index: uploadIndex,
|
|
24062
|
+
total: files.length,
|
|
24063
|
+
filename,
|
|
24064
|
+
sizeBytes,
|
|
24065
|
+
fileId: uploaded.id,
|
|
24066
|
+
elapsedMs: Date.now() - fileUploadStartedAtMs,
|
|
24067
|
+
logLabel,
|
|
24033
24068
|
});
|
|
24034
24069
|
}
|
|
24035
|
-
// Check if it's a URL
|
|
24036
|
-
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
24037
|
-
// Download the file
|
|
24038
|
-
const response = await fetch(source);
|
|
24039
|
-
if (!response.ok) {
|
|
24040
|
-
console.error(`Failed to download ${source}: ${response.statusText}`);
|
|
24041
|
-
continue;
|
|
24042
|
-
}
|
|
24043
|
-
const buffer = await response.arrayBuffer();
|
|
24044
|
-
let filename = source.split('/').pop() || 'downloaded-file';
|
|
24045
|
-
try {
|
|
24046
|
-
const url = new URL(source);
|
|
24047
|
-
filename = url.pathname.split('/').pop() || filename;
|
|
24048
|
-
}
|
|
24049
|
-
catch (error) {
|
|
24050
|
-
// Keep default filename
|
|
24051
|
-
}
|
|
24052
|
-
const blob = new Blob([buffer]);
|
|
24053
|
-
const file = new File([blob], filename);
|
|
24054
|
-
fileStreams.push(file);
|
|
24055
|
-
}
|
|
24056
|
-
else {
|
|
24057
|
-
/*
|
|
24058
|
-
TODO: [🐱🚀] Resolve problem with browser environment
|
|
24059
|
-
// Assume it's a local file path
|
|
24060
|
-
// Note: This will work in Node.js environment
|
|
24061
|
-
// For browser environments, this would need different handling
|
|
24062
|
-
const fs = await import('fs');
|
|
24063
|
-
const fileStream = fs.createReadStream(source);
|
|
24064
|
-
fileStreams.push(fileStream);
|
|
24065
|
-
*/
|
|
24066
|
-
}
|
|
24067
24070
|
}
|
|
24068
24071
|
catch (error) {
|
|
24069
|
-
|
|
24072
|
+
assertsError(error);
|
|
24073
|
+
const serializedError = serializeError(error);
|
|
24074
|
+
failedUploads.push({ index: uploadIndex, filename, error: serializedError });
|
|
24075
|
+
console.error('[🤰]', 'Failed to upload knowledge source file', {
|
|
24076
|
+
index: uploadIndex,
|
|
24077
|
+
total: files.length,
|
|
24078
|
+
filename,
|
|
24079
|
+
sizeBytes,
|
|
24080
|
+
elapsedMs: Date.now() - fileUploadStartedAtMs,
|
|
24081
|
+
logLabel,
|
|
24082
|
+
error: serializedError,
|
|
24083
|
+
});
|
|
24070
24084
|
}
|
|
24071
24085
|
}
|
|
24072
|
-
|
|
24073
|
-
|
|
24074
|
-
|
|
24075
|
-
|
|
24076
|
-
|
|
24086
|
+
};
|
|
24087
|
+
const workerCount = Math.min(maxConcurrency, files.length);
|
|
24088
|
+
const workers = Array.from({ length: workerCount }, () => processFiles(fileIterator));
|
|
24089
|
+
await Promise.all(workers);
|
|
24090
|
+
if (this.options.isVerbose) {
|
|
24091
|
+
console.info('[🤰]', 'Finished uploading knowledge source files', {
|
|
24092
|
+
vectorStoreId,
|
|
24093
|
+
fileCount: files.length,
|
|
24094
|
+
uploadedCount,
|
|
24095
|
+
failedCount: failedUploads.length,
|
|
24096
|
+
elapsedMs: Date.now() - uploadStartedAtMs,
|
|
24097
|
+
failedSamples: failedUploads.slice(0, 3),
|
|
24098
|
+
logLabel,
|
|
24099
|
+
});
|
|
24100
|
+
}
|
|
24101
|
+
if (fileIds.length === 0) {
|
|
24102
|
+
console.error('[🤰]', 'No knowledge source files were uploaded', {
|
|
24103
|
+
vectorStoreId,
|
|
24104
|
+
fileCount: files.length,
|
|
24105
|
+
failedCount: failedUploads.length,
|
|
24106
|
+
logLabel,
|
|
24107
|
+
});
|
|
24108
|
+
return null;
|
|
24109
|
+
}
|
|
24110
|
+
const batch = await client.beta.vectorStores.fileBatches.create(vectorStoreId, {
|
|
24111
|
+
file_ids: fileIds,
|
|
24112
|
+
});
|
|
24113
|
+
const expectedBatchId = batch.id;
|
|
24114
|
+
const expectedBatchIdValid = expectedBatchId.startsWith('vsfb_');
|
|
24115
|
+
if (!expectedBatchIdValid) {
|
|
24116
|
+
console.error('[🤰]', 'Vector store file batch id looks invalid', {
|
|
24117
|
+
vectorStoreId,
|
|
24118
|
+
batchId: expectedBatchId,
|
|
24119
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
24120
|
+
logLabel,
|
|
24121
|
+
});
|
|
24122
|
+
}
|
|
24123
|
+
else if (batch.vector_store_id !== vectorStoreId) {
|
|
24124
|
+
console.error('[🤰]', 'Vector store file batch vector store id mismatch', {
|
|
24125
|
+
vectorStoreId,
|
|
24126
|
+
batchId: expectedBatchId,
|
|
24127
|
+
batchVectorStoreId: batch.vector_store_id,
|
|
24128
|
+
logLabel,
|
|
24129
|
+
});
|
|
24130
|
+
}
|
|
24131
|
+
if (this.options.isVerbose) {
|
|
24132
|
+
console.info('[🤰]', 'Created vector store file batch', {
|
|
24133
|
+
vectorStoreId,
|
|
24134
|
+
batchId: expectedBatchId,
|
|
24135
|
+
fileCount: fileIds.length,
|
|
24136
|
+
logLabel,
|
|
24137
|
+
});
|
|
24138
|
+
}
|
|
24139
|
+
const pollStartedAtMs = Date.now();
|
|
24140
|
+
const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
|
|
24141
|
+
const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
|
|
24142
|
+
let lastStatus;
|
|
24143
|
+
let lastCountsKey = '';
|
|
24144
|
+
let lastProgressKey = '';
|
|
24145
|
+
let lastLogAtMs = 0;
|
|
24146
|
+
let lastProgressAtMs = pollStartedAtMs;
|
|
24147
|
+
let lastDiagnosticsAtMs = pollStartedAtMs;
|
|
24148
|
+
let latestBatch = batch;
|
|
24149
|
+
let loggedBatchIdMismatch = false;
|
|
24150
|
+
let shouldPoll = true;
|
|
24151
|
+
while (shouldPoll) {
|
|
24152
|
+
latestBatch = await client.beta.vectorStores.fileBatches.retrieve(vectorStoreId, expectedBatchId);
|
|
24153
|
+
const counts = latestBatch.file_counts;
|
|
24154
|
+
const countsKey = `${counts.completed}/${counts.failed}/${counts.in_progress}/${counts.cancelled}/${counts.total}`;
|
|
24155
|
+
const nowMs = Date.now();
|
|
24156
|
+
const returnedBatchId = latestBatch.id;
|
|
24157
|
+
// [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
|
|
24158
|
+
// We only consider it a mismatch if the returned ID looks like a Batch ID.
|
|
24159
|
+
const batchIdMismatch = returnedBatchId !== expectedBatchId && returnedBatchId.startsWith('vsfb_');
|
|
24160
|
+
const diagnosticsBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
|
|
24161
|
+
const shouldLog = this.options.isVerbose &&
|
|
24162
|
+
(latestBatch.status !== lastStatus ||
|
|
24163
|
+
countsKey !== lastCountsKey ||
|
|
24164
|
+
nowMs - lastLogAtMs >= progressLogIntervalMs);
|
|
24165
|
+
if (batchIdMismatch && !loggedBatchIdMismatch) {
|
|
24166
|
+
console.error('[🤰]', 'Vector store file batch id mismatch', {
|
|
24167
|
+
vectorStoreId,
|
|
24168
|
+
expectedBatchId,
|
|
24169
|
+
returnedBatchId,
|
|
24170
|
+
status: latestBatch.status,
|
|
24171
|
+
fileCounts: counts,
|
|
24172
|
+
logLabel,
|
|
24173
|
+
});
|
|
24174
|
+
loggedBatchIdMismatch = true;
|
|
24175
|
+
}
|
|
24176
|
+
if (countsKey !== lastProgressKey) {
|
|
24177
|
+
lastProgressKey = countsKey;
|
|
24178
|
+
lastProgressAtMs = nowMs;
|
|
24179
|
+
}
|
|
24180
|
+
if (shouldLog) {
|
|
24181
|
+
console.info('[🤰]', 'Vector store file batch status', {
|
|
24182
|
+
vectorStoreId,
|
|
24183
|
+
batchId: expectedBatchId,
|
|
24184
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24185
|
+
status: latestBatch.status,
|
|
24186
|
+
fileCounts: counts,
|
|
24187
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
24188
|
+
logLabel,
|
|
24189
|
+
});
|
|
24190
|
+
// [🤰] If there are in-progress files for a long time, log their details
|
|
24191
|
+
if (counts.in_progress > 0 && nowMs - lastProgressAtMs > VECTOR_STORE_STALL_LOG_THRESHOLD_MS) {
|
|
24192
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
24193
|
+
client,
|
|
24194
|
+
vectorStoreId,
|
|
24195
|
+
batchId: diagnosticsBatchId,
|
|
24196
|
+
uploadedFiles,
|
|
24197
|
+
logLabel,
|
|
24198
|
+
reason: 'stalled',
|
|
24199
|
+
});
|
|
24200
|
+
}
|
|
24201
|
+
lastStatus = latestBatch.status;
|
|
24202
|
+
lastCountsKey = countsKey;
|
|
24203
|
+
lastLogAtMs = nowMs;
|
|
24204
|
+
}
|
|
24205
|
+
if (nowMs - lastProgressAtMs >= diagnosticsIntervalMs &&
|
|
24206
|
+
nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
|
|
24207
|
+
lastDiagnosticsAtMs = nowMs;
|
|
24208
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
24209
|
+
client,
|
|
24210
|
+
vectorStoreId,
|
|
24211
|
+
batchId: diagnosticsBatchId,
|
|
24212
|
+
uploadedFiles,
|
|
24213
|
+
logLabel,
|
|
24214
|
+
reason: 'stalled',
|
|
24215
|
+
});
|
|
24216
|
+
}
|
|
24217
|
+
if (latestBatch.status === 'completed') {
|
|
24218
|
+
if (this.options.isVerbose) {
|
|
24219
|
+
console.info('[🤰]', 'Vector store file batch completed', {
|
|
24220
|
+
vectorStoreId,
|
|
24221
|
+
batchId: expectedBatchId,
|
|
24222
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24223
|
+
fileCounts: latestBatch.file_counts,
|
|
24224
|
+
elapsedMs: Date.now() - uploadStartedAtMs,
|
|
24225
|
+
logLabel,
|
|
24077
24226
|
});
|
|
24227
|
+
}
|
|
24228
|
+
if (latestBatch.file_counts.failed > 0) {
|
|
24229
|
+
console.error('[🤰]', 'Vector store file batch completed with failures', {
|
|
24230
|
+
vectorStoreId,
|
|
24231
|
+
batchId: expectedBatchId,
|
|
24232
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24233
|
+
fileCounts: latestBatch.file_counts,
|
|
24234
|
+
logLabel,
|
|
24235
|
+
});
|
|
24236
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
24237
|
+
client,
|
|
24238
|
+
vectorStoreId,
|
|
24239
|
+
batchId: diagnosticsBatchId,
|
|
24240
|
+
uploadedFiles,
|
|
24241
|
+
logLabel,
|
|
24242
|
+
reason: 'failed',
|
|
24243
|
+
});
|
|
24244
|
+
}
|
|
24245
|
+
shouldPoll = false;
|
|
24246
|
+
continue;
|
|
24247
|
+
}
|
|
24248
|
+
if (latestBatch.status === 'failed' || latestBatch.status === 'cancelled') {
|
|
24249
|
+
console.error('[🤰]', 'Vector store file batch did not complete', {
|
|
24250
|
+
vectorStoreId,
|
|
24251
|
+
batchId: expectedBatchId,
|
|
24252
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24253
|
+
status: latestBatch.status,
|
|
24254
|
+
fileCounts: latestBatch.file_counts,
|
|
24255
|
+
elapsedMs: Date.now() - uploadStartedAtMs,
|
|
24256
|
+
logLabel,
|
|
24257
|
+
});
|
|
24258
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
24259
|
+
client,
|
|
24260
|
+
vectorStoreId,
|
|
24261
|
+
batchId: diagnosticsBatchId,
|
|
24262
|
+
uploadedFiles,
|
|
24263
|
+
logLabel,
|
|
24264
|
+
reason: 'failed',
|
|
24265
|
+
});
|
|
24266
|
+
shouldPoll = false;
|
|
24267
|
+
continue;
|
|
24268
|
+
}
|
|
24269
|
+
if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
|
|
24270
|
+
console.error('[🤰]', 'Timed out waiting for vector store file batch', {
|
|
24271
|
+
vectorStoreId,
|
|
24272
|
+
batchId: expectedBatchId,
|
|
24273
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24274
|
+
fileCounts: latestBatch.file_counts,
|
|
24275
|
+
elapsedMs: nowMs - pollStartedAtMs,
|
|
24276
|
+
uploadTimeoutMs,
|
|
24277
|
+
logLabel,
|
|
24278
|
+
});
|
|
24279
|
+
await this.logVectorStoreFileBatchDiagnostics({
|
|
24280
|
+
client,
|
|
24281
|
+
vectorStoreId,
|
|
24282
|
+
batchId: diagnosticsBatchId,
|
|
24283
|
+
uploadedFiles,
|
|
24284
|
+
logLabel,
|
|
24285
|
+
reason: 'timeout',
|
|
24286
|
+
});
|
|
24287
|
+
if (this.shouldContinueOnVectorStoreStall()) {
|
|
24288
|
+
console.warn('[🤰]', 'Continuing despite vector store timeout as requested', {
|
|
24289
|
+
vectorStoreId,
|
|
24290
|
+
logLabel,
|
|
24291
|
+
});
|
|
24292
|
+
shouldPoll = false;
|
|
24293
|
+
continue;
|
|
24294
|
+
}
|
|
24295
|
+
try {
|
|
24296
|
+
const cancelBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
|
|
24297
|
+
if (!cancelBatchId.startsWith('vsfb_')) {
|
|
24298
|
+
console.error('[🤰]', 'Skipping vector store file batch cancel (invalid batch id)', {
|
|
24299
|
+
vectorStoreId,
|
|
24300
|
+
batchId: cancelBatchId,
|
|
24301
|
+
logLabel,
|
|
24302
|
+
});
|
|
24303
|
+
}
|
|
24304
|
+
else {
|
|
24305
|
+
await client.beta.vectorStores.fileBatches.cancel(vectorStoreId, cancelBatchId);
|
|
24306
|
+
}
|
|
24078
24307
|
if (this.options.isVerbose) {
|
|
24079
|
-
console.info('[🤰]', '
|
|
24308
|
+
console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
|
|
24080
24309
|
vectorStoreId,
|
|
24081
|
-
|
|
24310
|
+
batchId: batchIdMismatch && returnedBatchId.startsWith('vsfb_')
|
|
24311
|
+
? returnedBatchId
|
|
24312
|
+
: expectedBatchId,
|
|
24313
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24314
|
+
logLabel,
|
|
24082
24315
|
});
|
|
24083
24316
|
}
|
|
24084
24317
|
}
|
|
24085
24318
|
catch (error) {
|
|
24086
|
-
|
|
24319
|
+
assertsError(error);
|
|
24320
|
+
console.error('[🤰]', 'Failed to cancel vector store file batch after timeout', {
|
|
24321
|
+
vectorStoreId,
|
|
24322
|
+
batchId: expectedBatchId,
|
|
24323
|
+
...(batchIdMismatch ? { returnedBatchId } : {}),
|
|
24324
|
+
logLabel,
|
|
24325
|
+
error: serializeError(error),
|
|
24326
|
+
});
|
|
24327
|
+
}
|
|
24328
|
+
shouldPoll = false;
|
|
24329
|
+
continue;
|
|
24330
|
+
}
|
|
24331
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
24332
|
+
}
|
|
24333
|
+
return latestBatch;
|
|
24334
|
+
}
|
|
24335
|
+
/**
|
|
24336
|
+
* Creates a vector store and uploads knowledge sources, returning its ID.
|
|
24337
|
+
*/
|
|
24338
|
+
async createVectorStoreWithKnowledgeSources(options) {
|
|
24339
|
+
const { client, name, knowledgeSources, logLabel } = options;
|
|
24340
|
+
const knowledgeSourcesCount = knowledgeSources.length;
|
|
24341
|
+
const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
|
|
24342
|
+
if (this.options.isVerbose) {
|
|
24343
|
+
console.info('[🤰]', 'Creating vector store with knowledge sources', {
|
|
24344
|
+
name,
|
|
24345
|
+
knowledgeSourcesCount,
|
|
24346
|
+
downloadTimeoutMs,
|
|
24347
|
+
logLabel,
|
|
24348
|
+
});
|
|
24349
|
+
}
|
|
24350
|
+
const vectorStore = await client.beta.vectorStores.create({
|
|
24351
|
+
name: `${name} Knowledge Base`,
|
|
24352
|
+
});
|
|
24353
|
+
const vectorStoreId = vectorStore.id;
|
|
24354
|
+
if (this.options.isVerbose) {
|
|
24355
|
+
console.info('[🤰]', 'Vector store created', {
|
|
24356
|
+
vectorStoreId,
|
|
24357
|
+
logLabel,
|
|
24358
|
+
});
|
|
24359
|
+
}
|
|
24360
|
+
const fileStreams = [];
|
|
24361
|
+
const skippedSources = [];
|
|
24362
|
+
let totalBytes = 0;
|
|
24363
|
+
const processingStartedAtMs = Date.now();
|
|
24364
|
+
for (const [index, source] of knowledgeSources.entries()) {
|
|
24365
|
+
try {
|
|
24366
|
+
const sourceType = source.startsWith('http') || source.startsWith('https') ? 'url' : 'file';
|
|
24367
|
+
if (this.options.isVerbose) {
|
|
24368
|
+
console.info('[🤰]', 'Processing knowledge source', {
|
|
24369
|
+
index: index + 1,
|
|
24370
|
+
total: knowledgeSourcesCount,
|
|
24371
|
+
source,
|
|
24372
|
+
sourceType,
|
|
24373
|
+
logLabel,
|
|
24374
|
+
});
|
|
24375
|
+
}
|
|
24376
|
+
// Check if it's a URL
|
|
24377
|
+
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
24378
|
+
const downloadResult = await this.downloadKnowledgeSourceFile({
|
|
24379
|
+
source,
|
|
24380
|
+
timeoutMs: downloadTimeoutMs,
|
|
24381
|
+
logLabel,
|
|
24382
|
+
});
|
|
24383
|
+
if (downloadResult) {
|
|
24384
|
+
fileStreams.push(downloadResult.file);
|
|
24385
|
+
totalBytes += downloadResult.sizeBytes;
|
|
24386
|
+
}
|
|
24387
|
+
else {
|
|
24388
|
+
skippedSources.push({ source, reason: 'download_failed' });
|
|
24389
|
+
}
|
|
24087
24390
|
}
|
|
24391
|
+
else {
|
|
24392
|
+
skippedSources.push({ source, reason: 'unsupported_source_type' });
|
|
24393
|
+
if (this.options.isVerbose) {
|
|
24394
|
+
console.info('[🤰]', 'Skipping knowledge source (unsupported type)', {
|
|
24395
|
+
source,
|
|
24396
|
+
sourceType,
|
|
24397
|
+
logLabel,
|
|
24398
|
+
});
|
|
24399
|
+
}
|
|
24400
|
+
/*
|
|
24401
|
+
TODO: [?????] Resolve problem with browser environment
|
|
24402
|
+
// Assume it's a local file path
|
|
24403
|
+
// Note: This will work in Node.js environment
|
|
24404
|
+
// For browser environments, this would need different handling
|
|
24405
|
+
const fs = await import('fs');
|
|
24406
|
+
const fileStream = fs.createReadStream(source);
|
|
24407
|
+
fileStreams.push(fileStream);
|
|
24408
|
+
*/
|
|
24409
|
+
}
|
|
24410
|
+
}
|
|
24411
|
+
catch (error) {
|
|
24412
|
+
assertsError(error);
|
|
24413
|
+
skippedSources.push({ source, reason: 'processing_error' });
|
|
24414
|
+
console.error('[🤰]', 'Error processing knowledge source', {
|
|
24415
|
+
source,
|
|
24416
|
+
logLabel,
|
|
24417
|
+
error: serializeError(error),
|
|
24418
|
+
});
|
|
24088
24419
|
}
|
|
24089
24420
|
}
|
|
24421
|
+
if (this.options.isVerbose) {
|
|
24422
|
+
console.info('[🤰]', 'Finished processing knowledge sources', {
|
|
24423
|
+
total: knowledgeSourcesCount,
|
|
24424
|
+
downloadedCount: fileStreams.length,
|
|
24425
|
+
skippedCount: skippedSources.length,
|
|
24426
|
+
totalBytes,
|
|
24427
|
+
elapsedMs: Date.now() - processingStartedAtMs,
|
|
24428
|
+
skippedSamples: skippedSources.slice(0, 3),
|
|
24429
|
+
logLabel,
|
|
24430
|
+
});
|
|
24431
|
+
}
|
|
24432
|
+
if (fileStreams.length > 0) {
|
|
24433
|
+
if (this.options.isVerbose) {
|
|
24434
|
+
console.info('[🤰]', 'Uploading files to vector store', {
|
|
24435
|
+
vectorStoreId,
|
|
24436
|
+
fileCount: fileStreams.length,
|
|
24437
|
+
totalBytes,
|
|
24438
|
+
maxConcurrency: this.getKnowledgeSourceUploadMaxConcurrency(),
|
|
24439
|
+
pollIntervalMs: this.getKnowledgeSourceUploadPollIntervalMs(),
|
|
24440
|
+
uploadTimeoutMs: this.getKnowledgeSourceUploadTimeoutMs(),
|
|
24441
|
+
logLabel,
|
|
24442
|
+
});
|
|
24443
|
+
}
|
|
24444
|
+
try {
|
|
24445
|
+
await this.uploadKnowledgeSourceFilesToVectorStore({
|
|
24446
|
+
client,
|
|
24447
|
+
vectorStoreId,
|
|
24448
|
+
files: fileStreams,
|
|
24449
|
+
totalBytes,
|
|
24450
|
+
logLabel,
|
|
24451
|
+
});
|
|
24452
|
+
}
|
|
24453
|
+
catch (error) {
|
|
24454
|
+
assertsError(error);
|
|
24455
|
+
console.error('[🤰]', 'Error uploading files to vector store', {
|
|
24456
|
+
vectorStoreId,
|
|
24457
|
+
logLabel,
|
|
24458
|
+
error: serializeError(error),
|
|
24459
|
+
});
|
|
24460
|
+
}
|
|
24461
|
+
}
|
|
24462
|
+
else if (this.options.isVerbose) {
|
|
24463
|
+
console.info('[🤰]', 'No knowledge source files to upload', {
|
|
24464
|
+
vectorStoreId,
|
|
24465
|
+
skippedCount: skippedSources.length,
|
|
24466
|
+
logLabel,
|
|
24467
|
+
});
|
|
24468
|
+
}
|
|
24469
|
+
return {
|
|
24470
|
+
vectorStoreId,
|
|
24471
|
+
uploadedFileCount: fileStreams.length,
|
|
24472
|
+
skippedCount: skippedSources.length,
|
|
24473
|
+
totalBytes,
|
|
24474
|
+
};
|
|
24475
|
+
}
|
|
24476
|
+
async createNewAssistant(options) {
|
|
24477
|
+
var _a, _b, _c;
|
|
24478
|
+
if (!this.isCreatingNewAssistantsAllowed) {
|
|
24479
|
+
throw new NotAllowed(`Creating new assistants is not allowed. Set \`isCreatingNewAssistantsAllowed: true\` in options to enable this feature.`);
|
|
24480
|
+
}
|
|
24481
|
+
// await this.playground();
|
|
24482
|
+
const { name, instructions, knowledgeSources, tools } = options;
|
|
24483
|
+
const preparationStartedAtMs = Date.now();
|
|
24484
|
+
const knowledgeSourcesCount = (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0;
|
|
24485
|
+
const toolsCount = (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0;
|
|
24486
|
+
if (this.options.isVerbose) {
|
|
24487
|
+
console.info('[🤰]', 'Starting OpenAI assistant creation', {
|
|
24488
|
+
name,
|
|
24489
|
+
knowledgeSourcesCount,
|
|
24490
|
+
toolsCount,
|
|
24491
|
+
instructionsLength: instructions.length,
|
|
24492
|
+
});
|
|
24493
|
+
}
|
|
24494
|
+
const client = await this.getClient();
|
|
24495
|
+
let vectorStoreId;
|
|
24496
|
+
// If knowledge sources are provided, create a vector store with them
|
|
24497
|
+
if (knowledgeSources && knowledgeSources.length > 0) {
|
|
24498
|
+
const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
|
|
24499
|
+
client,
|
|
24500
|
+
name,
|
|
24501
|
+
knowledgeSources,
|
|
24502
|
+
logLabel: 'assistant creation',
|
|
24503
|
+
});
|
|
24504
|
+
vectorStoreId = vectorStoreResult.vectorStoreId;
|
|
24505
|
+
}
|
|
24090
24506
|
// Create assistant with vector store attached
|
|
24091
24507
|
const assistantConfig = {
|
|
24092
24508
|
name,
|
|
@@ -24152,91 +24568,14 @@
|
|
|
24152
24568
|
const client = await this.getClient();
|
|
24153
24569
|
let vectorStoreId;
|
|
24154
24570
|
// If knowledge sources are provided, create a vector store with them
|
|
24155
|
-
// TODO: [🧠] Reuse vector store creation logic from createNewAssistant
|
|
24156
24571
|
if (knowledgeSources && knowledgeSources.length > 0) {
|
|
24157
|
-
|
|
24158
|
-
|
|
24159
|
-
|
|
24160
|
-
|
|
24161
|
-
|
|
24162
|
-
});
|
|
24163
|
-
}
|
|
24164
|
-
// Create a vector store
|
|
24165
|
-
const vectorStore = await client.beta.vectorStores.create({
|
|
24166
|
-
name: `${name} Knowledge Base`,
|
|
24572
|
+
const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
|
|
24573
|
+
client,
|
|
24574
|
+
name: name !== null && name !== void 0 ? name : assistantId,
|
|
24575
|
+
knowledgeSources,
|
|
24576
|
+
logLabel: 'assistant update',
|
|
24167
24577
|
});
|
|
24168
|
-
vectorStoreId =
|
|
24169
|
-
if (this.options.isVerbose) {
|
|
24170
|
-
console.info('[🤰]', 'Vector store created for assistant update', {
|
|
24171
|
-
vectorStoreId,
|
|
24172
|
-
});
|
|
24173
|
-
}
|
|
24174
|
-
// Upload files from knowledge sources to the vector store
|
|
24175
|
-
const fileStreams = [];
|
|
24176
|
-
for (const [index, source] of knowledgeSources.entries()) {
|
|
24177
|
-
try {
|
|
24178
|
-
if (this.options.isVerbose) {
|
|
24179
|
-
console.info('[🤰]', 'Processing knowledge source for update', {
|
|
24180
|
-
index: index + 1,
|
|
24181
|
-
total: knowledgeSources.length,
|
|
24182
|
-
source,
|
|
24183
|
-
sourceType: source.startsWith('http') || source.startsWith('https') ? 'url' : 'file',
|
|
24184
|
-
});
|
|
24185
|
-
}
|
|
24186
|
-
// Check if it's a URL
|
|
24187
|
-
if (source.startsWith('http://') || source.startsWith('https://')) {
|
|
24188
|
-
// Download the file
|
|
24189
|
-
const response = await fetch(source);
|
|
24190
|
-
if (!response.ok) {
|
|
24191
|
-
console.error(`Failed to download ${source}: ${response.statusText}`);
|
|
24192
|
-
continue;
|
|
24193
|
-
}
|
|
24194
|
-
const buffer = await response.arrayBuffer();
|
|
24195
|
-
let filename = source.split('/').pop() || 'downloaded-file';
|
|
24196
|
-
try {
|
|
24197
|
-
const url = new URL(source);
|
|
24198
|
-
filename = url.pathname.split('/').pop() || filename;
|
|
24199
|
-
}
|
|
24200
|
-
catch (error) {
|
|
24201
|
-
// Keep default filename
|
|
24202
|
-
}
|
|
24203
|
-
const blob = new Blob([buffer]);
|
|
24204
|
-
const file = new File([blob], filename);
|
|
24205
|
-
fileStreams.push(file);
|
|
24206
|
-
}
|
|
24207
|
-
else {
|
|
24208
|
-
/*
|
|
24209
|
-
TODO: [🐱🚀] Resolve problem with browser environment
|
|
24210
|
-
// Assume it's a local file path
|
|
24211
|
-
// Note: This will work in Node.js environment
|
|
24212
|
-
// For browser environments, this would need different handling
|
|
24213
|
-
const fs = await import('fs');
|
|
24214
|
-
const fileStream = fs.createReadStream(source);
|
|
24215
|
-
fileStreams.push(fileStream);
|
|
24216
|
-
*/
|
|
24217
|
-
}
|
|
24218
|
-
}
|
|
24219
|
-
catch (error) {
|
|
24220
|
-
console.error(`Error processing knowledge source ${source}:`, error);
|
|
24221
|
-
}
|
|
24222
|
-
}
|
|
24223
|
-
// Batch upload files to the vector store
|
|
24224
|
-
if (fileStreams.length > 0) {
|
|
24225
|
-
try {
|
|
24226
|
-
await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
|
|
24227
|
-
files: fileStreams,
|
|
24228
|
-
});
|
|
24229
|
-
if (this.options.isVerbose) {
|
|
24230
|
-
console.info('[🤰]', 'Uploaded files to vector store for update', {
|
|
24231
|
-
vectorStoreId,
|
|
24232
|
-
fileCount: fileStreams.length,
|
|
24233
|
-
});
|
|
24234
|
-
}
|
|
24235
|
-
}
|
|
24236
|
-
catch (error) {
|
|
24237
|
-
console.error('Error uploading files to vector store:', error);
|
|
24238
|
-
}
|
|
24239
|
-
}
|
|
24578
|
+
vectorStoreId = vectorStoreResult.vectorStoreId;
|
|
24240
24579
|
}
|
|
24241
24580
|
const assistantUpdate = {
|
|
24242
24581
|
name,
|
|
@@ -24340,7 +24679,6 @@
|
|
|
24340
24679
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
24341
24680
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
24342
24681
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
24343
|
-
* - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
24344
24682
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
24345
24683
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
24346
24684
|
*
|
|
@@ -24503,70 +24841,25 @@
|
|
|
24503
24841
|
}, // Cast to avoid readonly mismatch from spread
|
|
24504
24842
|
};
|
|
24505
24843
|
console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
|
|
24506
|
-
if (
|
|
24844
|
+
if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
|
|
24845
|
+
// ... deprecated path ...
|
|
24507
24846
|
const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
|
|
24508
|
-
const cached = AgentLlmExecutionTools.
|
|
24509
|
-
let
|
|
24510
|
-
if (
|
|
24511
|
-
|
|
24512
|
-
console.log(`1️⃣ Using cached OpenAI Agent Vector Store for agent ${this.title}...`);
|
|
24513
|
-
}
|
|
24514
|
-
// Create new instance with cached vectorStoreId
|
|
24515
|
-
// We need to access options from the original tool.
|
|
24516
|
-
// We assume isOpenAiAgentExecutionTools implies it has options we can clone.
|
|
24517
|
-
// But protected options are not accessible.
|
|
24518
|
-
// We can cast to access options if they were public, or use a method to clone.
|
|
24519
|
-
// OpenAiAgentExecutionTools doesn't have a clone method.
|
|
24520
|
-
// However, we can just assume the passed tool *might* not have the vector store yet, or we are replacing it.
|
|
24521
|
-
// Actually, if the passed tool IS OpenAiAgentExecutionTools, we should use it as a base.
|
|
24522
|
-
// TODO: [🧠] This is a bit hacky, accessing protected options or recreating tools.
|
|
24523
|
-
// Ideally OpenAiAgentExecutionTools should have a method `withVectorStoreId`.
|
|
24524
|
-
agentTools = new OpenAiAgentExecutionTools({
|
|
24525
|
-
...this.options.llmTools.options,
|
|
24526
|
-
vectorStoreId: cached.vectorStoreId,
|
|
24527
|
-
});
|
|
24528
|
-
}
|
|
24529
|
-
else {
|
|
24847
|
+
const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
|
|
24848
|
+
let assistant;
|
|
24849
|
+
if (this.options.assistantPreparationMode === 'external') {
|
|
24850
|
+
assistant = this.options.llmTools;
|
|
24530
24851
|
if (this.options.isVerbose) {
|
|
24531
|
-
console.
|
|
24532
|
-
|
|
24533
|
-
|
|
24534
|
-
if (modelRequirements.knowledgeSources && modelRequirements.knowledgeSources.length > 0) {
|
|
24535
|
-
const client = await this.options.llmTools.getClient();
|
|
24536
|
-
vectorStoreId = await OpenAiAgentExecutionTools.createVectorStore(client, this.title, modelRequirements.knowledgeSources);
|
|
24537
|
-
}
|
|
24538
|
-
if (vectorStoreId) {
|
|
24539
|
-
AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
|
|
24540
|
-
vectorStoreId,
|
|
24541
|
-
requirementsHash,
|
|
24852
|
+
console.info('[🤰]', 'Using externally managed OpenAI Assistant', {
|
|
24853
|
+
agent: this.title,
|
|
24854
|
+
assistantId: assistant.assistantId,
|
|
24542
24855
|
});
|
|
24543
24856
|
}
|
|
24544
|
-
|
|
24545
|
-
|
|
24546
|
-
|
|
24857
|
+
AgentLlmExecutionTools.assistantCache.set(this.title, {
|
|
24858
|
+
assistantId: assistant.assistantId,
|
|
24859
|
+
requirementsHash,
|
|
24547
24860
|
});
|
|
24548
24861
|
}
|
|
24549
|
-
|
|
24550
|
-
// Note: Unlike Assistants API, Responses API expects instructions (system message) to be passed in the call.
|
|
24551
|
-
// So we use promptWithAgentModelRequirements which has the system message prepended.
|
|
24552
|
-
// But we need to make sure we pass knowledgeSources in modelRequirements so OpenAiAgentExecutionTools can fallback to warning if vectorStoreId is missing (though we just handled it).
|
|
24553
|
-
const promptForAgent = {
|
|
24554
|
-
...promptWithAgentModelRequirements,
|
|
24555
|
-
modelRequirements: {
|
|
24556
|
-
...promptWithAgentModelRequirements.modelRequirements,
|
|
24557
|
-
knowledgeSources: modelRequirements.knowledgeSources
|
|
24558
|
-
? [...modelRequirements.knowledgeSources]
|
|
24559
|
-
: undefined, // Pass knowledge sources explicitly
|
|
24560
|
-
},
|
|
24561
|
-
};
|
|
24562
|
-
underlyingLlmResult = await agentTools.callChatModelStream(promptForAgent, onProgress);
|
|
24563
|
-
}
|
|
24564
|
-
else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
|
|
24565
|
-
// ... deprecated path ...
|
|
24566
|
-
const requirementsHash = cryptoJs.SHA256(JSON.stringify(modelRequirements)).toString();
|
|
24567
|
-
const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
|
|
24568
|
-
let assistant;
|
|
24569
|
-
if (cached) {
|
|
24862
|
+
else if (cached) {
|
|
24570
24863
|
if (cached.requirementsHash === requirementsHash) {
|
|
24571
24864
|
if (this.options.isVerbose) {
|
|
24572
24865
|
console.info('[🤰]', 'Using cached OpenAI Assistant', {
|
|
@@ -24632,11 +24925,6 @@
|
|
|
24632
24925
|
requirementsHash,
|
|
24633
24926
|
});
|
|
24634
24927
|
}
|
|
24635
|
-
// [0] Expose prepared externals
|
|
24636
|
-
if (this.preparedExternals) {
|
|
24637
|
-
this /* <- TODO: !!!!!! Remove */.preparedExternals.openaiAssistantId =
|
|
24638
|
-
assistant.assistantId;
|
|
24639
|
-
}
|
|
24640
24928
|
// Create modified chat prompt with agent system message specific to OpenAI Assistant
|
|
24641
24929
|
const promptWithAgentModelRequirementsForOpenAiAssistantExecutionTools = {
|
|
24642
24930
|
...promptWithAgentModelRequirements,
|
|
@@ -24758,7 +25046,6 @@
|
|
|
24758
25046
|
* - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
|
|
24759
25047
|
* - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
|
|
24760
25048
|
* - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
|
|
24761
|
-
* - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
|
|
24762
25049
|
* - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
|
|
24763
25050
|
* - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
|
|
24764
25051
|
*
|
|
@@ -24790,6 +25077,7 @@
|
|
|
24790
25077
|
super({
|
|
24791
25078
|
isVerbose: options.isVerbose,
|
|
24792
25079
|
llmTools: getSingleLlmExecutionTools(options.executionTools.llm),
|
|
25080
|
+
assistantPreparationMode: options.assistantPreparationMode,
|
|
24793
25081
|
agentSource: agentSource.value, // <- TODO: [🐱🚀] Allow to pass BehaviorSubject<string_book> OR refresh llmExecutionTools.callChat on agentSource change
|
|
24794
25082
|
});
|
|
24795
25083
|
_Agent_instances.add(this);
|
|
@@ -24829,10 +25117,6 @@
|
|
|
24829
25117
|
* Human-readable titles for tool functions
|
|
24830
25118
|
*/
|
|
24831
25119
|
this.toolTitles = {};
|
|
24832
|
-
/**
|
|
24833
|
-
* Externals prepared for the agent, like OpenAI assistant, etc.
|
|
24834
|
-
*/
|
|
24835
|
-
this.preparedExternals = {};
|
|
24836
25120
|
// TODO: [🐱🚀] Add `Agent` simple "mocked" learning by appending to agent source
|
|
24837
25121
|
// TODO: [🐱🚀] Add `Agent` learning by promptbookAgent
|
|
24838
25122
|
this.teacherAgent = options.teacherAgent;
|
|
@@ -25069,6 +25353,63 @@
|
|
|
25069
25353
|
* TODO: [🧠][😰]Agent is not working with the parameters, should it be?
|
|
25070
25354
|
*/
|
|
25071
25355
|
|
|
25356
|
+
/**
|
|
25357
|
+
* Resolve a remote META IMAGE value into an absolute URL when possible.
|
|
25358
|
+
*/
|
|
25359
|
+
function resolveRemoteImageUrl(imageUrl, agentUrl) {
|
|
25360
|
+
if (!imageUrl) {
|
|
25361
|
+
return undefined;
|
|
25362
|
+
}
|
|
25363
|
+
if (imageUrl.startsWith('http://') ||
|
|
25364
|
+
imageUrl.startsWith('https://') ||
|
|
25365
|
+
imageUrl.startsWith('data:') ||
|
|
25366
|
+
imageUrl.startsWith('blob:')) {
|
|
25367
|
+
return imageUrl;
|
|
25368
|
+
}
|
|
25369
|
+
try {
|
|
25370
|
+
return new URL(imageUrl, agentUrl).href;
|
|
25371
|
+
}
|
|
25372
|
+
catch (_a) {
|
|
25373
|
+
return imageUrl;
|
|
25374
|
+
}
|
|
25375
|
+
}
|
|
25376
|
+
/**
|
|
25377
|
+
* Format a META commitment line when the value is provided.
|
|
25378
|
+
*/
|
|
25379
|
+
function formatMetaLine(label, value) {
|
|
25380
|
+
if (!value) {
|
|
25381
|
+
return null;
|
|
25382
|
+
}
|
|
25383
|
+
return `META ${label} ${value}`;
|
|
25384
|
+
}
|
|
25385
|
+
/**
|
|
25386
|
+
* Build a minimal agent source snapshot for remote agents.
|
|
25387
|
+
*/
|
|
25388
|
+
function buildRemoteAgentSource(profile, meta) {
|
|
25389
|
+
const metaLines = [
|
|
25390
|
+
formatMetaLine('FULLNAME', meta === null || meta === void 0 ? void 0 : meta.fullname),
|
|
25391
|
+
formatMetaLine('IMAGE', meta === null || meta === void 0 ? void 0 : meta.image),
|
|
25392
|
+
formatMetaLine('DESCRIPTION', meta === null || meta === void 0 ? void 0 : meta.description),
|
|
25393
|
+
formatMetaLine('COLOR', meta === null || meta === void 0 ? void 0 : meta.color),
|
|
25394
|
+
formatMetaLine('FONT', meta === null || meta === void 0 ? void 0 : meta.font),
|
|
25395
|
+
formatMetaLine('LINK', meta === null || meta === void 0 ? void 0 : meta.link),
|
|
25396
|
+
]
|
|
25397
|
+
.filter((line) => Boolean(line))
|
|
25398
|
+
.join('\n');
|
|
25399
|
+
const personaBlock = profile.personaDescription
|
|
25400
|
+
? spaceTrim__default["default"]((block) => `
|
|
25401
|
+
PERSONA
|
|
25402
|
+
${block(profile.personaDescription || '')}
|
|
25403
|
+
`)
|
|
25404
|
+
: '';
|
|
25405
|
+
return book `
|
|
25406
|
+
${profile.agentName}
|
|
25407
|
+
|
|
25408
|
+
${metaLines}
|
|
25409
|
+
|
|
25410
|
+
${personaBlock}
|
|
25411
|
+
`;
|
|
25412
|
+
}
|
|
25072
25413
|
/**
|
|
25073
25414
|
* Represents one AI Agent
|
|
25074
25415
|
*
|
|
@@ -25083,6 +25424,7 @@
|
|
|
25083
25424
|
*/
|
|
25084
25425
|
class RemoteAgent extends Agent {
|
|
25085
25426
|
static async connect(options) {
|
|
25427
|
+
var _a, _b, _c;
|
|
25086
25428
|
const agentProfileUrl = `${options.agentUrl}/api/profile`;
|
|
25087
25429
|
const profileResponse = await fetch(agentProfileUrl);
|
|
25088
25430
|
// <- TODO: [🐱🚀] What about closed-source agents?
|
|
@@ -25102,14 +25444,14 @@
|
|
|
25102
25444
|
|
|
25103
25445
|
`));
|
|
25104
25446
|
}
|
|
25105
|
-
const profile = await profileResponse.json();
|
|
25447
|
+
const profile = (await profileResponse.json());
|
|
25448
|
+
const resolvedMeta = {
|
|
25449
|
+
...(profile.meta || {}),
|
|
25450
|
+
image: resolveRemoteImageUrl((_a = profile.meta) === null || _a === void 0 ? void 0 : _a.image, options.agentUrl),
|
|
25451
|
+
};
|
|
25106
25452
|
// Note: We are creating dummy agent source because we don't have the source from the remote agent
|
|
25107
25453
|
// But we populate the metadata from the profile
|
|
25108
|
-
const agentSource = new rxjs.BehaviorSubject(
|
|
25109
|
-
${profile.agentName}
|
|
25110
|
-
|
|
25111
|
-
${profile.personaDescription}
|
|
25112
|
-
`);
|
|
25454
|
+
const agentSource = new rxjs.BehaviorSubject(buildRemoteAgentSource(profile, resolvedMeta));
|
|
25113
25455
|
// <- TODO: [🐱🚀] createBookFromProfile
|
|
25114
25456
|
// <- TODO: [🐱🚀] Support updating and self-updating
|
|
25115
25457
|
const remoteAgent = new RemoteAgent({
|
|
@@ -25132,10 +25474,10 @@
|
|
|
25132
25474
|
});
|
|
25133
25475
|
remoteAgent._remoteAgentName = profile.agentName;
|
|
25134
25476
|
remoteAgent._remoteAgentHash = profile.agentHash;
|
|
25135
|
-
remoteAgent.personaDescription = profile.personaDescription;
|
|
25136
|
-
remoteAgent.initialMessage = profile.initialMessage;
|
|
25137
|
-
remoteAgent.links = profile.links;
|
|
25138
|
-
remoteAgent.meta =
|
|
25477
|
+
remoteAgent.personaDescription = (_b = profile.personaDescription) !== null && _b !== void 0 ? _b : null;
|
|
25478
|
+
remoteAgent.initialMessage = (_c = profile.initialMessage) !== null && _c !== void 0 ? _c : null;
|
|
25479
|
+
remoteAgent.links = profile.links || [];
|
|
25480
|
+
remoteAgent.meta = resolvedMeta;
|
|
25139
25481
|
remoteAgent.capabilities = profile.capabilities || [];
|
|
25140
25482
|
remoteAgent.samples = profile.samples || [];
|
|
25141
25483
|
remoteAgent.toolTitles = profile.toolTitles || {};
|