@promptbook/cli 0.110.0-5 → 0.110.0-8

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.
Files changed (24) hide show
  1. package/esm/index.es.js +1149 -593
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/typings/src/_packages/openai.index.d.ts +8 -0
  4. package/esm/typings/src/_packages/types.index.d.ts +4 -0
  5. package/esm/typings/src/book-components/Chat/AgentChip/AgentChip.d.ts +5 -1
  6. package/esm/typings/src/book-components/Chat/LlmChat/LlmChatProps.d.ts +4 -1
  7. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +0 -3
  8. package/esm/typings/src/execution/LlmExecutionTools.d.ts +2 -1
  9. package/esm/typings/src/llm-providers/agent/Agent.d.ts +1 -0
  10. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +5 -0
  11. package/esm/typings/src/llm-providers/agent/AgentOptions.d.ts +4 -3
  12. package/esm/typings/src/llm-providers/agent/CreateAgentLlmExecutionToolsOptions.d.ts +7 -5
  13. package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +2 -1
  14. package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +111 -0
  15. package/esm/typings/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOptions.d.ts +15 -0
  16. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +3 -42
  17. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionToolsOptions.d.ts +2 -33
  18. package/esm/typings/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +135 -0
  19. package/esm/typings/src/llm-providers/openai/utils/mapToolsToOpenAi.d.ts +1 -1
  20. package/esm/typings/src/utils/toolCalls/getToolCallIdentity.d.ts +10 -0
  21. package/esm/typings/src/version.d.ts +1 -1
  22. package/package.json +6 -2
  23. package/umd/index.umd.js +1152 -597
  24. package/umd/index.umd.js.map +1 -1
package/esm/index.es.js CHANGED
@@ -31,6 +31,7 @@ import { OpenAIClient, AzureKeyCredential } from '@azure/openai';
31
31
  import { Subject, BehaviorSubject } from 'rxjs';
32
32
  import { lookup, extension } from 'mime-types';
33
33
  import { parse, unparse } from 'papaparse';
34
+ import { Agent as Agent$1, setDefaultOpenAIClient, setDefaultOpenAIKey, fileSearchTool, tool, run } from '@openai/agents';
34
35
  import OpenAI from 'openai';
35
36
 
36
37
  // ⚠️ WARNING: This code has been generated so that any manual changes will be overwritten
@@ -47,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
47
48
  * @generated
48
49
  * @see https://github.com/webgptorg/promptbook
49
50
  */
50
- const PROMPTBOOK_ENGINE_VERSION = '0.110.0-5';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.110.0-8';
51
52
  /**
52
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
53
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -27028,16 +27029,11 @@ class OpenAiCompatibleExecutionTools {
27028
27029
  const openAiOptions = { ...this.options };
27029
27030
  delete openAiOptions.isVerbose;
27030
27031
  delete openAiOptions.userId;
27031
- // Enhanced configuration for better ECONNRESET handling
27032
+ // Enhanced configuration with retries and timeouts.
27032
27033
  const enhancedOptions = {
27033
27034
  ...openAiOptions,
27034
27035
  timeout: API_REQUEST_TIMEOUT,
27035
27036
  maxRetries: CONNECTION_RETRIES_LIMIT,
27036
- defaultHeaders: {
27037
- Connection: 'keep-alive',
27038
- 'Keep-Alive': 'timeout=30, max=100',
27039
- ...openAiOptions.defaultHeaders,
27040
- },
27041
27037
  };
27042
27038
  this.client = new OpenAI(enhancedOptions);
27043
27039
  }
@@ -28457,521 +28453,68 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
28457
28453
  }
28458
28454
  }
28459
28455
 
28460
- /**
28461
- * Uploads files to OpenAI and returns their IDs
28462
- *
28463
- * @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
28464
- */
28465
- async function uploadFilesToOpenAi(client, files) {
28466
- const fileIds = [];
28467
- for (const file of files) {
28468
- // Note: OpenAI API expects a File object or a ReadStream
28469
- // In browser environment, we can pass the File object directly
28470
- // In Node.js environment, we might need to convert it or use a different approach
28471
- // But since `Prompt.files` already contains `File` objects, we try to pass them directly
28472
- const uploadedFile = await client.files.create({
28473
- file: file,
28474
- purpose: 'assistants',
28475
- });
28476
- fileIds.push(uploadedFile.id);
28477
- }
28478
- return fileIds;
28479
- }
28480
-
28481
28456
  const DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS = 30000;
28482
28457
  const DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS = 900000;
28483
28458
  const VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS = 15000;
28484
28459
  const VECTOR_STORE_STALL_LOG_THRESHOLD_MS = 30000;
28485
28460
  /**
28486
- * Execution Tools for calling OpenAI API Assistants
28487
- *
28488
- * This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
28489
- *
28490
- * Note: [🦖] There are several different things in Promptbook:
28491
- * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
28492
- * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
28493
- * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
28494
- * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
28495
- * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
28461
+ * Base class for OpenAI execution tools that need hosted vector stores.
28496
28462
  *
28497
28463
  * @public exported from `@promptbook/openai`
28498
28464
  */
