@promptbook/markitdown 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 +1409 -783
  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 +1409 -783
  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
@@ -24,7 +24,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
24
24
  * @generated
25
25
  * @see https://github.com/webgptorg/promptbook
26
26
  */
27
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-71';
27
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
28
28
  /**
29
29
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
30
30
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2032,6 +2032,60 @@ function validatePipelineString(pipelineString) {
2032
2032
  }
2033
2033
  // TODO: [🧠][🈴] Where is the best location for this file
2034
2034
 
2035
+ /**
2036
+ * Appends one markdown block to an existing markdown document.
2037
+ *
2038
+ * @private internal utility of `pipelineJsonToString`
2039
+ */
2040
+ function appendMarkdownBlock(pipelineString, markdownBlock) {
2041
+ return spaceTrim$1((block) => `
2042
+ ${block(pipelineString)}
2043
+
2044
+ ${block(markdownBlock)}
2045
+ `);
2046
+ }
2047
+
2048
+ /**
2049
+ * Collects pipeline-level commands in the existing serialization order.
2050
+ *
2051
+ * @private internal utility of `pipelineJsonToString`
2052
+ */
2053
+ function createPipelineCommands(pipelineJson) {
2054
+ const { pipelineUrl, bookVersion, parameters } = pipelineJson;
2055
+ const commands = [];
2056
+ if (pipelineUrl) {
2057
+ commands.push(`PIPELINE URL ${pipelineUrl}`);
2058
+ }
2059
+ if (bookVersion !== `undefined`) {
2060
+ commands.push(`BOOK VERSION ${bookVersion}`);
2061
+ }
2062
+ commands.push(...createParameterCommands(parameters, 'INPUT PARAMETER', ({ isInput }) => isInput));
2063
+ commands.push(...createParameterCommands(parameters, 'OUTPUT PARAMETER', ({ isOutput }) => isOutput));
2064
+ return commands;
2065
+ }
2066
+ /**
2067
+ * Builds one group of parameter commands while preserving the original parameter order.
2068
+ *
2069
+ * @private internal utility of `createPipelineCommands`
2070
+ */
2071
+ function createParameterCommands(parameters, commandPrefix, isIncluded) {
2072
+ return parameters
2073
+ .filter((parameter) => isIncluded(parameter))
2074
+ .map((parameter) => `${commandPrefix} ${parameterJsonToString(parameter)}`);
2075
+ }
2076
+ /**
2077
+ * Converts one parameter JSON declaration to the serialized inline form.
2078
+ *
2079
+ * @private internal utility of `createPipelineCommands`
2080
+ */
2081
+ function parameterJsonToString(parameterJson) {
2082
+ const { name, description } = parameterJson;
2083
+ if (!description) {
2084
+ return `{${name}}`;
2085
+ }
2086
+ return `{${name}} ${description}`;
2087
+ }
2088
+
2035
2089
  /**
2036
2090
  * Prettify the html code
2037
2091
  *
@@ -2045,6 +2099,31 @@ function prettifyMarkdown(content) {
2045
2099
  return (content + `\n\n<!-- Note: Prettier removed from Promptbook -->`);
2046
2100
  }
2047
2101
 
2102
+ /**
2103
+ * Creates the initial markdown heading and description of a pipeline.
2104
+ *
2105
+ * @private internal utility of `pipelineJsonToString`
2106
+ */
2107
+ function createPipelineIntroduction(pipelineJson) {
2108
+ const { title, description } = pipelineJson;
2109
+ const pipelineIntroduction = spaceTrim$1((block) => `
2110
+ # ${title}
2111
+
2112
+ ${block(description || '')}
2113
+ `);
2114
+ // TODO: [main] !!5 This increases size of the bundle and is probably not necessary
2115
+ return prettifyMarkdown(pipelineIntroduction);
2116
+ }
2117
+
2118
+ /**
2119
+ * Renders commands as markdown bullet items.
2120
+ *
2121
+ * @private internal utility of `pipelineJsonToString`
2122
+ */
2123
+ function stringifyCommands(commands) {
2124
+ return commands.map((command) => `- ${command}`).join('\n');
2125
+ }
2126
+
2048
2127
  /**
2049
2128
  * Makes first letter of a string uppercase
2050
2129
  *
@@ -2057,141 +2136,186 @@ function capitalize(word) {
2057
2136
  }
2058
2137
 
2059
2138
  /**
2060
- * Converts promptbook in JSON format to string format
2139
+ * Collects all task-specific serialization details.
2061
2140
  *
2062
- * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
2063
- * @param pipelineJson Promptbook in JSON format (.bookc)
2064
- * @returns Promptbook in string format (.book.md)
2141
+ * @private internal utility of `pipelineJsonToString`
2142
+ */
2143
+ function createTaskSerialization(task) {
2144
+ const taskTypeSerialization = createTaskTypeSerialization(task);
2145
+ return {
2146
+ commands: [
2147
+ ...taskTypeSerialization.commands,
2148
+ ...createJokerCommands(task),
2149
+ ...createPostprocessingCommands(task),
2150
+ ...createExpectationCommands(task),
2151
+ ...createFormatCommands(task),
2152
+ ],
2153
+ contentLanguage: taskTypeSerialization.contentLanguage,
2154
+ };
2155
+ }
2156
+ /**
2157
+ * Collects commands and content language driven by the task type.
2065
2158
  *
2066
- * @public exported from `@promptbook/core`
2159
+ * @private internal utility of `createTaskSerialization`
2067
2160
  */
