@promptbook/legacy-documents 0.112.0-71 → 0.112.0-73

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 (195) hide show
  1. package/esm/index.es.js +1424 -798
  2. package/esm/index.es.js.map +1 -1
  3. package/esm/src/book-3.0/Book.d.ts +6 -0
  4. package/esm/src/book-components/Chat/save/_common/chatExportRendering.d.ts +28 -0
  5. package/esm/src/book-components/Chat/save/_common/getPromptbookExportBranding.d.ts +18 -0
  6. package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +1 -1
  7. package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.test.d.ts +1 -0
  8. package/esm/src/book-components/Chat/save/index.d.ts +4 -4
  9. package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +1 -1
  10. package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.test.d.ts +1 -0
  11. package/esm/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -2
  12. package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +2 -2
  13. package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.test.d.ts +1 -0
  14. package/esm/src/book-components/Chat/save/react/reactSaveFormatDefinition.test.d.ts +1 -0
  15. package/esm/src/book-components/Chat/utils/getToolCallChipletInfo.test.d.ts +1 -0
  16. package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +26 -0
  17. package/esm/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +14 -2
  18. package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +2 -0
  19. package/esm/src/cli/cli-commands/common/handleActionErrors.d.ts +9 -4
  20. package/esm/src/cli/cli-commands/run/prepareRunCommandResources.d.ts +20 -0
  21. package/esm/src/cli/cli-commands/run/resolveRunInputParameters.d.ts +12 -0
  22. package/esm/src/cli/cli-commands/run/runCommandAction.d.ts +21 -0
  23. package/esm/src/cli/cli-commands/run/runPipelineExecution.d.ts +14 -0
  24. package/esm/src/cli/cli-commands/run.d.ts +1 -1
  25. package/esm/src/conversion/parsePipeline/applyPipelineHead.d.ts +8 -0
  26. package/esm/src/conversion/parsePipeline/createInitialPipelineJson.d.ts +8 -0
  27. package/esm/src/conversion/parsePipeline/createUniqueSectionNameResolver.d.ts +14 -0
  28. package/esm/src/conversion/parsePipeline/defineParameter.d.ts +8 -0
  29. package/esm/src/conversion/parsePipeline/extractPipelineDescription.d.ts +6 -0
  30. package/esm/src/conversion/parsePipeline/finalizeParsedPipeline.d.ts +8 -0
  31. package/esm/src/conversion/parsePipeline/getPipelineIdentification.d.ts +7 -0
  32. package/esm/src/conversion/parsePipeline/parsePreparedPipelineSections.d.ts +18 -0
  33. package/esm/src/conversion/parsePipeline/preparePipelineString.d.ts +8 -0
  34. package/esm/src/conversion/parsePipeline/processPipelineSection.d.ts +9 -0
  35. package/esm/src/conversion/pipelineJsonToString/appendMarkdownBlock.d.ts +7 -0
  36. package/esm/src/conversion/pipelineJsonToString/createPipelineCommands.d.ts +7 -0
  37. package/esm/src/conversion/pipelineJsonToString/createPipelineIntroduction.d.ts +8 -0
  38. package/esm/src/conversion/pipelineJsonToString/createTaskSerialization.d.ts +23 -0
  39. package/esm/src/conversion/pipelineJsonToString/stringifyCommands.d.ts +7 -0
  40. package/esm/src/conversion/pipelineJsonToString/stringifyTask.d.ts +8 -0
  41. package/esm/src/conversion/pipelineJsonToString.test.d.ts +1 -0
  42. package/esm/src/execution/createPipelineExecutor/executeSingleAttempt.d.ts +31 -0
  43. package/esm/src/execution/createPipelineExecutor/handleAttemptFailure.d.ts +40 -0
  44. package/esm/src/execution/createPipelineExecutor/reportPromptExecution.d.ts +34 -0
  45. package/esm/src/execution/resolveTaskTldr.d.ts +32 -0
  46. package/esm/src/execution/resolveTaskTldr.test.d.ts +1 -0
  47. package/esm/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +22 -63
  48. package/esm/src/llm-providers/agent/AgentLlmExecutionToolsAgentKitRunner.d.ts +51 -0
  49. package/esm/src/llm-providers/agent/AgentLlmExecutionToolsOpenAiAssistantRunner.d.ts +43 -0
  50. package/esm/src/llm-providers/agent/AgentLlmExecutionToolsPromptPreparer.d.ts +41 -0
  51. package/esm/src/llm-providers/agent/emitAgentLlmExecutionToolsAssistantPreparationProgress.d.ts +26 -0
  52. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +16 -93
  53. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsInputBuilder.d.ts +41 -0
  54. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOutputTypeMapper.d.ts +56 -0
  55. package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +99 -0
  56. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +24 -120
  57. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsProgressReporter.d.ts +62 -0
  58. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsPromptBuilder.d.ts +29 -0
  59. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +63 -0
  60. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +89 -0
  61. package/esm/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +9 -28
  62. package/esm/src/llm-providers/openai/OpenAiCompatibleModelCatalog.d.ts +31 -0
  63. package/esm/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +57 -0
  64. package/esm/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +29 -0
  65. package/esm/src/llm-providers/openai/OpenAiVectorStoreFileBatchHandler.d.ts +51 -0
  66. package/esm/src/llm-providers/openai/OpenAiVectorStoreFileBatchPoller.d.ts +75 -0
  67. package/esm/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +1 -98
  68. package/esm/src/llm-providers/openai/OpenAiVectorStoreKnowledgeSourcePreparer.d.ts +44 -0
  69. package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatProgressReporter.d.ts +86 -0
  70. package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatPromptBuilder.d.ts +57 -0
  71. package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatToolCaller.d.ts +57 -0
  72. package/esm/src/remote-server/startRemoteServer/RemoteServerRuntime.d.ts +14 -0
  73. package/esm/src/remote-server/startRemoteServer/SocketResponse.d.ts +9 -0
  74. package/esm/src/remote-server/startRemoteServer/StartRemoteServerConfiguration.d.ts +18 -0
  75. package/esm/src/remote-server/startRemoteServer/createRemoteServerExpressApp.d.ts +7 -0
  76. package/esm/src/remote-server/startRemoteServer/createRemoteServerHandle.d.ts +11 -0
  77. package/esm/src/remote-server/startRemoteServer/createSocketServer.d.ts +9 -0
  78. package/esm/src/remote-server/startRemoteServer/getExecutionToolsFromIdentification.d.ts +12 -0
  79. package/esm/src/remote-server/startRemoteServer/registerBookRoutes.d.ts +7 -0
  80. package/esm/src/remote-server/startRemoteServer/registerExecutionRoutes.d.ts +7 -0
  81. package/esm/src/remote-server/startRemoteServer/registerListModelsSocketHandler.d.ts +8 -0
  82. package/esm/src/remote-server/startRemoteServer/registerLoginRoute.d.ts +7 -0
  83. package/esm/src/remote-server/startRemoteServer/registerNotFoundRoute.d.ts +7 -0
  84. package/esm/src/remote-server/startRemoteServer/registerOpenAiCompatibleChatCompletionsRoute.d.ts +7 -0
  85. package/esm/src/remote-server/startRemoteServer/registerOpenApiRoutes.d.ts +7 -0
  86. package/esm/src/remote-server/startRemoteServer/registerPreparePipelineSocketHandler.d.ts +8 -0
  87. package/esm/src/remote-server/startRemoteServer/registerPromptSocketHandler.d.ts +8 -0
  88. package/esm/src/remote-server/startRemoteServer/registerRemoteServerHttpRoutes.d.ts +7 -0
  89. package/esm/src/remote-server/startRemoteServer/registerRemoteServerSocketHandlers.d.ts +8 -0
  90. package/esm/src/remote-server/startRemoteServer/registerServerIndexRoute.d.ts +7 -0
  91. package/esm/src/remote-server/startRemoteServer/resolveStartRemoteServerConfiguration.d.ts +8 -0
  92. package/esm/src/remote-server/startRemoteServer/respondToSocketRequest.d.ts +8 -0
  93. package/esm/src/remote-server/startRemoteServer/startListening.d.ts +9 -0
  94. package/esm/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +14 -1
  95. package/esm/src/utils/serialization/serializeToPromptbookJavascript.d.ts +2 -0
  96. package/esm/src/utils/serialization/serializeToPromptbookJavascript.test.d.ts +1 -0
  97. package/esm/src/version.d.ts +1 -1
  98. package/package.json +2 -2
  99. package/umd/index.umd.js +1424 -798
  100. package/umd/index.umd.js.map +1 -1
  101. package/umd/src/book-3.0/Book.d.ts +6 -0
  102. package/umd/src/book-components/Chat/save/_common/chatExportRendering.d.ts +28 -0
  103. package/umd/src/book-components/Chat/save/_common/getPromptbookExportBranding.d.ts +18 -0
  104. package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +1 -1
  105. package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.test.d.ts +1 -0
  106. package/umd/src/book-components/Chat/save/index.d.ts +4 -4
  107. package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +1 -1
  108. package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.test.d.ts +1 -0
  109. package/umd/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -2
  110. package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +2 -2
  111. package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.test.d.ts +1 -0
  112. package/umd/src/book-components/Chat/save/react/reactSaveFormatDefinition.test.d.ts +1 -0
  113. package/umd/src/book-components/Chat/utils/getToolCallChipletInfo.test.d.ts +1 -0
  114. package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +26 -0
  115. package/umd/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +14 -2
  116. package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +2 -0
  117. package/umd/src/cli/cli-commands/common/handleActionErrors.d.ts +9 -4
  118. package/umd/src/cli/cli-commands/run/prepareRunCommandResources.d.ts +20 -0
  119. package/umd/src/cli/cli-commands/run/resolveRunInputParameters.d.ts +12 -0
  120. package/umd/src/cli/cli-commands/run/runCommandAction.d.ts +21 -0
  121. package/umd/src/cli/cli-commands/run/runPipelineExecution.d.ts +14 -0
  122. package/umd/src/cli/cli-commands/run.d.ts +1 -1
  123. package/umd/src/conversion/parsePipeline/applyPipelineHead.d.ts +8 -0
  124. package/umd/src/conversion/parsePipeline/createInitialPipelineJson.d.ts +8 -0
  125. package/umd/src/conversion/parsePipeline/createUniqueSectionNameResolver.d.ts +14 -0
  126. package/umd/src/conversion/parsePipeline/defineParameter.d.ts +8 -0
  127. package/umd/src/conversion/parsePipeline/extractPipelineDescription.d.ts +6 -0
  128. package/umd/src/conversion/parsePipeline/finalizeParsedPipeline.d.ts +8 -0
  129. package/umd/src/conversion/parsePipeline/getPipelineIdentification.d.ts +7 -0
  130. package/umd/src/conversion/parsePipeline/parsePreparedPipelineSections.d.ts +18 -0
  131. package/umd/src/conversion/parsePipeline/preparePipelineString.d.ts +8 -0
  132. package/umd/src/conversion/parsePipeline/processPipelineSection.d.ts +9 -0
  133. package/umd/src/conversion/pipelineJsonToString/appendMarkdownBlock.d.ts +7 -0
  134. package/umd/src/conversion/pipelineJsonToString/createPipelineCommands.d.ts +7 -0
  135. package/umd/src/conversion/pipelineJsonToString/createPipelineIntroduction.d.ts +8 -0
  136. package/umd/src/conversion/pipelineJsonToString/createTaskSerialization.d.ts +23 -0
  137. package/umd/src/conversion/pipelineJsonToString/stringifyCommands.d.ts +7 -0
  138. package/umd/src/conversion/pipelineJsonToString/stringifyTask.d.ts +8 -0
  139. package/umd/src/conversion/pipelineJsonToString.test.d.ts +1 -0
  140. package/umd/src/execution/createPipelineExecutor/executeSingleAttempt.d.ts +31 -0
  141. package/umd/src/execution/createPipelineExecutor/handleAttemptFailure.d.ts +40 -0
  142. package/umd/src/execution/createPipelineExecutor/reportPromptExecution.d.ts +34 -0
  143. package/umd/src/execution/resolveTaskTldr.d.ts +32 -0
  144. package/umd/src/execution/resolveTaskTldr.test.d.ts +1 -0
  145. package/umd/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +22 -63
  146. package/umd/src/llm-providers/agent/AgentLlmExecutionToolsAgentKitRunner.d.ts +51 -0
  147. package/umd/src/llm-providers/agent/AgentLlmExecutionToolsOpenAiAssistantRunner.d.ts +43 -0
  148. package/umd/src/llm-providers/agent/AgentLlmExecutionToolsPromptPreparer.d.ts +41 -0
  149. package/umd/src/llm-providers/agent/emitAgentLlmExecutionToolsAssistantPreparationProgress.d.ts +26 -0
  150. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +16 -93
  151. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsInputBuilder.d.ts +41 -0
  152. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOutputTypeMapper.d.ts +56 -0
  153. package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +99 -0
  154. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +24 -120
  155. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsProgressReporter.d.ts +62 -0
  156. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsPromptBuilder.d.ts +29 -0
  157. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +63 -0
  158. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +89 -0
  159. package/umd/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +9 -28
  160. package/umd/src/llm-providers/openai/OpenAiCompatibleModelCatalog.d.ts +31 -0
  161. package/umd/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +57 -0
  162. package/umd/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +29 -0
  163. package/umd/src/llm-providers/openai/OpenAiVectorStoreFileBatchHandler.d.ts +51 -0
  164. package/umd/src/llm-providers/openai/OpenAiVectorStoreFileBatchPoller.d.ts +75 -0
  165. package/umd/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +1 -98
  166. package/umd/src/llm-providers/openai/OpenAiVectorStoreKnowledgeSourcePreparer.d.ts +44 -0
  167. package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatProgressReporter.d.ts +86 -0
  168. package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatPromptBuilder.d.ts +57 -0
  169. package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatToolCaller.d.ts +57 -0
  170. package/umd/src/remote-server/startRemoteServer/RemoteServerRuntime.d.ts +14 -0
  171. package/umd/src/remote-server/startRemoteServer/SocketResponse.d.ts +9 -0
  172. package/umd/src/remote-server/startRemoteServer/StartRemoteServerConfiguration.d.ts +18 -0
  173. package/umd/src/remote-server/startRemoteServer/createRemoteServerExpressApp.d.ts +7 -0
  174. package/umd/src/remote-server/startRemoteServer/createRemoteServerHandle.d.ts +11 -0
  175. package/umd/src/remote-server/startRemoteServer/createSocketServer.d.ts +9 -0
  176. package/umd/src/remote-server/startRemoteServer/getExecutionToolsFromIdentification.d.ts +12 -0
  177. package/umd/src/remote-server/startRemoteServer/registerBookRoutes.d.ts +7 -0
  178. package/umd/src/remote-server/startRemoteServer/registerExecutionRoutes.d.ts +7 -0
  179. package/umd/src/remote-server/startRemoteServer/registerListModelsSocketHandler.d.ts +8 -0
  180. package/umd/src/remote-server/startRemoteServer/registerLoginRoute.d.ts +7 -0
  181. package/umd/src/remote-server/startRemoteServer/registerNotFoundRoute.d.ts +7 -0
  182. package/umd/src/remote-server/startRemoteServer/registerOpenAiCompatibleChatCompletionsRoute.d.ts +7 -0
  183. package/umd/src/remote-server/startRemoteServer/registerOpenApiRoutes.d.ts +7 -0
  184. package/umd/src/remote-server/startRemoteServer/registerPreparePipelineSocketHandler.d.ts +8 -0
  185. package/umd/src/remote-server/startRemoteServer/registerPromptSocketHandler.d.ts +8 -0
  186. package/umd/src/remote-server/startRemoteServer/registerRemoteServerHttpRoutes.d.ts +7 -0
  187. package/umd/src/remote-server/startRemoteServer/registerRemoteServerSocketHandlers.d.ts +8 -0
  188. package/umd/src/remote-server/startRemoteServer/registerServerIndexRoute.d.ts +7 -0
  189. package/umd/src/remote-server/startRemoteServer/resolveStartRemoteServerConfiguration.d.ts +8 -0
  190. package/umd/src/remote-server/startRemoteServer/respondToSocketRequest.d.ts +8 -0
  191. package/umd/src/remote-server/startRemoteServer/startListening.d.ts +9 -0
  192. package/umd/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +14 -1
  193. package/umd/src/utils/serialization/serializeToPromptbookJavascript.d.ts +2 -0
  194. package/umd/src/utils/serialization/serializeToPromptbookJavascript.test.d.ts +1 -0
  195. package/umd/src/version.d.ts +1 -1