28499
- class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
28500
- /**
28501
- * Creates OpenAI Execution Tools.
28502
- *
28503
- * @param options which are relevant are directly passed to the OpenAI client
28504
- */
28505
- constructor(options) {
28506
- var _a;
28507
- if (options.isProxied) {
28508
- throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
28509
- }
28510
- super(options);
28511
- this.isCreatingNewAssistantsAllowed = false;
28512
- this.assistantId = options.assistantId;
28513
- this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
28514
- if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
28515
- throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
28516
- }
28517
- // <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
28518
- // TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
28519
- }
28520
- get title() {
28521
- return 'OpenAI Assistant';
28522
- }
28523
- get description() {
28524
- return 'Use single assistant provided by OpenAI';
28525
- }
28526
- /**
28527
- * Calls OpenAI API to use a chat model.
28528
- */
28529
- async callChatModel(prompt) {
28530
- return this.callChatModelStream(prompt, () => { });
28531
- }
28532
- /**
28533
- * Calls OpenAI API to use a chat model with streaming.
28534
- */
28535
- async callChatModelStream(prompt, onProgress) {
28536
- var _a, _b, _c, _d, _e, _f;
28537
- if (this.options.isVerbose) {
28538
- console.info('💬 OpenAI callChatModel call', { prompt });
28539
- }
28540
- const { content, parameters, modelRequirements /*, format*/ } = prompt;
28541
- const client = await this.getClient();
28542
- // TODO: [☂] Use here more modelRequirements
28543
- if (modelRequirements.modelVariant !== 'CHAT') {
28544
- throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
28545
- }
28546
- // TODO: [👨‍👨‍👧‍👧] Remove:
28547
- for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
28548
- if (modelRequirements[key] !== undefined) {
28549
- throw new NotYetImplementedError(`In \`OpenAiAssistantExecutionTools\` you cannot specify \`${key}\``);
28550
- }
28551
- }
28552
- /*
28553
- TODO: [👨‍👨‍👧‍👧] Implement all of this for Assistants
28554
- const modelName = modelRequirements.modelName || this.getDefaultChatModel().modelName;
28555
- const modelSettings = {
28556
- model: modelName,
28557
-
28558
- temperature: modelRequirements.temperature,
28559
-
28560
- // <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
28561
- // <- Note: [🧆]
28562
- } as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
28563
-
28564
- if (format === 'JSON') {
28565
- modelSettings.response_format = {
28566
- type: 'json_object',
28567
- };
28568
- }
28569
- */
28570
- // <- TODO: [🚸] Not all models are compatible with JSON mode
28571
- // > 'response_format' of type 'json_object' is not supported with this model.
28572
- const rawPromptContent = templateParameters(content, {
28573
- ...parameters,
28574
- modelName: 'assistant',
28575
- // <- [🧠] What is the best value here
28576
- });
28577
- // Build thread messages: include previous thread messages + current user message
28578
- const threadMessages = [];
28579
- // TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
28580
- // Add previous messages from thread (if any)
28581
- if ('thread' in prompt && Array.isArray(prompt.thread)) {
28582
- const previousMessages = prompt.thread.map((msg) => ({
28583
- role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
28584
- content: msg.content,
28585
- }));
28586
- threadMessages.push(...previousMessages);
28587
- }
28588
- // Always add the current user message
28589
- const currentUserMessage = {
28590
- role: 'user',
28591
- content: rawPromptContent,
28592
- };
28593
- if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
28594
- const fileIds = await uploadFilesToOpenAi(client, prompt.files);
28595
- currentUserMessage.attachments = fileIds.map((fileId) => ({
28596
- file_id: fileId,
28597
- tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
28598
- }));
28599
- }
28600
- threadMessages.push(currentUserMessage);
28601
- // Check if tools are being used - if so, use non-streaming mode
28602
- const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
28603
- const start = $getCurrentDate();
28604
- let complete;
28605
- // [🐱‍🚀] When tools are present, we need to use the non-streaming Runs API
28606
- // because streaming doesn't support tool execution flow properly
28607
- if (hasTools) {
28608
- onProgress({
28609
- content: '',
28610
- modelName: 'assistant',
28611
- timing: { start, complete: $getCurrentDate() },
28612
- usage: UNCERTAIN_USAGE,
28613
- rawPromptContent,
28614
- rawRequest: null,
28615
- rawResponse: null,
28616
- });
28617
- const rawRequest = {
28618
- assistant_id: this.assistantId,
28619
- thread: {
28620
- messages: threadMessages,
28621
- },
28622
- tools: mapToolsToOpenAi(modelRequirements.tools),
28623
- };
28624
- if (this.options.isVerbose) {
28625
- console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
28626
- }
28627
- // Create thread and run
28628
- const threadAndRun = await client.beta.threads.createAndRun(rawRequest);
28629
- let run = threadAndRun;
28630
- const completedToolCalls = [];
28631
- const toolCallStartedAt = new Map();
28632
- // Poll until run completes or requires action
28633
- while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
28634
- if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
28635
- // Execute tools
28636
- const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
28637
- const toolOutputs = [];
28638
- for (const toolCall of toolCalls) {
28639
- if (toolCall.type === 'function') {
28640
- const functionName = toolCall.function.name;
28641
- const functionArgs = JSON.parse(toolCall.function.arguments);
28642
- const calledAt = $getCurrentDate();
28643
- if (toolCall.id) {
28644
- toolCallStartedAt.set(toolCall.id, calledAt);
28645
- }
28646
- onProgress({
28647
- content: '',
28648
- modelName: 'assistant',
28649
- timing: { start, complete: $getCurrentDate() },
28650
- usage: UNCERTAIN_USAGE,
28651
- rawPromptContent,
28652
- rawRequest: null,
28653
- rawResponse: null,
28654
- toolCalls: [
28655
- {
28656
- name: functionName,
28657
- arguments: toolCall.function.arguments,
28658
- result: '',
28659
- rawToolCall: toolCall,
28660
- createdAt: calledAt,
28661
- },
28662
- ],
28663
- });
28664
- if (this.options.isVerbose) {
28665
- console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
28666
- }
28667
- // Get execution tools for script execution
28668
- const executionTools = this.options
28669
- .executionTools;
28670
- if (!executionTools || !executionTools.script) {
28671
- throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
28672
- }
28673
- // TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
28674
- const scriptTools = Array.isArray(executionTools.script)
28675
- ? executionTools.script
28676
- : [executionTools.script];
28677
- let functionResponse;
28678
- let errors;
28679
- try {
28680
- const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
28681
- functionResponse = await scriptTool.execute({
28682
- scriptLanguage: 'javascript',
28683
- script: `
28684
- const args = ${JSON.stringify(functionArgs)};
28685
- return await ${functionName}(args);
28686
- `,
28687
- parameters: prompt.parameters,
28688
- });
28689
- if (this.options.isVerbose) {
28690
- console.info(`✅ Tool ${functionName} executed:`, functionResponse);
28691
- }
28692
- }
28693
- catch (error) {
28694
- assertsError(error);
28695
- const serializedError = serializeError(error);
28696
- errors = [serializedError];
28697
- functionResponse = spaceTrim$2((block) => `
28698
-
28699
- The invoked tool \`${functionName}\` failed with error:
28700
-
28701
- \`\`\`json
28702
- ${block(JSON.stringify(serializedError, null, 4))}
28703
- \`\`\`
28704
-
28705
- `);
28706
- console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
28707
- console.error(error);
28708
- }
28709
- toolOutputs.push({
28710
- tool_call_id: toolCall.id,
28711
- output: functionResponse,
28712
- });
28713
- completedToolCalls.push({
28714
- name: functionName,
28715
- arguments: toolCall.function.arguments,
28716
- result: functionResponse,
28717
- rawToolCall: toolCall,
28718
- createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
28719
- errors,
28720
- });
28721
- }
28722
- }
28723
- // Submit tool outputs
28724
- run = await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
28725
- tool_outputs: toolOutputs,
28726
- });
28727
- }
28728
- else {
28729
- // Wait a bit before polling again
28730
- await new Promise((resolve) => setTimeout(resolve, 500));
28731
- run = await client.beta.threads.runs.retrieve(run.thread_id, run.id);
28732
- }
28733
- }
28734
- if (run.status !== 'completed') {
28735
- throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
28736
- }
28737
- // Get messages from the thread
28738
- const messages = await client.beta.threads.messages.list(run.thread_id);
28739
- const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
28740
- if (assistantMessages.length === 0) {
28741
- throw new PipelineExecutionError('No assistant messages found after run completion');
28742
- }
28743
- const lastMessage = assistantMessages[0];
28744
- const textContent = lastMessage.content.find((c) => c.type === 'text');
28745
- if (!textContent || textContent.type !== 'text') {
28746
- throw new PipelineExecutionError('No text content in assistant response');
28747
- }
28748
- complete = $getCurrentDate();
28749
- const resultContent = textContent.text.value;
28750
- const usage = UNCERTAIN_USAGE;
28751
- // Progress callback with final result
28752
- const finalChunk = {
28753
- content: resultContent,
28754
- modelName: 'assistant',
28755
- timing: { start, complete },
28756
- usage,
28757
- rawPromptContent,
28758
- rawRequest,
28759
- rawResponse: { run, messages: messages.data },
28760
- toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
28761
- };
28762
- onProgress(finalChunk);
28763
- return exportJson({
28764
- name: 'promptResult',
28765
- message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
28766
- order: [],
28767
- value: finalChunk,
28768
- });
28769
- }
28770
- // Streaming mode (without tools)
28771
- const rawRequest = {
28772
- // TODO: [👨‍👨‍👧‍👧] ...modelSettings,
28773
- // TODO: [👨‍👨‍👧‍👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
28774
- assistant_id: this.assistantId,
28775
- thread: {
28776
- messages: threadMessages,
28777
- },
28778
- tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
28779
- // <- TODO: Add user identification here> user: this.options.user,
28780
- };
28781
- if (this.options.isVerbose) {
28782
- console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
28783
- }
28784
- const stream = await client.beta.threads.createAndRunStream(rawRequest);
28785
- stream.on('connect', () => {
28786
- if (this.options.isVerbose) {
28787
- console.info('connect', stream.currentEvent);
28788
- }
28789
- });
28790
- stream.on('textDelta', (textDelta, snapshot) => {
28791
- if (this.options.isVerbose && textDelta.value) {
28792
- console.info('textDelta', textDelta.value);
28793
- }
28794
- const chunk = {
28795
- content: snapshot.value,
28796
- modelName: 'assistant',
28797
- timing: {
28798
- start,
28799
- complete: $getCurrentDate(),
28800
- },
28801
- usage: UNCERTAIN_USAGE,
28802
- rawPromptContent,
28803
- rawRequest,
28804
- rawResponse: snapshot,
28805
- };
28806
- onProgress(chunk);
28807
- });
28808
- stream.on('messageCreated', (message) => {
28809
- if (this.options.isVerbose) {
28810
- console.info('messageCreated', message);
28811
- }
28812
- });
28813
- stream.on('messageDone', (message) => {
28814
- if (this.options.isVerbose) {
28815
- console.info('messageDone', message);
28816
- }
28817
- });
28818
- // TODO: [🐱‍🚀] Handle tool calls in assistants
28819
- // Note: OpenAI Assistant streaming with tool calls requires special handling.
28820
- // The stream will pause when a tool call is needed, and we need to:
28821
- // 1. Wait for the run to reach 'requires_action' status
28822
- // 2. Execute the tool calls
28823
- // 3. Submit tool outputs via a separate API call (not on the stream)
28824
- // 4. Continue the run
28825
- // This requires switching to non-streaming mode or using the Runs API directly.
28826
- // For now, tools with assistants should use the non-streaming chat completions API instead.
28827
- const rawResponse = await stream.finalMessages();
28828
- if (this.options.isVerbose) {
28829
- console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
28830
- }
28831
- if (rawResponse.length !== 1) {
28832
- throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
28833
- }
28834
- if (rawResponse[0].content.length !== 1) {
28835
- throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
28836
- }
28837
- if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
28838
- 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`);
28839
- }
28840
- let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
28841
- // Process annotations to replace file IDs with filenames
28842
- if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
28843
- const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
28844
- // Map to store file ID -> filename to avoid duplicate requests
28845
- const fileIdToName = new Map();
28846
- for (const annotation of annotations) {
28847
- if (annotation.type === 'file_citation') {
28848
- const fileId = annotation.file_citation.file_id;
28849
- let filename = fileIdToName.get(fileId);
28850
- if (!filename) {
28851
- try {
28852
- const file = await client.files.retrieve(fileId);
28853
- filename = file.filename;
28854
- fileIdToName.set(fileId, filename);
28855
- }
28856
- catch (error) {
28857
- console.error(`Failed to retrieve file info for ${fileId}`, error);
28858
- // Fallback to "Source" or keep original if fetch fails
28859
- filename = 'Source';
28860
- }
28861
- }
28862
- if (filename && resultContent) {
28863
- // Replace the citation marker with filename
28864
- // Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
28865
- // Note: annotation.text contains the exact marker like 【4:0†source】
28866
- const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
28867
- resultContent = resultContent.replace(annotation.text, newText);
28868
- }
28869
- }
28870
- }
28871
- }
28872
- // eslint-disable-next-line prefer-const
28873
- complete = $getCurrentDate();
28874
- const usage = UNCERTAIN_USAGE;
28875
- // <- TODO: [🥘] Compute real usage for assistant
28876
- // ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
28877
- if (resultContent === null) {
28878
- throw new PipelineExecutionError('No response message from OpenAI');
28879
- }
28880
- return exportJson({
28881
- name: 'promptResult',
28882
- message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
28883
- order: [],
28884
- value: {
28885
- content: resultContent,
28886
- modelName: 'assistant',
28887
- // <- TODO: [🥘] Detect used model in assistant
28888
- // ?> model: rawResponse.model || modelName,
28889
- timing: {
28890
- start,
28891
- complete,
28892
- },
28893
- usage,
28894
- rawPromptContent,
28895
- rawRequest,
28896
- rawResponse,
28897
- // <- [🗯]
28898
- },
28899
- });
28900
- }
28901
- /*
28902
- public async playground() {
28903
- const client = await this.getClient();
28904
-
28905
- // List all assistants
28906
- const assistants = await client.beta.assistants.list();
28907
-
28908
- // Get details of a specific assistant
28909
- const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
28910
- const assistant = await client.beta.assistants.retrieve(assistantId);
28911
-
28912
- // Update an assistant
28913
- const updatedAssistant = await client.beta.assistants.update(assistantId, {
28914
- name: assistant.name + '(M)',
28915
- description: 'Updated description via Promptbook',
28916
- metadata: {
28917
- [Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
28918
- },
28919
- });
28920
-
28921
- await forEver();
28922
- }
28923
- */
28924
- /**
28925
- * Get an existing assistant tool wrapper
28926
- */
28927
- getAssistant(assistantId) {
28928
- return new OpenAiAssistantExecutionTools({
28929
- ...this.options,
28930
- isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
28931
- assistantId,
28932
- });
28933
- }
28465
+ class OpenAiVectorStoreHandler extends OpenAiExecutionTools {
28934
28466
  /**
28935
28467
  * Returns the per-knowledge-source download timeout in milliseconds.
28936
28468
  */
28937
28469
  getKnowledgeSourceDownloadTimeoutMs() {
28938
28470
  var _a;
28939
- return (_a = this.assistantOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
28471
+ return (_a = this.vectorStoreOptions.knowledgeSourceDownloadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_DOWNLOAD_TIMEOUT_MS;
28940
28472
  }
28941
28473
  /**
28942
28474
  * Returns the max concurrency for knowledge source uploads.
28943
28475
  */
28944
28476
  getKnowledgeSourceUploadMaxConcurrency() {
28945
28477
  var _a;
28946
- return (_a = this.assistantOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
28478
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadMaxConcurrency) !== null && _a !== void 0 ? _a : 5;
28947
28479
  }
28948
28480
  /**
28949
28481
  * Returns the polling interval in milliseconds for vector store uploads.
28950
28482
  */
28951
28483
  getKnowledgeSourceUploadPollIntervalMs() {
28952
28484
  var _a;
28953
- return (_a = this.assistantOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
28485
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadPollIntervalMs) !== null && _a !== void 0 ? _a : 5000;
28954
28486
  }
28955
28487
  /**
28956
28488
  * Returns the overall upload timeout in milliseconds for vector store uploads.
28957
28489
  */
28958
28490
  getKnowledgeSourceUploadTimeoutMs() {
28959
28491
  var _a;
28960
- return (_a = this.assistantOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
28492
+ return (_a = this.vectorStoreOptions.knowledgeSourceUploadTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_KNOWLEDGE_SOURCE_UPLOAD_TIMEOUT_MS;
28961
28493
  }
28962
28494
  /**
28963
28495
  * Returns true if we should continue even if vector store ingestion stalls.
28964
28496
  */
28965
28497
  shouldContinueOnVectorStoreStall() {
28966
28498
  var _a;
28967
- return (_a = this.assistantOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
28499
+ return (_a = this.vectorStoreOptions.shouldContinueOnVectorStoreStall) !== null && _a !== void 0 ? _a : true;
28968
28500
  }
28969
28501
  /**
28970
- * Returns assistant-specific options with extended settings.
28502
+ * Returns vector-store-specific options with extended settings.
28971
28503
  */
28972
- get assistantOptions() {
28504
+ get vectorStoreOptions() {
28973
28505
  return this.options;
28974
28506
  }
28507
+ /**
28508
+ * Returns the OpenAI vector stores API surface, supporting stable and beta SDKs.
28509
+ */
28510
+ getVectorStoresApi(client) {
28511
+ var _a, _b;
28512
+ const vectorStores = (_a = client.vectorStores) !== null && _a !== void 0 ? _a : (_b = client.beta) === null || _b === void 0 ? void 0 : _b.vectorStores;
28513
+ if (!vectorStores) {
28514
+ 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.');
28515
+ }
28516
+ return vectorStores;
28517
+ }
28975
28518
  /**
28976
28519
  * Downloads a knowledge source URL into a File for vector store upload.
28977
28520
  */
@@ -29044,7 +28587,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29044
28587
  * Logs vector store file batch diagnostics to help trace ingestion stalls or failures.
29045
28588
  */
29046
28589
  async logVectorStoreFileBatchDiagnostics(options) {
29047
- var _a, _b;
28590
+ var _a, _b, _c, _d, _e;
29048
28591
  const { client, vectorStoreId, batchId, uploadedFiles, logLabel, reason } = options;
29049
28592
  if (reason === 'stalled' && !this.options.isVerbose) {
29050
28593
  return;
@@ -29063,8 +28606,10 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29063
28606
  fileIdToMetadata.set(file.fileId, file);
29064
28607
  }
29065
28608
  try {
28609
+ const vectorStores = this.getVectorStoresApi(client);
29066
28610
  const limit = Math.min(100, Math.max(10, uploadedFiles.length));
29067
- const batchFilesPage = await client.beta.vectorStores.fileBatches.listFiles(vectorStoreId, batchId, {
28611
+ const batchFilesPage = await vectorStores.fileBatches.listFiles(batchId, {
28612
+ vector_store_id: vectorStoreId,
29068
28613
  limit,
29069
28614
  });
29070
28615
  const batchFiles = (_a = batchFilesPage.data) !== null && _a !== void 0 ? _a : [];
@@ -29078,23 +28623,27 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29078
28623
  const inProgressSamples = [];
29079
28624
  const batchFileIds = new Set();
29080
28625
  for (const file of batchFiles) {
29081
- batchFileIds.add(file.id);
29082
- statusCounts[file.status] = ((_b = statusCounts[file.status]) !== null && _b !== void 0 ? _b : 0) + 1;
29083
- const metadata = fileIdToMetadata.get(file.id);
29084
- if (file.last_error) {
28626
+ const status = (_b = file.status) !== null && _b !== void 0 ? _b : 'unknown';
28627
+ statusCounts[status] = ((_c = statusCounts[status]) !== null && _c !== void 0 ? _c : 0) + 1;
28628
+ const vectorStoreFileId = file.id;
28629
+ const uploadedFileId = (_d = file.file_id) !== null && _d !== void 0 ? _d : file.fileId;
28630
+ const fileId = uploadedFileId !== null && uploadedFileId !== void 0 ? uploadedFileId : vectorStoreFileId;
28631
+ batchFileIds.add(fileId);
28632
+ const metadata = fileIdToMetadata.get(fileId);
28633
+ if (status === 'failed') {
29085
28634
  errorSamples.push({
29086
- fileId: file.id,
28635
+ fileId,
28636
+ status,
28637
+ error: (_e = file.last_error) === null || _e === void 0 ? void 0 : _e.message,
29087
28638
  filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
29088
- sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
29089
- status: file.status,
29090
- lastError: file.last_error,
28639
+ vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
29091
28640
  });
29092
28641
  }
29093
- else if (file.status === 'in_progress' && inProgressSamples.length < 5) {
28642
+ if (status === 'in_progress') {
29094
28643
  inProgressSamples.push({
29095
- fileId: file.id,
28644
+ fileId,
29096
28645
  filename: metadata === null || metadata === void 0 ? void 0 : metadata.filename,
29097
- sizeBytes: metadata === null || metadata === void 0 ? void 0 : metadata.sizeBytes,
28646
+ vectorStoreFileId: uploadedFileId ? vectorStoreFileId : undefined,
29098
28647
  });
29099
28648
  }
29100
28649
  }
@@ -29106,7 +28655,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29106
28655
  filename: file.filename,
29107
28656
  sizeBytes: file.sizeBytes,
29108
28657
  }));
29109
- const vectorStore = await client.beta.vectorStores.retrieve(vectorStoreId);
28658
+ const vectorStore = await vectorStores.retrieve(vectorStoreId);
29110
28659
  const logPayload = {
29111
28660
  vectorStoreId,
29112
28661
  batchId,
@@ -29140,8 +28689,9 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29140
28689
  * Uploads knowledge source files to the vector store and polls until processing completes.
29141
28690
  */
29142
28691
  async uploadKnowledgeSourceFilesToVectorStore(options) {
29143
- var _a, _b, _c, _d;
28692
+ var _a, _b, _c, _d, _e, _f;
29144
28693
  const { client, vectorStoreId, files, totalBytes, logLabel } = options;
28694
+ const vectorStores = this.getVectorStoresApi(client);
29145
28695
  const uploadStartedAtMs = Date.now();
29146
28696
  const maxConcurrency = Math.max(1, this.getKnowledgeSourceUploadMaxConcurrency());
29147
28697
  const pollIntervalMs = Math.max(1000, this.getKnowledgeSourceUploadPollIntervalMs());
@@ -29260,7 +28810,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29260
28810
  });
29261
28811
  return null;
29262
28812
  }
29263
- const batch = await client.beta.vectorStores.fileBatches.create(vectorStoreId, {
28813
+ const batch = await vectorStores.fileBatches.create(vectorStoreId, {
29264
28814
  file_ids: fileIds,
29265
28815
  });
29266
28816
  const expectedBatchId = batch.id;
@@ -29292,7 +28842,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29292
28842
  const pollStartedAtMs = Date.now();
29293
28843
  const progressLogIntervalMs = Math.max(VECTOR_STORE_PROGRESS_LOG_INTERVAL_MIN_MS, pollIntervalMs);
29294
28844
  const diagnosticsIntervalMs = Math.max(60000, pollIntervalMs * 5);
29295
- let lastStatus;
28845
+ // let lastStatus: string | undefined;
29296
28846
  let lastCountsKey = '';
29297
28847
  let lastProgressKey = '';
29298
28848
  let lastLogAtMs = 0;
@@ -29300,118 +28850,139 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29300
28850
  let lastDiagnosticsAtMs = pollStartedAtMs;
29301
28851
  let latestBatch = batch;
29302
28852
  let loggedBatchIdMismatch = false;
28853
+ let loggedBatchIdFallback = false;
28854
+ let loggedBatchIdInvalid = false;
29303
28855
  let shouldPoll = true;
29304
28856
  while (shouldPoll) {
29305
- latestBatch = await client.beta.vectorStores.fileBatches.retrieve(vectorStoreId, expectedBatchId);
29306
- const counts = latestBatch.file_counts;
29307
- const countsKey = `${counts.completed}/${counts.failed}/${counts.in_progress}/${counts.cancelled}/${counts.total}`;
29308
28857
  const nowMs = Date.now();
29309
- const returnedBatchId = latestBatch.id;
29310
28858
  // [🤰] Note: Sometimes OpenAI returns Vector Store object instead of Batch object, or IDs get swapped.
29311
- // We only consider it a mismatch if the returned ID looks like a Batch ID.
29312
- const batchIdMismatch = returnedBatchId !== expectedBatchId && returnedBatchId.startsWith('vsfb_');
29313
- const diagnosticsBatchId = batchIdMismatch && returnedBatchId.startsWith('vsfb_') ? returnedBatchId : expectedBatchId;
29314
- const shouldLog = this.options.isVerbose &&
29315
- (latestBatch.status !== lastStatus ||
29316
- countsKey !== lastCountsKey ||
29317
- nowMs - lastLogAtMs >= progressLogIntervalMs);
28859
+ const rawBatchId = typeof latestBatch.id === 'string' ? latestBatch.id : '';
28860
+ const rawVectorStoreId = latestBatch.vector_store_id;
28861
+ let returnedBatchId = rawBatchId;
28862
+ let returnedBatchIdValid = typeof returnedBatchId === 'string' && returnedBatchId.startsWith('vsfb_');
28863
+ if (!returnedBatchIdValid && expectedBatchIdValid) {
28864
+ if (!loggedBatchIdFallback) {
28865
+ console.error('[🤰]', 'Vector store file batch id missing from response; falling back to expected', {
28866
+ vectorStoreId,
28867
+ expectedBatchId,
28868
+ returnedBatchId,
28869
+ rawVectorStoreId,
28870
+ logLabel,
28871
+ });
28872
+ loggedBatchIdFallback = true;
28873
+ }
28874
+ returnedBatchId = expectedBatchId;
28875
+ returnedBatchIdValid = true;
28876
+ }
28877
+ if (!returnedBatchIdValid && !loggedBatchIdInvalid) {
28878
+ console.error('[🤰]', 'Vector store file batch id is invalid; stopping polling', {
28879
+ vectorStoreId,
28880
+ expectedBatchId,
28881
+ returnedBatchId,
28882
+ rawVectorStoreId,
28883
+ logLabel,
28884
+ });
28885
+ loggedBatchIdInvalid = true;
28886
+ }
28887
+ const batchIdMismatch = expectedBatchIdValid && returnedBatchIdValid && returnedBatchId !== expectedBatchId;
29318
28888
  if (batchIdMismatch && !loggedBatchIdMismatch) {
29319
28889
  console.error('[🤰]', 'Vector store file batch id mismatch', {
29320
28890
  vectorStoreId,
29321
28891
  expectedBatchId,
29322
28892
  returnedBatchId,
29323
- status: latestBatch.status,
29324
- fileCounts: counts,
29325
28893
  logLabel,
29326
28894
  });
29327
28895
  loggedBatchIdMismatch = true;
29328
28896
  }
29329
- if (countsKey !== lastProgressKey) {
29330
- lastProgressKey = countsKey;
28897
+ if (returnedBatchIdValid) {
28898
+ latestBatch = await vectorStores.fileBatches.retrieve(returnedBatchId, {
28899
+ vector_store_id: vectorStoreId,
28900
+ });
28901
+ }
28902
+ else {
28903
+ shouldPoll = false;
28904
+ continue;
28905
+ }
28906
+ const status = (_e = latestBatch.status) !== null && _e !== void 0 ? _e : 'unknown';
28907
+ const fileCounts = (_f = latestBatch.file_counts) !== null && _f !== void 0 ? _f : {};
28908
+ const progressKey = JSON.stringify(fileCounts);
28909
+ const statusCountsKey = `${status}-${progressKey}`;
28910
+ const isProgressing = progressKey !== lastProgressKey;
28911
+ if (isProgressing) {
29331
28912
  lastProgressAtMs = nowMs;
28913
+ lastProgressKey = progressKey;
29332
28914
  }
29333
- if (shouldLog) {
28915
+ if (this.options.isVerbose &&
28916
+ (statusCountsKey !== lastCountsKey || nowMs - lastLogAtMs >= progressLogIntervalMs)) {
29334
28917
  console.info('[🤰]', 'Vector store file batch status', {
29335
28918
  vectorStoreId,
29336
- batchId: expectedBatchId,
29337
- ...(batchIdMismatch ? { returnedBatchId } : {}),
29338
- status: latestBatch.status,
29339
- fileCounts: counts,
28919
+ batchId: returnedBatchId,
28920
+ status,
28921
+ fileCounts,
29340
28922
  elapsedMs: nowMs - pollStartedAtMs,
29341
28923
  logLabel,
29342
28924
  });
29343
- // [🤰] If there are in-progress files for a long time, log their details
29344
- if (counts.in_progress > 0 && nowMs - lastProgressAtMs > VECTOR_STORE_STALL_LOG_THRESHOLD_MS) {
29345
- await this.logVectorStoreFileBatchDiagnostics({
29346
- client,
29347
- vectorStoreId,
29348
- batchId: diagnosticsBatchId,
29349
- uploadedFiles,
29350
- logLabel,
29351
- reason: 'stalled',
29352
- });
29353
- }
29354
- lastStatus = latestBatch.status;
29355
- lastCountsKey = countsKey;
28925
+ lastCountsKey = statusCountsKey;
29356
28926
  lastLogAtMs = nowMs;
29357
28927
  }
29358
- if (nowMs - lastProgressAtMs >= diagnosticsIntervalMs &&
28928
+ if (status === 'in_progress' &&
28929
+ nowMs - lastProgressAtMs >= VECTOR_STORE_STALL_LOG_THRESHOLD_MS &&
29359
28930
  nowMs - lastDiagnosticsAtMs >= diagnosticsIntervalMs) {
29360
28931
  lastDiagnosticsAtMs = nowMs;
29361
28932
  await this.logVectorStoreFileBatchDiagnostics({
29362
28933
  client,
29363
28934
  vectorStoreId,
29364
- batchId: diagnosticsBatchId,
28935
+ batchId: returnedBatchId,
29365
28936
  uploadedFiles,
29366
28937
  logLabel,
29367
28938
  reason: 'stalled',
29368
28939
  });
29369
28940
  }
29370
- if (latestBatch.status === 'completed') {
28941
+ if (status === 'completed') {
29371
28942
  if (this.options.isVerbose) {
29372
28943
  console.info('[🤰]', 'Vector store file batch completed', {
29373
28944
  vectorStoreId,
29374
- batchId: expectedBatchId,
29375
- ...(batchIdMismatch ? { returnedBatchId } : {}),
29376
- fileCounts: latestBatch.file_counts,
29377
- elapsedMs: Date.now() - uploadStartedAtMs,
29378
- logLabel,
29379
- });
29380
- }
29381
- if (latestBatch.file_counts.failed > 0) {
29382
- console.error('[🤰]', 'Vector store file batch completed with failures', {
29383
- vectorStoreId,
29384
- batchId: expectedBatchId,
29385
- ...(batchIdMismatch ? { returnedBatchId } : {}),
29386
- fileCounts: latestBatch.file_counts,
28945
+ batchId: returnedBatchId,
28946
+ fileCounts,
28947
+ elapsedMs: nowMs - pollStartedAtMs,
29387
28948
  logLabel,
29388
28949
  });
29389
- await this.logVectorStoreFileBatchDiagnostics({
29390
- client,
29391
- vectorStoreId,
29392
- batchId: diagnosticsBatchId,
29393
- uploadedFiles,
29394
- logLabel,
29395
- reason: 'failed',
29396
- });
29397
28950
  }
29398
28951
  shouldPoll = false;
29399
28952
  continue;
29400
28953
  }
29401
- if (latestBatch.status === 'failed' || latestBatch.status === 'cancelled') {
28954
+ if (status === 'failed') {
28955
+ console.error('[🤰]', 'Vector store file batch completed with failures', {
28956
+ vectorStoreId,
28957
+ batchId: returnedBatchId,
28958
+ fileCounts,
28959
+ elapsedMs: nowMs - pollStartedAtMs,
28960
+ logLabel,
28961
+ });
28962
+ await this.logVectorStoreFileBatchDiagnostics({
28963
+ client,
28964
+ vectorStoreId,
28965
+ batchId: returnedBatchId,
28966
+ uploadedFiles,
28967
+ logLabel,
28968
+ reason: 'failed',
28969
+ });
28970
+ shouldPoll = false;
28971
+ continue;
28972
+ }
28973
+ if (status === 'cancelled') {
29402
28974
  console.error('[🤰]', 'Vector store file batch did not complete', {
29403
28975
  vectorStoreId,
29404
- batchId: expectedBatchId,
29405
- ...(batchIdMismatch ? { returnedBatchId } : {}),
29406
- status: latestBatch.status,
29407
- fileCounts: latestBatch.file_counts,
29408
- elapsedMs: Date.now() - uploadStartedAtMs,
28976
+ batchId: returnedBatchId,
28977
+ status,
28978
+ fileCounts,
28979
+ elapsedMs: nowMs - pollStartedAtMs,
29409
28980
  logLabel,
29410
28981
  });
29411
28982
  await this.logVectorStoreFileBatchDiagnostics({
29412
28983
  client,
29413
28984
  vectorStoreId,
29414
- batchId: diagnosticsBatchId,
28985
+ batchId: returnedBatchId,
29415
28986
  uploadedFiles,
29416
28987
  logLabel,
29417
28988
  reason: 'failed',
@@ -29422,9 +28993,8 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29422
28993
  if (nowMs - pollStartedAtMs >= uploadTimeoutMs) {
29423
28994
  console.error('[🤰]', 'Timed out waiting for vector store file batch', {
29424
28995
  vectorStoreId,
29425
- batchId: expectedBatchId,
29426
- ...(batchIdMismatch ? { returnedBatchId } : {}),
29427
- fileCounts: latestBatch.file_counts,
28996
+ batchId: returnedBatchId,
28997
+ fileCounts,
29428
28998
  elapsedMs: nowMs - pollStartedAtMs,
29429
28999
  uploadTimeoutMs,
29430
29000
  logLabel,
@@ -29432,7 +29002,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29432
29002
  await this.logVectorStoreFileBatchDiagnostics({
29433
29003
  client,
29434
29004
  vectorStoreId,
29435
- batchId: diagnosticsBatchId,
29005
+ batchId: returnedBatchId,
29436
29006
  uploadedFiles,
29437
29007
  logLabel,
29438
29008
  reason: 'timeout',
@@ -29455,7 +29025,9 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29455
29025
  });
29456
29026
  }
29457
29027
  else {
29458
- await client.beta.vectorStores.fileBatches.cancel(vectorStoreId, cancelBatchId);
29028
+ await vectorStores.fileBatches.cancel(cancelBatchId, {
29029
+ vector_store_id: vectorStoreId,
29030
+ });
29459
29031
  }
29460
29032
  if (this.options.isVerbose) {
29461
29033
  console.info('[🤰]', 'Cancelled vector store file batch after timeout', {
@@ -29490,6 +29062,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29490
29062
  */
29491
29063
  async createVectorStoreWithKnowledgeSources(options) {
29492
29064
  const { client, name, knowledgeSources, logLabel } = options;
29065
+ const vectorStores = this.getVectorStoresApi(client);
29493
29066
  const knowledgeSourcesCount = knowledgeSources.length;
29494
29067
  const downloadTimeoutMs = this.getKnowledgeSourceDownloadTimeoutMs();
29495
29068
  if (this.options.isVerbose) {
@@ -29500,7 +29073,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29500
29073
  logLabel,
29501
29074
  });
29502
29075
  }
29503
- const vectorStore = await client.beta.vectorStores.create({
29076
+ const vectorStore = await vectorStores.create({
29504
29077
  name: `${name} Knowledge Base`,
29505
29078
  });
29506
29079
  const vectorStoreId = vectorStore.id;
@@ -29551,7 +29124,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29551
29124
  });
29552
29125
  }
29553
29126
  /*
29554
- TODO: [?????] Resolve problem with browser environment
29127
+ TODO: [🤰] Resolve problem with browser environment
29555
29128
  // Assume it's a local file path
29556
29129
  // Note: This will work in Node.js environment
29557
29130
  // For browser environments, this would need different handling
@@ -29626,6 +29199,478 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29626
29199
  totalBytes,
29627
29200
  };
29628
29201
  }
29202
+ }
29203
+
29204
+ /**
29205
+ * Uploads files to OpenAI and returns their IDs
29206
+ *
29207
+ * @private utility for `OpenAiAssistantExecutionTools` and `OpenAiCompatibleExecutionTools`
29208
+ */
29209
+ async function uploadFilesToOpenAi(client, files) {
29210
+ const fileIds = [];
29211
+ for (const file of files) {
29212
+ // Note: OpenAI API expects a File object or a ReadStream
29213
+ // In browser environment, we can pass the File object directly
29214
+ // In Node.js environment, we might need to convert it or use a different approach
29215
+ // But since `Prompt.files` already contains `File` objects, we try to pass them directly
29216
+ const uploadedFile = await client.files.create({
29217
+ file: file,
29218
+ purpose: 'assistants',
29219
+ });
29220
+ fileIds.push(uploadedFile.id);
29221
+ }
29222
+ return fileIds;
29223
+ }
29224
+
29225
+ /**
29226
+ * Execution Tools for calling OpenAI API Assistants
29227
+ *
29228
+ * This is useful for calling OpenAI API with a single assistant, for more wide usage use `OpenAiExecutionTools`.
29229
+ *
29230
+ * Note: [🦖] There are several different things in Promptbook:
29231
+ * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
29232
+ * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
29233
+ * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
29234
+ * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
29235
+ * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
29236
+ *
29237
+ * @deprecated Use `OpenAiAgentKitExecutionTools` instead.
29238
+ * @public exported from `@promptbook/openai`
29239
+ */
29240
+ class OpenAiAssistantExecutionTools extends OpenAiVectorStoreHandler {
29241
+ /**
29242
+ * Creates OpenAI Execution Tools.
29243
+ *
29244
+ * @param options which are relevant are directly passed to the OpenAI client
29245
+ */
29246
+ constructor(options) {
29247
+ var _a;
29248
+ if (options.isProxied) {
29249
+ throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI assistants`);
29250
+ }
29251
+ super(options);
29252
+ this.isCreatingNewAssistantsAllowed = false;
29253
+ this.assistantId = options.assistantId;
29254
+ this.isCreatingNewAssistantsAllowed = (_a = options.isCreatingNewAssistantsAllowed) !== null && _a !== void 0 ? _a : false;
29255
+ if (this.assistantId === null && !this.isCreatingNewAssistantsAllowed) {
29256
+ throw new NotAllowed(`Assistant ID is null and creating new assistants is not allowed - this configuration does not make sense`);
29257
+ }
29258
+ // <- TODO: !!! `OpenAiAssistantExecutionToolsOptions` - Allow `assistantId: null` together with `isCreatingNewAssistantsAllowed: true`
29259
+ // TODO: [👱] Make limiter same as in `OpenAiExecutionTools`
29260
+ }
29261
+ get title() {
29262
+ return 'OpenAI Assistant';
29263
+ }
29264
+ get description() {
29265
+ return 'Use single assistant provided by OpenAI';
29266
+ }
29267
+ /**
29268
+ * Calls OpenAI API to use a chat model.
29269
+ */
29270
+ async callChatModel(prompt) {
29271
+ return this.callChatModelStream(prompt, () => { });
29272
+ }
29273
+ /**
29274
+ * Calls OpenAI API to use a chat model with streaming.
29275
+ */
29276
+ async callChatModelStream(prompt, onProgress) {
29277
+ var _a, _b, _c, _d, _e, _f;
29278
+ if (this.options.isVerbose) {
29279
+ console.info('💬 OpenAI callChatModel call', { prompt });
29280
+ }
29281
+ const { content, parameters, modelRequirements /*, format*/ } = prompt;
29282
+ const client = await this.getClient();
29283
+ // TODO: [☂] Use here more modelRequirements
29284
+ if (modelRequirements.modelVariant !== 'CHAT') {
29285
+ throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
29286
+ }
29287
+ // TODO: [👨‍👨‍👧‍👧] Remove:
29288
+ for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
29289
+ if (modelRequirements[key] !== undefined) {
29290
+ throw new NotYetImplementedError(`In \`OpenAiAssistantExecutionTools\` you cannot specify \`${key}\``);
29291
+ }
29292
+ }
29293
+ /*
29294
+ TODO: [👨‍👨‍👧‍👧] Implement all of this for Assistants
29295
+ const modelName = modelRequirements.modelName || this.getDefaultChatModel().modelName;
29296
+ const modelSettings = {
29297
+ model: modelName,
29298
+
29299
+ temperature: modelRequirements.temperature,
29300
+
29301
+ // <- TODO: [🈁] Use `seed` here AND/OR use is `isDeterministic` for entire execution tools
29302
+ // <- Note: [🧆]
29303
+ } as OpenAI.Chat.Completions.CompletionCreateParamsNonStreaming; // <- TODO: Guard here types better
29304
+
29305
+ if (format === 'JSON') {
29306
+ modelSettings.response_format = {
29307
+ type: 'json_object',
29308
+ };
29309
+ }
29310
+ */
29311
+ // <- TODO: [🚸] Not all models are compatible with JSON mode
29312
+ // > 'response_format' of type 'json_object' is not supported with this model.
29313
+ const rawPromptContent = templateParameters(content, {
29314
+ ...parameters,
29315
+ modelName: 'assistant',
29316
+ // <- [🧠] What is the best value here
29317
+ });
29318
+ // Build thread messages: include previous thread messages + current user message
29319
+ const threadMessages = [];
29320
+ // TODO: [🈹] Maybe this should not be here but in other place, look at commit 39d705e75e5bcf7a818c3af36bc13e1c8475c30c
29321
+ // Add previous messages from thread (if any)
29322
+ if ('thread' in prompt && Array.isArray(prompt.thread)) {
29323
+ const previousMessages = prompt.thread.map((msg) => ({
29324
+ role: (msg.sender === 'assistant' ? 'assistant' : 'user'),
29325
+ content: msg.content,
29326
+ }));
29327
+ threadMessages.push(...previousMessages);
29328
+ }
29329
+ // Always add the current user message
29330
+ const currentUserMessage = {
29331
+ role: 'user',
29332
+ content: rawPromptContent,
29333
+ };
29334
+ if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
29335
+ const fileIds = await uploadFilesToOpenAi(client, prompt.files);
29336
+ currentUserMessage.attachments = fileIds.map((fileId) => ({
29337
+ file_id: fileId,
29338
+ tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
29339
+ }));
29340
+ }
29341
+ threadMessages.push(currentUserMessage);
29342
+ // Check if tools are being used - if so, use non-streaming mode
29343
+ const hasTools = modelRequirements.tools !== undefined && modelRequirements.tools.length > 0;
29344
+ const start = $getCurrentDate();
29345
+ let complete;
29346
+ // [🐱‍🚀] When tools are present, we need to use the non-streaming Runs API
29347
+ // because streaming doesn't support tool execution flow properly
29348
+ if (hasTools) {
29349
+ onProgress({
29350
+ content: '',
29351
+ modelName: 'assistant',
29352
+ timing: { start, complete: $getCurrentDate() },
29353
+ usage: UNCERTAIN_USAGE,
29354
+ rawPromptContent,
29355
+ rawRequest: null,
29356
+ rawResponse: null,
29357
+ });
29358
+ const rawRequest = {
29359
+ assistant_id: this.assistantId,
29360
+ thread: {
29361
+ messages: threadMessages,
29362
+ },
29363
+ tools: mapToolsToOpenAi(modelRequirements.tools),
29364
+ };
29365
+ if (this.options.isVerbose) {
29366
+ console.info(colors.bgWhite('rawRequest (non-streaming with tools)'), JSON.stringify(rawRequest, null, 4));
29367
+ }
29368
+ // Create thread and run
29369
+ let run = (await client.beta.threads.createAndRun(rawRequest));
29370
+ const completedToolCalls = [];
29371
+ const toolCallStartedAt = new Map();
29372
+ // Poll until run completes or requires action
29373
+ while (run.status === 'queued' || run.status === 'in_progress' || run.status === 'requires_action') {
29374
+ if (run.status === 'requires_action' && ((_a = run.required_action) === null || _a === void 0 ? void 0 : _a.type) === 'submit_tool_outputs') {
29375
+ // Execute tools
29376
+ const toolCalls = run.required_action.submit_tool_outputs.tool_calls;
29377
+ const toolOutputs = [];
29378
+ for (const toolCall of toolCalls) {
29379
+ if (toolCall.type === 'function') {
29380
+ const functionName = toolCall.function.name;
29381
+ const functionArgs = JSON.parse(toolCall.function.arguments);
29382
+ const calledAt = $getCurrentDate();
29383
+ if (toolCall.id) {
29384
+ toolCallStartedAt.set(toolCall.id, calledAt);
29385
+ }
29386
+ onProgress({
29387
+ content: '',
29388
+ modelName: 'assistant',
29389
+ timing: { start, complete: $getCurrentDate() },
29390
+ usage: UNCERTAIN_USAGE,
29391
+ rawPromptContent,
29392
+ rawRequest: null,
29393
+ rawResponse: null,
29394
+ toolCalls: [
29395
+ {
29396
+ name: functionName,
29397
+ arguments: toolCall.function.arguments,
29398
+ result: '',
29399
+ rawToolCall: toolCall,
29400
+ createdAt: calledAt,
29401
+ },
29402
+ ],
29403
+ });
29404
+ if (this.options.isVerbose) {
29405
+ console.info(`🔧 Executing tool: ${functionName}`, functionArgs);
29406
+ }
29407
+ // Get execution tools for script execution
29408
+ const executionTools = this.options
29409
+ .executionTools;
29410
+ if (!executionTools || !executionTools.script) {
29411
+ throw new PipelineExecutionError(`Model requested tool '${functionName}' but no executionTools.script were provided in OpenAiAssistantExecutionTools options`);
29412
+ }
29413
+ // TODO: [DRY] Use some common tool caller (similar to OpenAiCompatibleExecutionTools)
29414
+ const scriptTools = Array.isArray(executionTools.script)
29415
+ ? executionTools.script
29416
+ : [executionTools.script];
29417
+ let functionResponse;
29418
+ let errors;
29419
+ try {
29420
+ const scriptTool = scriptTools[0]; // <- TODO: [🧠] Which script tool to use?
29421
+ functionResponse = await scriptTool.execute({
29422
+ scriptLanguage: 'javascript',
29423
+ script: `
29424
+ const args = ${JSON.stringify(functionArgs)};
29425
+ return await ${functionName}(args);
29426
+ `,
29427
+ parameters: prompt.parameters,
29428
+ });
29429
+ if (this.options.isVerbose) {
29430
+ console.info(`✅ Tool ${functionName} executed:`, functionResponse);
29431
+ }
29432
+ }
29433
+ catch (error) {
29434
+ assertsError(error);
29435
+ const serializedError = serializeError(error);
29436
+ errors = [serializedError];
29437
+ functionResponse = spaceTrim$2((block) => `
29438
+
29439
+ The invoked tool \`${functionName}\` failed with error:
29440
+
29441
+ \`\`\`json
29442
+ ${block(JSON.stringify(serializedError, null, 4))}
29443
+ \`\`\`
29444
+
29445
+ `);
29446
+ console.error(colors.bgRed(`❌ Error executing tool ${functionName}:`));
29447
+ console.error(error);
29448
+ }
29449
+ toolOutputs.push({
29450
+ tool_call_id: toolCall.id,
29451
+ output: functionResponse,
29452
+ });
29453
+ completedToolCalls.push({
29454
+ name: functionName,
29455
+ arguments: toolCall.function.arguments,
29456
+ result: functionResponse,
29457
+ rawToolCall: toolCall,
29458
+ createdAt: toolCall.id ? toolCallStartedAt.get(toolCall.id) || calledAt : calledAt,
29459
+ errors,
29460
+ });
29461
+ }
29462
+ }
29463
+ // Submit tool outputs
29464
+ run = (await client.beta.threads.runs.submitToolOutputs(run.thread_id, run.id, {
29465
+ tool_outputs: toolOutputs,
29466
+ }));
29467
+ }
29468
+ else {
29469
+ // Wait a bit before polling again
29470
+ await new Promise((resolve) => setTimeout(resolve, 500));
29471
+ run = (await client.beta.threads.runs.retrieve(run.thread_id, run.id));
29472
+ }
29473
+ }
29474
+ if (run.status !== 'completed') {
29475
+ throw new PipelineExecutionError(`Assistant run failed with status: ${run.status}`);
29476
+ }
29477
+ // Get messages from the thread
29478
+ const messages = await client.beta.threads.messages.list(run.thread_id);
29479
+ const assistantMessages = messages.data.filter((msg) => msg.role === 'assistant');
29480
+ if (assistantMessages.length === 0) {
29481
+ throw new PipelineExecutionError('No assistant messages found after run completion');
29482
+ }
29483
+ const lastMessage = assistantMessages[0];
29484
+ const textContent = lastMessage.content.find((c) => c.type === 'text');
29485
+ if (!textContent || textContent.type !== 'text') {
29486
+ throw new PipelineExecutionError('No text content in assistant response');
29487
+ }
29488
+ complete = $getCurrentDate();
29489
+ const resultContent = textContent.text.value;
29490
+ const usage = UNCERTAIN_USAGE;
29491
+ // Progress callback with final result
29492
+ const finalChunk = {
29493
+ content: resultContent,
29494
+ modelName: 'assistant',
29495
+ timing: { start, complete },
29496
+ usage,
29497
+ rawPromptContent,
29498
+ rawRequest,
29499
+ rawResponse: { run, messages: messages.data },
29500
+ toolCalls: completedToolCalls.length > 0 ? completedToolCalls : undefined,
29501
+ };
29502
+ onProgress(finalChunk);
29503
+ return exportJson({
29504
+ name: 'promptResult',
29505
+ message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\` (with tools)`,
29506
+ order: [],
29507
+ value: finalChunk,
29508
+ });
29509
+ }
29510
+ // Streaming mode (without tools)
29511
+ const rawRequest = {
29512
+ // TODO: [👨‍👨‍👧‍👧] ...modelSettings,
29513
+ // TODO: [👨‍👨‍👧‍👧][🧠] What about system message for assistants, does it make sense - combination of OpenAI assistants with Promptbook Personas
29514
+ assistant_id: this.assistantId,
29515
+ thread: {
29516
+ messages: threadMessages,
29517
+ },
29518
+ tools: modelRequirements.tools === undefined ? undefined : mapToolsToOpenAi(modelRequirements.tools),
29519
+ // <- TODO: Add user identification here> user: this.options.user,
29520
+ };
29521
+ if (this.options.isVerbose) {
29522
+ console.info(colors.bgWhite('rawRequest (streaming)'), JSON.stringify(rawRequest, null, 4));
29523
+ }
29524
+ const stream = await client.beta.threads.createAndRunStream(rawRequest);
29525
+ stream.on('connect', () => {
29526
+ if (this.options.isVerbose) {
29527
+ console.info('connect', stream.currentEvent);
29528
+ }
29529
+ });
29530
+ stream.on('textDelta', (textDelta, snapshot) => {
29531
+ if (this.options.isVerbose && textDelta.value) {
29532
+ console.info('textDelta', textDelta.value);
29533
+ }
29534
+ const chunk = {
29535
+ content: snapshot.value,
29536
+ modelName: 'assistant',
29537
+ timing: {
29538
+ start,
29539
+ complete: $getCurrentDate(),
29540
+ },
29541
+ usage: UNCERTAIN_USAGE,
29542
+ rawPromptContent,
29543
+ rawRequest,
29544
+ rawResponse: snapshot,
29545
+ };
29546
+ onProgress(chunk);
29547
+ });
29548
+ stream.on('messageCreated', (message) => {
29549
+ if (this.options.isVerbose) {
29550
+ console.info('messageCreated', message);
29551
+ }
29552
+ });
29553
+ stream.on('messageDone', (message) => {
29554
+ if (this.options.isVerbose) {
29555
+ console.info('messageDone', message);
29556
+ }
29557
+ });
29558
+ // TODO: [🐱‍🚀] Handle tool calls in assistants
29559
+ // Note: OpenAI Assistant streaming with tool calls requires special handling.
29560
+ // The stream will pause when a tool call is needed, and we need to:
29561
+ // 1. Wait for the run to reach 'requires_action' status
29562
+ // 2. Execute the tool calls
29563
+ // 3. Submit tool outputs via a separate API call (not on the stream)
29564
+ // 4. Continue the run
29565
+ // This requires switching to non-streaming mode or using the Runs API directly.
29566
+ // For now, tools with assistants should use the non-streaming chat completions API instead.
29567
+ const rawResponse = await stream.finalMessages();
29568
+ if (this.options.isVerbose) {
29569
+ console.info(colors.bgWhite('rawResponse'), JSON.stringify(rawResponse, null, 4));
29570
+ }
29571
+ if (rawResponse.length !== 1) {
29572
+ throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse.length} finalMessages from OpenAI`);
29573
+ }
29574
+ if (rawResponse[0].content.length !== 1) {
29575
+ throw new PipelineExecutionError(`There is NOT 1 BUT ${rawResponse[0].content.length} finalMessages content from OpenAI`);
29576
+ }
29577
+ if (((_b = rawResponse[0].content[0]) === null || _b === void 0 ? void 0 : _b.type) !== 'text') {
29578
+ 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`);
29579
+ }
29580
+ let resultContent = (_d = rawResponse[0].content[0]) === null || _d === void 0 ? void 0 : _d.text.value;
29581
+ // Process annotations to replace file IDs with filenames
29582
+ if ((_e = rawResponse[0].content[0]) === null || _e === void 0 ? void 0 : _e.text.annotations) {
29583
+ const annotations = (_f = rawResponse[0].content[0]) === null || _f === void 0 ? void 0 : _f.text.annotations;
29584
+ // Map to store file ID -> filename to avoid duplicate requests
29585
+ const fileIdToName = new Map();
29586
+ for (const annotation of annotations) {
29587
+ if (annotation.type === 'file_citation') {
29588
+ const fileId = annotation.file_citation.file_id;
29589
+ let filename = fileIdToName.get(fileId);
29590
+ if (!filename) {
29591
+ try {
29592
+ const file = await client.files.retrieve(fileId);
29593
+ filename = file.filename;
29594
+ fileIdToName.set(fileId, filename);
29595
+ }
29596
+ catch (error) {
29597
+ console.error(`Failed to retrieve file info for ${fileId}`, error);
29598
+ // Fallback to "Source" or keep original if fetch fails
29599
+ filename = 'Source';
29600
+ }
29601
+ }
29602
+ if (filename && resultContent) {
29603
+ // Replace the citation marker with filename
29604
+ // Regex to match the second part of the citation: 【id†source】 -> 【id†filename】
29605
+ // Note: annotation.text contains the exact marker like 【4:0†source】
29606
+ const newText = annotation.text.replace(/†.*?】/, `†${filename}】`);
29607
+ resultContent = resultContent.replace(annotation.text, newText);
29608
+ }
29609
+ }
29610
+ }
29611
+ }
29612
+ // eslint-disable-next-line prefer-const
29613
+ complete = $getCurrentDate();
29614
+ const usage = UNCERTAIN_USAGE;
29615
+ // <- TODO: [🥘] Compute real usage for assistant
29616
+ // ?> const usage = computeOpenAiUsage(content, resultContent || '', rawResponse);
29617
+ if (resultContent === null) {
29618
+ throw new PipelineExecutionError('No response message from OpenAI');
29619
+ }
29620
+ return exportJson({
29621
+ name: 'promptResult',
29622
+ message: `Result of \`OpenAiAssistantExecutionTools.callChatModelStream\``,
29623
+ order: [],
29624
+ value: {
29625
+ content: resultContent,
29626
+ modelName: 'assistant',
29627
+ // <- TODO: [🥘] Detect used model in assistant
29628
+ // ?> model: rawResponse.model || modelName,
29629
+ timing: {
29630
+ start,
29631
+ complete,
29632
+ },
29633
+ usage,
29634
+ rawPromptContent,
29635
+ rawRequest,
29636
+ rawResponse,
29637
+ // <- [🗯]
29638
+ },
29639
+ });
29640
+ }
29641
+ /*
29642
+ public async playground() {
29643
+ const client = await this.getClient();
29644
+
29645
+ // List all assistants
29646
+ const assistants = await client.beta.assistants.list();
29647
+
29648
+ // Get details of a specific assistant
29649
+ const assistantId = 'asst_MO8fhZf4dGloCfXSHeLcIik0';
29650
+ const assistant = await client.beta.assistants.retrieve(assistantId);
29651
+
29652
+ // Update an assistant
29653
+ const updatedAssistant = await client.beta.assistants.update(assistantId, {
29654
+ name: assistant.name + '(M)',
29655
+ description: 'Updated description via Promptbook',
29656
+ metadata: {
29657
+ [Math.random().toString(36).substring(2, 15)]: new Date().toISOString(),
29658
+ },
29659
+ });
29660
+
29661
+ await forEver();
29662
+ }
29663
+ */
29664
+ /**
29665
+ * Get an existing assistant tool wrapper
29666
+ */
29667
+ getAssistant(assistantId) {
29668
+ return new OpenAiAssistantExecutionTools({
29669
+ ...this.options,
29670
+ isCreatingNewAssistantsAllowed: this.isCreatingNewAssistantsAllowed,
29671
+ assistantId,
29672
+ });
29673
+ }
29629
29674
  async createNewAssistant(options) {
29630
29675
  var _a, _b, _c;
29631
29676
  if (!this.isCreatingNewAssistantsAllowed) {
@@ -29771,7 +29816,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29771
29816
  * Discriminant for type guards
29772
29817
  */
29773
29818
  get discriminant() {
29774
- return DISCRIMINANT;
29819
+ return DISCRIMINANT$1;
29775
29820
  }
29776
29821
  /**
29777
29822
  * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAssistantExecutionTools`
@@ -29779,7 +29824,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29779
29824
  * Note: This is useful when you can possibly have multiple versions of `@promptbook/openai` installed
29780
29825
  */
29781
29826
  static isOpenAiAssistantExecutionTools(llmExecutionTools) {
29782
- return llmExecutionTools.discriminant === DISCRIMINANT;
29827
+ return llmExecutionTools.discriminant === DISCRIMINANT$1;
29783
29828
  }
29784
29829
  }
29785
29830
  /**
@@ -29787,7 +29832,7 @@ class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
29787
29832
  *
29788
29833
  * @private const of `OpenAiAssistantExecutionTools`
29789
29834
  */
29790
- const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
29835
+ const DISCRIMINANT$1 = 'OPEN_AI_ASSISTANT_V1';
29791
29836
  /**
29792
29837
  * TODO: !!!!! [✨🥚] Knowledge should work both with and without scrapers
29793
29838
  * TODO: [🙎] In `OpenAiAssistantExecutionTools` Allow to create abstract assistants with `isCreatingNewAssistantsAllowed`
@@ -32373,6 +32418,40 @@ function isAssistantPreparationToolCall(toolCall) {
32373
32418
  return toolCall.name === ASSISTANT_PREPARATION_TOOL_CALL_NAME;
32374
32419
  }
32375
32420
 
32421
+ /**
32422
+ * Builds a stable identity string for tool calls across partial updates.
32423
+ *
32424
+ * @param toolCall - Tool call entry to identify.
32425
+ * @returns Stable identity string for deduplication.
32426
+ *
32427
+ * @private function of <Chat/>
32428
+ */
32429
+ function getToolCallIdentity(toolCall) {
32430
+ const rawToolCall = toolCall.rawToolCall;
32431
+ const rawId = (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.id) || (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.callId) || (rawToolCall === null || rawToolCall === void 0 ? void 0 : rawToolCall.call_id);
32432
+ if (rawId) {
32433
+ return `id:${rawId}`;
32434
+ }
32435
+ if (toolCall.createdAt) {
32436
+ return `time:${toolCall.createdAt}:${toolCall.name}`;
32437
+ }
32438
+ const argsKey = (() => {
32439
+ if (typeof toolCall.arguments === 'string') {
32440
+ return toolCall.arguments;
32441
+ }
32442
+ if (!toolCall.arguments) {
32443
+ return '';
32444
+ }
32445
+ try {
32446
+ return JSON.stringify(toolCall.arguments);
32447
+ }
32448
+ catch (_a) {
32449
+ return '';
32450
+ }
32451
+ })();
32452
+ return `fallback:${toolCall.name}:${argsKey}`;
32453
+ }
32454
+
32376
32455
  /*! *****************************************************************************
32377
32456
  Copyright (c) Microsoft Corporation.
32378
32457
 
@@ -32594,6 +32673,426 @@ function promptbookifyAiText(text) {
32594
32673
  * TODO: [🧠][✌️] Make some Promptbook-native token system
32595
32674
  */
32596
32675
 
32676
+ const DEFAULT_AGENT_KIT_MODEL_NAME = 'gpt-5.2';
32677
+ /**
32678
+ * Execution tools for OpenAI AgentKit (Agents SDK).
32679
+ *
32680
+ * @public exported from `@promptbook/openai`
32681
+ */
32682
+ class OpenAiAgentKitExecutionTools extends OpenAiVectorStoreHandler {
32683
+ /**
32684
+ * Creates OpenAI AgentKit execution tools.
32685
+ */
32686
+ constructor(options) {
32687
+ var _a;
32688
+ if (options.isProxied) {
32689
+ throw new NotYetImplementedError(`Proxy mode is not yet implemented for OpenAI AgentKit`);
32690
+ }
32691
+ super(options);
32692
+ this.preparedAgentKitAgent = null;
32693
+ this.agentKitModelName = (_a = options.agentKitModelName) !== null && _a !== void 0 ? _a : DEFAULT_AGENT_KIT_MODEL_NAME;
32694
+ }
32695
+ get title() {
32696
+ return 'OpenAI AgentKit';
32697
+ }
32698
+ get description() {
32699
+ return 'Use OpenAI AgentKit for agent-style chat with tools and knowledge';
32700
+ }
32701
+ /**
32702
+ * Calls OpenAI AgentKit with a chat prompt (non-streaming).
32703
+ */
32704
+ async callChatModel(prompt) {
32705
+ return this.callChatModelStream(prompt, () => { });
32706
+ }
32707
+ /**
32708
+ * Calls OpenAI AgentKit with a chat prompt (streaming).
32709
+ */
32710
+ async callChatModelStream(prompt, onProgress) {
32711
+ const { content, parameters, modelRequirements } = prompt;
32712
+ if (modelRequirements.modelVariant !== 'CHAT') {
32713
+ throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
32714
+ }
32715
+ for (const key of ['maxTokens', 'modelName', 'seed', 'temperature']) {
32716
+ if (modelRequirements[key] !== undefined) {
32717
+ throw new NotYetImplementedError(`In \`OpenAiAgentKitExecutionTools\` you cannot specify \`${key}\``);
32718
+ }
32719
+ }
32720
+ const rawPromptContent = templateParameters(content, {
32721
+ ...parameters,
32722
+ modelName: this.agentKitModelName,
32723
+ });
32724
+ const preparedAgentKitAgent = await this.prepareAgentKitAgent({
32725
+ name: (prompt.title || 'Agent'),
32726
+ instructions: modelRequirements.systemMessage || '',
32727
+ knowledgeSources: modelRequirements.knowledgeSources,
32728
+ tools: 'tools' in prompt && Array.isArray(prompt.tools) ? prompt.tools : modelRequirements.tools,
32729
+ });
32730
+ return this.callChatModelStreamWithPreparedAgent({
32731
+ openAiAgentKitAgent: preparedAgentKitAgent.agent,
32732
+ prompt,
32733
+ rawPromptContent,
32734
+ onProgress,
32735
+ });
32736
+ }
32737
+ /**
32738
+ * Returns a prepared AgentKit agent when the server wants to manage caching externally.
32739
+ */
32740
+ getPreparedAgentKitAgent() {
32741
+ return this.preparedAgentKitAgent;
32742
+ }
32743
+ /**
32744
+ * Stores a prepared AgentKit agent for later reuse by external cache managers.
32745
+ */
32746
+ setPreparedAgentKitAgent(preparedAgent) {
32747
+ this.preparedAgentKitAgent = preparedAgent;
32748
+ }
32749
+ /**
32750
+ * Creates a new tools instance bound to a prepared AgentKit agent.
32751
+ */
32752
+ getPreparedAgentTools(preparedAgent) {
32753
+ const tools = new OpenAiAgentKitExecutionTools(this.agentKitOptions);
32754
+ tools.setPreparedAgentKitAgent(preparedAgent);
32755
+ return tools;
32756
+ }
32757
+ /**
32758
+ * Prepares an AgentKit agent with optional knowledge sources and tool definitions.
32759
+ */
32760
+ async prepareAgentKitAgent(options) {
32761
+ var _a, _b;
32762
+ const { name, instructions, knowledgeSources, tools, vectorStoreId: cachedVectorStoreId, storeAsPrepared, } = options;
32763
+ await this.ensureAgentKitDefaults();
32764
+ if (this.options.isVerbose) {
32765
+ console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
32766
+ name,
32767
+ instructionsLength: instructions.length,
32768
+ knowledgeSourcesCount: (_a = knowledgeSources === null || knowledgeSources === void 0 ? void 0 : knowledgeSources.length) !== null && _a !== void 0 ? _a : 0,
32769
+ toolsCount: (_b = tools === null || tools === void 0 ? void 0 : tools.length) !== null && _b !== void 0 ? _b : 0,
32770
+ });
32771
+ }
32772
+ let vectorStoreId = cachedVectorStoreId;
32773
+ if (!vectorStoreId && knowledgeSources && knowledgeSources.length > 0) {
32774
+ const vectorStoreResult = await this.createVectorStoreWithKnowledgeSources({
32775
+ client: await this.getClient(),
32776
+ name,
32777
+ knowledgeSources,
32778
+ logLabel: 'agentkit preparation',
32779
+ });
32780
+ vectorStoreId = vectorStoreResult.vectorStoreId;
32781
+ }
32782
+ else if (vectorStoreId && this.options.isVerbose) {
32783
+ console.info('[🤰]', 'Using cached vector store for AgentKit agent', {
32784
+ name,
32785
+ vectorStoreId,
32786
+ });
32787
+ }
32788
+ const agentKitTools = this.buildAgentKitTools({ tools, vectorStoreId });
32789
+ const openAiAgentKitAgent = new Agent$1({
32790
+ name,
32791
+ model: this.agentKitModelName,
32792
+ instructions: instructions || 'You are a helpful assistant.',
32793
+ tools: agentKitTools,
32794
+ });
32795
+ const preparedAgent = {
32796
+ agent: openAiAgentKitAgent,
32797
+ vectorStoreId,
32798
+ };
32799
+ if (storeAsPrepared) {
32800
+ this.setPreparedAgentKitAgent(preparedAgent);
32801
+ }
32802
+ if (this.options.isVerbose) {
32803
+ console.info('[🤰]', 'OpenAI AgentKit agent ready', {
32804
+ name,
32805
+ model: this.agentKitModelName,
32806
+ toolCount: agentKitTools.length,
32807
+ hasVectorStore: Boolean(vectorStoreId),
32808
+ });
32809
+ }
32810
+ return preparedAgent;
32811
+ }
32812
+ /**
32813
+ * Ensures the AgentKit SDK is wired to the OpenAI client and API key.
32814
+ */
32815
+ async ensureAgentKitDefaults() {
32816
+ const client = await this.getClient();
32817
+ setDefaultOpenAIClient(client);
32818
+ const apiKey = this.agentKitOptions.apiKey;
32819
+ if (apiKey && typeof apiKey === 'string') {
32820
+ setDefaultOpenAIKey(apiKey);
32821
+ }
32822
+ }
32823
+ /**
32824
+ * Builds the tool list for AgentKit, including hosted file search when applicable.
32825
+ */
32826
+ buildAgentKitTools(options) {
32827
+ var _a;
32828
+ const { tools, vectorStoreId } = options;
32829
+ const agentKitTools = [];
32830
+ if (vectorStoreId) {
32831
+ agentKitTools.push(fileSearchTool(vectorStoreId));
32832
+ }
32833
+ if (tools && tools.length > 0) {
32834
+ const scriptTools = this.resolveScriptTools();
32835
+ for (const toolDefinition of tools) {
32836
+ agentKitTools.push(tool({
32837
+ name: toolDefinition.name,
32838
+ description: toolDefinition.description,
32839
+ parameters: toolDefinition.parameters
32840
+ ? {
32841
+ ...toolDefinition.parameters,
32842
+ additionalProperties: false,
32843
+ required: (_a = toolDefinition.parameters.required) !== null && _a !== void 0 ? _a : [],
32844
+ }
32845
+ : undefined,
32846
+ strict: false,
32847
+ execute: async (input, runContext, details) => {
32848
+ var _a, _b, _c;
32849
+ const scriptTool = scriptTools[0];
32850
+ const functionName = toolDefinition.name;
32851
+ const calledAt = $getCurrentDate();
32852
+ const callId = (_a = details === null || details === void 0 ? void 0 : details.toolCall) === null || _a === void 0 ? void 0 : _a.callId;
32853
+ const functionArgs = input !== null && input !== void 0 ? input : {};
32854
+ if (this.options.isVerbose) {
32855
+ console.info('[🤰]', 'Executing AgentKit tool', {
32856
+ functionName,
32857
+ callId,
32858
+ calledAt,
32859
+ });
32860
+ }
32861
+ try {
32862
+ return await scriptTool.execute({
32863
+ scriptLanguage: 'javascript',
32864
+ script: `
32865
+ const args = ${JSON.stringify(functionArgs)};
32866
+ return await ${functionName}(args);
32867
+ `,
32868
+ 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 : {},
32869
+ });
32870
+ }
32871
+ catch (error) {
32872
+ assertsError(error);
32873
+ const serializedError = serializeError(error);
32874
+ const errorMessage = spaceTrim$2((block) => `
32875
+
32876
+ The invoked tool \`${functionName}\` failed with error:
32877
+
32878
+ \`\`\`json
32879
+ ${block(JSON.stringify(serializedError, null, 4))}
32880
+ \`\`\`
32881
+
32882
+ `);
32883
+ console.error('[🤰]', 'AgentKit tool execution failed', {
32884
+ functionName,
32885
+ callId,
32886
+ error: serializedError,
32887
+ });
32888
+ return errorMessage;
32889
+ }
32890
+ },
32891
+ }));
32892
+ }
32893
+ }
32894
+ return agentKitTools;
32895
+ }
32896
+ /**
32897
+ * Resolves the configured script tools for tool execution.
32898
+ */
32899
+ resolveScriptTools() {
32900
+ const executionTools = this.options.executionTools;
32901
+ if (!executionTools || !executionTools.script) {
32902
+ throw new PipelineExecutionError(`Model requested tools but no executionTools.script were provided in OpenAiAgentKitExecutionTools options`);
32903
+ }
32904
+ return Array.isArray(executionTools.script) ? executionTools.script : [executionTools.script];
32905
+ }
32906
+ /**
32907
+ * Runs a prepared AgentKit agent and streams results back to the caller.
32908
+ */
32909
+ async callChatModelStreamWithPreparedAgent(options) {
32910
+ var _a, _b, _c, _d;
32911
+ const { openAiAgentKitAgent, prompt, onProgress } = options;
32912
+ const rawPromptContent = (_a = options.rawPromptContent) !== null && _a !== void 0 ? _a : templateParameters(prompt.content, {
32913
+ ...prompt.parameters,
32914
+ modelName: this.agentKitModelName,
32915
+ });
32916
+ const start = $getCurrentDate();
32917
+ let latestContent = '';
32918
+ const toolCalls = [];
32919
+ const toolCallIndexById = new Map();
32920
+ const inputItems = await this.buildAgentKitInputItems(prompt, rawPromptContent);
32921
+ const rawRequest = {
32922
+ agentName: openAiAgentKitAgent.name,
32923
+ input: inputItems,
32924
+ };
32925
+ const streamResult = await run(openAiAgentKitAgent, inputItems, {
32926
+ stream: true,
32927
+ context: { parameters: prompt.parameters },
32928
+ });
32929
+ for await (const event of streamResult) {
32930
+ if (event.type === 'raw_model_stream_event' && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'output_text_delta') {
32931
+ latestContent += event.data.delta;
32932
+ onProgress({
32933
+ content: latestContent,
32934
+ modelName: this.agentKitModelName,
32935
+ timing: { start, complete: $getCurrentDate() },
32936
+ usage: UNCERTAIN_USAGE,
32937
+ rawPromptContent: rawPromptContent,
32938
+ rawRequest: null,
32939
+ rawResponse: {},
32940
+ });
32941
+ continue;
32942
+ }
32943
+ if (event.type === 'run_item_stream_event') {
32944
+ const rawItem = (_c = event.item) === null || _c === void 0 ? void 0 : _c.rawItem;
32945
+ if (event.name === 'tool_called' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call') {
32946
+ const toolCall = {
32947
+ name: rawItem.name,
32948
+ arguments: rawItem.arguments,
32949
+ rawToolCall: rawItem,
32950
+ createdAt: $getCurrentDate(),
32951
+ };
32952
+ toolCallIndexById.set(rawItem.callId, toolCalls.length);
32953
+ toolCalls.push(toolCall);
32954
+ onProgress({
32955
+ content: latestContent,
32956
+ modelName: this.agentKitModelName,
32957
+ timing: { start, complete: $getCurrentDate() },
32958
+ usage: UNCERTAIN_USAGE,
32959
+ rawPromptContent: rawPromptContent,
32960
+ rawRequest: null,
32961
+ rawResponse: {},
32962
+ toolCalls: [toolCall],
32963
+ });
32964
+ }
32965
+ if (event.name === 'tool_output' && (rawItem === null || rawItem === void 0 ? void 0 : rawItem.type) === 'function_call_result') {
32966
+ const index = toolCallIndexById.get(rawItem.callId);
32967
+ const result = this.formatAgentKitToolOutput(rawItem.output);
32968
+ if (index !== undefined) {
32969
+ const existingToolCall = toolCalls[index];
32970
+ const completedToolCall = {
32971
+ ...existingToolCall,
32972
+ result,
32973
+ rawToolCall: rawItem,
32974
+ };
32975
+ toolCalls[index] = completedToolCall;
32976
+ onProgress({
32977
+ content: latestContent,
32978
+ modelName: this.agentKitModelName,
32979
+ timing: { start, complete: $getCurrentDate() },
32980
+ usage: UNCERTAIN_USAGE,
32981
+ rawPromptContent: rawPromptContent,
32982
+ rawRequest: null,
32983
+ rawResponse: {},
32984
+ toolCalls: [completedToolCall],
32985
+ });
32986
+ }
32987
+ }
32988
+ }
32989
+ }
32990
+ await streamResult.completed;
32991
+ const complete = $getCurrentDate();
32992
+ const finalContent = ((_d = streamResult.finalOutput) !== null && _d !== void 0 ? _d : latestContent);
32993
+ const finalResult = {
32994
+ content: finalContent,
32995
+ modelName: this.agentKitModelName,
32996
+ timing: { start, complete },
32997
+ usage: UNCERTAIN_USAGE,
32998
+ rawPromptContent: rawPromptContent,
32999
+ rawRequest,
33000
+ rawResponse: { runResult: streamResult },
33001
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
33002
+ };
33003
+ onProgress(finalResult);
33004
+ return finalResult;
33005
+ }
33006
+ /**
33007
+ * Builds AgentKit input items from the prompt and optional thread.
33008
+ */
33009
+ async buildAgentKitInputItems(prompt, rawPromptContent) {
33010
+ var _a;
33011
+ const inputItems = [];
33012
+ if ('thread' in prompt && Array.isArray(prompt.thread)) {
33013
+ for (const message of prompt.thread) {
33014
+ const sender = message.sender;
33015
+ const content = (_a = message.content) !== null && _a !== void 0 ? _a : '';
33016
+ if (sender === 'assistant' || sender === 'agent') {
33017
+ inputItems.push({
33018
+ role: 'assistant',
33019
+ status: 'completed',
33020
+ content: [{ type: 'output_text', text: content }],
33021
+ });
33022
+ }
33023
+ else {
33024
+ inputItems.push({
33025
+ role: 'user',
33026
+ content,
33027
+ });
33028
+ }
33029
+ }
33030
+ }
33031
+ const userContent = await this.buildAgentKitUserContent(prompt, rawPromptContent);
33032
+ inputItems.push({
33033
+ role: 'user',
33034
+ content: userContent,
33035
+ });
33036
+ return inputItems;
33037
+ }
33038
+ /**
33039
+ * Builds the user message content for AgentKit runs, including file inputs when provided.
33040
+ */
33041
+ async buildAgentKitUserContent(prompt, rawPromptContent) {
33042
+ if ('files' in prompt && Array.isArray(prompt.files) && prompt.files.length > 0) {
33043
+ const fileItems = await Promise.all(prompt.files.map(async (file) => {
33044
+ const arrayBuffer = await file.arrayBuffer();
33045
+ const base64 = Buffer.from(arrayBuffer).toString('base64');
33046
+ return {
33047
+ type: 'input_image',
33048
+ image: `data:${file.type};base64,${base64}`,
33049
+ };
33050
+ }));
33051
+ return [{ type: 'input_text', text: rawPromptContent }, ...fileItems];
33052
+ }
33053
+ return rawPromptContent;
33054
+ }
33055
+ /**
33056
+ * Normalizes AgentKit tool outputs into a string for Promptbook tool call results.
33057
+ */
33058
+ formatAgentKitToolOutput(output) {
33059
+ if (typeof output === 'string') {
33060
+ return output;
33061
+ }
33062
+ if (output && typeof output === 'object') {
33063
+ const textOutput = output;
33064
+ if (textOutput.type === 'text' && typeof textOutput.text === 'string') {
33065
+ return textOutput.text;
33066
+ }
33067
+ }
33068
+ return JSON.stringify(output !== null && output !== void 0 ? output : null);
33069
+ }
33070
+ /**
33071
+ * Returns AgentKit-specific options.
33072
+ */
33073
+ get agentKitOptions() {
33074
+ return this.options;
33075
+ }
33076
+ /**
33077
+ * Discriminant for type guards.
33078
+ */
33079
+ get discriminant() {
33080
+ return DISCRIMINANT;
33081
+ }
33082
+ /**
33083
+ * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentKitExecutionTools`.
33084
+ */
33085
+ static isOpenAiAgentKitExecutionTools(llmExecutionTools) {
33086
+ return llmExecutionTools.discriminant === DISCRIMINANT;
33087
+ }
33088
+ }
33089
+ /**
33090
+ * Discriminant for type guards.
33091
+ *
33092
+ * @private const of `OpenAiAgentKitExecutionTools`
33093
+ */
33094
+ const DISCRIMINANT = 'OPEN_AI_AGENT_KIT_V1';
33095
+
32597
33096
  /**
32598
33097
  * Emits a progress update to signal assistant preparation before long setup work.
32599
33098
  */