2068
- function pipelineJsonToString(pipelineJson) {
2069
- const { title, pipelineUrl, bookVersion, description, parameters, tasks } = pipelineJson;
2070
- let pipelineString = spaceTrim$1((block) => `
2071
- # ${title}
2072
-
2073
- ${block(description || '')}
2074
- `);
2161
+ function createTaskTypeSerialization(task) {
2162
+ if (task.taskType === 'PROMPT_TASK') {
2163
+ return {
2164
+ commands: createPromptTaskCommands(task),
2165
+ contentLanguage: 'text',
2166
+ };
2167
+ }
2168
+ if (task.taskType === 'SIMPLE_TASK') {
2169
+ return {
2170
+ commands: ['SIMPLE TEMPLATE'],
2171
+ contentLanguage: 'text',
2172
+ };
2173
+ }
2174
+ if (task.taskType === 'SCRIPT_TASK') {
2175
+ return {
2176
+ commands: ['SCRIPT'],
2177
+ contentLanguage: task.contentLanguage || '',
2178
+ };
2179
+ }
2180
+ if (task.taskType === 'DIALOG_TASK') {
2181
+ return {
2182
+ commands: ['DIALOG'],
2183
+ contentLanguage: 'text',
2184
+ };
2185
+ }
2186
+ return {
2187
+ commands: [],
2188
+ contentLanguage: 'text',
2189
+ };
2190
+ }
2191
+ /**
2192
+ * Collects prompt-task-specific commands.
2193
+ *
2194
+ * @private internal utility of `createTaskSerialization`
2195
+ */
2196
+ function createPromptTaskCommands(task) {
2197
+ const { modelName, modelVariant } = task.modelRequirements || {};
2075
2198
  const commands = [];
2076
- if (pipelineUrl) {
2077
- commands.push(`PIPELINE URL ${pipelineUrl}`);
2199
+ // Note: Do nothing, it is default
2200
+ // commands.push(`PROMPT`);
2201
+ if (modelVariant) {
2202
+ commands.push(`MODEL VARIANT ${capitalize(modelVariant)}`);
2078
2203
  }
2079
- if (bookVersion !== `undefined`) {
2080
- commands.push(`BOOK VERSION ${bookVersion}`);
2204
+ if (modelName) {
2205
+ commands.push(`MODEL NAME \`${modelName}\``);
2081
2206
  }
2082
- // TODO: [main] !!5 This increases size of the bundle and is probably not necessary
2083
- pipelineString = prettifyMarkdown(pipelineString);
2084
- for (const parameter of parameters.filter(({ isInput }) => isInput)) {
2085
- commands.push(`INPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
2207
+ return commands;
2208
+ }
2209
+ /**
2210
+ * Collects joker commands.
2211
+ *
2212
+ * @private internal utility of `createTaskSerialization`
2213
+ */
2214
+ function createJokerCommands(task) {
2215
+ var _a;
2216
+ return ((_a = task.jokerParameterNames) === null || _a === void 0 ? void 0 : _a.map((joker) => `JOKER {${joker}}`)) || [];
2217
+ }
2218
+ /**
2219
+ * Collects postprocessing commands.
2220
+ *
2221
+ * @private internal utility of `createTaskSerialization`
2222
+ */
2223
+ function createPostprocessingCommands(task) {
2224
+ var _a;
2225
+ return ((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || [];
2226
+ }
2227
+ /**
2228
+ * Collects expectation commands.
2229
+ *
2230
+ * @private internal utility of `createTaskSerialization`
2231
+ */
2232
+ function createExpectationCommands(task) {
2233
+ if (!task.expectations) {
2234
+ return [];
2086
2235
  }
2087
- for (const parameter of parameters.filter(({ isOutput }) => isOutput)) {
2088
- commands.push(`OUTPUT PARAMETER ${taskParameterJsonToString(parameter)}`);
2236
+ return Object.entries(task.expectations).flatMap(([unit, expectation]) => createExpectationCommandsForUnit(unit, expectation.min, expectation.max));
2237
+ }
2238
+ /**
2239
+ * Collects expectation commands for a single unit.
2240
+ *
2241
+ * @private internal utility of `createTaskSerialization`
2242
+ */
2243
+ function createExpectationCommandsForUnit(unit, min, max) {
2244
+ if (min === max) {
2245
+ return [`EXPECT EXACTLY ${min} ${formatExpectationUnit(unit, min)}`];
2089
2246
  }
2090
- pipelineString = spaceTrim$1((block) => `
2091
- ${block(pipelineString)}
2092
-
2093
- ${block(commands.map((command) => `- ${command}`).join('\n'))}
2094
- `);
2095
- for (const task of tasks) {
2096
- const {
2097
- /* Note: Not using:> name, */
2098
- title, description,
2099
- /* Note: dependentParameterNames, */
2100
- jokerParameterNames: jokers, taskType, content, postprocessingFunctionNames: postprocessing, expectations, format, resultingParameterName, } = task;
2101
- const commands = [];
2102
- let contentLanguage = 'text';
2103
- if (taskType === 'PROMPT_TASK') {
2104
- const { modelRequirements } = task;
2105
- const { modelName, modelVariant } = modelRequirements || {};
2106
- // Note: Do nothing, it is default
2107
- // commands.push(`PROMPT`);
2108
- if (modelVariant) {
2109
- commands.push(`MODEL VARIANT ${capitalize(modelVariant)}`);
2110
- }
2111
- if (modelName) {
2112
- commands.push(`MODEL NAME \`${modelName}\``);
2113
- }
2114
- }
2115
- else if (taskType === 'SIMPLE_TASK') {
2116
- commands.push(`SIMPLE TEMPLATE`);
2117
- // Note: Nothing special here
2118
- }
2119
- else if (taskType === 'SCRIPT_TASK') {
2120
- commands.push(`SCRIPT`);
2121
- if (task.contentLanguage) {
2122
- contentLanguage = task.contentLanguage;
2123
- }
2124
- else {
2125
- contentLanguage = '';
2126
- }
2127
- }
2128
- else if (taskType === 'DIALOG_TASK') {
2129
- commands.push(`DIALOG`);
2130
- // Note: Nothing special here
2131
- } // <- }else if([🅱]
2132
- if (jokers) {
2133
- for (const joker of jokers) {
2134
- commands.push(`JOKER {${joker}}`);
2135
- }
2136
- } /* not else */
2137
- if (postprocessing) {
2138
- for (const postprocessingFunctionName of postprocessing) {
2139
- commands.push(`POSTPROCESSING \`${postprocessingFunctionName}\``);
2140
- }
2141
- } /* not else */
2142
- if (expectations) {
2143
- for (const [unit, { min, max }] of Object.entries(expectations)) {
2144
- if (min === max) {
2145
- commands.push(`EXPECT EXACTLY ${min} ${capitalize(unit + (min > 1 ? 's' : ''))}`);
2146
- }
2147
- else {
2148
- if (min !== undefined) {
2149
- commands.push(`EXPECT MIN ${min} ${capitalize(unit + (min > 1 ? 's' : ''))}`);
2150
- } /* not else */
2151
- if (max !== undefined) {
2152
- commands.push(`EXPECT MAX ${max} ${capitalize(unit + (max > 1 ? 's' : ''))}`);
2153
- }
2154
- }
2155
- }
2156
- } /* not else */
2157
- if (format) {
2158
- if (format === 'JSON') {
2159
- // TODO: @deprecated remove
2160
- commands.push(`FORMAT JSON`);
2161
- }
2162
- } /* not else */
2163
- pipelineString = spaceTrim$1((block) => `
2164
- ${block(pipelineString)}
2247
+ const commands = [];
2248
+ if (min !== undefined) {
2249
+ commands.push(`EXPECT MIN ${min} ${formatExpectationUnit(unit, min)}`);
2250
+ }
2251
+ if (max !== undefined) {
2252
+ commands.push(`EXPECT MAX ${max} ${formatExpectationUnit(unit, max)}`);
2253
+ }
2254
+ return commands;
2255
+ }
2256
+ /**
2257
+ * Formats the expectation unit exactly as the legacy serializer does.
2258
+ *
2259
+ * @private internal utility of `createTaskSerialization`
2260
+ */
2261
+ function formatExpectationUnit(unit, amount) {
2262
+ return capitalize(unit + (amount > 1 ? 's' : ''));
2263
+ }
2264
+ /**
2265
+ * Collects format commands.
2266
+ *
2267
+ * @private internal utility of `createTaskSerialization`
2268
+ */
2269
+ function createFormatCommands(task) {
2270
+ if (task.format === 'JSON') {
2271
+ // TODO: @deprecated remove
2272
+ return ['FORMAT JSON'];
2273
+ }
2274
+ return [];
2275
+ }
2165
2276
 
2166
- ## ${title}
2277
+ /**
2278
+ * Stringifies one task section of the pipeline.
2279
+ *
2280
+ * @private internal utility of `pipelineJsonToString`
2281
+ */
2282
+ function stringifyTask(task) {
2283
+ const { title, description, content, resultingParameterName } = task;
2284
+ const { commands, contentLanguage } = createTaskSerialization(task);
2285
+ return spaceTrim$1((block) => `
2286
+ ## ${title}
2167
2287
 
2168
- ${block(description || '')}
2288
+ ${block(description || '')}
2169
2289
 
2170
- ${block(commands.map((command) => `- ${command}`).join('\n'))}
2290
+ ${block(stringifyCommands(commands))}
2171
2291
 
2172
- \`\`\`${contentLanguage}
2173
- ${block(spaceTrim$1(content))}
2174
- \`\`\`
2292
+ \`\`\`${contentLanguage}
2293
+ ${block(spaceTrim$1(content))}
2294
+ \`\`\`
2175
2295
 
2176
- \`-> {${resultingParameterName}}\`
2177
- `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
2178
- // <- TODO: [main] !!3 Escape
2179
- // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2180
- }
2181
- return validatePipelineString(pipelineString);
2296
+ \`-> {${resultingParameterName}}\`
2297
+ `); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
2298
+ // <- TODO: [main] !!3 Escape
2299
+ // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2182
2300
  }
2301
+
2183
2302
  /**
2184
- * Handles task parameter Json to string.
2303
+ * Converts promptbook in JSON format to string format
2185
2304
  *
2186
- * @private internal utility of `pipelineJsonToString`
2305
+ * @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
2306
+ * @param pipelineJson Promptbook in JSON format (.bookc)
2307
+ * @returns Promptbook in string format (.book.md)
2308
+ *
2309
+ * @public exported from `@promptbook/core`
2187
2310
  */
2188
- function taskParameterJsonToString(taskParameterJson) {
2189
- const { name, description } = taskParameterJson;
2190
- let parameterString = `{${name}}`;
2191
- if (description) {
2192
- parameterString = `${parameterString} ${description}`;
2311
+ function pipelineJsonToString(pipelineJson) {
2312
+ let pipelineString = createPipelineIntroduction(pipelineJson);
2313
+ const pipelineCommands = createPipelineCommands(pipelineJson);
2314
+ pipelineString = appendMarkdownBlock(pipelineString, stringifyCommands(pipelineCommands));
2315
+ for (const task of pipelineJson.tasks) {
2316
+ pipelineString = appendMarkdownBlock(pipelineString, stringifyTask(task));
2193
2317
  }
2194
- return parameterString;
2318
+ return validatePipelineString(pipelineString);
2195
2319
  }
2196
2320
  // TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
2197
2321
  // TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
@@ -2631,233 +2755,519 @@ function validatePipeline(pipeline) {
2631
2755
  */
2632
2756
  function validatePipeline_InnerFunction(pipeline) {
2633
2757
  // TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
2634
- const pipelineIdentification = (() => {
2635
- // Note: This is a 😐 implementation of [🚞]
2636
- const _ = [];
2637
- if (pipeline.sourceFile !== undefined) {
2638
- _.push(`File: ${pipeline.sourceFile}`);
2639
- }
2640
- if (pipeline.pipelineUrl !== undefined) {
2641
- _.push(`Url: ${pipeline.pipelineUrl}`);
2758
+ const context = createPipelineValidationContext(pipeline);
2759
+ validatePipelineMetadata(context);
2760
+ validatePipelineCollectionsStructure(context);
2761
+ validatePipelineParameters(context);
2762
+ validatePipelineTasks(context);
2763
+ validatePipelineDependencyResolution(context);
2764
+ // Note: Check that formfactor is corresponding to the pipeline interface
2765
+ // TODO: !!6 Implement this
2766
+ // pipeline.formfactorName
2767
+ }
2768
+ /**
2769
+ * Creates the shared validation context for one pipeline.
2770
+ *
2771
+ * @private internal utility of `validatePipeline`
2772
+ */
2773
+ function createPipelineValidationContext(pipeline) {
2774
+ return {
2775
+ pipeline,
2776
+ pipelineIdentification: getPipelineIdentification(pipeline),
2777
+ };
2778
+ }
2779
+ /**
2780
+ * Builds a short file/url identification block for validation errors.
2781
+ *
2782
+ * @private internal utility of `validatePipeline`
2783
+ */
2784
+ function getPipelineIdentification(pipeline) {
2785
+ // Note: This is a 😐 implementation of [🚞]
2786
+ const pipelineIdentificationParts = [];
2787
+ if (pipeline.sourceFile !== undefined) {
2788
+ pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
2789
+ }
2790
+ if (pipeline.pipelineUrl !== undefined) {
2791
+ pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
2792
+ }
2793
+ return pipelineIdentificationParts.join('\n');
2794
+ }
2795
+ /**
2796
+ * Validates pipeline-level metadata fields.
2797
+ *
2798
+ * @private internal step of `validatePipeline`
2799
+ */
2800
+ function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
2801
+ validatePipelineUrl(pipeline, pipelineIdentification);
2802
+ validatePipelineBookVersion(pipeline, pipelineIdentification);
2803
+ }
2804
+ /**
2805
+ * Validates that the expected top-level collections have array structure.
2806
+ *
2807
+ * @private internal step of `validatePipeline`
2808
+ */
2809
+ function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification }) {
2810
+ validatePipelineParametersCollection(pipeline, pipelineIdentification);
2811
+ validatePipelineTasksCollection(pipeline, pipelineIdentification);
2812
+ }
2813
+ /**
2814
+ * Validates all pipeline parameter declarations.
2815
+ *
2816
+ * @private internal step of `validatePipeline`
2817
+ */
2818
+ function validatePipelineParameters({ pipeline, pipelineIdentification }) {
2819
+ for (const parameter of pipeline.parameters) {
2820
+ validatePipelineParameter(parameter, pipeline, pipelineIdentification);
2821
+ }
2822
+ }
2823
+ /**
2824
+ * Validates all pipeline tasks and their per-task invariants.
2825
+ *
2826
+ * @private internal step of `validatePipeline`
2827
+ */
2828
+ function validatePipelineTasks({ pipeline, pipelineIdentification }) {
2829
+ // Note: All input parameters are defined - so that they can be used as result of some task
2830
+ const definedParameters = createInitiallyDefinedParameters(pipeline);
2831
+ for (const task of pipeline.tasks) {
2832
+ validatePipelineTask(task, definedParameters, pipelineIdentification);
2833
+ }
2834
+ }
2835
+ /**
2836
+ * Validates that task dependencies can be resolved without cycles or missing definitions.
2837
+ *
2838
+ * @private internal step of `validatePipeline`
2839
+ */
2840
+ function validatePipelineDependencyResolution({ pipeline, pipelineIdentification }) {
2841
+ let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
2842
+ let loopLimit = LOOP_LIMIT;
2843
+ while (hasUnresolvedTasks(dependencyResolutionState)) {
2844
+ if (loopLimit-- < 0) {
2845
+ throw createDependencyResolutionLoopLimitError(pipelineIdentification);
2642
2846
  }
2643
- return _.join('\n');
2644
- })();
2645
- if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
2646
- // <- Note: [🚲]
2647
- throw new PipelineLogicError(spaceTrim$1((block) => `
2648
- Invalid promptbook URL "${pipeline.pipelineUrl}"
2649
-
2650
- ${block(pipelineIdentification)}
2651
- `));
2847
+ dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
2652
2848
  }
2653
- if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
2654
- // <- Note: [🚲]
2655
- throw new PipelineLogicError(spaceTrim$1((block) => `
2656
- Invalid Promptbook Version "${pipeline.bookVersion}"
2849
+ }
2850
+ /**
2851
+ * Validates one pipeline parameter declaration.
2852
+ *
2853
+ * @private internal step of `validatePipeline`
2854
+ */
2855
+ function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
2856
+ validateParameterDirection(parameter, pipelineIdentification);
2857
+ validateParameterUsage(parameter, pipeline, pipelineIdentification);
2858
+ validateParameterDefinition(parameter, pipeline, pipelineIdentification);
2859
+ }
2860
+ /**
2861
+ * Validates one pipeline task and its invariants.
2862
+ *
2863
+ * @private internal step of `validatePipeline`
2864
+ */
2865
+ function validatePipelineTask(task, definedParameters, pipelineIdentification) {
2866
+ validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
2867
+ validateTaskJokers(task, pipelineIdentification);
2868
+ validateTaskExpectations(task, pipelineIdentification);
2869
+ }
2870
+ /**
2871
+ * Validates the pipeline URL, when present.
2872
+ *
2873
+ * @private internal utility of `validatePipeline`
2874
+ */
2875
+ function validatePipelineUrl(pipeline, pipelineIdentification) {
2876
+ if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
2877
+ return;
2878
+ }
2879
+ // <- Note: [🚲]
2880
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2881
+ Invalid promptbook URL "${pipeline.pipelineUrl}"
2657
2882
 
2658
- ${block(pipelineIdentification)}
2659
- `));
2883
+ ${block(pipelineIdentification)}
2884
+ `));
2885
+ }
2886
+ /**
2887
+ * Validates the Promptbook version, when present.
2888
+ *
2889
+ * @private internal utility of `validatePipeline`
2890
+ */
2891
+ function validatePipelineBookVersion(pipeline, pipelineIdentification) {
2892
+ if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
2893
+ return;
2660
2894
  }
2895
+ // <- Note: [🚲]
2896
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2897
+ Invalid Promptbook Version "${pipeline.bookVersion}"
2898
+
2899
+ ${block(pipelineIdentification)}
2900
+ `));
2901
+ }
2902
+ /**
2903
+ * Validates that `pipeline.parameters` is an array.
2904
+ *
2905
+ * @private internal utility of `validatePipeline`
2906
+ */
2907
+ function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
2661
2908
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
2662
- if (!Array.isArray(pipeline.parameters)) {
2663
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2664
- throw new ParseError(spaceTrim$1((block) => `
2665
- Pipeline is valid JSON but with wrong structure
2909
+ if (Array.isArray(pipeline.parameters)) {
2910
+ return;
2911
+ }
2912
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2913
+ throw new ParseError(spaceTrim$1((block) => `
2914
+ Pipeline is valid JSON but with wrong structure
2666
2915
 
2667
- \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
2916
+ \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
2668
2917
 
2669
- ${block(pipelineIdentification)}
2670
- `));
2671
- }
2918
+ ${block(pipelineIdentification)}
2919
+ `));
2920
+ }
2921
+ /**
2922
+ * Validates that `pipeline.tasks` is an array.
2923
+ *
2924
+ * @private internal utility of `validatePipeline`
2925
+ */
2926
+ function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
2672
2927
  // TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