package/esm/index.es.js CHANGED
@@ -26,7 +26,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
26
26
  * @generated
27
27
  * @see https://github.com/webgptorg/promptbook
28
28
  */
29
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-71';
29
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
30
30
  /**
31
31
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
32
32
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -1787,6 +1787,60 @@ function validatePipelineString(pipelineString) {
1787
1787
  }
1788
1788
  // TODO: [🧠][🈴] Where is the best location for this file
1789
1789
 
1790
+ /**
1791
+ * Appends one markdown block to an existing markdown document.
1792
+ *
1793
+ * @private internal utility of `pipelineJsonToString`
1794
+ */
1795
+ function appendMarkdownBlock(pipelineString, markdownBlock) {
1796
+ return spaceTrim$1((block) => `
1797
+ ${block(pipelineString)}
1798
+
1799
+ ${block(markdownBlock)}
1800
+ `);
1801
+ }
1802
+
1803
+ /**
1804
+ * Collects pipeline-level commands in the existing serialization order.
1805
+ *
1806
+ * @private internal utility of `pipelineJsonToString`
1807
+ */
1808
+ function createPipelineCommands(pipelineJson) {
1809
+ const { pipelineUrl, bookVersion, parameters } = pipelineJson;
1810
+ const commands = [];
1811
+ if (pipelineUrl) {
1812
+ commands.push(`PIPELINE URL ${pipelineUrl}`);
1813
+ }
1814
+ if (bookVersion !== `undefined`) {
1815
+ commands.push(`BOOK VERSION ${bookVersion}`);
1816
+ }
1817
+ commands.push(...createParameterCommands(parameters, 'INPUT PARAMETER', ({ isInput }) => isInput));
1818
+ commands.push(...createParameterCommands(parameters, 'OUTPUT PARAMETER', ({ isOutput }) => isOutput));
1819
+ return commands;
1820
+ }
1821
+ /**
1822
+ * Builds one group of parameter commands while preserving the original parameter order.
1823
+ *
1824
+ * @private internal utility of `createPipelineCommands`
1825
+ */
1826
+ function createParameterCommands(parameters, commandPrefix, isIncluded) {
1827
+ return parameters
1828
+ .filter((parameter) => isIncluded(parameter))
1829
+ .map((parameter) => `${commandPrefix} ${parameterJsonToString(parameter)}`);
1830
+ }
1831
+ /**
1832
+ * Converts one parameter JSON declaration to the serialized inline form.
1833
+ *
1834
+ * @private internal utility of `createPipelineCommands`
1835
+ */
1836
+ function parameterJsonToString(parameterJson) {
1837
+ const { name, description } = parameterJson;
1838
+ if (!description) {
1839
+ return `{${name}}`;
1840
+ }
1841
+ return `{${name}} ${description}`;
1842
+ }
1843
+
1790
1844
  /**
1791
1845
  * Prettify the html code
1792
1846
  *
@@ -1800,6 +1854,31 @@ function prettifyMarkdown(content) {
1800
1854
  return (content + `\n\n<!-- Note: Prettier removed from Promptbook -->`);
1801
1855
  }
1802
1856
 
1857
+ /**
1858
+ * Creates the initial markdown heading and description of a pipeline.
1859
+ *
1860
+ * @private internal utility of `pipelineJsonToString`
1861
+ */
1862
+ function createPipelineIntroduction(pipelineJson) {
1863
+ const { title, description } = pipelineJson;
1864
+ const pipelineIntroduction = spaceTrim$1((block) => `
1865
+ # ${title}
1866
+
1867
+ ${block(description || '')}
1868
+ `);
1869
+ // TODO: [main] !!5 This increases size of the bundle and is probably not necessary
1870
+ return prettifyMarkdown(pipelineIntroduction);
1871
+ }
1872
+
1873
+ /**
1874
+ * Renders commands as markdown bullet items.
1875
+ *
1876
+ * @private internal utility of `pipelineJsonToString`
1877
+ */
1878
+ function stringifyCommands(commands) {
1879
+ return commands.map((command) => `- ${command}`).join('\n');
1880
+ }
1881
+
1803
1882
  /**
1804
1883
  * Makes first letter of a string uppercase
1805
1884
  *
@@ -1812,141 +1891,186 @@ function capitalize(word) {
1812
1891
  }
1813
1892
 
1814
1893
  /**
1815
- * Converts promptbook in JSON format to string format
1894
+ * Collects all task-specific serialization details.
1816
1895
  *
1817
- * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
1818
- * @param pipelineJson Promptbook in JSON format (.bookc)
1819
- * @returns Promptbook in string format (.book.md)
1896
+ * @private internal utility of `pipelineJsonToString`
1897
+ */
1898
+ function createTaskSerialization(task) {
1899
+ const taskTypeSerialization = createTaskTypeSerialization(task);
1900
+ return {
1901
+ commands: [
1902
+ ...taskTypeSerialization.commands,
1903
+ ...createJokerCommands(task),
1904
+ ...createPostprocessingCommands(task),
1905
+ ...createExpectationCommands(task),
1906
+ ...createFormatCommands(task),
1907
+ ],
1908
+ contentLanguage: taskTypeSerialization.contentLanguage,
1909
+ };
1910
+ }
1911
+ /**
1912
+ * Collects commands and content language driven by the task type.
1820
1913
  *
1821
- * @public exported from `@promptbook/core`
1914
+ * @private internal utility of `createTaskSerialization`
1822
1915
  */