@@ -32630,6 +33129,7 @@ function emitAssistantPreparationProgress(options) {
32630
33129
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
32631
33130
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
32632
33131
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
33132
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
32633
33133
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
32634
33134
  *
32635
33135
  * @public exported from `@promptbook/core`
@@ -32764,6 +33264,7 @@ class AgentLlmExecutionTools {
32764
33264
  * Calls the chat model with agent-specific system prompt and requirements with streaming
32765
33265
  */
32766
33266
  async callChatModelStream(prompt, onProgress) {
33267
+ var _a, _b;
32767
33268
  // Ensure we're working with a chat prompt
32768
33269
  if (prompt.modelRequirements.modelVariant !== 'CHAT') {
32769
33270
  throw new Error('AgentLlmExecutionTools only supports chat prompts');
@@ -32791,7 +33292,75 @@ class AgentLlmExecutionTools {
32791
33292
  }, // Cast to avoid readonly mismatch from spread
32792
33293
  };
32793
33294
  console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
32794
- if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
33295
+ if (OpenAiAgentKitExecutionTools.isOpenAiAgentKitExecutionTools(this.options.llmTools)) {
33296
+ const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
33297
+ const vectorStoreHash = SHA256(JSON.stringify((_a = modelRequirements.knowledgeSources) !== null && _a !== void 0 ? _a : [])).toString();
33298
+ const cachedVectorStore = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
33299
+ const cachedAgentKit = AgentLlmExecutionTools.agentKitAgentCache.get(this.title);
33300
+ let preparedAgentKit = this.options.assistantPreparationMode === 'external'
33301
+ ? this.options.llmTools.getPreparedAgentKitAgent()
33302
+ : null;
33303
+ const vectorStoreId = (preparedAgentKit === null || preparedAgentKit === void 0 ? void 0 : preparedAgentKit.vectorStoreId) ||
33304
+ (cachedVectorStore && cachedVectorStore.requirementsHash === vectorStoreHash
33305
+ ? cachedVectorStore.vectorStoreId
33306
+ : undefined);
33307
+ if (!preparedAgentKit && cachedAgentKit && cachedAgentKit.requirementsHash === requirementsHash) {
33308
+ if (this.options.isVerbose) {
33309
+ console.info('[🤰]', 'Using cached OpenAI AgentKit agent', {
33310
+ agent: this.title,
33311
+ });
33312
+ }
33313
+ preparedAgentKit = {
33314
+ agent: cachedAgentKit.agent,
33315
+ vectorStoreId: cachedAgentKit.vectorStoreId,
33316
+ };
33317
+ }
33318
+ if (!preparedAgentKit) {
33319
+ if (this.options.isVerbose) {
33320
+ console.info('[🤰]', 'Preparing OpenAI AgentKit agent', {
33321
+ agent: this.title,
33322
+ });
33323
+ }
33324
+ if (!vectorStoreId && ((_b = modelRequirements.knowledgeSources) === null || _b === void 0 ? void 0 : _b.length)) {
33325
+ emitAssistantPreparationProgress({
33326
+ onProgress,
33327
+ prompt,
33328
+ modelName: this.modelName,
33329
+ phase: 'Creating knowledge base',
33330
+ });
33331
+ }
33332
+ emitAssistantPreparationProgress({
33333
+ onProgress,
33334
+ prompt,
33335
+ modelName: this.modelName,
33336
+ phase: 'Preparing AgentKit agent',
33337
+ });
33338
+ preparedAgentKit = await this.options.llmTools.prepareAgentKitAgent({
33339
+ name: this.title,
33340
+ instructions: modelRequirements.systemMessage || '',
33341
+ knowledgeSources: modelRequirements.knowledgeSources,
33342
+ tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
33343
+ vectorStoreId,
33344
+ });
33345
+ }
33346
+ if (preparedAgentKit.vectorStoreId) {
33347
+ AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
33348
+ vectorStoreId: preparedAgentKit.vectorStoreId,
33349
+ requirementsHash: vectorStoreHash,
33350
+ });
33351
+ }
33352
+ AgentLlmExecutionTools.agentKitAgentCache.set(this.title, {
33353
+ agent: preparedAgentKit.agent,
33354
+ requirementsHash,
33355
+ vectorStoreId: preparedAgentKit.vectorStoreId,
33356
+ });
33357
+ underlyingLlmResult = await this.options.llmTools.callChatModelStreamWithPreparedAgent({
33358
+ openAiAgentKitAgent: preparedAgentKit.agent,
33359
+ prompt: promptWithAgentModelRequirements,
33360
+ onProgress,
33361
+ });
33362
+ }
33363
+ else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
32795
33364
  // ... deprecated path ...
32796
33365
  const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
32797
33366
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
@@ -32916,6 +33485,10 @@ class AgentLlmExecutionTools {
32916
33485
  return agentResult;
32917
33486
  }
32918
33487
  }