2673
- if (!Array.isArray(pipeline.tasks)) {
2674
- // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2675
- throw new ParseError(spaceTrim$1((block) => `
2676
- Pipeline is valid JSON but with wrong structure
2928
+ if (Array.isArray(pipeline.tasks)) {
2929
+ return;
2930
+ }
2931
+ // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
2932
+ throw new ParseError(spaceTrim$1((block) => `
2933
+ Pipeline is valid JSON but with wrong structure
2677
2934
 
2678
- \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
2935
+ \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
2679
2936
 
2680
- ${block(pipelineIdentification)}
2681
- `));
2937
+ ${block(pipelineIdentification)}
2938
+ `));
2939
+ }
2940
+ /**
2941
+ * Validates that one parameter does not declare incompatible directions.
2942
+ *
2943
+ * @private internal utility of `validatePipeline`
2944
+ */
2945
+ function validateParameterDirection(parameter, pipelineIdentification) {
2946
+ if (!parameter.isInput || !parameter.isOutput) {
2947
+ return;
2682
2948
  }
2683
- /*
2684
- TODO: [🧠][🅾] Should be empty pipeline valid or not
2685
- // Note: Check that pipeline has some tasks
2686
- if (pipeline.tasks.length === 0) {
2687
- throw new PipelineLogicError(
2688
- spaceTrim(
2689
- (block) => `
2690
- Pipeline must have at least one task
2949
+ const parameterName = parameter.name;
2950
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2691
2951
 
2692
- ${block(pipelineIdentification)}
2693
- `,
2694
- ),
2695
- );
2696
- }
2697
- */
2698
- // Note: Check each parameter individually
2699
- for (const parameter of pipeline.parameters) {
2700
- if (parameter.isInput && parameter.isOutput) {
2701
- throw new PipelineLogicError(spaceTrim$1((block) => `
2952
+ Parameter \`{${parameterName}}\` can not be both input and output
2702
2953
 
2703
- Parameter \`{${parameter.name}}\` can not be both input and output
2954
+ ${block(pipelineIdentification)}
2955
+ `));
2956
+ }
2957
+ /**
2958
+ * Validates that one intermediate parameter is actually consumed by at least one task.
2959
+ *
2960
+ * @private internal utility of `validatePipeline`
2961
+ */
2962
+ function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
2963
+ if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
2964
+ return;
2965
+ }
2966
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2967
+ Parameter \`{${parameter.name}}\` is created but not used
2704
2968
 
2705
- ${block(pipelineIdentification)}
2706
- `));
2707
- }
2708
- // Note: Testing that parameter is either intermediate or output BUT not created and unused
2709
- if (!parameter.isInput &&
2710
- !parameter.isOutput &&
2711
- !pipeline.tasks.some((task) => task.dependentParameterNames.includes(parameter.name))) {
2712
- throw new PipelineLogicError(spaceTrim$1((block) => `
2713
- Parameter \`{${parameter.name}}\` is created but not used
2969
+ You can declare {${parameter.name}} as output parameter by adding in the header:
2970
+ - OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
2714
2971
 
2715
- You can declare {${parameter.name}} as output parameter by adding in the header:
2716
- - OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
2972
+ ${block(pipelineIdentification)}
2717
2973
 
2718
- ${block(pipelineIdentification)}
2974
+ `));
2975
+ }
2976
+ /**
2977
+ * Validates that one non-input parameter is produced by at least one task.
2978
+ *
2979
+ * @private internal utility of `validatePipeline`
2980
+ */
2981
+ function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
2982
+ if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
2983
+ return;
2984
+ }
2985
+ throw new PipelineLogicError(spaceTrim$1((block) => `
2986
+ Parameter \`{${parameter.name}}\` is declared but not defined
2719
2987
 
2720
- `));
2721
- }
2722
- // Note: Testing that parameter is either input or result of some task
2723
- if (!parameter.isInput && !pipeline.tasks.some((task) => task.resultingParameterName === parameter.name)) {
2724
- throw new PipelineLogicError(spaceTrim$1((block) => `
2725
- Parameter \`{${parameter.name}}\` is declared but not defined
2988
+ You can do one of these:
2989
+ 1) Remove declaration of \`{${parameter.name}}\`
2990
+ 2) Add task that results in \`-> {${parameter.name}}\`
2726
2991
 
2727
- You can do one of these:
2728
- 1) Remove declaration of \`{${parameter.name}}\`
2729
- 2) Add task that results in \`-> {${parameter.name}}\`
2992
+ ${block(pipelineIdentification)}
2993
+ `));
2994
+ }
2995
+ /**
2996
+ * Checks whether one parameter is consumed by at least one task dependency list.
2997
+ *
2998
+ * @private internal utility of `validatePipeline`
2999
+ */
3000
+ function isParameterUsedByAnyTask(parameter, tasks) {
3001
+ return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
3002
+ }
3003
+ /**
3004
+ * Checks whether one parameter is produced by at least one task.
3005
+ *
3006
+ * @private internal utility of `validatePipeline`
3007
+ */
3008
+ function isParameterDefinedByAnyTask(parameter, tasks) {
3009
+ return tasks.some((task) => task.resultingParameterName === parameter.name);
3010
+ }
3011
+ /**
3012
+ * Collects the parameter names that are already defined before task validation starts.
3013
+ *
3014
+ * @private internal utility of `validatePipeline`
3015
+ */
3016
+ function createInitiallyDefinedParameters(pipeline) {
3017
+ return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
3018
+ }
3019
+ /**
3020
+ * Validates one task result parameter declaration and marks it as defined.
3021
+ *
3022
+ * @private internal utility of `validatePipeline`
3023
+ */
3024
+ function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
3025
+ if (definedParameters.has(task.resultingParameterName)) {
3026
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3027
+ Parameter \`{${task.resultingParameterName}}\` is defined multiple times
2730
3028
 
2731
- ${block(pipelineIdentification)}
2732
- `));
2733
- }
3029
+ ${block(pipelineIdentification)}
3030
+ `));
2734
3031
  }
2735
- // Note: All input parameters are defined - so that they can be used as result of some task
2736
- const definedParameters = new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
2737
- // Note: Checking each task individually
2738
- for (const task of pipeline.tasks) {
2739
- if (definedParameters.has(task.resultingParameterName)) {
2740
- throw new PipelineLogicError(spaceTrim$1((block) => `
2741
- Parameter \`{${task.resultingParameterName}}\` is defined multiple times
3032
+ if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
3033
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3034
+ Parameter name {${task.resultingParameterName}} is reserved, please use different name
2742
3035
 
2743
- ${block(pipelineIdentification)}
2744
- `));
2745
- }
2746
- if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
2747
- throw new PipelineLogicError(spaceTrim$1((block) => `
2748
- Parameter name {${task.resultingParameterName}} is reserved, please use different name
3036
+ ${block(pipelineIdentification)}
3037
+ `));
3038
+ }
3039
+ definedParameters.add(task.resultingParameterName);
3040
+ }
3041
+ /**
3042
+ * Validates joker parameters for one task.
3043
+ *
3044
+ * @private internal utility of `validatePipeline`
3045
+ */
3046
+ function validateTaskJokers(task, pipelineIdentification) {
3047
+ if (!hasTaskJokers(task)) {
3048
+ return;
3049
+ }
3050
+ validateTaskSupportsJokers(task, pipelineIdentification);
3051
+ validateTaskJokerDependencies(task, pipelineIdentification);
3052
+ }
3053
+ /**
3054
+ * Checks whether one task declares any joker parameters.
3055
+ *
3056
+ * @private internal utility of `validatePipeline`
3057
+ */
3058
+ function hasTaskJokers(task) {
3059
+ return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
3060
+ }
3061
+ /**
3062
+ * Validates that a task has the required supporting features when using jokers.
3063
+ *
3064
+ * @private internal utility of `validatePipeline`
3065
+ */
3066
+ function validateTaskSupportsJokers(task, pipelineIdentification) {
3067
+ if (task.format ||
3068
+ task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
3069
+ return;
3070
+ }
3071
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3072
+ Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
2749
3073
 
2750
- ${block(pipelineIdentification)}
2751
- `));
3074
+ ${block(pipelineIdentification)}
3075
+ `));
3076
+ }
3077
+ /**
3078
+ * Validates that every joker parameter is also listed among task dependencies.
3079
+ *
3080
+ * @private internal utility of `validatePipeline`
3081
+ */
3082
+ function validateTaskJokerDependencies(task, pipelineIdentification) {
3083
+ for (const joker of task.jokerParameterNames) {
3084
+ if (task.dependentParameterNames.includes(joker)) {
3085
+ continue;
2752
3086
  }
2753
- definedParameters.add(task.resultingParameterName);
2754
- if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
2755
- if (!task.format &&
2756
- !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
2757
- throw new PipelineLogicError(spaceTrim$1((block) => `
2758
- Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
2759
-
2760
- ${block(pipelineIdentification)}
2761
- `));
2762
- }
2763
- for (const joker of task.jokerParameterNames) {
2764
- if (!task.dependentParameterNames.includes(joker)) {
2765
- throw new PipelineLogicError(spaceTrim$1((block) => `
2766
- Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
3087
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3088
+ Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
2767
3089
 
2768
- ${block(pipelineIdentification)}
2769
- `));
2770
- }
2771
- }
2772
- }
2773
- if (task.expectations) {
2774
- for (const [unit, { min, max }] of Object.entries(task.expectations)) {
2775
- if (min !== undefined && max !== undefined && min > max) {
2776
- throw new PipelineLogicError(spaceTrim$1((block) => `
2777
- Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
3090
+ ${block(pipelineIdentification)}
3091
+ `));
3092
+ }
3093
+ }
3094
+ /**
3095
+ * Validates all expectation bounds configured on one task.
3096
+ *
3097
+ * @private internal utility of `validatePipeline`
3098
+ */
3099
+ function validateTaskExpectations(task, pipelineIdentification) {
3100
+ if (!task.expectations) {
3101
+ return;
3102
+ }
3103
+ for (const [unit, { min, max }] of Object.entries(task.expectations)) {
3104
+ validateTaskExpectationRange(unit, min, max, pipelineIdentification);
3105
+ validateTaskExpectationMin(unit, min, pipelineIdentification);
3106
+ validateTaskExpectationMax(unit, max, pipelineIdentification);
3107
+ }
3108
+ }
3109
+ /**
3110
+ * Validates the minimum and maximum expectation ordering for one unit.
3111
+ *
3112
+ * @private internal utility of `validatePipeline`
3113
+ */
3114
+ function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
3115
+ if (min === undefined || max === undefined || min <= max) {
3116
+ return;
3117
+ }
3118
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3119
+ Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
2778
3120
 
2779
- ${block(pipelineIdentification)}
2780
- `));
2781
- }
2782
- if (min !== undefined && min < 0) {
2783
- throw new PipelineLogicError(spaceTrim$1((block) => `
2784
- Min expectation of ${unit} must be zero or positive
3121
+ ${block(pipelineIdentification)}
3122
+ `));
3123
+ }
3124
+ /**
3125
+ * Validates the minimum expectation bound for one unit.
3126
+ *
3127
+ * @private internal utility of `validatePipeline`
3128
+ */
3129
+ function validateTaskExpectationMin(unit, min, pipelineIdentification) {
3130
+ if (min === undefined || min >= 0) {
3131
+ return;
3132
+ }
3133
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3134
+ Min expectation of ${unit} must be zero or positive
2785
3135
 
2786
- ${block(pipelineIdentification)}
2787
- `));
2788
- }
2789
- if (max !== undefined && max <= 0) {
2790
- throw new PipelineLogicError(spaceTrim$1((block) => `
2791
- Max expectation of ${unit} must be positive
3136
+ ${block(pipelineIdentification)}
3137
+ `));
3138
+ }
3139
+ /**
3140
+ * Validates the maximum expectation bound for one unit.
3141
+ *
3142
+ * @private internal utility of `validatePipeline`
3143
+ */
3144
+ function validateTaskExpectationMax(unit, max, pipelineIdentification) {
3145
+ if (max === undefined || max > 0) {
3146
+ return;
3147
+ }
3148
+ throw new PipelineLogicError(spaceTrim$1((block) => `
3149
+ Max expectation of ${unit} must be positive
2792
3150
 
2793
- ${block(pipelineIdentification)}
2794
- `));
2795
- }
2796
- }
2797
- }
3151
+ ${block(pipelineIdentification)}
3152
+ `));
3153
+ }
3154
+ /**
3155
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
3156
+ *
3157
+ * @private internal utility of `validatePipeline`
3158
+ */
3159
+ function createInitialDependencyResolutionState(pipeline) {
3160
+ return {
3161
+ resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
3162
+ unresolvedTasks: [...pipeline.tasks],
3163
+ };
3164
+ }
3165
+ /**
3166
+ * Checks whether dependency resolution still has tasks left to process.
3167
+ *
3168
+ * @private internal utility of `validatePipeline`
3169
+ */
3170
+ function hasUnresolvedTasks({ unresolvedTasks }) {
3171
+ return unresolvedTasks.length > 0;
3172
+ }
3173
+ /**
3174
+ * Resolves the next batch of currently satisfiable tasks.
3175
+ *
3176
+ * @private internal utility of `validatePipeline`
3177
+ */
3178
+ function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
3179
+ const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
3180
+ if (currentlyResolvedTasks.length === 0) {
3181
+ throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
2798
3182
  }
2799
- // Note: Detect circular dependencies
2800
- let resovedParameters = pipeline.parameters
3183
+ return {
3184
+ resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
3185
+ unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
3186
+ };
3187
+ }
3188
+ /**
3189
+ * Collects the parameter names that are already resolvable before dependency traversal starts.
3190
+ *
3191
+ * @private internal utility of `validatePipeline`
3192
+ */
3193
+ function createInitiallyResolvedParameterNames(pipeline) {
3194
+ let resolvedParameterNames = pipeline.parameters
2801
3195
  .filter(({ isInput }) => isInput)
2802
3196
  .map(({ name }) => name);
2803
- // Note: All reserved parameters are resolved
2804
3197
  for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
2805
- resovedParameters = [...resovedParameters, reservedParameterName];
3198
+ resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
2806
3199
  }
2807
- let unresovedTasks = [...pipeline.tasks];
2808
- let loopLimit = LOOP_LIMIT;
2809
- while (unresovedTasks.length > 0) {
2810
- if (loopLimit-- < 0) {
2811
- // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
2812
- throw new UnexpectedError(spaceTrim$1((block) => `
2813
- Loop limit reached during detection of circular dependencies in \`validatePipeline\`
3200
+ return resolvedParameterNames;
3201
+ }
3202
+ /**
3203
+ * Adds newly resolved task outputs to the resolved parameter list.
3204
+ *
3205
+ * @private internal utility of `validatePipeline`
3206
+ */
3207
+ function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
3208
+ return [
3209
+ ...resolvedParameterNames,
3210
+ ...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
3211
+ ];
3212
+ }
3213
+ /**
3214
+ * Selects tasks whose dependencies are already resolved.
3215
+ *
3216
+ * @private internal utility of `validatePipeline`
3217
+ */
3218
+ function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
3219
+ return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
3220
+ }
3221
+ /**
3222
+ * Creates the unexpected loop-limit error for dependency resolution.
3223
+ *
3224
+ * @private internal utility of `validatePipeline`
3225
+ */
3226
+ function createDependencyResolutionLoopLimitError(pipelineIdentification) {
3227
+ // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
3228
+ return new UnexpectedError(spaceTrim$1((block) => `
3229
+ Loop limit reached during detection of circular dependencies in \`validatePipeline\`
2814
3230
 
2815
- ${block(pipelineIdentification)}
2816
- `));
2817
- }
2818
- const currentlyResovedTasks = unresovedTasks.filter((task) => task.dependentParameterNames.every((name) => resovedParameters.includes(name)));
2819
- if (currentlyResovedTasks.length === 0) {
2820
- throw new PipelineLogicError(
2821
- // TODO: [🐎] DRY
2822
- spaceTrim$1((block) => `
3231
+ ${block(pipelineIdentification)}
3232
+ `));
3233
+ }
3234
+ /**
3235
+ * Creates the detailed error for unresolved or circular task dependencies.
3236
+ *
3237
+ * @private internal utility of `validatePipeline`
3238
+ */
3239
+ function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
3240
+ return new PipelineLogicError(
3241
+ // TODO: [🐎] DRY
3242
+ spaceTrim$1((block) => `
2823
3243
 
2824
- Can not resolve some parameters:
2825
- Either you are using a parameter that is not defined, or there are some circular dependencies.
3244
+ Can not resolve some parameters:
3245
+ Either you are using a parameter that is not defined, or there are some circular dependencies.
2826
3246
 
2827
- ${block(pipelineIdentification)}
3247
+ ${block(pipelineIdentification)}
2828
3248
 
2829
- **Can not resolve:**
2830
- ${block(unresovedTasks
2831
- .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
2832
- .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
2833
- .join(' and ')}`)
2834
- .join('\n'))}
3249
+ **Can not resolve:**
3250
+ ${block(unresolvedTasks
3251
+ .map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
3252
+ .map((dependentParameterName) => `\`{${dependentParameterName}}\``)
3253
+ .join(' and ')}`)
3254
+ .join('\n'))}
2835
3255
 
2836
- **Resolved:**
2837
- ${block(resovedParameters
2838
- .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
2839
- .map((name) => `- Parameter \`{${name}}\``)
2840
- .join('\n'))}
3256
+ **Resolved:**
3257
+ ${block(resolvedParameterNames
3258
+ .filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
3259
+ .map((name) => `- Parameter \`{${name}}\``)
3260
+ .join('\n'))}
2841
3261
 