1823
- function pipelineJsonToString(pipelineJson) {
1824
- const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
1825
- let pipelineString = spaceTrim$1((block) => `
1826
- # ${title}
1827
-
1828
- ${block(description || '')}
1829
- `);
1916
+ function createTaskTypeSerialization(task) {
1917
+ if (task.taskType === 'PROMPT_TASK') {
1918
+ return {
1919
+ commands: createPromptTaskCommands(task),
1920
+ contentLanguage: 'text',
1921
+ };
1922
+ }
1923
+ if (task.taskType === 'SIMPLE_TASK') {
1924
+ return {
1925
+ commands: ['SIMPLE TEMPLATE'],
1926
+ contentLanguage: 'text',
1927
+ };
1928
+ }
1929
+ if (task.taskType === 'SCRIPT_TASK') {
1930
+ return {
1931
+ commands: ['SCRIPT'],
1932
+ contentLanguage: task.contentLanguage || '',
1933
+ };
1934
+ }
1935
+ if (task.taskType === 'DIALOG_TASK') {
1936
+ return {
1937
+ commands: ['DIALOG'],
1938
+ contentLanguage: 'text',
1939
+ };
1940
+ }
1941
+ return {
1942
+ commands: [],
1943
+ contentLanguage: 'text',
1944
+ };
1945
+ }
1946
+ /**
1947
+ * Collects prompt-task-specific commands.
1948
+ *
1949
+ * @private internal utility of `createTaskSerialization`
1950
+ */
1951
+ function createPromptTaskCommands(task) {
1952
+ const { modelName, modelVariant } = task.modelRequirements || {};
1830
1953
  const commands = [];
1831
- if (pipelineUrl) {
1832
- commands.push(`PIPELINE URL ${pipelineUrl}`);
1954
+ // Note: Do nothing, it is default
1955
+ // commands.push(`PROMPT`);
1956
+ if (modelVariant) {
1957
+ commands.push(`MODEL VARIANT ${capitalize(modelVariant)}`);
1833
1958
  }
1834
- if (bookVersion !== `undefined`) {
1835
- commands.push(`BOOK VERSION ${bookVersion}`);
1959
+ if (modelName) {
1960
+ commands.push(`MODEL NAME \`${modelName}\``);
1836
1961
  }
1837
- // TODO: [main] !!5 This increases size of the bundle and is probably not necessary
1838
- pipelineString = prettifyMarkdown(pipelineString);
1839
- for (const parameter of parameters.filter(({ isInput }) => isInput)) {
1840
- commands.push(`INPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
1962
+ return commands;
1963
+ }
1964
+ /**
1965
+ * Collects joker commands.
1966
+ *
1967
+ * @private internal utility of `createTaskSerialization`
1968
+ */
1969
+ function createJokerCommands(task) {
1970
+ var _a;
1971
+ return ((_a = task.jokerParameterNames) === null || _a === void 0 ? void 0 : _a.map((joker) => `JOKER {${joker}}`)) || [];
1972
+ }
1973
+ /**
1974
+ * Collects postprocessing commands.
1975
+ *
1976
+ * @private internal utility of `createTaskSerialization`
1977
+ */
1978
+ function createPostprocessingCommands(task) {
1979
+ var _a;
1980
+ return ((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || [];
1981
+ }
1982
+ /**
1983
+ * Collects expectation commands.
1984
+ *
1985
+ * @private internal utility of `createTaskSerialization`
1986
+ */
1987
+ function createExpectationCommands(task) {
1988
+ if (!task.expectations) {
1989
+ return [];
1841
1990
  }
1842
- for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
1843
- commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
1991
+ return Object.entries(task.expectations).flatMap(([unit, expectation]) => createExpectationCommandsForUnit(unit, expectation.min, expectation.max));
1992
+ }
1993
+ /**
1994
+ * Collects expectation commands for a single unit.
1995
+ *
1996
+ * @private internal utility of `createTaskSerialization`
1997
+ */
1998
+ function createExpectationCommandsForUnit(unit, min, max) {
1999
+ if (min === max) {
2000
+ return [`EXPECT EXACTLY ${min} ${formatExpectationUnit(unit, min)}`];
1844
2001
  }
1845
- pipelineString = spaceTrim$1((block) => `
1846
- ${block(pipelineString)}
1847
-
1848
- ${block(commands.map((command) => `- ${command}`).join('\n'))}
1849
- `);
1850
- for (const task of tasks) {
1851
- const {
1852
- /* Note: Not using:> name, */
1853
- title, description,
1854
- /* Note: dependentParameterNames, */
1855
- jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
1856
- const commands = [];
1857
- let contentLanguage = 'text';
1858
- if (taskType === 'PROMPT_TASK') {
1859
- const { modelRequirements } = task;
1860
- const { modelName, modelVariant } = modelRequirements || {};
1861
- // Note: Do nothing, it is default
1862
- // commands.push(`PROMPT`);
1863
- if (modelVariant) {
1864
- commands.push(`MODEL VARIANT ${capitalize(modelVariant)}`);
1865
- }
1866
- if (modelName) {
1867
- commands.push(`MODEL NAME \`${modelName}\``);
1868
- }
1869
- }
1870
- else if (taskType === 'SIMPLE_TASK') {
1871
- commands.push(`SIMPLE TEMPLATE`);
1872
- // Note: Nothing special here
1873
- }
1874
- else if (taskType === 'SCRIPT_TASK') {
1875
- commands.push(`SCRIPT`);
1876
- if (task.contentLanguage) {
1877
- contentLanguage = task.contentLanguage;
1878
- }
1879
- else {
1880
- contentLanguage = '';
1881
- }
1882
- }
1883
- else if (taskType === 'DIALOG_TASK') {
1884
- commands.push(`DIALOG`);
1885
- // Note: Nothing special here
1886
- } // <- }else if([🅱]
1887
- if (jokers) {
1888
- for (const joker of jokers) {
1889
- commands.push(`JOKER {${joker}}`);
1890
- }
1891
- } /* not else */
1892
- if (postprocessing) {
1893
- for (const postprocessingFunctionName of postprocessing) {
1894
- commands.push(`POSTPROCESSING \`${postprocessingFunctionName}\``);
1895
- }
1896
- } /* not else */
1897
- if (expectations) {
1898
- for (const [unit, { min, max }] of Object.entries(expectations)) {
1899
- if (min === max) {
1900
- commands.push(`EXPECT EXACTLY ${min} ${capitalize(unit + (min > 1 ? 's' : ''))}`);
1901
- }
1902
- else {
1903
- if (min !== undefined) {
1904
- commands.push(`EXPECT MIN ${min} ${capitalize(unit + (min > 1 ? 's' : ''))}`);
1905
- } /* not else */
1906
- if (max !== undefined) {
1907
- commands.push(`EXPECT MAX ${max} ${capitalize(unit + (max > 1 ? 's' : ''))}`);
1908
- }
1909
- }
1910
- }
1911
- } /* not else */
1912
- if (format) {
1913
- if (format === 'JSON') {
1914
- // TODO: @deprecated remove
1915
- commands.push(`FORMAT JSON`);
1916
- }
1917
- } /* not else */
1918
- pipelineString = spaceTrim$1((block) => `
1919
- ${block(pipelineString)}
2002
+ const commands = [];
2003
+ if (min !== undefined) {
2004
+ commands.push(`EXPECT MIN ${min} ${formatExpectationUnit(unit, min)}`);
2005
+ }
2006
+ if (max !== undefined) {
2007
+ commands.push(`EXPECT MAX ${max} ${formatExpectationUnit(unit, max)}`);
2008
+ }
2009
+ return commands;
2010
+ }
2011
+ /**
2012
+ * Formats the expectation unit exactly as the legacy serializer does.
2013
+ *
2014
+ * @private internal utility of `createTaskSerialization`
2015
+ */
2016
+ function formatExpectationUnit(unit, amount) {
2017
+ return capitalize(unit + (amount > 1 ? 's' : ''));
2018
+ }
2019
+ /**
2020
+ * Collects format commands.
2021
+ *
2022
+ * @private internal utility of `createTaskSerialization`
2023
+ */
2024
+ function createFormatCommands(task) {
2025
+ if (task.format === 'JSON') {
2026
+ // TODO: @deprecated remove
2027
+ return ['FORMAT JSON'];
2028
+ }
2029
+ return [];
2030
+ }
1920
2031
 
1921
- ## ${title}
2032
+ /**
2033
+ * Stringifies one task section of the pipeline.
2034
+ *
2035
+ * @private internal utility of `pipelineJsonToString`
2036
+ */
2037
+ function stringifyTask(task) {
2038
+ const { title, description, content, resultingParameterName } = task;
2039
+ const { commands, contentLanguage } = createTaskSerialization(task);
2040
+ return spaceTrim$1((block) => `
2041
+ ## ${title}
1922
2042
 
1923
- ${block(description || '')}
2043
+ ${block(description || '')}
1924
2044
 
1925
- ${block(commands.map((command) => `- ${command}`).join('\n'))}
2045
+ ${block(stringifyCommands(commands))}
1926
2046
 
1927
- \`\`\`${contentLanguage}
1928
- ${block(spaceTrim$1(content))}
1929
- \`\`\`
2047
+ \`\`\`${contentLanguage}
2048
+ ${block(spaceTrim$1(content))}
2049
+ \`\`\`
1930
2050
 
1931
- \`-> {${resultingParameterName}}\`
1932
- `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
1933
- // <- TODO: [main] !!3 Escape
1934
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
1935
- }
1936
- return validatePipelineString(pipelineString);
2051
+ \`-> {${resultingParameterName}}\`
2052
+ `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
2053
+ // <- TODO: [main] !!3 Escape
2054
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
1937
2055
  }
2056
+
1938
2057
  /**
1939
- * Handles task parameter Json to string.
2058
+ * Converts promptbook in JSON format to string format
1940
2059
  *
1941
- * @private internal utility of `pipelineJsonToString`
2060
+ * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
2061
+ * @param pipelineJson Promptbook in JSON format (.bookc)
2062
+ * @returns Promptbook in string format (.book.md)
2063
+ *
2064
+ * @public exported from `@promptbook/core`
1942
2065
  */
1943
- function taskParameterJsonToString(taskParameterJson) {
1944
- const { name, description } = taskParameterJson;
1945
- let parameterString = `{${name}}`;
1946
- if (description) {
1947
- parameterString = `${parameterString} ${description}`;
2066
+ function pipelineJsonToString(pipelineJson) {
2067
+ let pipelineString = createPipelineIntroduction(pipelineJson);
2068
+ const pipelineCommands = createPipelineCommands(pipelineJson);
2069
+ pipelineString = appendMarkdownBlock(pipelineString, stringifyCommands(pipelineCommands));
2070
+ for (const task of pipelineJson.tasks) {
2071
+ pipelineString = appendMarkdownBlock(pipelineString, stringifyTask(task));
1948
2072
  }
1949
- return parameterString;
2073
+ return validatePipelineString(pipelineString);
1950
2074
  }
1951
2075
  // TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
1952
2076
  // TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
@@ -2386,233 +2510,519 @@ function validatePipeline(pipeline) {
2386
2510
  */
2387
2511
  function validatePipeline_InnerFunction(pipeline) {
2388
2512
  // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
2389
- const pipelineIdentification = (() => {
2390
- // Note: This is a 😐 implementation of [🚞]
2391
- const _ = [];
2392
- if (pipeline.sourceFile !== undefined) {
2393
- _.push(`File: ${pipeline.sourceFile}`);
2394
- }
2395
- if (pipeline.pipelineUrl !== undefined) {
2396
- _.push(`Url: ${pipeline.pipelineUrl}`);
2513
+ const context = createPipelineValidationContext(pipeline);
2514
+ validatePipelineMetadata(context);
2515
+ validatePipelineCollectionsStructure(context);
2516
+ validatePipelineParameters(context);
2517
+ validatePipelineTasks(context);
2518
+ validatePipelineDependencyResolution(context);
2519
+ // Note: Check that formfactor is corresponding to the pipeline interface
2520
+ // TODO: !!6 Implement this
2521
+ // pipeline.formfactorName
2522
+ }
2523
+ /**
2524
+ * Creates the shared validation context for one pipeline.
2525
+ *
2526
+ * @private internal utility of `validatePipeline`
2527
+ */
2528
+ function createPipelineValidationContext(pipeline) {
2529
+ return {
2530
+ pipeline,
2531
+ pipelineIdentification: getPipelineIdentification(pipeline),
2532
+ };
2533
+ }
2534
+ /**
2535
+ * Builds a short file/url identification block for validation errors.
2536
+ *
2537
+ * @private internal utility of `validatePipeline`
2538
+ */
2539
+ function getPipelineIdentification(pipeline) {
2540
+ // Note: This is a 😐 implementation of [🚞]
2541
+ const pipelineIdentificationParts = [];
2542
+ if (pipeline.sourceFile !== undefined) {
2543
+ pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
2544
+ }
2545
+ if (pipeline.pipelineUrl !== undefined) {
2546
+ pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
2547
+ }
2548
+ return pipelineIdentificationParts.join('\n');
2549
+ }
2550
+ /**
2551
+ * Validates pipeline-level metadata fields.
2552
+ *
2553
+ * @private internal step of `validatePipeline`
2554
+ */
2555
+ function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
2556
+ validatePipelineUrl(pipeline, pipelineIdentification);
2557
+ validatePipelineBookVersion(pipeline, pipelineIdentification);
2558
+ }
2559
+ /**
2560
+ * Validates that the expected top-level collections have array structure.
2561
+ *
2562
+ * @private internal step of `validatePipeline`
2563
+ */
2564
+ function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification }) {
2565
+ validatePipelineParametersCollection(pipeline, pipelineIdentification);
2566
+ validatePipelineTasksCollection(pipeline, pipelineIdentification);
2567
+ }
2568
+ /**
2569
+ * Validates all pipeline parameter declarations.
2570
+ *
2571
+ * @private internal step of `validatePipeline`
2572
+ */
2573
+ function validatePipelineParameters({ pipeline, pipelineIdentification }) {
2574
+ for (const parameter of pipeline.parameters) {
2575
+ validatePipelineParameter(parameter, pipeline, pipelineIdentification);
2576
+ }
2577
+ }
2578
+ /**
2579
+ * Validates all pipeline tasks and their per-task invariants.
2580
+ *
2581
+ * @private internal step of `validatePipeline`
2582
+ */
2583
+ function validatePipelineTasks({ pipeline, pipelineIdentification }) {
2584
+ // Note: All input parameters are defined - so that they can be used as result of some task
2585
+ const definedParameters = createInitiallyDefinedParameters(pipeline);
2586
+ for (const task of pipeline.tasks) {
2587
+ validatePipelineTask(task, definedParameters, pipelineIdentification);
2588
+ }
2589
+ }
2590
+ /**
2591
+ * Validates that task dependencies can be resolved without cycles or missing definitions.
2592
+ *
2593
+ * @private internal step of `validatePipeline`
2594
+ */
2595
+ function validatePipelineDependencyResolution({ pipeline, pipelineIdentification }) {
2596
+ let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
2597
+ let loopLimit = LOOP_LIMIT;
2598
+ while (hasUnresolvedTasks(dependencyResolutionState)) {
2599
+ if (loopLimit-- < 0) {
2600
+ throw createDependencyResolutionLoopLimitError(pipelineIdentification);
2397
2601
  }
2398
- return _.join('\n');
2399
- })();
2400
- if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
2401
- // <- Note: [🚲]
2402
- throw new PipelineLogicError(spaceTrim$1((block) => `
2403
- Invalid promptbook URL "${pipeline.pipelineUrl}"
2404
-
2405
- ${block(pipelineIdentification)}
2406
- `));
2602
+ dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
2407
2603
  }
2408
- if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
2409
- // <- Note: [🚲]
2410
- throw new PipelineLogicError(spaceTrim$1((block) => `
2411
- Invalid Promptbook Version "${pipeline.bookVersion}"
2604
+ }
2605
+ /**
2606
+ * Validates one pipeline parameter declaration.
2607
+ *
2608
+ * @private internal step of `validatePipeline`
2609
+ */
2610
+ function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
2611
+ validateParameterDirection(parameter, pipelineIdentification);
2612
+ validateParameterUsage(parameter, pipeline, pipelineIdentification);
2613
+ validateParameterDefinition(parameter, pipeline, pipelineIdentification);
2614
+ }
2615
+ /**
2616
+ * Validates one pipeline task and its invariants.
2617
+ *
2618
+ * @private internal step of `validatePipeline`
2619
+ */
2620
+ function validatePipelineTask(task, definedParameters, pipelineIdentification) {
2621
+ validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
2622
+ validateTaskJokers(task, pipelineIdentification);
2623
+ validateTaskExpectations(task, pipelineIdentification);
2624
+ }
2625
+ /**
2626
+ * Validates the pipeline URL, when present.
2627
+ *
2628
+ * @private internal utility of `validatePipeline`
2629
+ */
2630
+ function validatePipelineUrl(pipeline, pipelineIdentification) {
2631
+ if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
2632
+ return;
2633
+ }
2634
+ // <- Note: [🚲]
2635
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2636
+ Invalid promptbook URL "${pipeline.pipelineUrl}"
2412
2637
 
2413
- ${block(pipelineIdentification)}
2414
- `));
2638
+ ${block(pipelineIdentification)}
2639
+ `));
2640
+ }
2641
+ /**
2642
+ * Validates the Promptbook version, when present.
2643
+ *
2644
+ * @private internal utility of `validatePipeline`
2645
+ */
2646
+ function validatePipelineBookVersion(pipeline, pipelineIdentification) {
2647
+ if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
2648
+ return;
2415
2649
  }
2650
+ // <- Note: [🚲]
2651
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2652
+ Invalid Promptbook Version "${pipeline.bookVersion}"
2653
+
2654
+ ${block(pipelineIdentification)}
2655
+ `));
2656
+ }
2657
+ /**
2658
+ * Validates that `pipeline.parameters` is an array.
2659
+ *
2660
+ * @private internal utility of `validatePipeline`
2661
+ */
2662
+ function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
2416
2663
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
2417
- if (!Array.isArray(pipeline.parameters)) {
2418
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2419
- throw new ParseError(spaceTrim$1((block) => `
2420
- Pipeline is valid JSON but with wrong structure
2664
+ if (Array.isArray(pipeline.parameters)) {
2665
+ return;
2666
+ }
2667
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2668
+ throw new ParseError(spaceTrim$1((block) => `
2669
+ Pipeline is valid JSON but with wrong structure
2421
2670
 
2422
- \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
2671
+ \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
2423
2672
 
2424
- ${block(pipelineIdentification)}
2425
- `));
2426
- }
2673
+ ${block(pipelineIdentification)}
2674
+ `));
2675
+ }
2676
+ /**
2677
+ * Validates that `pipeline.tasks` is an array.
2678
+ *
2679
+ * @private internal utility of `validatePipeline`
2680
+ */
2681
+ function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
2427
2682
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
2428
- if (!Array.isArray(pipeline.tasks)) {
2429
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2430
- throw new ParseError(spaceTrim$1((block) => `
2431
- Pipeline is valid JSON but with wrong structure
2683
+ if (Array.isArray(pipeline.tasks)) {
2684
+ return;
2685
+ }
2686
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2687
+ throw new ParseError(spaceTrim$1((block) => `
2688
+ Pipeline is valid JSON but with wrong structure
2432
2689
 
2433
- \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
2690
+ \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
2434
2691
 
2435
- ${block(pipelineIdentification)}
2436
- `));
2692
+ ${block(pipelineIdentification)}
2693
+ `));
2694
+ }
2695
+ /**
2696
+ * Validates that one parameter does not declare incompatible directions.
2697
+ *
2698
+ * @private internal utility of `validatePipeline`
2699
+ */
2700
+ function validateParameterDirection(parameter, pipelineIdentification) {
2701
+ if (!parameter.isInput || !parameter.isOutput) {
2702
+ return;
2437
2703
  }
2438
- /*
2439
- TODO: [🧠][🅾] Should be empty pipeline valid or not
2440
- // Note: Check that pipeline has some tasks
2441
- if (pipeline.tasks.length === 0) {
2442
- throw new PipelineLogicError(
2443
- spaceTrim(
2444
- (block) => `
2445
- Pipeline must have at least one task
2704
+ const parameterName = parameter.name;
2705
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2446
2706
 
2447
- ${block(pipelineIdentification)}
2448
- `,
2449
- ),
2450
- );
2451
- }
2452
- */
2453
- // Note: Check each parameter individually
2454
- for (const parameter of pipeline.parameters) {
2455
- if (parameter.isInput && parameter.isOutput) {
2456
- throw new PipelineLogicError(spaceTrim$1((block) => `
2707
+ Parameter \`{${parameterName}}\` can not be both input and output
2457
2708
 
2458
- Parameter \`{${parameter.name}}\` can not be both input and output
2709
+ ${block(pipelineIdentification)}
2710
+ `));
2711
+ }
2712
+ /**
2713
+ * Validates that one intermediate parameter is actually consumed by at least one task.
2714
+ *
2715
+ * @private internal utility of `validatePipeline`
2716
+ */
2717
+ function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
2718
+ if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
2719
+ return;
2720
+ }
2721
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2722
+ Parameter \`{${parameter.name}}\` is created but not used
2459
2723
 
2460
- ${block(pipelineIdentification)}
2461
- `));
2462
- }
2463
- // Note: Testing that parameter is either intermediate or output BUT not created and unused
2464
- if (!parameter.isInput &&
2465
- !parameter.isOutput &&
2466
- !pipeline.tasks.some((task) => task.dependentParameterNames.includes(parameter.name))) {
2467
- throw new PipelineLogicError(spaceTrim$1((block) => `
2468
- Parameter \`{${parameter.name}}\` is created but not used
2724
+ You can declare {${parameter.name}} as output parameter by adding in the header:
2725
+ - OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
2469
2726
 
2470
- You can declare {${parameter.name}} as output parameter by adding in the header:
2471
- - OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
2727
+ ${block(pipelineIdentification)}
2472
2728
 
2473
- ${block(pipelineIdentification)}
2729
+ `));
2730
+ }
2731
+ /**
2732
+ * Validates that one non-input parameter is produced by at least one task.
2733
+ *
2734
+ * @private internal utility of `validatePipeline`
2735
+ */
2736
+ function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
2737
+ if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
2738
+ return;
2739
+ }
2740
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2741
+ Parameter \`{${parameter.name}}\` is declared but not defined
2474
2742
 
2475
- `));
2476
- }
2477
- // Note: Testing that parameter is either input or result of some task
2478
- if (!parameter.isInput && !pipeline.tasks.some((task) => task.resultingParameterName === parameter.name)) {
2479
- throw new PipelineLogicError(spaceTrim$1((block) => `
2480
- Parameter \`{${parameter.name}}\` is declared but not defined
2743
+ You can do one of these:
2744
+ 1) Remove declaration of \`{${parameter.name}}\`
2745
+ 2) Add task that results in \`-> {${parameter.name}}\`
2481
2746
 
2482
- You can do one of these:
2483
- 1) Remove declaration of \`{${parameter.name}}\`
2484
- 2) Add task that results in \`-> {${parameter.name}}\`
2747
+ ${block(pipelineIdentification)}
2748
+ `));
2749
+ }
2750
+ /**
2751
+ * Checks whether one parameter is consumed by at least one task dependency list.
2752
+ *
2753
+ * @private internal utility of `validatePipeline`
2754
+ */
2755
+ function isParameterUsedByAnyTask(parameter, tasks) {
2756
+ return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
2757
+ }
2758
+ /**
2759
+ * Checks whether one parameter is produced by at least one task.
2760
+ *
2761
+ * @private internal utility of `validatePipeline`
2762
+ */
2763
+ function isParameterDefinedByAnyTask(parameter, tasks) {
2764
+ return tasks.some((task) => task.resultingParameterName === parameter.name);
2765
+ }
2766
+ /**
2767
+ * Collects the parameter names that are already defined before task validation starts.
2768
+ *
2769
+ * @private internal utility of `validatePipeline`
2770
+ */
2771
+ function createInitiallyDefinedParameters(pipeline) {
2772
+ return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
2773
+ }
2774
+ /**
2775
+ * Validates one task result parameter declaration and marks it as defined.
2776
+ *
2777
+ * @private internal utility of `validatePipeline`
2778
+ */
2779
+ function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
2780
+ if (definedParameters.has(task.resultingParameterName)) {
2781
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2782
+ Parameter \`{${task.resultingParameterName}}\` is defined multiple times
2485
2783
 
2486
- ${block(pipelineIdentification)}
2487
- `));
2488
- }
2784
+ ${block(pipelineIdentification)}
2785
+ `));
2489
2786
  }
2490
- // Note: All input parameters are defined - so that they can be used as result of some task
2491
- const definedParameters = new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
2492
- // Note: Checking each task individually
2493
- for (const task of pipeline.tasks) {
2494
- if (definedParameters.has(task.resultingParameterName)) {
2495
- throw new PipelineLogicError(spaceTrim$1((block) => `
2496
- Parameter \`{${task.resultingParameterName}}\` is defined multiple times
2787
+ if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
2788
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2789
+ Parameter name {${task.resultingParameterName}} is reserved, please use different name
2497
2790
 
2498
- ${block(pipelineIdentification)}
2499
- `));
2500
- }
2501
- if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
2502
- throw new PipelineLogicError(spaceTrim$1((block) => `
2503
- Parameter name {${task.resultingParameterName}} is reserved, please use different name
2791
+ ${block(pipelineIdentification)}
2792
+ `));
2793
+ }
2794
+ definedParameters.add(task.resultingParameterName);
2795
+ }
2796
+ /**
2797
+ * Validates joker parameters for one task.
2798
+ *
2799
+ * @private internal utility of `validatePipeline`
2800
+ */
2801
+ function validateTaskJokers(task, pipelineIdentification) {
2802
+ if (!hasTaskJokers(task)) {
2803
+ return;
2804
+ }
2805
+ validateTaskSupportsJokers(task, pipelineIdentification);
2806
+ validateTaskJokerDependencies(task, pipelineIdentification);
2807
+ }
2808
+ /**
2809
+ * Checks whether one task declares any joker parameters.
2810
+ *
2811
+ * @private internal utility of `validatePipeline`
2812
+ */
2813
+ function hasTaskJokers(task) {
2814
+ return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
2815
+ }
2816
+ /**
2817
+ * Validates that a task has the required supporting features when using jokers.
2818
+ *
2819
+ * @private internal utility of `validatePipeline`
2820
+ */
2821
+ function validateTaskSupportsJokers(task, pipelineIdentification) {
2822
+ if (task.format ||
2823
+ task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
2824
+ return;
2825
+ }
2826
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2827
+ Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
2504
2828
 
2505
- ${block(pipelineIdentification)}
2506
- `));
2829
+ ${block(pipelineIdentification)}
2830
+ `));
2831
+ }
2832
+ /**
2833
+ * Validates that every joker parameter is also listed among task dependencies.
2834
+ *
2835
+ * @private internal utility of `validatePipeline`
2836
+ */
2837
+ function validateTaskJokerDependencies(task, pipelineIdentification) {
2838
+ for (const joker of task.jokerParameterNames) {
2839
+ if (task.dependentParameterNames.includes(joker)) {
2840
+ continue;
2507
2841
  }
2508
- definedParameters.add(task.resultingParameterName);
2509
- if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
2510
- if (!task.format &&
2511
- !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
2512
- throw new PipelineLogicError(spaceTrim$1((block) => `
2513
- Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
2514
-
2515
- ${block(pipelineIdentification)}
2516
- `));
2517
- }
2518
- for (const joker of task.jokerParameterNames) {
2519
- if (!task.dependentParameterNames.includes(joker)) {
2520
- throw new PipelineLogicError(spaceTrim$1((block) => `
2521
- Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
2842
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2843
+ Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
2522
2844
 
2523
- ${block(pipelineIdentification)}
2524
- `));
2525
- }
2526
- }
2527
- }
2528
- if (task.expectations) {
2529
- for (const [unit, { min, max }] of Object.entries(task.expectations)) {
2530
- if (min !== undefined && max !== undefined && min > max) {
2531
- throw new PipelineLogicError(spaceTrim$1((block) => `
2532
- Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
2845
+ ${block(pipelineIdentification)}
2846
+ `));
2847
+ }
2848
+ }
2849
+ /**
2850
+ * Validates all expectation bounds configured on one task.
2851
+ *
2852
+ * @private internal utility of `validatePipeline`
2853
+ */
2854
+ function validateTaskExpectations(task, pipelineIdentification) {
2855
+ if (!task.expectations) {
2856
+ return;
2857
+ }
2858
+ for (const [unit, { min, max }] of Object.entries(task.expectations)) {
2859
+ validateTaskExpectationRange(unit, min, max, pipelineIdentification);
2860
+ validateTaskExpectationMin(unit, min, pipelineIdentification);
2861
+ validateTaskExpectationMax(unit, max, pipelineIdentification);
2862
+ }
2863
+ }
2864
+ /**
2865
+ * Validates the minimum and maximum expectation ordering for one unit.
2866
+ *
2867
+ * @private internal utility of `validatePipeline`
2868
+ */
2869
+ function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
2870
+ if (min === undefined || max === undefined || min <= max) {
2871
+ return;
2872
+ }
2873
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2874
+ Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
2533
2875
 
2534
- ${block(pipelineIdentification)}
2535
- `));
2536
- }
2537
- if (min !== undefined && min < 0) {
2538
- throw new PipelineLogicError(spaceTrim$1((block) => `
2539
- Min expectation of ${unit} must be zero or positive
2876
+ ${block(pipelineIdentification)}
2877
+ `));
2878
+ }
2879
+ /**
2880
+ * Validates the minimum expectation bound for one unit.
2881
+ *
2882
+ * @private internal utility of `validatePipeline`
2883
+ */
2884
+ function validateTaskExpectationMin(unit, min, pipelineIdentification) {
2885
+ if (min === undefined || min >= 0) {
2886
+ return;
2887
+ }
2888
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2889
+ Min expectation of ${unit} must be zero or positive
2540
2890
 
2541
- ${block(pipelineIdentification)}
2542
- `));
2543
- }
2544
- if (max !== undefined && max <= 0) {
2545
- throw new PipelineLogicError(spaceTrim$1((block) => `
2546
- Max expectation of ${unit} must be positive
2891
+ ${block(pipelineIdentification)}
2892
+ `));
2893
+ }
2894
+ /**
2895
+ * Validates the maximum expectation bound for one unit.
2896
+ *
2897
+ * @private internal utility of `validatePipeline`
2898
+ */
2899
+ function validateTaskExpectationMax(unit, max, pipelineIdentification) {
2900
+ if (max === undefined || max > 0) {
2901
+ return;
2902
+ }
2903
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2904
+ Max expectation of ${unit} must be positive
2547
2905
 
2548
- ${block(pipelineIdentification)}
2549
- `));
2550
- }
2551
- }
2552
- }
2906
+ ${block(pipelineIdentification)}
2907
+ `));
2908
+ }
2909
+ /**
2910
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
2911
+ *
2912
+ * @private internal utility of `validatePipeline`
2913
+ */
2914
+ function createInitialDependencyResolutionState(pipeline) {
2915
+ return {
2916
+ resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
2917
+ unresolvedTasks: [...pipeline.tasks],
2918
+ };
2919
+ }
2920
+ /**
2921
+ * Checks whether dependency resolution still has tasks left to process.
2922
+ *
2923
+ * @private internal utility of `validatePipeline`
2924
+ */
2925
+ function hasUnresolvedTasks({ unresolvedTasks }) {
2926
+ return unresolvedTasks.length > 0;
2927
+ }
2928
+ /**
2929
+ * Resolves the next batch of currently satisfiable tasks.
2930
+ *
2931
+ * @private internal utility of `validatePipeline`
2932
+ */
2933
+ function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
2934
+ const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
2935
+ if (currentlyResolvedTasks.length === 0) {
2936
+ throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
2553
2937
  }