33488
+ /**
33489
+ * Cached AgentKit agents to avoid rebuilding identical instances.
33490
+ */
33491
+ AgentLlmExecutionTools.agentKitAgentCache = new Map();
32919
33492
  /**
32920
33493
  * Cache of OpenAI assistants to avoid creating duplicates
32921
33494
  */
@@ -32997,6 +33570,7 @@ function buildTeacherSummary(commitments, used) {
32997
33570
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
32998
33571
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
32999
33572
  * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
33573
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
33000
33574
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
33001
33575
  *
33002
33576
  * @public exported from `@promptbook/core`
@@ -33367,7 +33941,8 @@ function buildRemoteAgentSource(profile, meta) {
33367
33941
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
33368
33942
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
33369
33943
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
33370
- * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
33944
+ * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
33945
+ * - `OpenAiAgentKitExecutionTools` - which is a specific implementation of `LlmExecutionTools` backed by OpenAI AgentKit
33371
33946
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
33372
33947
  *
33373
33948
  * @public exported from `@promptbook/core`
@@ -33531,26 +34106,7 @@ class RemoteAgent extends Agent {
33531
34106
  };
33532
34107
  };
33533
34108
  const getToolCallKey = (toolCall) => {
33534
- var _a;
33535
- const rawId = (_a = toolCall.rawToolCall) === null || _a === void 0 ? void 0 : _a.id;
33536
- if (rawId) {
33537
- return `id:${rawId}`;
33538
- }
33539
- const argsKey = (() => {
33540
- if (typeof toolCall.arguments === 'string') {
33541
- return toolCall.arguments;
33542
- }
33543
- if (!toolCall.arguments) {
33544
- return '';
33545
- }
33546
- try {
33547
- return JSON.stringify(toolCall.arguments);
33548
- }
33549
- catch (_a) {
33550
- return '';
33551
- }
33552
- })();
33553
- return `${toolCall.name}:${toolCall.createdAt || ''}:${argsKey}`;
34109
+ return getToolCallIdentity(toolCall);
33554
34110
  };
33555
34111
  const mergeToolCall = (existing, incoming) => {
33556
34112
  const incomingResult = incoming.result;