2842
3262
 
2843
- **Reserved (which are available):**
2844
- ${block(resovedParameters
2845
- .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
2846
- .map((name) => `- Parameter \`{${name}}\``)
2847
- .join('\n'))}
3263
+ **Reserved (which are available):**
3264
+ ${block(resolvedParameterNames
3265
+ .filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
3266
+ .map((name) => `- Parameter \`{${name}}\``)
3267
+ .join('\n'))}
2848
3268
 
2849
3269
 
2850
- `));
2851
- }
2852
- resovedParameters = [
2853
- ...resovedParameters,
2854
- ...currentlyResovedTasks.map(({ resultingParameterName }) => resultingParameterName),
2855
- ];
2856
- unresovedTasks = unresovedTasks.filter((task) => !currentlyResovedTasks.includes(task));
2857
- }
2858
- // Note: Check that formfactor is corresponding to the pipeline interface
2859
- // TODO: !!6 Implement this
2860
- // pipeline.formfactorName
3270
+ `));
2861
3271
  }
2862
3272
  /**
2863
3273
  * TODO: [🧞‍♀️] Do not allow joker + foreach
@@ -3553,77 +3963,288 @@ function assertsTaskSuccessful(executionResult) {
3553
3963
  // TODO: [🧠] Can this return type be better typed than void
3554
3964
 
3555
3965
  /**
3556
- * Helper to create a new task
3966
+ * Resolves the short task summary shown in the UI.
3557
3967
  *
3558
- * @private internal helper function
3968
+ * @private internal helper function of `ExecutionTask`
3559
3969
  */