2554
- // Note: Detect circular dependencies
2555
- let resovedParameters = pipeline.parameters
2938
+ return {
2939
+ resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
2940
+ unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
2941
+ };
2942
+ }
2943
+ /**
2944
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
2945
+ *
2946
+ * @private internal utility of `validatePipeline`
2947
+ */
2948
+ function createInitiallyResolvedParameterNames(pipeline) {
2949
+ let resolvedParameterNames = pipeline.parameters
2556
2950
  .filter(({ isInput }) => isInput)
2557
2951
  .map(({ name }) => name);
2558
- // Note: All reserved parameters are resolved
2559
2952
  for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
2560
- resovedParameters = [...resovedParameters, reservedParameterName];
2953
+ resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
2561
2954
  }
2562
- let unresovedTasks = [...pipeline.tasks];
2563
- let loopLimit = LOOP_LIMIT;
2564
- while (unresovedTasks.length > 0) {
2565
- if (loopLimit-- < 0) {
2566
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
2567
- throw new UnexpectedError(spaceTrim$1((block) => `
2568
- Loop limit reached during detection of circular dependencies in \`validatePipeline\`
2955
+ return resolvedParameterNames;
2956
+ }
2957
+ /**
2958
+ * Adds newly resolved task outputs to the resolved parameter list.
2959
+ *
2960
+ * @private internal utility of `validatePipeline`
2961
+ */
2962
+ function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
2963
+ return [
2964
+ ...resolvedParameterNames,
2965
+ ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
2966
+ ];
2967
+ }
2968
+ /**
2969
+ * Selects tasks whose dependencies are already resolved.
2970
+ *
2971
+ * @private internal utility of `validatePipeline`
2972
+ */
2973
+ function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
2974
+ return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
2975
+ }
2976
+ /**
2977
+ * Creates the unexpected loop-limit error for dependency resolution.
2978
+ *
2979
+ * @private internal utility of `validatePipeline`
2980
+ */
2981
+ function createDependencyResolutionLoopLimitError(pipelineIdentification) {
2982
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
2983
+ return new UnexpectedError(spaceTrim$1((block) => `
2984
+ Loop limit reached during detection of circular dependencies in \`validatePipeline\`
2569
2985
 
2570
- ${block(pipelineIdentification)}
2571
- `));
2572
- }
2573
- const currentlyResovedTasks = unresovedTasks.filter((task) => task.dependentParameterNames.every((name) => resovedParameters.includes(name)));
2574
- if (currentlyResovedTasks.length === 0) {
2575
- throw new PipelineLogicError(
2576
- // TODO: [🐎] DRY
2577
- spaceTrim$1((block) => `
2986
+ ${block(pipelineIdentification)}
2987
+ `));
2988
+ }
2989
+ /**
2990
+ * Creates the detailed error for unresolved or circular task dependencies.
2991
+ *
2992
+ * @private internal utility of `validatePipeline`
2993
+ */
2994
+ function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
2995
+ return new PipelineLogicError(
2996
+ // TODO: [🐎] DRY
2997
+ spaceTrim$1((block) => `
2578
2998
 
2579
- Can not resolve some parameters:
2580
- Either you are using a parameter that is not defined, or there are some circular dependencies.
2999
+ Can not resolve some parameters:
3000
+ Either you are using a parameter that is not defined, or there are some circular dependencies.
2581
3001
 
2582
- ${block(pipelineIdentification)}
3002
+ ${block(pipelineIdentification)}
2583
3003
 
2584
- **Can not resolve:**
2585
- ${block(unresovedTasks
2586
- .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
2587
- .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
2588
- .join(' and ')}`)
2589
- .join('\n'))}
3004
+ **Can not resolve:**
3005
+ ${block(unresolvedTasks
3006
+ .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
3007
+ .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
3008
+ .join(' and ')}`)
3009
+ .join('\n'))}
2590
3010
 
2591
- **Resolved:**
2592
- ${block(resovedParameters
2593
- .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
2594
- .map((name) => `- Parameter \`{${name}}\``)
2595
- .join('\n'))}
3011
+ **Resolved:**
3012
+ ${block(resolvedParameterNames
3013
+ .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
3014
+ .map((name) => `- Parameter \`{${name}}\``)
3015
+ .join('\n'))}
2596
3016
 
