@promptbook/remote-server 0.105.0-26 → 0.105.0-30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/index.es.js CHANGED
@@ -39,7 +39,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
39
39
  * @generated
40
40
  * @see https://github.com/webgptorg/promptbook
41
41
  */
42
- const PROMPTBOOK_ENGINE_VERSION = '0.105.0-26';
42
+ const PROMPTBOOK_ENGINE_VERSION = '0.105.0-30';
43
43
  /**
44
44
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
45
45
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -19164,7 +19164,11 @@ class OpenAiCompatibleExecutionTools {
19164
19164
  quality: currentModelRequirements.quality,
19165
19165
  style: currentModelRequirements.style,
19166
19166
  };
19167
- const rawPromptContent = templateParameters(content, { ...parameters, modelName });
19167
+ let rawPromptContent = templateParameters(content, { ...parameters, modelName });
19168
+ if ('attachments' in prompt && Array.isArray(prompt.attachments) && prompt.attachments.length > 0) {
19169
+ rawPromptContent +=
19170
+ '\n\n' + prompt.attachments.map((attachment) => `Image attachment: ${attachment.url}`).join('\n');
19171
+ }
19168
19172
  const rawRequest = {
19169
19173
  ...modelSettings,
19170
19174
  prompt: rawPromptContent,
@@ -19457,6 +19461,207 @@ class OpenAiExecutionTools extends OpenAiCompatibleExecutionTools {
19457
19461
  }
19458
19462
  }
19459
19463
 
19464
+ /**
19465
+ * Execution Tools for calling OpenAI API using the Responses API (Agents)
19466
+ *
19467
+ * @public exported from `@promptbook/openai`
19468
+ */
19469
+ class OpenAiAgentExecutionTools extends OpenAiExecutionTools {
19470
+ constructor(options) {
19471
+ super(options);
19472
+ this.vectorStoreId = options.vectorStoreId;
19473
+ }
19474
+ get title() {
19475
+ return 'OpenAI Agent';
19476
+ }
19477
+ get description() {
19478
+ return 'Use OpenAI Responses API (Agentic)';
19479
+ }
19480
+ /**
19481
+ * Calls OpenAI API to use a chat model with streaming.
19482
+ */
19483
+ async callChatModelStream(prompt, onProgress) {
19484
+ if (this.options.isVerbose) {
19485
+ console.info('💬 OpenAI Agent callChatModel call', { prompt });
19486
+ }
19487
+ const { content, parameters, modelRequirements } = prompt;
19488
+ const client = await this.getClient();
19489
+ if (modelRequirements.modelVariant !== 'CHAT') {
19490
+ throw new PipelineExecutionError('Use callChatModel only for CHAT variant');
19491
+ }
19492
+ const rawPromptContent = templateParameters(content, {
19493
+ ...parameters,
19494
+ modelName: 'agent',
19495
+ });
19496
+ // Build input items
19497
+ const input = []; // TODO: Type properly when OpenAI types are updated
19498
+ // Add previous messages from thread (if any)
19499
+ if ('thread' in prompt && Array.isArray(prompt.thread)) {
19500
+ const previousMessages = prompt.thread.map((msg) => ({
19501
+ role: msg.sender === 'assistant' ? 'assistant' : 'user',
19502
+ content: msg.content,
19503
+ }));
19504
+ input.push(...previousMessages);
19505
+ }
19506
+ // Add current user message
19507
+ input.push({
19508
+ role: 'user',
19509
+ content: rawPromptContent,
19510
+ });
19511
+ // Prepare tools
19512
+ const tools = modelRequirements.tools ? mapToolsToOpenAi(modelRequirements.tools) : undefined;
19513
+ // Add file_search if vector store is present
19514
+ const agentTools = tools ? [...tools] : [];
19515
+ let toolResources = undefined;
19516
+ if (this.vectorStoreId) {
19517
+ agentTools.push({ type: 'file_search' });
19518
+ toolResources = {
19519
+ file_search: {
19520
+ vector_store_ids: [this.vectorStoreId],
19521
+ },
19522
+ };
19523
+ }
19524
+ // Add file_search also if knowledgeSources are present in the prompt (passed via AgentLlmExecutionTools)
19525
+ if (modelRequirements.knowledgeSources &&
19526
+ modelRequirements.knowledgeSources.length > 0 &&
19527
+ !this.vectorStoreId) {
19528
+ // Note: Vector store should have been created by AgentLlmExecutionTools and passed via options.
19529
+ // If we are here, it means we have knowledge sources but no vector store ID.
19530
+ // We can't easily create one here without persisting it.
19531
+ console.warn('Knowledge sources provided but no vector store ID. Creating temporary vector store is not implemented in callChatModelStream.');
19532
+ }
19533
+ const start = $getCurrentDate();
19534
+ // Construct the request
19535
+ const rawRequest = {
19536
+ // TODO: Type properly as OpenAI.Responses.CreateResponseParams
19537
+ model: modelRequirements.modelName || 'gpt-4o',
19538
+ input,
19539
+ instructions: modelRequirements.systemMessage,
19540
+ tools: agentTools.length > 0 ? agentTools : undefined,
19541
+ tool_resources: toolResources,
19542
+ store: false, // Stateless by default as we pass full history
19543
+ };
19544
+ if (this.options.isVerbose) {
19545
+ console.info(colors.bgWhite('rawRequest (Responses API)'), JSON.stringify(rawRequest, null, 4));
19546
+ }
19547
+ // Call Responses API
19548
+ // Note: Using any cast because types might not be updated yet
19549
+ const response = await client.responses.create(rawRequest);
19550
+ if (this.options.isVerbose) {
19551
+ console.info(colors.bgWhite('rawResponse'), JSON.stringify(response, null, 4));
19552
+ }
19553
+ const complete = $getCurrentDate();
19554
+ let resultContent = '';
19555
+ const toolCalls = [];
19556
+ // Parse output items
19557
+ if (response.output) {
19558
+ for (const item of response.output) {
19559
+ if (item.type === 'message' && item.role === 'assistant') {
19560
+ for (const contentPart of item.content) {
19561
+ if (contentPart.type === 'output_text') {
19562
+ // "output_text" based on migration guide, or "text"? Guide says "output_text" in example.
19563
+ resultContent += contentPart.text;
19564
+ }
19565
+ else if (contentPart.type === 'text') {
19566
+ resultContent += contentPart.text.value || contentPart.text;
19567
+ }
19568
+ }
19569
+ }
19570
+ else if (item.type === 'function_call') ;
19571
+ }
19572
+ }
19573
+ // Use output_text helper if available (mentioned in guide)
19574
+ if (response.output_text) {
19575
+ resultContent = response.output_text;
19576
+ }
19577
+ // TODO: Handle tool calls properly (Requires clearer docs or experimentation)
19578
+ onProgress({
19579
+ content: resultContent,
19580
+ modelName: response.model || 'agent',
19581
+ timing: { start, complete },
19582
+ usage: UNCERTAIN_USAGE,
19583
+ rawPromptContent,
19584
+ rawRequest,
19585
+ rawResponse: response,
19586
+ });
19587
+ return exportJson({
19588
+ name: 'promptResult',
19589
+ message: `Result of \`OpenAiAgentExecutionTools.callChatModelStream\``,
19590
+ order: [],
19591
+ value: {
19592
+ content: resultContent,
19593
+ modelName: response.model || 'agent',
19594
+ timing: { start, complete },
19595
+ usage: UNCERTAIN_USAGE,
19596
+ rawPromptContent,
19597
+ rawRequest,
19598
+ rawResponse: response,
19599
+ toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
19600
+ },
19601
+ });
19602
+ }
19603
+ /**
19604
+ * Creates a vector store from knowledge sources
19605
+ */
19606
+ static async createVectorStore(client, name, knowledgeSources) {
19607
+ // Create a vector store
19608
+ const vectorStore = await client.beta.vectorStores.create({
19609
+ name: `${name} Knowledge Base`,
19610
+ });
19611
+ const vectorStoreId = vectorStore.id;
19612
+ // Upload files from knowledge sources to the vector store
19613
+ const fileStreams = [];
19614
+ for (const source of knowledgeSources) {
19615
+ try {
19616
+ // Check if it's a URL
19617
+ if (source.startsWith('http://') || source.startsWith('https://')) {
19618
+ // Download the file
19619
+ const response = await fetch(source);
19620
+ if (!response.ok) {
19621
+ console.error(`Failed to download ${source}: ${response.statusText}`);
19622
+ continue;
19623
+ }
19624
+ const buffer = await response.arrayBuffer();
19625
+ const filename = source.split('/').pop() || 'downloaded-file';
19626
+ const blob = new Blob([buffer]);
19627
+ const file = new File([blob], filename);
19628
+ fileStreams.push(file);
19629
+ }
19630
+ else {
19631
+ // Local files not supported in browser env easily, same as before
19632
+ }
19633
+ }
19634
+ catch (error) {
19635
+ console.error(`Error processing knowledge source ${source}:`, error);
19636
+ }
19637
+ }
19638
+ // Batch upload files to the vector store
19639
+ if (fileStreams.length > 0) {
19640
+ try {
19641
+ await client.beta.vectorStores.fileBatches.uploadAndPoll(vectorStoreId, {
19642
+ files: fileStreams,
19643
+ });
19644
+ }
19645
+ catch (error) {
19646
+ console.error('Error uploading files to vector store:', error);
19647
+ }
19648
+ }
19649
+ return vectorStoreId;
19650
+ }
19651
+ /**
19652
+ * Discriminant for type guards
19653
+ */
19654
+ get discriminant() {
19655
+ return 'OPEN_AI_AGENT';
19656
+ }
19657
+ /**
19658
+ * Type guard to check if given `LlmExecutionTools` are instanceof `OpenAiAgentExecutionTools`
19659
+ */
19660
+ static isOpenAiAgentExecutionTools(llmExecutionTools) {
19661
+ return llmExecutionTools.discriminant === 'OPEN_AI_AGENT';
19662
+ }
19663
+ }
19664
+
19460
19665
  /**
19461
19666
  * Uploads files to OpenAI and returns their IDs
19462
19667
  *
@@ -19491,6 +19696,7 @@ async function uploadFilesToOpenAi(client, files) {
19491
19696
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
19492
19697
  *
19493
19698
  * @public exported from `@promptbook/openai`
19699
+ * @deprecated Use `OpenAiAgentExecutionTools` instead which uses the new OpenAI Responses API
19494
19700
  */