3560
- function createTask(options) {
3561
- const { taskType, taskProcessCallback } = options;
3562
- let { title } = options;
3563
- // TODO: [🐙] DRY
3564
- const taskId = `${taskType.toLowerCase().substring(0, 4)}-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
3565
- let status = 'RUNNING';
3566
- const createdAt = new Date();
3567
- let updatedAt = createdAt;
3568
- const errors = [];
3569
- const warnings = [];
3570
- const llmCalls = [];
3571
- let currentValue = {};
3572
- let customTldr = null;
3573
- const partialResultSubject = new Subject();
3574
- // <- Note: Not using `BehaviorSubject` because on error we can't access the last value
3575
- const finalResultPromise = /* not await */ taskProcessCallback((newOngoingResult) => {
3576
- if (newOngoingResult.title) {
3577
- title = newOngoingResult.title;
3578
- }
3579
- updatedAt = new Date();
3580
- Object.assign(currentValue, newOngoingResult);
3581
- // <- TODO: assign deep
3582
- partialResultSubject.next(newOngoingResult);
3583
- }, (tldrInfo) => {
3584
- customTldr = tldrInfo;
3585
- updatedAt = new Date();
3586
- }, (llmCall) => {
3587
- llmCalls.push(llmCall);
3588
- updatedAt = new Date();
3589
- });
3590
- finalResultPromise
3591
- .catch((error) => {
3592
- errors.push(error);
3593
- partialResultSubject.error(error);
3594
- })
3595
- .then((executionResult) => {
3596
- if (executionResult) {
3597
- try {
3598
- updatedAt = new Date();
3599
- errors.push(...executionResult.errors);
3600
- warnings.push(...executionResult.warnings);
3601
- // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
3602
- // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
3603
- // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
3604
- // And delete `ExecutionTask.currentValue.preparedPipeline`
3605
- assertsTaskSuccessful(executionResult);
3606
- status = 'FINISHED';
3607
- currentValue = jsonStringsToJsons(executionResult);
3608
- // <- TODO: [🧠] Is this a good idea to convert JSON strins to JSONs?
3609
- partialResultSubject.next(executionResult);
3610
- }
3611
- catch (error) {
3612
- assertsError(error);
3613
- status = 'ERROR';
3614
- errors.push(error);
3615
- partialResultSubject.error(error);
3616
- }
3617
- }
3618
- partialResultSubject.complete();
3619
- });
3620
- async function asPromise(options) {
3621
- const { isCrashedOnError = true } = options || {};
3622
- const finalResult = await finalResultPromise;
3623
- if (isCrashedOnError) {
3624
- assertsTaskSuccessful(finalResult);
3625
- }
3626
- return finalResult;
3970
+ function resolveTaskTldr(options) {
3971
+ const { customTldr } = options;
3972
+ if (customTldr) {
3973
+ return customTldr;
3974
+ }
3975
+ return {
3976
+ percent: resolveTaskPercent(options),
3977
+ message: `${resolveTaskMessage(options)} (!!!fallback)`,
3978
+ };
3979
+ }
3980
+ /**
3981
+ * Resolves the best progress percentage for the current task state.
3982
+ *
3983
+ * @private internal helper function of `ExecutionTask`
3984
+ */
3985
+ function resolveTaskPercent(options) {
3986
+ const explicitPercent = getExplicitTaskPercent(options.currentValue);
3987
+ if (typeof explicitPercent === 'number') {
3988
+ return normalizeTaskPercent(explicitPercent);
3989
+ }
3990
+ return normalizeTaskPercent(calculateSimulatedTaskPercent(options));
3991
+ }
3992
+ /**
3993
+ * Picks a directly reported progress percentage from the task result snapshot.
3994
+ *
3995
+ * @private internal helper function of `ExecutionTask`
3996
+ */
3997
+ function getExplicitTaskPercent(currentValue) {
3998
+ var _a, _b, _c, _d, _e, _f;
3999
+ 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);
4000
+ }
4001
+ /**
4002
+ * Simulates progress when the task result does not expose an explicit percentage.
4003
+ *
4004
+ * @private internal helper function of `ExecutionTask`
4005
+ */
4006
+ function calculateSimulatedTaskPercent(options) {
4007
+ const { currentValue, status, createdAt } = options;
4008
+ const elapsedMs = new Date().getTime() - createdAt.getTime();
4009
+ const timeProgress = Math.min(elapsedMs / DEFAULT_TASK_SIMULATED_DURATION_MS, 1);
4010
+ const { subtaskCount, completedSubtasks } = summarizeTaskSubtasks(currentValue);
4011
+ if (status === 'FINISHED') {
4012
+ return 1;
4013
+ }
4014
+ if (status === 'ERROR') {
4015
+ return 0;
4016
+ }
4017
+ return Math.min(completedSubtasks / subtaskCount + (1 / subtaskCount) * timeProgress, 1);
4018
+ }
4019
+ /**
4020
+ * Counts total and completed subtasks used by the fallback progress simulation.
4021
+ *
4022
+ * @private internal helper function of `ExecutionTask`
4023
+ */
4024
+ function summarizeTaskSubtasks(currentValue) {
4025
+ if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks)) {
4026
+ return { subtaskCount: 1, completedSubtasks: 0 };
4027
+ }
4028
+ return {
4029
+ subtaskCount: currentValue.subtasks.length || 1,
4030
+ completedSubtasks: currentValue.subtasks.filter(isTaskSubtaskCompleted).length,
4031
+ };
4032
+ }
4033
+ /**
4034
+ * Tells whether a task subtask is already finished.
4035
+ *
4036
+ * @private internal helper function of `ExecutionTask`
4037
+ */
4038
+ function isTaskSubtaskCompleted(subtask) {
4039
+ return subtask.done || subtask.completed || false;
4040
+ }
4041
+ /**
4042
+ * Normalizes a progress percentage into the expected `0..1` range.
4043
+ *
4044
+ * @private internal helper function of `ExecutionTask`
4045
+ */
4046
+ function normalizeTaskPercent(percentRaw) {
4047
+ let percent = Number(percentRaw) || 0;
4048
+ if (percent < 0) {
4049
+ percent = 0;
4050
+ }
4051
+ if (percent > 1) {
4052
+ percent = 1;
4053
+ }
4054
+ return percent;
4055
+ }
4056
+ /**
4057
+ * Resolves the best human-readable status message for the current task state.
4058
+ *
4059
+ * @private internal helper function of `ExecutionTask`
4060
+ */
4061
+ function resolveTaskMessage(options) {
4062
+ return (getCurrentValueMessage(options.currentValue) ||
4063
+ getCurrentSubtaskMessage(options.currentValue) ||
4064
+ getLatestIssueMessage(options.errors, 'Error') ||
4065
+ getLatestIssueMessage(options.warnings, 'Warning') ||
4066
+ getStatusMessage(options.status));
4067
+ }
4068
+ /**
4069
+ * Picks a message already reported by the current task result snapshot.
4070
+ *
4071
+ * @private internal helper function of `ExecutionTask`
4072
+ */
4073
+ function getCurrentValueMessage(currentValue) {
4074
+ var _a, _b, _c, _d;
4075
+ 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;
4076
+ }
4077
+ /**
4078
+ * Builds a fallback message from the first unfinished subtask title.
4079
+ *
4080
+ * @private internal helper function of `ExecutionTask`
4081
+ */
4082
+ function getCurrentSubtaskMessage(currentValue) {
4083
+ if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks) || currentValue.subtasks.length === 0) {
4084
+ return undefined;
4085
+ }
4086
+ const currentSubtask = currentValue.subtasks.find((subtask) => !isTaskSubtaskCompleted(subtask));
4087
+ if (!(currentSubtask === null || currentSubtask === void 0 ? void 0 : currentSubtask.title)) {
4088
+ return undefined;
4089
+ }
4090
+ return `Working on ${currentSubtask.title}`;
4091
+ }
4092
+ /**
4093
+ * Picks the latest error or warning message, with the legacy generic fallback label.
4094
+ *
4095
+ * @private internal helper function of `ExecutionTask`
4096
+ */
4097
+ function getLatestIssueMessage(issues, fallbackMessage) {
4098
+ if (issues.length === 0) {
4099
+ return undefined;
4100
+ }
4101
+ return issues[issues.length - 1].message || fallbackMessage;
4102
+ }
4103
+ /**
4104
+ * Builds the final status-based fallback message.
4105
+ *
4106
+ * @private internal helper function of `ExecutionTask`
4107
+ */
4108
+ function getStatusMessage(status) {
4109
+ if (status === 'FINISHED') {
4110
+ return 'Finished';
4111
+ }
4112
+ if (status === 'ERROR') {
4113
+ return 'Error';
4114
+ }
4115
+ return 'Running';
4116
+ }
4117
+
4118
+ /**
4119
+ * Creates the initial mutable state for a task.
4120
+ *
4121
+ * @private internal helper function
4122
+ */
4123
+ function createTaskState(title, createdAt) {
4124
+ return {
4125
+ title,
4126
+ status: 'RUNNING',
4127
+ updatedAt: createdAt,
4128
+ errors: [],
4129
+ warnings: [],
4130
+ llmCalls: [],
4131
+ currentValue: {},
4132
+ customTldr: null,
4133
+ };
4134
+ }
4135
+ /**
4136
+ * Creates the partial-result updater passed into the task process callback.
4137
+ *
4138
+ * @private internal helper function
4139
+ */
4140
+ function createOngoingResultUpdater(taskState, partialResultSubject) {
4141
+ return (newOngoingResult) => {
4142
+ if (newOngoingResult.title) {
4143
+ taskState.title = newOngoingResult.title;
4144
+ }
4145
+ taskState.updatedAt = new Date();
4146
+ Object.assign(taskState.currentValue, newOngoingResult);
4147
+ // <- TODO: assign deep
4148
+ partialResultSubject.next(newOngoingResult);
4149
+ };
4150
+ }
4151
+ /**
4152
+ * Creates the custom-TLDR updater passed into the task process callback.
4153
+ *
4154
+ * @private internal helper function
4155
+ */
4156
+ function createTldrUpdater(taskState) {
4157
+ return (tldrInfo) => {
4158
+ taskState.customTldr = tldrInfo;
4159
+ taskState.updatedAt = new Date();
4160
+ };
4161
+ }
4162
+ /**
4163
+ * Creates the LLM call logger passed into the task process callback.
4164
+ *
4165
+ * @private internal helper function
4166
+ */
4167
+ function createLlmCallLogger(taskState) {
4168
+ return (llmCall) => {
4169
+ taskState.llmCalls.push(llmCall);
4170
+ taskState.updatedAt = new Date();
4171
+ };
4172
+ }
4173
+ /**
4174
+ * Wires the task promise into the observable/error lifecycle.
4175
+ *
4176
+ * @private internal helper function
4177
+ */
4178
+ function settleTaskPromise(finalResultPromise, taskState, partialResultSubject) {
4179
+ finalResultPromise
4180
+ .catch((error) => {
4181
+ taskState.errors.push(error);
4182
+ partialResultSubject.error(error);
4183
+ })
4184
+ .then((executionResult) => {
4185
+ if (executionResult) {
4186
+ try {
4187
+ finalizeTaskResult(executionResult, taskState, partialResultSubject);
4188
+ }
4189
+ catch (error) {
4190
+ failTaskResult(error, taskState, partialResultSubject);
4191
+ }
4192
+ }
4193
+ partialResultSubject.complete();
4194
+ });
4195
+ }
4196
+ /**
4197
+ * Applies the final successful task result into the mutable task state.
4198
+ *
4199
+ * @private internal helper function
4200
+ */
4201
+ function finalizeTaskResult(executionResult, taskState, partialResultSubject) {
4202
+ taskState.updatedAt = new Date();
4203
+ taskState.errors.push(...executionResult.errors);
4204
+ taskState.warnings.push(...executionResult.warnings);
4205
+ // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
4206
+ // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
4207
+ // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
4208
+ // And delete `ExecutionTask.currentValue.preparedPipeline`
4209
+ assertsTaskSuccessful(executionResult);
4210
+ taskState.status = 'FINISHED';
4211
+ taskState.currentValue = jsonStringsToJsons(executionResult);
4212
+ // <- TODO: [🧠] Is this a good idea to convert JSON strins to JSONs?
4213
+ partialResultSubject.next(executionResult);
4214
+ }
4215
+ /**
4216
+ * Records a final-result failure after the task promise itself resolved.
4217
+ *
4218
+ * @private internal helper function
4219
+ */
4220
+ function failTaskResult(error, taskState, partialResultSubject) {
4221
+ assertsError(error);
4222
+ taskState.status = 'ERROR';
4223
+ taskState.errors.push(error);
4224
+ partialResultSubject.error(error);
4225
+ }
4226
+ /**
4227
+ * Helper to create a new task
4228
+ *
4229
+ * @private internal helper function
4230
+ */
4231
+ function createTask(options) {
4232
+ const { taskType, title, taskProcessCallback } = options;
4233
+ // TODO: [🐙] DRY
4234
+ const taskId = `${taskType.toLowerCase().substring(0, 4)}-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
4235
+ const createdAt = new Date();
4236
+ const taskState = createTaskState(title, createdAt);
4237
+ const partialResultSubject = new Subject();
4238
+ // <- Note: Not using `BehaviorSubject` because on error we can't access the last value
4239
+ const finalResultPromise = /* not await */ taskProcessCallback(createOngoingResultUpdater(taskState, partialResultSubject), createTldrUpdater(taskState), createLlmCallLogger(taskState));
4240
+ settleTaskPromise(finalResultPromise, taskState, partialResultSubject);
4241
+ async function asPromise(options) {
4242
+ const { isCrashedOnError = true } = options || {};
4243
+ const finalResult = await finalResultPromise;
4244
+ if (isCrashedOnError) {
4245
+ assertsTaskSuccessful(finalResult);
4246
+ }
4247
+ return finalResult;
3627
4248
  }
3628
4249
  return {
3629
4250
  taskType,
@@ -3632,91 +4253,29 @@ function createTask(options) {
3632
4253
  return PROMPTBOOK_ENGINE_VERSION;
3633
4254
  },
3634
4255
  get title() {
3635
- return title;
4256
+ return taskState.title;
3636
4257
  // <- Note: [1] These must be getters to allow changing the value in the future
3637
4258
  },
3638
4259
  get status() {
3639
- return status;
4260
+ return taskState.status;
3640
4261
  // <- Note: [1] --||--
3641
4262
  },
3642
4263
  get tldr() {
3643
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3644
- // Use custom tldr if available
3645
- if (customTldr) {
3646
- return customTldr;
3647
- }
3648
- // Fallback to default implementation
3649
- const cv = currentValue;
3650
- // If explicit percent is provided, use it
3651
- 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;
3652
- // Simulate progress if not provided
3653
- if (typeof percentRaw !== 'number') {
3654
- // Simulate progress: evenly split across subtasks, based on elapsed time
3655
- const now = new Date();
3656
- const elapsedMs = now.getTime() - createdAt.getTime();
3657
- const totalMs = DEFAULT_TASK_SIMULATED_DURATION_MS;
3658
- // If subtasks are defined, split progress evenly
3659
- const subtaskCount = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) ? cv.subtasks.length : 1;
3660
- const completedSubtasks = Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks)
3661
- ? cv.subtasks.filter((s) => s.done || s.completed).length
3662
- : 0;
3663
- // Progress from completed subtasks
3664
- const subtaskProgress = subtaskCount > 0 ? completedSubtasks / subtaskCount : 0;
3665
- // Progress from elapsed time for current subtask
3666
- const timeProgress = Math.min(elapsedMs / totalMs, 1);
3667
- // Combine: completed subtasks + time progress for current subtask
3668
- percentRaw = Math.min(subtaskProgress + (1 / subtaskCount) * timeProgress, 1);
3669
- if (status === 'FINISHED')
3670
- percentRaw = 1;
3671
- if (status === 'ERROR')
3672
- percentRaw = 0;
3673
- }
3674
- // Clamp to [0,1]
3675
- let percent = Number(percentRaw) || 0;
3676
- if (percent < 0)
3677
- percent = 0;
3678
- if (percent > 1)
3679
- percent = 1;
3680
- // Build a short message: prefer explicit tldr.message, then common summary/message fields, then errors/warnings, then status
3681
- 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;
3682
- let message = messageFromResult;
3683
- if (!message) {
3684
- // If subtasks, show current subtask
3685
- if (Array.isArray(cv === null || cv === void 0 ? void 0 : cv.subtasks) && cv.subtasks.length > 0) {
3686
- const current = cv.subtasks.find((s) => !s.done && !s.completed);
3687
- if (current && current.title) {
3688
- message = `Working on ${current.title}`;
3689
- }
3690
- }
3691
- if (!message) {
3692
- if (errors.length) {
3693
- message = errors[errors.length - 1].message || 'Error';
3694
- }
3695
- else if (warnings.length) {
3696
- message = warnings[warnings.length - 1].message || 'Warning';
3697
- }
3698
- else if (status === 'FINISHED') {
3699
- message = 'Finished';
3700
- }
3701
- else if (status === 'ERROR') {
3702
- message = 'Error';
3703
- }
3704
- else {
3705
- message = 'Running';
3706
- }
3707
- }
3708
- }
3709
- return {
3710
- percent: percent,
3711
- message: message + ' (!!!fallback)',
3712
- };
4264
+ return resolveTaskTldr({
4265
+ customTldr: taskState.customTldr,
4266
+ currentValue: taskState.currentValue,
4267
+ status: taskState.status,
4268
+ createdAt,
4269
+ errors: taskState.errors,
4270
+ warnings: taskState.warnings,
4271
+ });
3713
4272
  },