2597
3017
 
2598
- **Reserved (which are available):**
2599
- ${block(resovedParameters
2600
- .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
2601
- .map((name) => `- Parameter \`{${name}}\``)
2602
- .join('\n'))}
3018
+ **Reserved (which are available):**
3019
+ ${block(resolvedParameterNames
3020
+ .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
3021
+ .map((name) => `- Parameter \`{${name}}\``)
3022
+ .join('\n'))}
2603
3023
 
2604
3024
 
2605
- `));
2606
- }
2607
- resovedParameters = [
2608
- ...resovedParameters,
2609
- ...currentlyResovedTasks.map(({ resultingParameterName }) => resultingParameterName),
2610
- ];
2611
- unresovedTasks = unresovedTasks.filter((task) => !currentlyResovedTasks.includes(task));
2612
- }
2613
- // Note: Check that formfactor is corresponding to the pipeline interface
2614
- // TODO: !!6 Implement this
2615
- // pipeline.formfactorName
3025
+ `));
2616
3026
  }
2617
3027
  /**
2618
3028
  * TODO: [🧞‍♀️] Do not allow joker + foreach
@@ -3290,77 +3700,288 @@ function assertsTaskSuccessful(executionResult) {
3290
3700
  // TODO: [🧠] Can this return type be better typed than void
3291
3701
 
3292
3702
  /**
3293
- * Helper to create a new task
3703
+ * Resolves the short task summary shown in the UI.
3294
3704
  *
3295
- * @private internal helper function
3705
+ * @private internal helper function of `ExecutionTask`
3296
3706
  */
3297
- function createTask(options) {
3298
- const { taskType, taskProcessCallback } = options;
3299
- let { title } = options;
3300
- // TODO: [🐙] DRY
3301
- const taskId = `${taskType.toLowerCase().substring(0, 4)}-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
3302
- let status = 'RUNNING';
3303
- const createdAt = new Date();
3304
- let updatedAt = createdAt;
3305
- const errors = [];
3306
- const warnings = [];
3307
- const llmCalls = [];
3308
- let currentValue = {};
3309
- let customTldr = null;
3310
- const partialResultSubject = new Subject();
3311
- // <- Note: Not using `BehaviorSubject` because on error we can't access the last value
3312
- const finalResultPromise = /* not await */ taskProcessCallback((newOngoingResult) => {
3313
- if (newOngoingResult.title) {
3314
- title = newOngoingResult.title;
3315
- }
3316
- updatedAt = new Date();
3317
- Object.assign(currentValue, newOngoingResult);
3318
- // <- TODO: assign deep
3319
- partialResultSubject.next(newOngoingResult);
3320
- }, (tldrInfo) => {
3321
- customTldr = tldrInfo;
3322
- updatedAt = new Date();
3323
- }, (llmCall) => {
3324
- llmCalls.push(llmCall);
3325
- updatedAt = new Date();
3326
- });
3327
- finalResultPromise
3328
- .catch((error) => {
3329
- errors.push(error);
3330
- partialResultSubject.error(error);
3331
- })
3332
- .then((executionResult) => {
3333
- if (executionResult) {
3334
- try {
3335
- updatedAt = new Date();
3336
- errors.push(...executionResult.errors);
3337
- warnings.push(...executionResult.warnings);
3338
- // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
3339
- // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
3340
- // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
3341
- // And delete `ExecutionTask.currentValue.preparedPipeline`
3342
- assertsTaskSuccessful(executionResult);
3343
- status = 'FINISHED';
3344
- currentValue = jsonStringsToJsons(executionResult);
3345
- // <- TODO: [🧠] Is this a good idea to convert JSON strins to JSONs?
3346
- partialResultSubject.next(executionResult);
3347
- }
3348
- catch (error) {
3349
- assertsError(error);
3350
- status = 'ERROR';
3351
- errors.push(error);
3352
- partialResultSubject.error(error);
3353
- }
3354
- }
3355
- partialResultSubject.complete();
3356
- });
3357
- async function asPromise(options) {
3358
- const { isCrashedOnError = true } = options || {};
3359
- const finalResult = await finalResultPromise;
3360
- if (isCrashedOnError) {
3361
- assertsTaskSuccessful(finalResult);
3362
- }
3363
- return finalResult;
3707
+ function resolveTaskTldr(options) {
3708
+ const { customTldr } = options;
3709
+ if (customTldr) {
3710
+ return customTldr;
3711
+ }
3712
+ return {
3713
+ percent: resolveTaskPercent(options),
3714
+ message: `${resolveTaskMessage(options)} (!!!fallback)`,
3715
+ };
3716
+ }
3717
+ /**
3718
+ * Resolves the best progress percentage for the current task state.
3719
+ *
3720
+ * @private internal helper function of `ExecutionTask`
3721
+ */
3722
+ function resolveTaskPercent(options) {
3723
+ const explicitPercent = getExplicitTaskPercent(options.currentValue);
3724
+ if (typeof explicitPercent === 'number') {
3725
+ return normalizeTaskPercent(explicitPercent);
3726
+ }
3727
+ return normalizeTaskPercent(calculateSimulatedTaskPercent(options));
3728
+ }
3729
+ /**
3730
+ * Picks a directly reported progress percentage from the task result snapshot.
3731
+ *
3732
+ * @private internal helper function of `ExecutionTask`
3733
+ */
3734
+ function getExplicitTaskPercent(currentValue) {
3735
+ var _a, _b, _c, _d, _e, _f;
3736
+ return ((_f = (_d = (_b = (_a = currentValue === null || currentValue === void 0 ? void 0 : currentValue.tldr) === null || _a === void 0 ? void 0 : _a.percent) !== null && _b !== void 0 ? _b : (_c = currentValue === null || currentValue === void 0 ? void 0 : currentValue.usage) === null || _c === void 0 ? void 0 : _c.percent) !== null && _d !== void 0 ? _d : (_e = currentValue === null || currentValue === void 0 ? void 0 : currentValue.progress) === null || _e === void 0 ? void 0 : _e.percent) !== null && _f !== void 0 ? _f : currentValue === null || currentValue === void 0 ? void 0 : currentValue.percent);
3737
+ }
3738
+ /**
3739
+ * Simulates progress when the task result does not expose an explicit percentage.
3740
+ *
3741
+ * @private internal helper function of `ExecutionTask`
3742
+ */
3743
+ function calculateSimulatedTaskPercent(options) {
3744
+ const { currentValue, status, createdAt } = options;
3745
+ const elapsedMs = new Date().getTime() - createdAt.getTime();
3746
+ const timeProgress = Math.min(elapsedMs / DEFAULT_TASK_SIMULATED_DURATION_MS, 1);
3747
+ const { subtaskCount, completedSubtasks } = summarizeTaskSubtasks(currentValue);
3748
+ if (status === 'FINISHED') {
3749
+ return 1;
3750
+ }
3751
+ if (status === 'ERROR') {
3752
+ return 0;
3753
+ }
3754
+ return Math.min(completedSubtasks / subtaskCount + (1 / subtaskCount) * timeProgress, 1);
3755
+ }
3756
+ /**
3757
+ * Counts total and completed subtasks used by the fallback progress simulation.
3758
+ *
3759
+ * @private internal helper function of `ExecutionTask`
3760
+ */
3761
+ function summarizeTaskSubtasks(currentValue) {
3762
+ if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks)) {
3763
+ return { subtaskCount: 1, completedSubtasks: 0 };
3764
+ }
3765
+ return {
3766
+ subtaskCount: currentValue.subtasks.length || 1,
3767
+ completedSubtasks: currentValue.subtasks.filter(isTaskSubtaskCompleted).length,
3768
+ };
3769
+ }
3770
+ /**
3771
+ * Tells whether a task subtask is already finished.
3772
+ *
3773
+ * @private internal helper function of `ExecutionTask`
3774
+ */
3775
+ function isTaskSubtaskCompleted(subtask) {
3776
+ return subtask.done || subtask.completed || false;
3777
+ }
3778
+ /**
3779
+ * Normalizes a progress percentage into the expected `0..1` range.
3780
+ *
3781
+ * @private internal helper function of `ExecutionTask`
3782
+ */
3783
+ function normalizeTaskPercent(percentRaw) {
3784
+ let percent = Number(percentRaw) || 0;
3785
+ if (percent < 0) {
3786
+ percent = 0;
3787
+ }
3788
+ if (percent > 1) {
3789
+ percent = 1;
3790
+ }
3791
+ return percent;
3792
+ }
3793
+ /**
3794
+ * Resolves the best human-readable status message for the current task state.
3795
+ *
3796
+ * @private internal helper function of `ExecutionTask`
3797
+ */
3798
+ function resolveTaskMessage(options) {
3799
+ return (getCurrentValueMessage(options.currentValue) ||
3800
+ getCurrentSubtaskMessage(options.currentValue) ||
3801
+ getLatestIssueMessage(options.errors, 'Error') ||
3802
+ getLatestIssueMessage(options.warnings, 'Warning') ||
3803
+ getStatusMessage(options.status));
3804
+ }
3805
+ /**
3806
+ * Picks a message already reported by the current task result snapshot.
3807
+ *
3808
+ * @private internal helper function of `ExecutionTask`
3809
+ */
3810
+ function getCurrentValueMessage(currentValue) {
3811
+ var _a, _b, _c, _d;
3812
+ return (_d = (_c = (_b = (_a = currentValue === null || currentValue === void 0 ? void 0 : currentValue.tldr) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : currentValue === null || currentValue === void 0 ? void 0 : currentValue.message) !== null && _c !== void 0 ? _c : currentValue === null || currentValue === void 0 ? void 0 : currentValue.summary) !== null && _d !== void 0 ? _d : currentValue === null || currentValue === void 0 ? void 0 : currentValue.statusMessage;
3813
+ }
3814
+ /**
3815
+ * Builds a fallback message from the first unfinished subtask title.
3816
+ *
3817
+ * @private internal helper function of `ExecutionTask`
3818
+ */
3819
+ function getCurrentSubtaskMessage(currentValue) {
3820
+ if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks) || currentValue.subtasks.length === 0) {
3821
+ return undefined;
3822
+ }
3823
+ const currentSubtask = currentValue.subtasks.find((subtask) => !isTaskSubtaskCompleted(subtask));
3824
+ if (!(currentSubtask === null || currentSubtask === void 0 ? void 0 : currentSubtask.title)) {
3825
+ return undefined;
3826
+ }
3827
+ return `Working on ${currentSubtask.title}`;
3828
+ }
3829
+ /**
3830
+ * Picks the latest error or warning message, with the legacy generic fallback label.
3831
+ *
3832
+ * @private internal helper function of `ExecutionTask`
3833
+ */
3834
+ function getLatestIssueMessage(issues, fallbackMessage) {
3835
+ if (issues.length === 0) {
3836
+ return undefined;
3837
+ }
3838
+ return issues[issues.length - 1].message || fallbackMessage;
3839
+ }
3840
+ /**
3841
+ * Builds the final status-based fallback message.
3842
+ *
3843
+ * @private internal helper function of `ExecutionTask`
3844
+ */
3845
+ function getStatusMessage(status) {
3846
+ if (status === 'FINISHED') {
3847
+ return 'Finished';
3848
+ }
3849
+ if (status === 'ERROR') {
3850
+ return 'Error';
3851
+ }
3852
+ return 'Running';
3853
+ }
3854
+
3855
+ /**
3856
+ * Creates the initial mutable state for a task.
3857
+ *
3858
+ * @private internal helper function
3859
+ */
3860
+ function createTaskState(title, createdAt) {
3861
+ return {
3862
+ title,
3863
+ status: 'RUNNING',
3864
+ updatedAt: createdAt,
3865
+ errors: [],
3866
+ warnings: [],
3867
+ llmCalls: [],
3868
+ currentValue: {},
3869
+ customTldr: null,
3870
+ };
3871
+ }
3872
+ /**
3873
+ * Creates the partial-result updater passed into the task process callback.
3874
+ *
3875
+ * @private internal helper function
3876
+ */
3877
+ function createOngoingResultUpdater(taskState, partialResultSubject) {
3878
+ return (newOngoingResult) => {
3879
+ if (newOngoingResult.title) {
3880
+ taskState.title = newOngoingResult.title;
3881
+ }
3882
+ taskState.updatedAt = new Date();
3883
+ Object.assign(taskState.currentValue, newOngoingResult);
3884
+ // <- TODO: assign deep
3885
+ partialResultSubject.next(newOngoingResult);
3886
+ };
3887
+ }
3888
+ /**
3889
+ * Creates the custom-TLDR updater passed into the task process callback.
3890
+ *
3891
+ * @private internal helper function
3892
+ */
3893
+ function createTldrUpdater(taskState) {
3894
+ return (tldrInfo) => {
3895
+ taskState.customTldr = tldrInfo;
3896
+ taskState.updatedAt = new Date();
3897
+ };
3898
+ }
3899
+ /**
3900
+ * Creates the LLM call logger passed into the task process callback.
3901
+ *
3902
+ * @private internal helper function
3903
+ */
3904
+ function createLlmCallLogger(taskState) {
3905
+ return (llmCall) => {
3906
+ taskState.llmCalls.push(llmCall);
3907
+ taskState.updatedAt = new Date();
3908
+ };
3909
+ }
3910
+ /**
3911
+ * Wires the task promise into the observable/error lifecycle.
3912
+ *
3913
+ * @private internal helper function
3914
+ */
3915
+ function settleTaskPromise(finalResultPromise, taskState, partialResultSubject) {
3916
+ finalResultPromise
3917
+ .catch((error) => {
3918
+ taskState.errors.push(error);
3919
+ partialResultSubject.error(error);
3920
+ })
3921
+ .then((executionResult) => {
3922
+ if (executionResult) {
3923
+ try {
3924
+ finalizeTaskResult(executionResult, taskState, partialResultSubject);
3925
+ }
3926
+ catch (error) {
3927
+ failTaskResult(error, taskState, partialResultSubject);
3928
+ }
3929
+ }
3930
+ partialResultSubject.complete();
3931
+ });
3932
+ }
3933
+ /**
3934
+ * Applies the final successful task result into the mutable task state.
3935
+ *
3936
+ * @private internal helper function
3937
+ */
3938
+ function finalizeTaskResult(executionResult, taskState, partialResultSubject) {
3939
+ taskState.updatedAt = new Date();
3940
+ taskState.errors.push(...executionResult.errors);
3941
+ taskState.warnings.push(...executionResult.warnings);
3942
+ // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
3943
+ // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
3944
+ // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
3945
+ // And delete `ExecutionTask.currentValue.preparedPipeline`
3946
+ assertsTaskSuccessful(executionResult);
3947
+ taskState.status = 'FINISHED';
3948
+ taskState.currentValue = jsonStringsToJsons(executionResult);
3949
+ // <- TODO: [🧠] Is this a good idea to convert JSON strins to JSONs?
3950
+ partialResultSubject.next(executionResult);
3951
+ }
3952
+ /**
3953
+ * Records a final-result failure after the task promise itself resolved.
3954
+ *
3955
+ * @private internal helper function
3956
+ */
3957
+ function failTaskResult(error, taskState, partialResultSubject) {
3958
+ assertsError(error);
3959
+ taskState.status = 'ERROR';
3960
+ taskState.errors.push(error);
3961
+ partialResultSubject.error(error);
3962
+ }
3963
+ /**
3964
+ * Helper to create a new task
3965
+ *
3966
+ * @private internal helper function
3967
+ */
3968
+ function createTask(options) {
3969
+ const { taskType, title, taskProcessCallback } = options;
3970
+ // TODO: [🐙] DRY
3971
+ const taskId = `${taskType.toLowerCase().substring(0, 4)}-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
3972
+ const createdAt = new Date();
3973
+ const taskState = createTaskState(title, createdAt);
3974
+ const partialResultSubject = new Subject();
3975
+ // <- Note: Not using `BehaviorSubject` because on error we can't access the last value
3976
+ const finalResultPromise = /* not await */ taskProcessCallback(createOngoingResultUpdater(taskState, partialResultSubject), createTldrUpdater(taskState), createLlmCallLogger(taskState));
3977
+ settleTaskPromise(finalResultPromise, taskState, partialResultSubject);
3978
+ async function asPromise(options) {
3979
+ const { isCrashedOnError = true } = options || {};
3980
+ const finalResult = await finalResultPromise;
3981
+ if (isCrashedOnError) {
3982
+ assertsTaskSuccessful(finalResult);
3983
+ }
3984
+ return finalResult;
3364
3985
  }
3365
3986
  return {
3366
3987
  taskType,
@@ -3369,91 +3990,29 @@ function createTask(options) {
3369
3990
  return PROMPTBOOK_ENGINE_VERSION;
3370
3991
  },
3371
3992
  get title() {
3372
- return title;
3993
+ return taskState.title;
3373
3994
  // <- Note: [1] These must be getters to allow changing the value in the future
3374
3995
  },
3375
3996
  get status() {
3376
- return status;
3997
+ return taskState.status;
3377
3998
  // <- Note: [1] --||--
3378
3999
  },
3379
4000
  get tldr() {
3380
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3381
- // Use custom tldr if available
3382
- if (customTldr) {
3383
- return customTldr;
3384
- }
3385
- // Fallback to default implementation
3386
- const cv = currentValue;
3387
- // If explicit percent is provided, use it
3388
- let percentRaw = (_f = (_d = (_b = (_a = cv === null || cv === void 0 ? void 0 : cv.tldr) === null || _a === void 0 ? void 0 : _a.percent) !== null && _b !== void 0 ? _b : (_c = cv === null || cv === void 0 ? void 0 : cv.usage) === null || _c === void 0 ? void 0 : _c.percent) !== null && _d !== void 0 ? _d : (_e = cv === null || cv === void 0 ? void 0 : cv.progress) === null || _e === void 0 ? void 0 : _e.percent) !== null && _f !== void 0 ? _f : cv === null || cv === void 0 ? void 0 : cv.percent;
3389
- // Simulate progress if not provided
3390
- if (typeof percentRaw !== 'number') {
3391
- // Simulate progress: evenly split across subtasks, based on elapsed time
3392
- const now = new Date();
3393
- const elapsedMs = now.getTime() - createdAt.getTime();
3394
- const totalMs = DEFAULT_TASK_SIMULATED_DURATION_MS;
3395
- // If subtasks are defined, split progress evenly
3396
- const subtaskCount = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) ? cv.subtasks.length : 1;
3397
- const completedSubtasks = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks)
3398
- ? cv.subtasks.filter((s) => s.done || s.completed).length
3399
- : 0;
3400
- // Progress from completed subtasks
3401
- const subtaskProgress = subtaskCount > 0 ? completedSubtasks / subtaskCount : 0;
3402
- // Progress from elapsed time for current subtask
3403
- const timeProgress = Math.min(elapsedMs / totalMs, 1);
3404
- // Combine: completed subtasks + time progress for current subtask
3405
- percentRaw = Math.min(subtaskProgress + (1 / subtaskCount) * timeProgress, 1);
3406
- if (status === 'FINISHED')
3407
- percentRaw = 1;
3408
- if (status === 'ERROR')
3409
- percentRaw = 0;
3410
- }
3411
- // Clamp to [0,1]
3412
- let percent = Number(percentRaw) || 0;
3413
- if (percent < 0)
3414
- percent = 0;
3415
- if (percent > 1)
3416
- percent = 1;
3417
- // Build a short message: prefer explicit tldr.message, then common summary/message fields, then errors/warnings, then status
3418
- const messageFromResult = (_k = (_j = (_h = (_g = cv === null || cv === void 0 ? void 0 : cv.tldr) === null || _g === void 0 ? void 0 : _g.message) !== null && _h !== void 0 ? _h : cv === null || cv === void 0 ? void 0 : cv.message) !== null && _j !== void 0 ? _j : cv === null || cv === void 0 ? void 0 : cv.summary) !== null && _k !== void 0 ? _k : cv === null || cv === void 0 ? void 0 : cv.statusMessage;
3419
- let message = messageFromResult;
3420
- if (!message) {
3421
- // If subtasks, show current subtask
3422
- if (Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) && cv.subtasks.length > 0) {
3423
- const current = cv.subtasks.find((s) => !s.done && !s.completed);
3424
- if (current && current.title) {
3425
- message = `Working on ${current.title}`;
3426
- }
3427
- }
3428
- if (!message) {
3429
- if (errors.length) {
3430
- message = errors[errors.length - 1].message || 'Error';
3431
- }
3432
- else if (warnings.length) {
3433
- message = warnings[warnings.length - 1].message || 'Warning';
3434
- }
3435
- else if (status === 'FINISHED') {
3436
- message = 'Finished';
3437
- }
3438
- else if (status === 'ERROR') {
3439
- message = 'Error';
3440
- }
3441
- else {
3442
- message = 'Running';
3443
- }
3444
- }
3445
- }
3446
- return {
3447
- percent: percent,
3448
- message: message + ' (!!!fallback)',
3449
- };
4001
+ return resolveTaskTldr({
4002
+ customTldr: taskState.customTldr,
4003
+ currentValue: taskState.currentValue,
4004
+ status: taskState.status,
4005
+ createdAt,
4006
+ errors: taskState.errors,
4007
+ warnings: taskState.warnings,
4008
+ });
3450
4009
  },