19495
19701
  class OpenAiAssistantExecutionTools extends OpenAiExecutionTools {
19496
19702
  /**
@@ -20131,7 +20337,8 @@ const DISCRIMINANT = 'OPEN_AI_ASSISTANT_V1';
20131
20337
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
20132
20338
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
20133
20339
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
20134
- * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
20340
+ * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
20341
+ * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
20135
20342
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
20136
20343
  *
20137
20344
  * @public exported from `@promptbook/core`
@@ -20259,15 +20466,78 @@ class AgentLlmExecutionTools {
20259
20466
  ...modelRequirements,
20260
20467
  // Spread tools to convert readonly array to mutable
20261
20468
  tools: modelRequirements.tools ? [...modelRequirements.tools] : chatPrompt.modelRequirements.tools,
20469
+ // Spread knowledgeSources to convert readonly array to mutable
20470
+ knowledgeSources: modelRequirements.knowledgeSources
20471
+ ? [...modelRequirements.knowledgeSources]
20472
+ : undefined,
20262
20473
  // Prepend agent system message to existing system message
20263
20474
  systemMessage: modelRequirements.systemMessage +
20264
20475
  (chatPrompt.modelRequirements.systemMessage
20265
20476
  ? `\n\n${chatPrompt.modelRequirements.systemMessage}`
20266
20477
  : ''),
20267
- },
20478
+ }, // Cast to avoid readonly mismatch from spread
20268
20479
  };
20269
20480
  console.log('!!!! promptWithAgentModelRequirements:', promptWithAgentModelRequirements);
20270
- if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
20481
+ if (OpenAiAgentExecutionTools.isOpenAiAgentExecutionTools(this.options.llmTools)) {
20482
+ const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
20483
+ const cached = AgentLlmExecutionTools.vectorStoreCache.get(this.title);
20484
+ let agentTools;
20485
+ if (cached && cached.requirementsHash === requirementsHash) {
20486
+ if (this.options.isVerbose) {
20487
+ console.log(`1️⃣ Using cached OpenAI Agent Vector Store for agent ${this.title}...`);
20488
+ }
20489
+ // Create new instance with cached vectorStoreId
20490
+ // We need to access options from the original tool.
20491
+ // We assume isOpenAiAgentExecutionTools implies it has options we can clone.
20492
+ // But protected options are not accessible.
20493
+ // We can cast to access options if they were public, or use a method to clone.
20494
+ // OpenAiAgentExecutionTools doesn't have a clone method.
20495
+ // However, we can just assume the passed tool *might* not have the vector store yet, or we are replacing it.
20496
+ // Actually, if the passed tool IS OpenAiAgentExecutionTools, we should use it as a base.
20497
+ // TODO: [🧠] This is a bit hacky, accessing protected options or recreating tools.
20498
+ // Ideally OpenAiAgentExecutionTools should have a method `withVectorStoreId`.
20499
+ agentTools = new OpenAiAgentExecutionTools({
20500
+ ...this.options.llmTools.options,
20501
+ vectorStoreId: cached.vectorStoreId,
20502
+ });
20503
+ }
20504
+ else {
20505
+ if (this.options.isVerbose) {
20506
+ console.log(`1️⃣ Creating/Updating OpenAI Agent Vector Store for agent ${this.title}...`);
20507
+ }
20508
+ let vectorStoreId;
20509
+ if (modelRequirements.knowledgeSources && modelRequirements.knowledgeSources.length > 0) {
20510
+ const client = await this.options.llmTools.getClient();
20511
+ vectorStoreId = await OpenAiAgentExecutionTools.createVectorStore(client, this.title, modelRequirements.knowledgeSources);
20512
+ }
20513
+ if (vectorStoreId) {
20514
+ AgentLlmExecutionTools.vectorStoreCache.set(this.title, {
20515
+ vectorStoreId,
20516
+ requirementsHash,
20517
+ });
20518
+ }
20519
+ agentTools = new OpenAiAgentExecutionTools({
20520
+ ...this.options.llmTools.options,
20521
+ vectorStoreId,
20522
+ });
20523
+ }
20524
+ // Create modified chat prompt with agent system message specific to OpenAI Agent
20525
+ // Note: Unlike Assistants API, Responses API expects instructions (system message) to be passed in the call.
20526
+ // So we use promptWithAgentModelRequirements which has the system message prepended.
20527
+ // But we need to make sure we pass knowledgeSources in modelRequirements so OpenAiAgentExecutionTools can fallback to warning if vectorStoreId is missing (though we just handled it).
20528
+ const promptForAgent = {
20529
+ ...promptWithAgentModelRequirements,
20530
+ modelRequirements: {
20531
+ ...promptWithAgentModelRequirements.modelRequirements,
20532
+ knowledgeSources: modelRequirements.knowledgeSources
20533
+ ? [...modelRequirements.knowledgeSources]
20534
+ : undefined, // Pass knowledge sources explicitly
20535
+ },
20536
+ };
20537
+ underlyingLlmResult = await agentTools.callChatModelStream(promptForAgent, onProgress);
20538
+ }
20539
+ else if (OpenAiAssistantExecutionTools.isOpenAiAssistantExecutionTools(this.options.llmTools)) {
20540
+ // ... deprecated path ...
20271
20541
  const requirementsHash = SHA256(JSON.stringify(modelRequirements)).toString();
20272
20542
  const cached = AgentLlmExecutionTools.assistantCache.get(this.title);
20273
20543
  let assistant;
@@ -20362,6 +20632,10 @@ class AgentLlmExecutionTools {
20362
20632
  * Cache of OpenAI assistants to avoid creating duplicates
20363
20633
  */
20364
20634
  AgentLlmExecutionTools.assistantCache = new Map();
20635
+ /**
20636
+ * Cache of OpenAI vector stores to avoid creating duplicates
20637
+ */
20638
+ AgentLlmExecutionTools.vectorStoreCache = new Map();
20365
20639
  /**
20366
20640
  * TODO: [🍚] Implement Destroyable pattern to free resources
20367
20641
  * TODO: [🧠] Adding parameter substitution support (here or should be responsibility of the underlying LLM Tools)
@@ -20375,7 +20649,8 @@ var _Agent_instances, _Agent_selfLearnNonce, _Agent_selfLearnSamples, _Agent_sel
20375
20649
  * - `Agent` - which represents an AI Agent with its source, memories, actions, etc. Agent is a higher-level abstraction which is internally using:
20376
20650
  * - `LlmExecutionTools` - which wraps one or more LLM models and provides an interface to execute them
20377
20651
  * - `AgentLlmExecutionTools` - which is a specific implementation of `LlmExecutionTools` that wraps another LlmExecutionTools and applies agent-specific system prompts and requirements
20378
- * - `OpenAiAssistantExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities, recommended for usage in `Agent` or `AgentLlmExecutionTools`
20652
+ * - `OpenAiAgentExecutionTools` - which is a specific implementation of `LlmExecutionTools` for OpenAI models with agent capabilities (using Responses API), recommended for usage in `Agent` or `AgentLlmExecutionTools`
20653
+ * - `OpenAiAssistantExecutionTools` - (Deprecated) which is a specific implementation of `LlmExecutionTools` for OpenAI models with assistant capabilities
20379
20654
  * - `RemoteAgent` - which is an `Agent` that connects to a Promptbook Agents Server
20380
20655
  *
20381
20656
  * @public exported from `@promptbook/core`