3714
4273
  get createdAt() {
3715
4274
  return createdAt;
3716
4275
  // <- Note: [1] --||--
3717
4276
  },
3718
4277
  get updatedAt() {
3719
- return updatedAt;
4278
+ return taskState.updatedAt;
3720
4279
  // <- Note: [1] --||--
3721
4280
  },
3722
4281
  asPromise,
@@ -3724,19 +4283,19 @@ function createTask(options) {
3724
4283
  return partialResultSubject.asObservable();
3725
4284
  },
3726
4285
  get errors() {
3727
- return errors;
4286
+ return taskState.errors;
3728
4287
  // <- Note: [1] --||--
3729
4288
  },
3730
4289
  get warnings() {
3731
- return warnings;
4290
+ return taskState.warnings;
3732
4291
  // <- Note: [1] --||--
3733
4292
  },
3734
4293
  get llmCalls() {
3735
- return [...llmCalls, { foo: '!!! bar' }];
4294
+ return [...taskState.llmCalls, { foo: '!!! bar' }];
3736
4295
  // <- Note: [1] --||--
3737
4296
  },
3738
4297
  get currentValue() {
3739
- return currentValue;
4298
+ return taskState.currentValue;
3740
4299
  // <- Note: [1] --||--
3741
4300
  },
3742
4301
  };
@@ -4654,210 +5213,275 @@ const promptbookFetch = async (urlOrRequest, init) => {
4654
5213
  * @public exported from `@promptbook/core`
4655
5214
  */
4656
5215
  async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
5216
+ const { knowledgeSourceContent } = knowledgeSource;
5217
+ const name = knowledgeSource.name || knowledgeSourceContentToName(knowledgeSourceContent);
5218
+ const { rootDirname = null, isVerbose = DEFAULT_IS_VERBOSE } = options || {};
5219
+ if (isValidUrl(knowledgeSourceContent)) {
5220
+ return makeUrlKnowledgeSourceHandler(knowledgeSourceContent, name, tools, options, isVerbose);
5221
+ }
5222
+ if (isValidFilePath(knowledgeSourceContent)) {
5223
+ return makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose);
5224
+ }
5225
+ return makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose);
5226
+ }
5227
+ /**
5228
+ * Creates a source handler for URL-based knowledge.
5229
+ *
5230
+ * @private internal utility of `makeKnowledgeSourceHandler`
5231
+ */
5232
+ async function makeUrlKnowledgeSourceHandler(url, name, tools, options, isVerbose) {
4657
5233
  var _a;
4658
5234
  const { fetch = promptbookFetch } = tools;
4659
- const { knowledgeSourceContent } = knowledgeSource;
4660
- let { name } = knowledgeSource;
4661
- const { rootDirname = null,
4662
- // <- TODO: process.cwd() if running in Node.js
4663
- isVerbose = DEFAULT_IS_VERBOSE, } = options || {};
4664
- if (!name) {
4665
- name = knowledgeSourceContentToName(knowledgeSourceContent);
5235
+ if (isVerbose) {
5236
+ console.info(`📄 [1] "${name}" is available at "${url}"`);
4666
5237
  }
4667
- if (isValidUrl(knowledgeSourceContent)) {
4668
- const url = knowledgeSourceContent;
5238
+ const response = await fetch(url); // <- TODO: [🧠] Scraping and fetch proxy
5239
+ const mimeType = ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
5240
+ if (tools.fs === undefined || !url.endsWith('.pdf' /* <- TODO: [💵] */)) {
5241
+ return makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose);
5242
+ }
5243
+ return cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose);
5244
+ }
5245
+ /**
5246
+ * Creates a source handler that reads directly from a fetched response.
5247
+ *
5248
+ * @private internal utility of `makeKnowledgeSourceHandler`
5249
+ */
5250
+ function makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose) {
5251
+ if (isVerbose) {
5252
+ console.info(`📄 [2] "${name}" tools.fs is not available or URL is not a PDF.`);
5253
+ }
5254
+ return {
5255
+ source: name,
5256
+ filename: null,
5257
+ url,
5258
+ mimeType,
5259
+ /*
5260
+ TODO: [🥽]
5261
+ > async asBlob() {
5262
+ > // TODO: [👨🏻‍🤝‍👨🏻] This can be called multiple times BUT when called second time, response in already consumed
5263
+ > const content = await response.blob();
5264
+ > return content;
5265
+ > },
5266
+ */
5267
+ async asJson() {
5268
+ // TODO: [👨🏻‍🤝‍👨🏻]
5269
+ const content = await response.json();
5270
+ return content;
5271
+ },
5272
+ async asText() {
5273
+ // TODO: [👨🏻‍🤝‍👨🏻]
5274
+ const content = await response.text();
5275
+ return content;
5276
+ },
5277
+ };
5278
+ }
5279
+ /**
5280
+ * Downloads a PDF knowledge source into cache when possible and falls back to in-memory content otherwise.
5281
+ *
5282
+ * @private internal utility of `makeKnowledgeSourceHandler`
5283
+ */
5284
+ async function cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose) {
5285
+ const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
5286
+ const filepath = createDownloadedKnowledgeSourceFilepath(url, mimeType);
5287
+ const fullFilepath = join(rootDirname, filepath);
5288
+ await createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose);
5289
+ const fileContent = Buffer.from(await response.arrayBuffer());
5290
+ if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
5291
+ 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.`);
5292
+ }
5293
+ const isCached = await writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose);
5294
+ if (!isCached) {
5295
+ return makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent);
5296
+ }
5297
+ // TODO: [💵] Check the file security
5298
+ // TODO: [🧹][🧠] Delete the file after the scraping is done
5299
+ if (isVerbose) {
5300
+ console.info(`📄 [5] "${name}" cached at "${fullFilepath}"`);
5301
+ }
5302
+ return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
5303
+ ...options,
5304
+ rootDirname,
5305
+ });
5306
+ }
5307
+ /**
5308
+ * Builds a stable cache filepath for a downloaded knowledge source.
5309
+ *
5310
+ * @private internal utility of `makeKnowledgeSourceHandler`
5311
+ */
5312
+ function createDownloadedKnowledgeSourceFilepath(url, mimeType) {
5313
+ const basename = url.split('/').pop() || titleToName(url);
5314
+ const hash = sha256(hexEncoder.parse(url)).toString( /* hex */);
5315
+ // <- TODO: [🥬] Encapsulate sha256 to some private utility function
5316
+ return join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
5317
+ }
5318
+ /**
5319
+ * Tries to create the cache directory needed for a downloaded knowledge source.
5320
+ *
5321
+ * @private internal utility of `makeKnowledgeSourceHandler`
5322
+ */
5323
+ async function createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose) {
5324
+ try {
5325
+ await tools.fs.mkdir(dirname(fullFilepath), { recursive: true });
5326
+ }
5327
+ catch (error) {
4669
5328
  if (isVerbose) {
4670
- console.info(`📄 [1] "${name}" is available at "${url}"`);
4671
- }
4672
- const response = await fetch(url); // <- TODO: [🧠] Scraping and fetch proxy
4673
- const mimeType = ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
4674
- if (tools.fs === undefined || !url.endsWith('.pdf' /* <- TODO: [💵] */)) {
4675
- if (isVerbose) {
4676
- console.info(`📄 [2] "${name}" tools.fs is not available or URL is not a PDF.`);
4677
- }
4678
- return {
4679
- source: name,
4680
- filename: null,
4681
- url,
4682
- mimeType,
4683
- /*
4684
- TODO: [🥽]
4685
- > async asBlob() {
4686
- > // TODO: [👨🏻‍🤝‍👨🏻] This can be called multiple times BUT when called second time, response in already consumed
4687
- > const content = await response.blob();
4688
- > return content;
4689
- > },
4690
- */
4691
- async asJson() {
4692
- // TODO: [👨🏻‍🤝‍👨🏻]
4693
- const content = await response.json();
4694
- return content;
4695
- },
4696
- async asText() {
4697
- // TODO: [👨🏻‍🤝‍👨🏻]
4698
- const content = await response.text();
4699
- return content;
4700
- },
4701
- };
4702
- }
4703
- const basename = url.split('/').pop() || titleToName(url);
4704
- const hash = sha256(hexEncoder.parse(url)).toString( /* hex */);
4705
- // <- TODO: [🥬] Encapsulate sha256 to some private utility function
4706
- const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
4707
- const filepath = join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
4708
- // Note: Try to create cache directory, but don't fail if filesystem has issues
4709
- try {
4710
- await tools.fs.mkdir(dirname(join(rootDirname, filepath)), { recursive: true });
4711
- }
4712
- catch (error) {
4713
- if (isVerbose) {
4714
- console.info(`📄 [3] "${name}" error creating cache directory`);
4715
- }
4716
- // Note: If we can't create cache directory, we'll handle it when trying to write the file
4717
- // This handles read-only filesystems, permission issues, and missing parent directories
4718
- if (error instanceof Error &&
4719
- (error.message.includes('EROFS') ||
4720
- error.message.includes('read-only') ||
4721
- error.message.includes('EACCES') ||
4722
- error.message.includes('EPERM') ||
4723
- error.message.includes('ENOENT'))) ;
4724
- else {
4725
- // Re-throw other unexpected errors
4726
- throw error;
4727
- }
4728
- }
4729
- const fileContent = Buffer.from(await response.arrayBuffer());
4730
- if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
4731
- 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.`);
4732
- }
4733
- // Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
4734
- try {
4735
- await tools.fs.writeFile(join(rootDirname, filepath), fileContent);
4736
- }
4737
- catch (error) {
4738
- if (isVerbose) {
4739
- console.info(`📄 [4] "${name}" error writing cache file`);
4740
- }
4741
- // Note: If we can't write to cache, we'll process the file directly from memory
4742
- // This handles read-only filesystems like Vercel
4743
- if (error instanceof Error &&
4744
- (error.message.includes('EROFS') ||
4745
- error.message.includes('read-only') ||
4746
- error.message.includes('EACCES') ||
4747
- error.message.includes('EPERM') ||
4748
- error.message.includes('ENOENT'))) {
4749
- // Return a handler that works directly with the downloaded content
4750
- return {
4751
- source: name,
4752
- filename: null,
4753
- url,
4754
- mimeType,
4755
- async asJson() {
4756
- return JSON.parse(fileContent.toString('utf-8'));
4757
- },
4758
- async asText() {
4759
- return fileContent.toString('utf-8');
4760
- },
4761
- };
4762
- }
4763
- else {
4764
- // Re-throw other unexpected errors
4765
- throw error;
4766
- }
5329
+ console.info(`📄 [3] "${name}" error creating cache directory`);
4767
5330
  }