3451
4010
  get createdAt() {
3452
4011
  return createdAt;
3453
4012
  // <- Note: [1] --||--
3454
4013
  },
3455
4014
  get updatedAt() {
3456
- return updatedAt;
4015
+ return taskState.updatedAt;
3457
4016
  // <- Note: [1] --||--
3458
4017
  },
3459
4018
  asPromise,
@@ -3461,19 +4020,19 @@ function createTask(options) {
3461
4020
  return partialResultSubject.asObservable();
3462
4021
  },
3463
4022
  get errors() {
3464
- return errors;
4023
+ return taskState.errors;
3465
4024
  // <- Note: [1] --||--
3466
4025
  },
3467
4026
  get warnings() {
3468
- return warnings;
4027
+ return taskState.warnings;
3469
4028
  // <- Note: [1] --||--
3470
4029
  },
3471
4030
  get llmCalls() {
3472
- return [...llmCalls, { foo: '!!! bar' }];
4031
+ return [...taskState.llmCalls, { foo: '!!! bar' }];
3473
4032
  // <- Note: [1] --||--
3474
4033
  },
3475
4034
  get currentValue() {
3476
- return currentValue;
4035
+ return taskState.currentValue;
3477
4036
  // <- Note: [1] --||--
3478
4037
  },
3479
4038
  };
@@ -4754,222 +5313,287 @@ const promptbookFetch = async (urlOrRequest, init) => {
4754
5313
  * @public exported from `@promptbook/core`
4755
5314
  */
4756
5315
  async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
4757
- var _a;
4758
- const { fetch = promptbookFetch } = tools;
4759
5316
  const { knowledgeSourceContent } = knowledgeSource;
4760
- let { name } = knowledgeSource;
4761
- const { rootDirname = null,
4762
- // <- TODO: process.cwd() if running in Node.js
4763
- isVerbose = DEFAULT_IS_VERBOSE, } = options || {};
4764
- if (!name) {
4765
- name = knowledgeSourceContentToName(knowledgeSourceContent);
4766
- }
5317
+ const name = knowledgeSource.name || knowledgeSourceContentToName(knowledgeSourceContent);
5318
+ const { rootDirname = null, isVerbose = DEFAULT_IS_VERBOSE } = options || {};
4767
5319
  if (isValidUrl(knowledgeSourceContent)) {
4768
- const url = knowledgeSourceContent;
4769
- if (isVerbose) {
4770
- console.info(`📄 [1] "${name}" is available at "${url}"`);
4771
- }
4772
- const response = await fetch(url); // <- TODO: [🧠] Scraping and fetch proxy
4773
- const mimeType = ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
4774
- if (tools.fs === undefined || !url.endsWith('.pdf' /* <- TODO: [💵] */)) {
4775
- if (isVerbose) {
4776
- console.info(`📄 [2] "${name}" tools.fs is not available or URL is not a PDF.`);
4777
- }
4778
- return {
4779
- source: name,
4780
- filename: null,
4781
- url,
4782
- mimeType,
4783
- /*
4784
- TODO: [🥽]
4785
- > async asBlob() {
4786
- > // TODO: [👨🏻‍🤝‍👨🏻] This can be called multiple times BUT when called second time, response in already consumed
4787
- > const content = await response.blob();
4788
- > return content;
4789
- > },
4790
- */
4791
- async asJson() {
4792
- // TODO: [👨🏻‍🤝‍👨🏻]
4793
- const content = await response.json();
4794
- return content;
4795
- },
4796
- async asText() {
4797
- // TODO: [👨🏻‍🤝‍👨🏻]
4798
- const content = await response.text();
4799
- return content;
4800
- },
4801
- };
4802
- }
4803
- const basename = url.split('/').pop() || titleToName(url);
4804
- const hash = sha256(hexEncoder.parse(url)).toString( /* hex */);
4805
- // <- TODO: [🥬] Encapsulate sha256 to some private utility function
4806
- const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
4807
- const filepath = join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
4808
- // Note: Try to create cache directory, but don't fail if filesystem has issues
4809
- try {
4810
- await tools.fs.mkdir(dirname(join(rootDirname, filepath)), { recursive: true });
4811
- }
4812
- catch (error) {
4813
- if (isVerbose) {
4814
- console.info(`📄 [3] "${name}" error creating cache directory`);
4815
- }
4816
- // Note: If we can't create cache directory, we'll handle it when trying to write the file
4817
- // This handles read-only filesystems, permission issues, and missing parent directories
4818
- if (error instanceof Error &&
4819
- (error.message.includes('EROFS') ||
4820
- error.message.includes('read-only') ||
4821
- error.message.includes('EACCES') ||
4822
- error.message.includes('EPERM') ||
4823
- error.message.includes('ENOENT'))) ;
4824
- else {
4825
- // Re-throw other unexpected errors
4826
- throw error;
4827
- }
4828
- }
4829
- const fileContent = Buffer.from(await response.arrayBuffer());
4830
- if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
4831
- throw new LimitReachedError(`File is too large (${Math.round(fileContent.length / 1024 / 1024)}MB). Maximum allowed size is ${Math.round(DEFAULT_MAX_FILE_SIZE / 1024 / 1024)}MB.`);
4832
- }
4833
- // Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
4834
- try {
4835
- await tools.fs.writeFile(join(rootDirname, filepath), fileContent);
4836
- }
4837
- catch (error) {
4838
- if (isVerbose) {
4839
- console.info(`📄 [4] "${name}" error writing cache file`);
4840
- }
4841
- // Note: If we can't write to cache, we'll process the file directly from memory
4842
- // This handles read-only filesystems like Vercel
4843
- if (error instanceof Error &&
4844
- (error.message.includes('EROFS') ||
4845
- error.message.includes('read-only') ||
4846
- error.message.includes('EACCES') ||
4847
- error.message.includes('EPERM') ||
4848
- error.message.includes('ENOENT'))) {
4849
- // Return a handler that works directly with the downloaded content
4850
- return {
4851
- source: name,
4852
- filename: null,
4853
- url,
4854
- mimeType,
4855
- async asJson() {
4856
- return JSON.parse(fileContent.toString('utf-8'));
4857
- },
4858
- async asText() {
4859
- return fileContent.toString('utf-8');
4860
- },
4861
- };
4862
- }
4863
- else {
4864
- // Re-throw other unexpected errors
4865
- throw error;
4866
- }
4867
- }
4868
- // TODO: [💵] Check the file security
4869
- // TODO: [🧹][🧠] Delete the file after the scraping is done
4870
- if (isVerbose) {
4871
- console.info(`📄 [5] "${name}" cached at "${join(rootDirname, filepath)}"`);
4872
- }
4873
- return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
4874
- ...options,
4875
- rootDirname,
4876
- });
4877
- }
4878
- else if (isValidFilePath(knowledgeSourceContent)) {
4879
- if (tools.fs === undefined) {
4880
- throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
4881
- // <- TODO: [🧠] What is the best error type here`
4882
- }
4883
- if (rootDirname === null) {
4884
- throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
4885
- // <- TODO: [🧠] What is the best error type here`
4886
- }
4887
- const filename = isAbsolute(knowledgeSourceContent)
4888
- ? knowledgeSourceContent
4889
- : join(rootDirname, knowledgeSourceContent).split('\\').join('/');
4890
- if (isVerbose) {
4891
- console.info(`📄 [6] "${name}" is a valid file "${filename}"`);
4892
- }
4893
- const fileExtension = getFileExtension(filename);
4894
- const mimeType = extensionToMimeType(fileExtension || '');
4895
- if (!(await isFileExisting(filename, tools.fs))) {
4896
- throw new NotFoundError(spaceTrim$1((block) => `
4897
- Can not make source handler for file which does not exist:
4898
-
4899
- File:
4900
- ${block(knowledgeSourceContent)}
4901
-
4902
- Full file path:
4903
- ${block(filename)}
4904
- `));
4905
- }
4906
- // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
4907
- return {
4908
- source: name,
4909
- filename,
4910
- url: null,
4911
- mimeType,
4912
- /*
4913
- TODO: [🥽]
4914
- > async asBlob() {
4915
- > const content = await tools.fs!.readFile(filename);
4916
- > return new Blob(
4917
- > [
4918
- > content,
4919
- > // <- TODO: [🥽] This is NOT tested, test it
4920
- > ],
4921
- > { type: mimeType },
4922
- > );
4923
- > },
4924
- */
4925
- async asJson() {
4926
- return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
4927
- },
4928
- async asText() {
4929
- return await tools.fs.readFile(filename, 'utf-8');
4930
- },
4931
- };
5320
+ return makeUrlKnowledgeSourceHandler(knowledgeSourceContent, name, tools, options, isVerbose);
4932
5321
  }
4933
- else {
4934
- if (isVerbose) {
4935
- console.info(`📄 [7] "${name}" is just a explicit string text with a knowledge source`);
4936
- console.info('---');
4937
- console.info(knowledgeSourceContent);
4938
- console.info('---');
4939
- }
4940
- return {
4941
- source: name,
4942
- filename: null,
4943
- url: null,
4944
- mimeType: 'text/markdown',
4945
- asText() {
4946
- return knowledgeSource.knowledgeSourceContent;
4947
- },
4948
- asJson() {
4949
- throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
4950
- },
4951
- /*
4952
- TODO: [🥽]
4953
- > asBlob() {
4954
- > throw new UnexpectedError(
4955
- > 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
4956
- > );
4957
- > },
4958
- */
4959
- };
5322
+ if (isValidFilePath(knowledgeSourceContent)) {
5323
+ return makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose);
4960
5324
  }
5325
+ return makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose);
4961
5326
  }
4962
-
4963
5327
  /**
4964
- * Prepares the knowledge pieces
5328
+ * Creates a source handler for URL-based knowledge.
4965
5329
  *
4966
- * @see https://github.com/webgptorg/promptbook/discussions/41
5330
+ * @private internal utility of `makeKnowledgeSourceHandler`
5331
+ */
5332
+ async function makeUrlKnowledgeSourceHandler(url, name, tools, options, isVerbose) {
5333
+ var _a;
5334
+ const { fetch = promptbookFetch } = tools;
5335
+ if (isVerbose) {
5336
+ console.info(`📄 [1] "${name}" is available at "${url}"`);
5337
+ }
5338
+ const response = await fetch(url); // <- TODO: [🧠] Scraping and fetch proxy
5339
+ const mimeType = ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
5340
+ if (tools.fs === undefined || !url.endsWith('.pdf' /* <- TODO: [💵] */)) {
5341
+ return makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose);
5342
+ }
5343
+ return cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose);
5344
+ }
5345
+ /**
5346
+ * Creates a source handler that reads directly from a fetched response.
4967
5347
  *
4968
- * @public exported from `@promptbook/core`
5348
+ * @private internal utility of `makeKnowledgeSourceHandler`
4969
5349
  */