4768
- // TODO: [💵] Check the file security
4769
- // TODO: [🧹][🧠] Delete the file after the scraping is done
4770
- if (isVerbose) {
4771
- console.info(`📄 [5] "${name}" cached at "${join(rootDirname, filepath)}"`);
5331
+ // Note: If we can't create cache directory, we'll handle it when trying to write the file
5332
+ // This handles read-only filesystems, permission issues, and missing parent directories
5333
+ if (!isIgnorableCacheFilesystemError(error)) {
5334
+ throw error;
4772
5335
  }
4773
- return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
4774
- ...options,
4775
- rootDirname,
4776
- });
4777
5336
  }
4778
- else if (isValidFilePath(knowledgeSourceContent)) {
4779
- if (tools.fs === undefined) {
4780
- throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
4781
- // <- TODO: [🧠] What is the best error type here`
4782
- }
4783
- if (rootDirname === null) {
4784
- throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
4785
- // <- TODO: [🧠] What is the best error type here`
4786
- }
4787
- const filename = isAbsolute(knowledgeSourceContent)
4788
- ? knowledgeSourceContent
4789
- : join(rootDirname, knowledgeSourceContent).split('\\').join('/');
5337
+ }
5338
+ /**
5339
+ * Tries to write downloaded content into cache and reports whether the cache was usable.
5340
+ *
5341
+ * @private internal utility of `makeKnowledgeSourceHandler`
5342
+ */
5343
+ async function writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose) {
5344
+ // Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
5345
+ try {
5346
+ await tools.fs.writeFile(fullFilepath, fileContent);
5347
+ return true;
5348
+ }
5349
+ catch (error) {
4790
5350
  if (isVerbose) {
4791
- console.info(`📄 [6] "${name}" is a valid file "${filename}"`);
5351
+ console.info(`📄 [4] "${name}" error writing cache file`);
4792
5352
  }
4793
- const fileExtension = getFileExtension(filename);
4794
- const mimeType = extensionToMimeType(fileExtension || '');
4795
- if (!(await isFileExisting(filename, tools.fs))) {
4796
- throw new NotFoundError(spaceTrim$1((block) => `
4797
- Can not make source handler for file which does not exist:
4798
-
4799
- File:
4800
- ${block(knowledgeSourceContent)}
4801
-
4802
- Full file path:
4803
- ${block(filename)}
4804
- `));
5353
+ // Note: If we can't write to cache, we'll process the file directly from memory
5354
+ // This handles read-only filesystems like Vercel
5355
+ if (isIgnorableCacheFilesystemError(error)) {
5356
+ return false;
4805
5357
  }
4806
- // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
4807
- return {
4808
- source: name,
4809
- filename,
4810
- url: null,
4811
- mimeType,
4812
- /*
4813
- TODO: [🥽]
4814
- > async asBlob() {
4815
- > const content = await tools.fs!.readFile(filename);
4816
- > return new Blob(
4817
- > [
4818
- > content,
4819
- > // <- TODO: [🥽] This is NOT tested, test it
4820
- > ],
4821
- > { type: mimeType },
4822
- > );
4823
- > },
4824
- */
4825
- async asJson() {
4826
- return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
4827
- },
4828
- async asText() {
4829
- return await tools.fs.readFile(filename, 'utf-8');
4830
- },
4831
- };
5358
+ throw error;
4832
5359
  }
4833
- else {
4834
- if (isVerbose) {
4835
- console.info(`📄 [7] "${name}" is just a explicit string text with a knowledge source`);
4836
- console.info('---');
4837
- console.info(knowledgeSourceContent);
4838
- console.info('---');
4839
- }
4840
- return {
4841
- source: name,
4842
- filename: null,
4843
- url: null,
4844
- mimeType: 'text/markdown',
4845
- asText() {
4846
- return knowledgeSource.knowledgeSourceContent;
4847
- },
4848
- asJson() {
4849
- throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
4850
- },
4851
- /*
4852
- TODO: [🥽]
4853
- > asBlob() {
4854
- > throw new UnexpectedError(
4855
- > 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
4856
- > );
4857
- > },
4858
- */
4859
- };
5360
+ }
5361
+ /**
5362
+ * Detects filesystem errors that should not fail optional caching.
5363
+ *
5364
+ * @private internal utility of `makeKnowledgeSourceHandler`
5365
+ */
5366
+ function isIgnorableCacheFilesystemError(error) {
5367
+ return (error instanceof Error &&
5368
+ (error.message.includes('EROFS') ||
5369
+ error.message.includes('read-only') ||
5370
+ error.message.includes('EACCES') ||
5371
+ error.message.includes('EPERM') ||
5372
+ error.message.includes('ENOENT')));
5373
+ }
5374
+ /**
5375
+ * Creates a source handler backed by already downloaded file content kept in memory.
5376
+ *
5377
+ * @private internal utility of `makeKnowledgeSourceHandler`
5378
+ */
5379
+ function makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent) {
5380
+ return {
5381
+ source: name,
5382
+ filename: null,
5383
+ url,
5384
+ mimeType,
5385
+ async asJson() {
5386
+ return JSON.parse(fileContent.toString('utf-8'));
5387
+ },
5388
+ async asText() {
5389
+ return fileContent.toString('utf-8');
5390
+ },
5391
+ };
5392
+ }
5393
+ /**
5394
+ * Creates a source handler for file-based knowledge.
5395
+ *
5396
+ * @private internal utility of `makeKnowledgeSourceHandler`
5397
+ */
5398
+ async function makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose) {
5399
+ if (tools.fs === undefined) {
5400
+ throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
5401
+ // <- TODO: [🧠] What is the best error type here`
5402
+ }
5403
+ if (rootDirname === null) {
5404
+ throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
5405
+ // <- TODO: [🧠] What is the best error type here`
5406
+ }
5407
+ const filename = isAbsolute(knowledgeSourceContent)
5408
+ ? knowledgeSourceContent
5409
+ : join(rootDirname, knowledgeSourceContent).split('\\').join('/');
5410
+ if (isVerbose) {
5411
+ console.info(`📄 [6] "${name}" is a valid file "${filename}"`);
5412
+ }
5413
+ const fileExtension = getFileExtension(filename);
5414
+ const mimeType = extensionToMimeType(fileExtension || '');
5415
+ if (!(await isFileExisting(filename, tools.fs))) {
5416
+ throw new NotFoundError(spaceTrim$1((block) => `
5417
+ Can not make source handler for file which does not exist:
5418
+
5419
+ File:
5420
+ ${block(knowledgeSourceContent)}
5421
+
5422
+ Full file path:
5423
+ ${block(filename)}
5424
+ `));
5425
+ }
5426
+ // TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
5427
+ return {
5428
+ source: name,
5429
+ filename,
5430
+ url: null,
5431
+ mimeType,
5432
+ /*
5433
+ TODO: [🥽]
5434
+ > async asBlob() {
5435
+ > const content = await tools.fs!.readFile(filename);
5436
+ > return new Blob(
5437
+ > [
5438
+ > content,
5439
+ > // <- TODO: [🥽] This is NOT tested, test it
5440
+ > ],
5441
+ > { type: mimeType },
5442
+ > );
5443
+ > },
5444
+ */
5445
+ async asJson() {
5446
+ return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
5447
+ },
5448
+ async asText() {
5449
+ return await tools.fs.readFile(filename, 'utf-8');
5450
+ },
5451
+ };
5452
+ }
5453
+ /**
5454
+ * Creates a source handler for inline text knowledge.
5455
+ *
5456
+ * @private internal utility of `makeKnowledgeSourceHandler`
5457
+ */
5458
+ function makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose) {
5459
+ if (isVerbose) {
5460
+ console.info(`📄 [7] "${name}" is just a explicit string text with a knowledge source`);
5461
+ console.info('---');
5462
+ console.info(knowledgeSourceContent);
5463
+ console.info('---');
4860
5464
  }
5465
+ return {
5466
+ source: name,
5467
+ filename: null,
5468
+ url: null,
5469
+ mimeType: 'text/markdown',
5470
+ asText() {
5471
+ return knowledgeSourceContent;
5472
+ },
5473
+ asJson() {
5474
+ throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
5475
+ },
5476
+ /*
5477
+ TODO: [🥽]
5478
+ > asBlob() {
5479
+ > throw new UnexpectedError(
5480
+ > 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
5481
+ > );
5482
+ > },
5483
+ */
5484
+ };
4861
5485
  }
4862
5486
 