4970
- async function prepareKnowledgePieces(knowledgeSources, tools, options) {
4971
- const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, rootDirname, isVerbose = DEFAULT_IS_VERBOSE } = options;
4972
- const knowledgePreparedUnflatten = new Array(knowledgeSources.length);
5350
+ function makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose) {
5351
+ if (isVerbose) {
5352
+ console.info(`📄 [2] "${name}" tools.fs is not available or URL is not a PDF.`);
5353
+ }
5354
+ return {
5355
+ source: name,
5356
+ filename: null,
5357
+ url,
5358
+ mimeType,
5359
+ /*
5360
+ TODO: [🥽]
5361
+ > async asBlob() {
5362
+ > // TODO: [👨🏻‍🤝‍👨🏻] This can be called multiple times BUT when called second time, response in already consumed
5363
+ > const content = await response.blob();
5364
+ > return content;
5365
+ > },
5366
+ */
5367
+ async asJson() {
5368
+ // TODO: [👨🏻‍🤝‍👨🏻]
5369
+ const content = await response.json();
5370
+ return content;
5371
+ },
5372
+ async asText() {
5373
+ // TODO: [👨🏻‍🤝‍👨🏻]
5374
+ const content = await response.text();
5375
+ return content;
5376
+ },
5377
+ };
5378
+ }
5379
+ /**
5380
+ * Downloads a PDF knowledge source into cache when possible and falls back to in-memory content otherwise.
5381
+ *
5382
+ * @private internal utility of `makeKnowledgeSourceHandler`
5383
+ */
5384
+ async function cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose) {
5385
+ const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
5386
+ const filepath = createDownloadedKnowledgeSourceFilepath(url, mimeType);
5387
+ const fullFilepath = join(rootDirname, filepath);
5388
+ await createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose);
5389
+ const fileContent = Buffer.from(await response.arrayBuffer());
5390
+ if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
5391
+ throw new LimitReachedError(`File is too large (${Math.round(fileContent.length / 1024 / 1024)}MB). Maximum allowed size is ${Math.round(DEFAULT_MAX_FILE_SIZE / 1024 / 1024)}MB.`);
5392
+ }
5393
+ const isCached = await writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose);
5394
+ if (!isCached) {
5395
+ return makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent);
5396
+ }
5397
+ // TODO: [💵] Check the file security
5398
+ // TODO: [🧹][🧠] Delete the file after the scraping is done
5399
+ if (isVerbose) {
5400
+ console.info(`📄 [5] "${name}" cached at "${fullFilepath}"`);
5401
+ }
5402
+ return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
5403
+ ...options,
5404
+ rootDirname,
5405
+ });
5406
+ }
5407
+ /**
5408
+ * Builds a stable cache filepath for a downloaded knowledge source.
5409
+ *
5410
+ * @private internal utility of `makeKnowledgeSourceHandler`
5411
+ */
5412
+ function createDownloadedKnowledgeSourceFilepath(url, mimeType) {
5413
+ const basename = url.split('/').pop() || titleToName(url);
5414
+ const hash = sha256(hexEncoder.parse(url)).toString( /* hex */);
5415
+ // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5416
+ return join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
5417
+ }
5418
+ /**
5419
+ * Tries to create the cache directory needed for a downloaded knowledge source.
5420
+ *
5421
+ * @private internal utility of `makeKnowledgeSourceHandler`
5422
+ */
5423
+ async function createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose) {
5424
+ try {
5425
+ await tools.fs.mkdir(dirname(fullFilepath), { recursive: true });
5426
+ }
5427
+ catch (error) {
5428
+ if (isVerbose) {
5429
+ console.info(`📄 [3] "${name}" error creating cache directory`);
5430
+ }
5431
+ // Note: If we can't create cache directory, we'll handle it when trying to write the file
5432
+ // This handles read-only filesystems, permission issues, and missing parent directories
5433
+ if (!isIgnorableCacheFilesystemError(error)) {
5434
+ throw error;
5435
+ }
5436
+ }
5437
+ }
5438
+ /**
5439
+ * Tries to write downloaded content into cache and reports whether the cache was usable.
5440
+ *
5441
+ * @private internal utility of `makeKnowledgeSourceHandler`
5442
+ */
5443
+ async function writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose) {
5444
+ // Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
5445
+ try {
5446
+ await tools.fs.writeFile(fullFilepath, fileContent);
5447
+ return true;
5448
+ }
5449
+ catch (error) {
5450
+ if (isVerbose) {
5451
+ console.info(`📄 [4] "${name}" error writing cache file`);
5452
+ }
5453
+ // Note: If we can't write to cache, we'll process the file directly from memory
5454
+ // This handles read-only filesystems like Vercel
5455
+ if (isIgnorableCacheFilesystemError(error)) {
5456
+ return false;
5457
+ }
5458
+ throw error;
5459
+ }
5460
+ }
5461
+ /**
5462
+ * Detects filesystem errors that should not fail optional caching.
5463
+ *
5464
+ * @private internal utility of `makeKnowledgeSourceHandler`
5465
+ */
5466
+ function isIgnorableCacheFilesystemError(error) {
5467
+ return (error instanceof Error &&
5468
+ (error.message.includes('EROFS') ||
5469
+ error.message.includes('read-only') ||
5470
+ error.message.includes('EACCES') ||
5471
+ error.message.includes('EPERM') ||
5472
+ error.message.includes('ENOENT')));
5473
+ }
5474
+ /**
5475
+ * Creates a source handler backed by already downloaded file content kept in memory.
5476
+ *
5477
+ * @private internal utility of `makeKnowledgeSourceHandler`
5478
+ */
5479
+ function makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent) {
5480
+ return {
5481
+ source: name,
5482
+ filename: null,
5483
+ url,
5484
+ mimeType,
5485
+ async asJson() {
5486
+ return JSON.parse(fileContent.toString('utf-8'));
5487
+ },
5488
+ async asText() {
5489
+ return fileContent.toString('utf-8');
5490
+ },
5491
+ };
5492
+ }
5493
+ /**
5494
+ * Creates a source handler for file-based knowledge.
5495
+ *
5496
+ * @private internal utility of `makeKnowledgeSourceHandler`
5497
+ */
5498
+ async function makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose) {
5499
+ if (tools.fs === undefined) {
5500
+ throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
5501
+ // <- TODO: [🧠] What is the best error type here`
5502
+ }
5503
+ if (rootDirname === null) {
5504
+ throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
5505
+ // <- TODO: [🧠] What is the best error type here`
5506
+ }
5507
+ const filename = isAbsolute(knowledgeSourceContent)
5508
+ ? knowledgeSourceContent
5509
+ : join(rootDirname, knowledgeSourceContent).split('\\').join('/');
5510
+ if (isVerbose) {
5511
+ console.info(`📄 [6] "${name}" is a valid file "${filename}"`);
5512
+ }
5513
+ const fileExtension = getFileExtension(filename);
5514
+ const mimeType = extensionToMimeType(fileExtension || '');
5515
+ if (!(await isFileExisting(filename, tools.fs))) {
5516
+ throw new NotFoundError(spaceTrim$1((block) => `
5517
+ Can not make source handler for file which does not exist:
5518
+
5519
+ File:
5520
+ ${block(knowledgeSourceContent)}
5521
+
5522
+ Full file path:
5523
+ ${block(filename)}
5524
+ `));
5525
+ }
5526
+ // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
5527
+ return {
5528
+ source: name,
5529
+ filename,
5530
+ url: null,
5531
+ mimeType,
5532
+ /*
5533
+ TODO: [🥽]
5534
+ > async asBlob() {
5535
+ > const content = await tools.fs!.readFile(filename);
5536
+ > return new Blob(
5537
+ > [
5538
+ > content,
5539
+ > // <- TODO: [🥽] This is NOT tested, test it
5540
+ > ],
5541
+ > { type: mimeType },
5542
+ > );
5543
+ > },
5544
+ */
5545
+ async asJson() {
5546
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
5547
+ },
5548
+ async asText() {
5549
+ return await tools.fs.readFile(filename, 'utf-8');
5550
+ },
5551
+ };
5552
+ }
5553
+ /**
5554
+ * Creates a source handler for inline text knowledge.
5555
+ *
5556
+ * @private internal utility of `makeKnowledgeSourceHandler`
5557
+ */
5558
+ function makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose) {
5559
+ if (isVerbose) {
5560
+ console.info(`📄 [7] "${name}" is just a explicit string text with a knowledge source`);
5561
+ console.info('---');
5562
+ console.info(knowledgeSourceContent);
5563
+ console.info('---');
5564
+ }
5565
+ return {
5566
+ source: name,
5567
+ filename: null,
5568
+ url: null,
5569
+ mimeType: 'text/markdown',
5570
+ asText() {
5571
+ return knowledgeSourceContent;
5572
+ },
5573
+ asJson() {
5574
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
5575
+ },
5576
+ /*
5577
+ TODO: [🥽]
5578
+ > asBlob() {
5579
+ > throw new UnexpectedError(
5580
+ > 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
5581
+ > );
5582
+ > },
5583
+ */
5584
+ };
5585
+ }
5586
+
5587
+ /**
5588
+ * Prepares the knowledge pieces
5589
+ *
5590
+ * @see https://github.com/webgptorg/promptbook/discussions/41
5591
+ *
5592
+ * @public exported from `@promptbook/core`
5593
+ */
5594
+ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
5595
+ const { maxParallelCount = DEFAULT_MAX_PARALLEL_COUNT, rootDirname, isVerbose = DEFAULT_IS_VERBOSE } = options;
5596
+ const knowledgePreparedUnflatten = new Array(knowledgeSources.length);
4973
5597
  await forEachAsync(knowledgeSources, { maxParallelCount }, async (knowledgeSource, index) => {
4974
5598
  try {
4975
5599
  let partialPieces = null;
@@ -6242,112 +6866,10 @@ function validatePromptResult(options) {
6242
6866
  }
6243
6867
  }
6244
6868
 
6245
- /**
6246
- * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
6247
- * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
6248
- * Throws errors if execution fails after all attempts.
6249
- *
6250
- * @param options - The options for execution, including task, parameters, pipeline, and configuration.
6251
- * @returns The result string of the executed task.
6252
- *
6253
- * @private internal utility of `createPipelineExecutor`
6254
- */
6255
- async function executeAttempts(options) {
6256
- const $ongoingTaskResult = createOngoingTaskResult();
6257
- const llmTools = getSingleLlmExecutionTools(options.tools.llm);
6258
- attempts: for (let attemptIndex = -options.jokerParameterNames.length; attemptIndex < options.maxAttempts; attemptIndex++) {
6259
- const attempt = createAttemptDescriptor({
6260
- attemptIndex,
6261
- jokerParameterNames: options.jokerParameterNames,
6262
- pipelineIdentification: options.pipelineIdentification,
6263
- });
6264
- resetAttemptExecutionState($ongoingTaskResult);
6265
- try {
6266
- await executeSingleAttempt({
6267
- attempt,
6268
- options,
6269
- llmTools,
6270
- $ongoingTaskResult,
6271
- });
6272
- break attempts;
6273
- }
6274
- catch (error) {
6275
- if (!(error instanceof ExpectError)) {
6276
- throw error;
6277
- }
6278
- recordFailedAttempt({
6279
- error,
6280
- attemptIndex,
6281
- onProgress: options.onProgress,
6282
- $ongoingTaskResult,
6283
- });
6284
- }
6285
- finally {
6286
- reportPromptExecution({
6287
- attempt,
6288
- task: options.task,
6289
- $executionReport: options.$executionReport,
6290
- logLlmCall: options.logLlmCall,
6291
- $ongoingTaskResult,
6292
- });
6293
- }
6294
- throwIfFinalAttemptFailed({
6295
- attemptIndex,
6296
- maxAttempts: options.maxAttempts,
6297
- maxExecutionAttempts: options.maxExecutionAttempts,
6298
- pipelineIdentification: options.pipelineIdentification,
6299
- $ongoingTaskResult,
6300
- });
6301
- }
6302
- return getSuccessfulResultString({
6303
- pipelineIdentification: options.pipelineIdentification,
6304
- $ongoingTaskResult,
6305
- });
6306
- }
6307
- /**
6308
- * Creates mutable attempt state for one task execution lifecycle.
6309
- */
6310
- function createOngoingTaskResult() {
6311
- return {
6312
- $result: null,
6313
- $resultString: null,
6314
- $expectError: null,
6315
- $scriptPipelineExecutionErrors: [],
6316
- $failedResults: [],
6317
- };
6318
- }
6319
- /**
6320
- * Resolves the bookkeeping for one loop iteration, including joker lookup.
6321
- */
6322
- function createAttemptDescriptor(options) {
6323
- const { attemptIndex, jokerParameterNames, pipelineIdentification } = options;
6324
- const isJokerAttempt = attemptIndex < 0;
6325
- const jokerParameterName = isJokerAttempt
6326
- ? jokerParameterNames[jokerParameterNames.length + attemptIndex]
6327
- : undefined;
6328
- if (isJokerAttempt && !jokerParameterName) {
6329
- throw new UnexpectedError(spaceTrim$1((block) => `
6330
- Joker not found in attempt ${attemptIndex}
6331
-
6332
- ${block(pipelineIdentification)}
6333
- `));
6334
- }
6335
- return {
6336
- attemptIndex,
6337
- isJokerAttempt,
6338
- jokerParameterName,
6339
- };
6340
- }
6341
- /**
6342
- * Clears the per-attempt result slots while preserving cumulative failure history.
6343
- */
6344
- function resetAttemptExecutionState($ongoingTaskResult) {
6345
- $ongoingTaskResult.$result = null;
6346
- $ongoingTaskResult.$resultString = null;
6347
- $ongoingTaskResult.$expectError = null;
6348
- }
6349
6869
  /**
6350
6870
  * Executes one loop iteration, from joker resolution or task execution through validation.
6871
+ *
6872
+ * @private function of `executeAttempts`
6351
6873
  */
6352
6874
  async function executeSingleAttempt(options) {
6353
6875
  const { attempt, options: executeAttemptsOptions, llmTools, $ongoingTaskResult } = options;
@@ -6662,11 +7184,15 @@ function validateAttemptResult(options) {
6662
7184
  // Update the result string in case format processing modified it (e.g., JSON extraction)
6663
7185
  $ongoingTaskResult.$resultString = validationResult.processedResultString;
6664
7186
  }
7187
+
6665
7188
  /**
6666
- * Stores one failed attempt and reports the expectation error upstream.
7189
+ * Stores one failed attempt, reports the expectation error, and throws the aggregated retry error after the final
7190
+ * regular attempt.
7191
+ *
7192
+ * @private function of `executeAttempts`
6667
7193
  */
6668
- function recordFailedAttempt(options) {
6669
- const { error, attemptIndex, onProgress, $ongoingTaskResult } = options;
7194
+ function handleAttemptFailure(options) {
7195
+ const { error, attemptIndex, maxAttempts, maxExecutionAttempts, onProgress, pipelineIdentification, $ongoingTaskResult, } = options;
6670
7196
  $ongoingTaskResult.$expectError = error;
6671
7197
  $ongoingTaskResult.$failedResults.push({
6672
7198
  attemptIndex,
@@ -6676,39 +7202,7 @@ function recordFailedAttempt(options) {
6676
7202
  onProgress({
6677
7203
  errors: [error],
6678
7204
  });
6679
- }
6680
- /**
6681
- * Appends the prompt execution report for prompt-task attempts.
6682
- */
6683
- function reportPromptExecution(options) {
6684
- const { attempt, task, $executionReport, logLlmCall, $ongoingTaskResult } = options;
6685
- if (attempt.isJokerAttempt || task.taskType !== 'PROMPT_TASK' || !$ongoingTaskResult.$prompt) {
6686
- return;
6687
- }
6688
- // Note: [2] When some expected parameter is not defined, error will occur in templateParameters
6689
- // In that case we don’t want to make a report about it because it’s not a llm execution error
6690
- const executionPromptReport = {
6691
- prompt: {
6692
- ...$ongoingTaskResult.$prompt,
6693
- // <- TODO: [🧠] How to pick everyhing except `pipelineUrl`
6694
- },
6695
- result: $ongoingTaskResult.$result || undefined,
6696
- error: $ongoingTaskResult.$expectError === null ? undefined : serializeError($ongoingTaskResult.$expectError),
6697
- };
6698
- $executionReport.promptExecutions.push(executionPromptReport);
6699
- if (logLlmCall) {
6700
- logLlmCall({
6701
- modelName: 'model' /* <- TODO: How to get model name from the report */,
6702
- report: executionPromptReport,
6703
- });
6704
- }
6705
- }
6706
- /**
6707
- * Throws the aggregated retry error after the last regular attempt fails expectations.
6708
- */
6709
- function throwIfFinalAttemptFailed(options) {
6710
- const { attemptIndex, maxAttempts, maxExecutionAttempts, pipelineIdentification, $ongoingTaskResult } = options;
6711
- if ($ongoingTaskResult.$expectError === null || attemptIndex !== maxAttempts - 1) {
7205
+ if (attemptIndex !== maxAttempts - 1) {
6712
7206
  return;
6713
7207
  }
6714
7208
  throw new PipelineExecutionError(spaceTrim$1((block) => {
@@ -6739,7 +7233,9 @@ function createFailuresSummary($failedResults) {
6739
7233
  ${block(quoteMultilineText(((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message) || ''))}
6740
7234
 
6741
7235
  Result:
6742
- ${block(failure.result === null ? 'null' : quoteMultilineText(spaceTrim$1(failure.result)))}
7236
+ ${block(failure.result === null
7237
+ ? 'null'
7238
+ : quoteMultilineText(spaceTrim$1(failure.result)))}
6743
7239
  `;
6744
7240
  }))
6745
7241
  .join('\n\n---\n\n');
@@ -6753,6 +7249,136 @@ function quoteMultilineText(text) {
6753
7249
  .map((line) => `> ${line}`)
6754
7250
  .join('\n');
6755
7251
  }
7252
+
7253
+ /**
7254
+ * Appends the prompt execution report for prompt-task attempts.
7255
+ *
7256
+ * @private function of `executeAttempts`
7257
+ */
7258
+ function reportPromptExecution(options) {
7259
+ const { attempt, task, $executionReport, logLlmCall, $ongoingTaskResult } = options;
7260
+ if (attempt.isJokerAttempt || task.taskType !== 'PROMPT_TASK' || !$ongoingTaskResult.$prompt) {
7261
+ return;
7262
+ }
7263
+ // Note: [2] When some expected parameter is not defined, error will occur in templateParameters
7264
+ // In that case we don’t want to make a report about it because it’s not a llm execution error
7265
+ const executionPromptReport = {
7266
+ prompt: {
7267
+ ...$ongoingTaskResult.$prompt,
7268
+ // <- TODO: [🧠] How to pick everyhing except `pipelineUrl`
7269
+ },
7270
+ result: $ongoingTaskResult.$result || undefined,
7271
+ error: $ongoingTaskResult.$expectError === null ? undefined : serializeError($ongoingTaskResult.$expectError),
7272
+ };
7273
+ $executionReport.promptExecutions.push(executionPromptReport);
7274
+ if (logLlmCall) {
7275
+ logLlmCall({
7276
+ modelName: 'model' /* <- TODO: How to get model name from the report */,
7277
+ report: executionPromptReport,
7278
+ });
7279
+ }
7280
+ }
7281
+
7282
+ /**
7283
+ * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
7284
+ * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
7285
+ * Throws errors if execution fails after all attempts.
7286
+ *
7287
+ * @param options - The options for execution, including task, parameters, pipeline, and configuration.
7288
+ * @returns The result string of the executed task.
7289
+ *
7290
+ * @private internal utility of `createPipelineExecutor`
7291
+ */
7292
+ async function executeAttempts(options) {
7293
+ const $ongoingTaskResult = createOngoingTaskResult();
7294
+ const llmTools = getSingleLlmExecutionTools(options.tools.llm);
7295
+ attempts: for (let attemptIndex = -options.jokerParameterNames.length; attemptIndex < options.maxAttempts; attemptIndex++) {
7296
+ const attempt = createAttemptDescriptor({
7297
+ attemptIndex,
7298
+ jokerParameterNames: options.jokerParameterNames,
7299
+ pipelineIdentification: options.pipelineIdentification,
7300
+ });
7301
+ resetAttemptExecutionState($ongoingTaskResult);
7302
+ try {
7303
+ await executeSingleAttempt({
7304
+ attempt,
7305
+ options,
7306
+ llmTools,
7307
+ $ongoingTaskResult,
7308
+ });
7309
+ break attempts;
7310
+ }
7311
+ catch (error) {
7312
+ if (!(error instanceof ExpectError)) {
7313
+ throw error;
7314
+ }
7315
+ handleAttemptFailure({
7316
+ error,
7317
+ attemptIndex,
7318
+ maxAttempts: options.maxAttempts,
7319
+ maxExecutionAttempts: options.maxExecutionAttempts,
7320
+ onProgress: options.onProgress,
7321
+ pipelineIdentification: options.pipelineIdentification,
7322
+ $ongoingTaskResult,
7323
+ });
7324
+ }
7325
+ finally {
7326
+ reportPromptExecution({
7327
+ attempt,
7328
+ task: options.task,
7329
+ $executionReport: options.$executionReport,
7330
+ logLlmCall: options.logLlmCall,
7331
+ $ongoingTaskResult,
7332
+ });
7333
+ }
7334
+ }
7335
+ return getSuccessfulResultString({
7336
+ pipelineIdentification: options.pipelineIdentification,
7337
+ $ongoingTaskResult,
7338
+ });
7339
+ }
7340
+ /**
7341
+ * Creates mutable attempt state for one task execution lifecycle.
7342
+ */
7343
+ function createOngoingTaskResult() {
7344
+ return {
7345
+ $result: null,
7346
+ $resultString: null,
7347
+ $expectError: null,
7348
+ $scriptPipelineExecutionErrors: [],
7349
+ $failedResults: [],
7350
+ };
7351
+ }
7352
+ /**
7353
+ * Resolves the bookkeeping for one loop iteration, including joker lookup.
7354
+ */
7355
+ function createAttemptDescriptor(options) {
7356
+ const { attemptIndex, jokerParameterNames, pipelineIdentification } = options;
7357
+ const isJokerAttempt = attemptIndex < 0;
7358
+ const jokerParameterName = isJokerAttempt
7359
+ ? jokerParameterNames[jokerParameterNames.length + attemptIndex]
7360
+ : undefined;
7361
+ if (isJokerAttempt && !jokerParameterName) {
7362
+ throw new UnexpectedError(spaceTrim$1((block) => `
7363
+ Joker not found in attempt ${attemptIndex}
7364
+
7365
+ ${block(pipelineIdentification)}
7366
+ `));
7367
+ }
7368
+ return {
7369
+ attemptIndex,
7370
+ isJokerAttempt,
7371
+ jokerParameterName,
7372
+ };
7373
+ }
7374
+ /**
7375
+ * Clears the per-attempt result slots while preserving cumulative failure history.
7376
+ */
7377
+ function resetAttemptExecutionState($ongoingTaskResult) {
7378
+ $ongoingTaskResult.$result = null;
7379
+ $ongoingTaskResult.$resultString = null;
7380
+ $ongoingTaskResult.$expectError = null;
7381
+ }
6756
7382
  /**
6757
7383
  * Returns the successful result string or raises an unexpected internal-state error.
6758
7384
  */