4863
5487
  /**
@@ -6142,112 +6766,10 @@ function validatePromptResult(options) {
6142
6766
  }
6143
6767
  }
6144
6768
 
6145
- /**
6146
- * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
6147
- * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
6148
- * Throws errors if execution fails after all attempts.
6149
- *
6150
- * @param options - The options for execution, including task, parameters, pipeline, and configuration.
6151
- * @returns The result string of the executed task.
6152
- *
6153
- * @private internal utility of `createPipelineExecutor`
6154
- */
6155
- async function executeAttempts(options) {
6156
- const $ongoingTaskResult = createOngoingTaskResult();
6157
- const llmTools = getSingleLlmExecutionTools(options.tools.llm);
6158
- attempts: for (let attemptIndex = -options.jokerParameterNames.length; attemptIndex < options.maxAttempts; attemptIndex++) {
6159
- const attempt = createAttemptDescriptor({
6160
- attemptIndex,
6161
- jokerParameterNames: options.jokerParameterNames,
6162
- pipelineIdentification: options.pipelineIdentification,
6163
- });
6164
- resetAttemptExecutionState($ongoingTaskResult);
6165
- try {
6166
- await executeSingleAttempt({
6167
- attempt,
6168
- options,
6169
- llmTools,
6170
- $ongoingTaskResult,
6171
- });
6172
- break attempts;
6173
- }
6174
- catch (error) {
6175
- if (!(error instanceof ExpectError)) {
6176
- throw error;
6177
- }
6178
- recordFailedAttempt({
6179
- error,
6180
- attemptIndex,
6181
- onProgress: options.onProgress,
6182
- $ongoingTaskResult,
6183
- });
6184
- }
6185
- finally {
6186
- reportPromptExecution({
6187
- attempt,
6188
- task: options.task,
6189
- $executionReport: options.$executionReport,
6190
- logLlmCall: options.logLlmCall,
6191
- $ongoingTaskResult,
6192
- });
6193
- }
6194
- throwIfFinalAttemptFailed({
6195
- attemptIndex,
6196
- maxAttempts: options.maxAttempts,
6197
- maxExecutionAttempts: options.maxExecutionAttempts,
6198
- pipelineIdentification: options.pipelineIdentification,
6199
- $ongoingTaskResult,
6200
- });
6201
- }
6202
- return getSuccessfulResultString({
6203
- pipelineIdentification: options.pipelineIdentification,
6204
- $ongoingTaskResult,
6205
- });
6206
- }
6207
- /**
6208
- * Creates mutable attempt state for one task execution lifecycle.
6209
- */
6210
- function createOngoingTaskResult() {
6211
- return {
6212
- $result: null,
6213
- $resultString: null,
6214
- $expectError: null,
6215
- $scriptPipelineExecutionErrors: [],
6216
- $failedResults: [],
6217
- };
6218
- }
6219
- /**
6220
- * Resolves the bookkeeping for one loop iteration, including joker lookup.
6221
- */
6222
- function createAttemptDescriptor(options) {
6223
- const { attemptIndex, jokerParameterNames, pipelineIdentification } = options;
6224
- const isJokerAttempt = attemptIndex < 0;
6225
- const jokerParameterName = isJokerAttempt
6226
- ? jokerParameterNames[jokerParameterNames.length + attemptIndex]
6227
- : undefined;
6228
- if (isJokerAttempt && !jokerParameterName) {
6229
- throw new UnexpectedError(spaceTrim$1((block) => `
6230
- Joker not found in attempt ${attemptIndex}
6231
-
6232
- ${block(pipelineIdentification)}
6233
- `));
6234
- }
6235
- return {
6236
- attemptIndex,
6237
- isJokerAttempt,
6238
- jokerParameterName,
6239
- };
6240
- }
6241
- /**
6242
- * Clears the per-attempt result slots while preserving cumulative failure history.
6243
- */
6244
- function resetAttemptExecutionState($ongoingTaskResult) {
6245
- $ongoingTaskResult.$result = null;
6246
- $ongoingTaskResult.$resultString = null;
6247
- $ongoingTaskResult.$expectError = null;
6248
- }
6249
6769
  /**
6250
6770
  * Executes one loop iteration, from joker resolution or task execution through validation.
6771
+ *
6772
+ * @private function of `executeAttempts`
6251
6773
  */
6252
6774
  async function executeSingleAttempt(options) {
6253
6775
  const { attempt, options: executeAttemptsOptions, llmTools, $ongoingTaskResult } = options;
@@ -6562,11 +7084,15 @@ function validateAttemptResult(options) {
6562
7084
  // Update the result string in case format processing modified it (e.g., JSON extraction)
6563
7085
  $ongoingTaskResult.$resultString = validationResult.processedResultString;
6564
7086
  }
7087
+
6565
7088
  /**
6566
- * Stores one failed attempt and reports the expectation error upstream.
7089
+ * Stores one failed attempt, reports the expectation error, and throws the aggregated retry error after the final
7090
+ * regular attempt.
7091
+ *
7092
+ * @private function of `executeAttempts`
6567
7093
  */
6568
- function recordFailedAttempt(options) {
6569
- const { error, attemptIndex, onProgress, $ongoingTaskResult } = options;
7094
+ function handleAttemptFailure(options) {
7095
+ const { error, attemptIndex, maxAttempts, maxExecutionAttempts, onProgress, pipelineIdentification, $ongoingTaskResult, } = options;
6570
7096
  $ongoingTaskResult.$expectError = error;
6571
7097
  $ongoingTaskResult.$failedResults.push({
6572
7098
  attemptIndex,
@@ -6576,39 +7102,7 @@ function recordFailedAttempt(options) {
6576
7102
  onProgress({
6577
7103
  errors: [error],
6578
7104
  });
6579
- }
6580
- /**
6581
- * Appends the prompt execution report for prompt-task attempts.
6582
- */
6583
- function reportPromptExecution(options) {
6584
- const { attempt, task, $executionReport, logLlmCall, $ongoingTaskResult } = options;
6585
- if (attempt.isJokerAttempt || task.taskType !== 'PROMPT_TASK' || !$ongoingTaskResult.$prompt) {
6586
- return;
6587
- }
6588
- // Note: [2] When some expected parameter is not defined, error will occur in templateParameters
6589
- // In that case we don’t want to make a report about it because it’s not a llm execution error
6590
- const executionPromptReport = {
6591
- prompt: {
6592
- ...$ongoingTaskResult.$prompt,
6593
- // <- TODO: [🧠] How to pick everyhing except `pipelineUrl`
6594
- },
6595
- result: $ongoingTaskResult.$result || undefined,
6596
- error: $ongoingTaskResult.$expectError === null ? undefined : serializeError($ongoingTaskResult.$expectError),
6597
- };
6598
- $executionReport.promptExecutions.push(executionPromptReport);
6599
- if (logLlmCall) {
6600
- logLlmCall({
6601
- modelName: 'model' /* <- TODO: How to get model name from the report */,
6602
- report: executionPromptReport,
6603
- });
6604
- }
6605
- }
6606
- /**
6607
- * Throws the aggregated retry error after the last regular attempt fails expectations.
6608
- */
6609
- function throwIfFinalAttemptFailed(options) {
6610
- const { attemptIndex, maxAttempts, maxExecutionAttempts, pipelineIdentification, $ongoingTaskResult } = options;
6611
- if ($ongoingTaskResult.$expectError === null || attemptIndex !== maxAttempts - 1) {
7105
+ if (attemptIndex !== maxAttempts - 1) {
6612
7106
  return;
6613
7107
  }
6614
7108
  throw new PipelineExecutionError(spaceTrim$1((block) => {
@@ -6639,7 +7133,9 @@ function createFailuresSummary($failedResults) {
6639
7133
  ${block(quoteMultilineText(((_b = failure.error) === null || _b === void 0 ? void 0 : _b.message) || ''))}
6640
7134
 
6641
7135
  Result:
6642
- ${block(failure.result === null ? 'null' : quoteMultilineText(spaceTrim$1(failure.result)))}
7136
+ ${block(failure.result === null
7137
+ ? 'null'
7138
+ : quoteMultilineText(spaceTrim$1(failure.result)))}
6643
7139
  `;
6644
7140
  }))
6645
7141
  .join('\n\n---\n\n');
@@ -6653,6 +7149,136 @@ function quoteMultilineText(text) {
6653
7149
  .map((line) => `> ${line}`)
6654
7150
  .join('\n');
6655
7151
  }
7152
+
7153
+ /**
7154
+ * Appends the prompt execution report for prompt-task attempts.
7155
+ *
7156
+ * @private function of `executeAttempts`
7157
+ */
7158
+ function reportPromptExecution(options) {
7159
+ const { attempt, task, $executionReport, logLlmCall, $ongoingTaskResult } = options;
7160
+ if (attempt.isJokerAttempt || task.taskType !== 'PROMPT_TASK' || !$ongoingTaskResult.$prompt) {
7161
+ return;
7162
+ }
7163
+ // Note: [2] When some expected parameter is not defined, error will occur in templateParameters
7164
+ // In that case we don’t want to make a report about it because it’s not a llm execution error
7165
+ const executionPromptReport = {
7166
+ prompt: {
7167
+ ...$ongoingTaskResult.$prompt,
7168
+ // <- TODO: [🧠] How to pick everyhing except `pipelineUrl`
7169
+ },
7170
+ result: $ongoingTaskResult.$result || undefined,
7171
+ error: $ongoingTaskResult.$expectError === null ? undefined : serializeError($ongoingTaskResult.$expectError),
7172
+ };
7173
+ $executionReport.promptExecutions.push(executionPromptReport);
7174
+ if (logLlmCall) {
7175
+ logLlmCall({
7176
+ modelName: 'model' /* <- TODO: How to get model name from the report */,
7177
+ report: executionPromptReport,
7178
+ });
7179
+ }
7180
+ }
7181
+
7182
+ /**
7183
+ * Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
7184
+ * (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
7185
+ * Throws errors if execution fails after all attempts.
7186
+ *
7187
+ * @param options - The options for execution, including task, parameters, pipeline, and configuration.
7188
+ * @returns The result string of the executed task.
7189
+ *
7190
+ * @private internal utility of `createPipelineExecutor`
7191
+ */
7192
+ async function executeAttempts(options) {
7193
+ const $ongoingTaskResult = createOngoingTaskResult();
7194
+ const llmTools = getSingleLlmExecutionTools(options.tools.llm);
7195
+ attempts: for (let attemptIndex = -options.jokerParameterNames.length; attemptIndex < options.maxAttempts; attemptIndex++) {
7196
+ const attempt = createAttemptDescriptor({
7197
+ attemptIndex,
7198
+ jokerParameterNames: options.jokerParameterNames,
7199
+ pipelineIdentification: options.pipelineIdentification,
7200
+ });
7201
+ resetAttemptExecutionState($ongoingTaskResult);
7202
+ try {
7203
+ await executeSingleAttempt({
7204
+ attempt,
7205
+ options,
7206
+ llmTools,
7207
+ $ongoingTaskResult,
7208
+ });
7209
+ break attempts;
7210
+ }
7211
+ catch (error) {
7212
+ if (!(error instanceof ExpectError)) {
7213
+ throw error;
7214
+ }
7215
+ handleAttemptFailure({
7216
+ error,
7217
+ attemptIndex,
7218
+ maxAttempts: options.maxAttempts,
7219
+ maxExecutionAttempts: options.maxExecutionAttempts,
7220
+ onProgress: options.onProgress,
7221
+ pipelineIdentification: options.pipelineIdentification,
7222
+ $ongoingTaskResult,
7223
+ });
7224
+ }
7225
+ finally {
7226
+ reportPromptExecution({
7227
+ attempt,
7228
+ task: options.task,
7229
+ $executionReport: options.$executionReport,
7230
+ logLlmCall: options.logLlmCall,
7231
+ $ongoingTaskResult,
7232
+ });
7233
+ }
7234
+ }
7235
+ return getSuccessfulResultString({
7236
+ pipelineIdentification: options.pipelineIdentification,
7237
+ $ongoingTaskResult,
7238
+ });
7239
+ }
7240
+ /**
7241
+ * Creates mutable attempt state for one task execution lifecycle.
7242
+ */
7243
+ function createOngoingTaskResult() {
7244
+ return {
7245
+ $result: null,
7246
+ $resultString: null,
7247
+ $expectError: null,
7248
+ $scriptPipelineExecutionErrors: [],
7249
+ $failedResults: [],
7250
+ };
7251
+ }
7252
+ /**
7253
+ * Resolves the bookkeeping for one loop iteration, including joker lookup.
7254
+ */
7255
+ function createAttemptDescriptor(options) {
7256
+ const { attemptIndex, jokerParameterNames, pipelineIdentification } = options;
7257
+ const isJokerAttempt = attemptIndex < 0;
7258
+ const jokerParameterName = isJokerAttempt
7259
+ ? jokerParameterNames[jokerParameterNames.length + attemptIndex]
7260
+ : undefined;
7261
+ if (isJokerAttempt && !jokerParameterName) {
7262
+ throw new UnexpectedError(spaceTrim$1((block) => `
7263
+ Joker not found in attempt ${attemptIndex}
7264
+
7265
+ ${block(pipelineIdentification)}
7266
+ `));
7267
+ }
7268
+ return {
7269
+ attemptIndex,
7270
+ isJokerAttempt,
7271
+ jokerParameterName,
7272
+ };
7273
+ }
7274
+ /**
7275
+ * Clears the per-attempt result slots while preserving cumulative failure history.
7276
+ */
7277
+ function resetAttemptExecutionState($ongoingTaskResult) {
7278
+ $ongoingTaskResult.$result = null;
7279
+ $ongoingTaskResult.$resultString = null;
7280
+ $ongoingTaskResult.$expectError = null;
7281
+ }
6656
7282
  /**
6657
7283
  * Returns the successful result string or raises an unexpected internal-state error.
6658
7284
  */