@promptbook/markitdown 0.112.0-72 → 0.112.0-79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/esm/index.es.js +1781 -1052
- package/esm/index.es.js.map +1 -1
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/esm/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
- package/esm/src/book-components/Chat/save/_common/chatExportRendering.d.ts +75 -0
- package/esm/src/book-components/Chat/save/_common/getPromptbookExportBranding.d.ts +18 -0
- package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +13 -1
- package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.test.d.ts +1 -0
- package/esm/src/book-components/Chat/save/index.d.ts +5 -5
- package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
- package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.test.d.ts +1 -0
- package/esm/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +4 -3
- package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +3 -3
- package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.test.d.ts +1 -0
- package/esm/src/book-components/Chat/save/react/reactSaveFormatDefinition.test.d.ts +1 -0
- package/esm/src/book-components/Chat/utils/renderMarkdown.d.ts +26 -0
- package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
- package/esm/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +2 -0
- package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/init.d.ts +9 -0
- package/esm/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
- package/esm/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
- package/esm/src/cli/cli-commands/agents-server/run.d.ts +14 -0
- package/esm/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
- package/esm/src/cli/cli-commands/agents-server.d.ts +8 -0
- package/esm/src/cli/cli-commands/common/handleActionErrors.d.ts +9 -4
- package/esm/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
- package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
- package/esm/src/cli/common/$deprecateCliCommand.d.ts +8 -0
- package/esm/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
- package/esm/src/conversion/pipelineJsonToString/appendMarkdownBlock.d.ts +7 -0
- package/esm/src/conversion/pipelineJsonToString/createPipelineCommands.d.ts +7 -0
- package/esm/src/conversion/pipelineJsonToString/createPipelineIntroduction.d.ts +8 -0
- package/esm/src/conversion/pipelineJsonToString/createTaskSerialization.d.ts +23 -0
- package/esm/src/conversion/pipelineJsonToString/stringifyCommands.d.ts +7 -0
- package/esm/src/conversion/pipelineJsonToString/stringifyTask.d.ts +8 -0
- package/esm/src/conversion/pipelineJsonToString.test.d.ts +1 -0
- package/esm/src/execution/createPipelineExecutor/executeSingleAttempt.d.ts +31 -0
- package/esm/src/execution/createPipelineExecutor/handleAttemptFailure.d.ts +40 -0
- package/esm/src/execution/createPipelineExecutor/reportPromptExecution.d.ts +34 -0
- package/esm/src/execution/resolveTaskTldr.d.ts +32 -0
- package/esm/src/execution/resolveTaskTldr.test.d.ts +1 -0
- package/esm/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +22 -63
- package/esm/src/llm-providers/agent/AgentLlmExecutionToolsAgentKitRunner.d.ts +51 -0
- package/esm/src/llm-providers/agent/AgentLlmExecutionToolsOpenAiAssistantRunner.d.ts +43 -0
- package/esm/src/llm-providers/agent/AgentLlmExecutionToolsPromptPreparer.d.ts +41 -0
- package/esm/src/llm-providers/agent/emitAgentLlmExecutionToolsAssistantPreparationProgress.d.ts +26 -0
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +16 -93
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsInputBuilder.d.ts +41 -0
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOutputTypeMapper.d.ts +56 -0
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +99 -0
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +24 -120
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsProgressReporter.d.ts +62 -0
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsPromptBuilder.d.ts +29 -0
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +63 -0
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +89 -0
- package/esm/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +9 -28
- package/esm/src/llm-providers/openai/OpenAiCompatibleModelCatalog.d.ts +31 -0
- package/esm/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +57 -0
- package/esm/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +29 -0
- package/esm/src/llm-providers/openai/OpenAiVectorStoreFileBatchHandler.d.ts +51 -0
- package/esm/src/llm-providers/openai/OpenAiVectorStoreFileBatchPoller.d.ts +75 -0
- package/esm/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +1 -98
- package/esm/src/llm-providers/openai/OpenAiVectorStoreKnowledgeSourcePreparer.d.ts +44 -0
- package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatProgressReporter.d.ts +86 -0
- package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatPromptBuilder.d.ts +57 -0
- package/esm/src/llm-providers/openai/utils/OpenAiCompatibleChatToolCaller.d.ts +57 -0
- package/esm/src/remote-server/startRemoteServer/RemoteServerRuntime.d.ts +14 -0
- package/esm/src/remote-server/startRemoteServer/SocketResponse.d.ts +9 -0
- package/esm/src/remote-server/startRemoteServer/StartRemoteServerConfiguration.d.ts +18 -0
- package/esm/src/remote-server/startRemoteServer/createRemoteServerExpressApp.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/createRemoteServerHandle.d.ts +11 -0
- package/esm/src/remote-server/startRemoteServer/createSocketServer.d.ts +9 -0
- package/esm/src/remote-server/startRemoteServer/getExecutionToolsFromIdentification.d.ts +12 -0
- package/esm/src/remote-server/startRemoteServer/registerBookRoutes.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerExecutionRoutes.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerListModelsSocketHandler.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/registerLoginRoute.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerNotFoundRoute.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerOpenAiCompatibleChatCompletionsRoute.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerOpenApiRoutes.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerPreparePipelineSocketHandler.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/registerPromptSocketHandler.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/registerRemoteServerHttpRoutes.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/registerRemoteServerSocketHandlers.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/registerServerIndexRoute.d.ts +7 -0
- package/esm/src/remote-server/startRemoteServer/resolveStartRemoteServerConfiguration.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/respondToSocketRequest.d.ts +8 -0
- package/esm/src/remote-server/startRemoteServer/startListening.d.ts +9 -0
- package/esm/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +14 -1
- package/esm/src/utils/color/Color.d.ts +4 -44
- package/esm/src/utils/color/ColorValue.d.ts +55 -0
- package/esm/src/utils/color/isHexColorString.d.ts +10 -0
- package/esm/src/utils/color/parseColorString.d.ts +11 -0
- package/esm/src/utils/serialization/serializeToPromptbookJavascript.d.ts +2 -0
- package/esm/src/utils/serialization/serializeToPromptbookJavascript.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +1781 -1052
- package/umd/index.umd.js.map +1 -1
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
- package/umd/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
- package/umd/src/book-components/Chat/save/_common/chatExportRendering.d.ts +75 -0
- package/umd/src/book-components/Chat/save/_common/getPromptbookExportBranding.d.ts +18 -0
- package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +13 -1
- package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.test.d.ts +1 -0
- package/umd/src/book-components/Chat/save/index.d.ts +5 -5
- package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
- package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.test.d.ts +1 -0
- package/umd/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +4 -3
- package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +3 -3
- package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.test.d.ts +1 -0
- package/umd/src/book-components/Chat/save/react/reactSaveFormatDefinition.test.d.ts +1 -0
- package/umd/src/book-components/Chat/utils/renderMarkdown.d.ts +26 -0
- package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
- package/umd/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +2 -0
- package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/init.d.ts +9 -0
- package/umd/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
- package/umd/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
- package/umd/src/cli/cli-commands/agents-server/run.d.ts +14 -0
- package/umd/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
- package/umd/src/cli/cli-commands/agents-server.d.ts +8 -0
- package/umd/src/cli/cli-commands/common/handleActionErrors.d.ts +9 -4
- package/umd/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
- package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
- package/umd/src/cli/common/$deprecateCliCommand.d.ts +8 -0
- package/umd/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
- package/umd/src/conversion/pipelineJsonToString/appendMarkdownBlock.d.ts +7 -0
- package/umd/src/conversion/pipelineJsonToString/createPipelineCommands.d.ts +7 -0
- package/umd/src/conversion/pipelineJsonToString/createPipelineIntroduction.d.ts +8 -0
- package/umd/src/conversion/pipelineJsonToString/createTaskSerialization.d.ts +23 -0
- package/umd/src/conversion/pipelineJsonToString/stringifyCommands.d.ts +7 -0
- package/umd/src/conversion/pipelineJsonToString/stringifyTask.d.ts +8 -0
- package/umd/src/conversion/pipelineJsonToString.test.d.ts +1 -0
- package/umd/src/execution/createPipelineExecutor/executeSingleAttempt.d.ts +31 -0
- package/umd/src/execution/createPipelineExecutor/handleAttemptFailure.d.ts +40 -0
- package/umd/src/execution/createPipelineExecutor/reportPromptExecution.d.ts +34 -0
- package/umd/src/execution/resolveTaskTldr.d.ts +32 -0
- package/umd/src/execution/resolveTaskTldr.test.d.ts +1 -0
- package/umd/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +22 -63
- package/umd/src/llm-providers/agent/AgentLlmExecutionToolsAgentKitRunner.d.ts +51 -0
- package/umd/src/llm-providers/agent/AgentLlmExecutionToolsOpenAiAssistantRunner.d.ts +43 -0
- package/umd/src/llm-providers/agent/AgentLlmExecutionToolsPromptPreparer.d.ts +41 -0
- package/umd/src/llm-providers/agent/emitAgentLlmExecutionToolsAssistantPreparationProgress.d.ts +26 -0
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +16 -93
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsInputBuilder.d.ts +41 -0
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsOutputTypeMapper.d.ts +56 -0
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +99 -0
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +24 -120
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsProgressReporter.d.ts +62 -0
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsPromptBuilder.d.ts +29 -0
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +63 -0
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +89 -0
- package/umd/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +9 -28
- package/umd/src/llm-providers/openai/OpenAiCompatibleModelCatalog.d.ts +31 -0
- package/umd/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +57 -0
- package/umd/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +29 -0
- package/umd/src/llm-providers/openai/OpenAiVectorStoreFileBatchHandler.d.ts +51 -0
- package/umd/src/llm-providers/openai/OpenAiVectorStoreFileBatchPoller.d.ts +75 -0
- package/umd/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +1 -98
- package/umd/src/llm-providers/openai/OpenAiVectorStoreKnowledgeSourcePreparer.d.ts +44 -0
- package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatProgressReporter.d.ts +86 -0
- package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatPromptBuilder.d.ts +57 -0
- package/umd/src/llm-providers/openai/utils/OpenAiCompatibleChatToolCaller.d.ts +57 -0
- package/umd/src/remote-server/startRemoteServer/RemoteServerRuntime.d.ts +14 -0
- package/umd/src/remote-server/startRemoteServer/SocketResponse.d.ts +9 -0
- package/umd/src/remote-server/startRemoteServer/StartRemoteServerConfiguration.d.ts +18 -0
- package/umd/src/remote-server/startRemoteServer/createRemoteServerExpressApp.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/createRemoteServerHandle.d.ts +11 -0
- package/umd/src/remote-server/startRemoteServer/createSocketServer.d.ts +9 -0
- package/umd/src/remote-server/startRemoteServer/getExecutionToolsFromIdentification.d.ts +12 -0
- package/umd/src/remote-server/startRemoteServer/registerBookRoutes.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerExecutionRoutes.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerListModelsSocketHandler.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/registerLoginRoute.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerNotFoundRoute.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerOpenAiCompatibleChatCompletionsRoute.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerOpenApiRoutes.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerPreparePipelineSocketHandler.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/registerPromptSocketHandler.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/registerRemoteServerHttpRoutes.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/registerRemoteServerSocketHandlers.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/registerServerIndexRoute.d.ts +7 -0
- package/umd/src/remote-server/startRemoteServer/resolveStartRemoteServerConfiguration.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/respondToSocketRequest.d.ts +8 -0
- package/umd/src/remote-server/startRemoteServer/startListening.d.ts +9 -0
- package/umd/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +14 -1
- package/umd/src/utils/color/Color.d.ts +4 -44
- package/umd/src/utils/color/ColorValue.d.ts +55 -0
- package/umd/src/utils/color/isHexColorString.d.ts +10 -0
- package/umd/src/utils/color/parseColorString.d.ts +11 -0
- package/umd/src/utils/serialization/serializeToPromptbookJavascript.d.ts +2 -0
- package/umd/src/utils/serialization/serializeToPromptbookJavascript.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
- package/esm/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
- package/esm/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
- package/umd/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
- package/umd/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
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-
|
|
27
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-79';
|
|
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
|
|
@@ -292,6 +292,111 @@ function checkChannelValue(channelName, value) {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Shared immutable channel storage and serialization helpers for `Color`.
|
|
297
|
+
*
|
|
298
|
+
* @private base class of Color
|
|
299
|
+
*/
|
|
300
|
+
class ColorValue {
|
|
301
|
+
constructor(red, green, blue, alpha = 255) {
|
|
302
|
+
this.red = red;
|
|
303
|
+
this.green = green;
|
|
304
|
+
this.blue = blue;
|
|
305
|
+
this.alpha = alpha;
|
|
306
|
+
checkChannelValue('Red', red);
|
|
307
|
+
checkChannelValue('Green', green);
|
|
308
|
+
checkChannelValue('Blue', blue);
|
|
309
|
+
checkChannelValue('Alpha', alpha);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Shortcut for `red` property
|
|
313
|
+
* Number from 0 to 255
|
|
314
|
+
* @alias red
|
|
315
|
+
*/
|
|
316
|
+
get r() {
|
|
317
|
+
return this.red;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Shortcut for `green` property
|
|
321
|
+
* Number from 0 to 255
|
|
322
|
+
* @alias green
|
|
323
|
+
*/
|
|
324
|
+
get g() {
|
|
325
|
+
return this.green;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Shortcut for `blue` property
|
|
329
|
+
* Number from 0 to 255
|
|
330
|
+
* @alias blue
|
|
331
|
+
*/
|
|
332
|
+
get b() {
|
|
333
|
+
return this.blue;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Shortcut for `alpha` property
|
|
337
|
+
* Number from 0 (transparent) to 255 (opaque)
|
|
338
|
+
* @alias alpha
|
|
339
|
+
*/
|
|
340
|
+
get a() {
|
|
341
|
+
return this.alpha;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Shortcut for `alpha` property
|
|
345
|
+
* Number from 0 (transparent) to 255 (opaque)
|
|
346
|
+
* @alias alpha
|
|
347
|
+
*/
|
|
348
|
+
get opacity() {
|
|
349
|
+
return this.alpha;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Shortcut for 1-`alpha` property
|
|
353
|
+
*/
|
|
354
|
+
get transparency() {
|
|
355
|
+
return 255 - this.alpha;
|
|
356
|
+
}
|
|
357
|
+
clone() {
|
|
358
|
+
return take(this.createColor(this.red, this.green, this.blue, this.alpha));
|
|
359
|
+
}
|
|
360
|
+
toString() {
|
|
361
|
+
return this.toHex();
|
|
362
|
+
}
|
|
363
|
+
toHex() {
|
|
364
|
+
if (this.alpha === 255) {
|
|
365
|
+
return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
|
|
366
|
+
.toString(16)
|
|
367
|
+
.padStart(2, '0')}`;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
|
|
371
|
+
.toString(16)
|
|
372
|
+
.padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
toRgb() {
|
|
376
|
+
if (this.alpha === 255) {
|
|
377
|
+
return `rgb(${this.red}, ${this.green}, ${this.blue})`;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
toHsl() {
|
|
384
|
+
throw new Error(`Getting HSL is not implemented`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Checks if the given value is a valid hex color string
|
|
390
|
+
*
|
|
391
|
+
* @param value - value to check
|
|
392
|
+
* @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
|
|
393
|
+
*
|
|
394
|
+
* @private function of Color
|
|
395
|
+
*/
|
|
396
|
+
function isHexColorString(value) {
|
|
397
|
+
return (typeof value === 'string' && /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
|
|
398
|
+
}
|
|
399
|
+
|
|
295
400
|
/**
|
|
296
401
|
* Constant for short hex lengths.
|
|
297
402
|
*/
|
|
@@ -503,16 +608,53 @@ function parseAlphaValue(value) {
|
|
|
503
608
|
|
|
504
609
|
/**
|
|
505
610
|
* Pattern matching hsl regex.
|
|
611
|
+
*
|
|
612
|
+
* @private function of Color
|
|
506
613
|
*/
|
|
507
614
|
const HSL_REGEX_PATTERN = /^hsl\(\s*([0-9.]+)\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/;
|
|
508
615
|
/**
|
|
509
616
|
* Pattern matching RGB regex.
|
|
617
|
+
*
|
|
618
|
+
* @private function of Color
|
|
510
619
|
*/
|
|
511
620
|
const RGB_REGEX_PATTERN = /^rgb\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
|
|
512
621
|
/**
|
|
513
622
|
* Pattern matching rgba regex.
|
|
623
|
+
*
|
|
624
|
+
* @private function of Color
|
|
514
625
|
*/
|
|
515
626
|
const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
|
|
627
|
+
/**
|
|
628
|
+
* Parses a supported color string into RGBA channels.
|
|
629
|
+
*
|
|
630
|
+
* @param color as a string for example `#009edd`, `rgb(0,158,221)`, `rgb(0%,62%,86.7%)`, `hsl(197.1,100%,43.3%)`, `red`, `darkgrey`,...
|
|
631
|
+
* @returns RGBA channel values.
|
|
632
|
+
*
|
|
633
|
+
* @private function of Color
|
|
634
|
+
*/
|
|
635
|
+
function parseColorString(color) {
|
|
636
|
+
const trimmed = color.trim();
|
|
637
|
+
const cssColor = CSS_COLORS[trimmed];
|
|
638
|
+
if (cssColor) {
|
|
639
|
+
return parseColorString(cssColor);
|
|
640
|
+
}
|
|
641
|
+
else if (isHexColorString(trimmed)) {
|
|
642
|
+
return parseHexColor(trimmed);
|
|
643
|
+
}
|
|
644
|
+
if (HSL_REGEX_PATTERN.test(trimmed)) {
|
|
645
|
+
return parseHslColor(trimmed);
|
|
646
|
+
}
|
|
647
|
+
else if (RGB_REGEX_PATTERN.test(trimmed)) {
|
|
648
|
+
return parseRgbColor(trimmed);
|
|
649
|
+
}
|
|
650
|
+
else if (RGBA_REGEX_PATTERN.test(trimmed)) {
|
|
651
|
+
return parseRgbaColor(trimmed);
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
516
658
|
/**
|
|
517
659
|
* Color object represents an RGB color with alpha channel
|
|
518
660
|
*
|
|
@@ -520,7 +662,7 @@ const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.
|
|
|
520
662
|
*
|
|
521
663
|
* @public exported from `@promptbook/color`
|
|
522
664
|
*/
|
|
523
|
-
class Color {
|
|
665
|
+
class Color extends ColorValue {
|
|
524
666
|
/**
|
|
525
667
|
* Creates a new Color instance from miscellaneous formats
|
|
526
668
|
* - It can receive Color instance and just return the same instance
|
|
@@ -593,25 +735,7 @@ class Color {
|
|
|
593
735
|
* @returns Color object
|
|
594
736
|
*/
|
|
595
737
|
static fromString(color) {
|
|
596
|
-
|
|
597
|
-
if (CSS_COLORS[trimmed]) {
|
|
598
|
-
return Color.fromString(CSS_COLORS[trimmed]);
|
|
599
|
-
}
|
|
600
|
-
else if (Color.isHexColorString(trimmed)) {
|
|
601
|
-
return Color.fromHex(trimmed);
|
|
602
|
-
}
|
|
603
|
-
if (HSL_REGEX_PATTERN.test(trimmed)) {
|
|
604
|
-
return Color.fromHsl(trimmed);
|
|
605
|
-
}
|
|
606
|
-
else if (RGB_REGEX_PATTERN.test(trimmed)) {
|
|
607
|
-
return Color.fromRgbString(trimmed);
|
|
608
|
-
}
|
|
609
|
-
else if (RGBA_REGEX_PATTERN.test(trimmed)) {
|
|
610
|
-
return Color.fromRgbaString(trimmed);
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
|
|
614
|
-
}
|
|
738
|
+
return Color.fromColorChannels(parseColorString(color));
|
|
615
739
|
}
|
|
616
740
|
/**
|
|
617
741
|
* Gets common color
|
|
@@ -641,8 +765,7 @@ class Color {
|
|
|
641
765
|
* @returns Color object
|
|
642
766
|
*/
|
|
643
767
|
static fromHex(hex) {
|
|
644
|
-
|
|
645
|
-
return take(new Color(red, green, blue, alpha));
|
|
768
|
+
return Color.fromColorChannels(parseHexColor(hex));
|
|
646
769
|
}
|
|
647
770
|
/**
|
|
648
771
|
* Creates a new Color instance from color in hsl format
|
|
@@ -651,8 +774,7 @@ class Color {
|
|
|
651
774
|
* @returns Color object
|
|
652
775
|
*/
|
|
653
776
|
static fromHsl(hsl) {
|
|
654
|
-
|
|
655
|
-
return take(new Color(red, green, blue, alpha));
|
|
777
|
+
return Color.fromColorChannels(parseHslColor(hsl));
|
|
656
778
|
}
|
|
657
779
|
/**
|
|
658
780
|
* Creates a new Color instance from color in rgb format
|
|
@@ -661,8 +783,7 @@ class Color {
|
|
|
661
783
|
* @returns Color object
|
|
662
784
|
*/
|
|
663
785
|
static fromRgbString(rgb) {
|
|
664
|
-
|
|
665
|
-
return take(new Color(red, green, blue, alpha));
|
|
786
|
+
return Color.fromColorChannels(parseRgbColor(rgb));
|
|
666
787
|
}
|
|
667
788
|
/**
|
|
668
789
|
* Creates a new Color instance from color in rbga format
|
|
@@ -671,8 +792,7 @@ class Color {
|
|
|
671
792
|
* @returns Color object
|
|
672
793
|
*/
|
|
673
794
|
static fromRgbaString(rgba) {
|
|
674
|
-
|
|
675
|
-
return take(new Color(red, green, blue, alpha));
|
|
795
|
+
return Color.fromColorChannels(parseRgbaColor(rgba));
|
|
676
796
|
}
|
|
677
797
|
/**
|
|
678
798
|
* Creates a new Color for color channels values
|
|
@@ -684,7 +804,7 @@ class Color {
|
|
|
684
804
|
* @returns Color object
|
|
685
805
|
*/
|
|
686
806
|
static fromValues(red, green, blue, alpha = 255) {
|
|
687
|
-
return
|
|
807
|
+
return Color.fromColorChannels({ red, green, blue, alpha });
|
|
688
808
|
}
|
|
689
809
|
/**
|
|
690
810
|
* Checks if the given value is a valid Color object.
|
|
@@ -717,8 +837,7 @@ class Color {
|
|
|
717
837
|
* @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
|
|
718
838
|
*/
|
|
719
839
|
static isHexColorString(value) {
|
|
720
|
-
return (
|
|
721
|
-
/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
|
|
840
|
+
return isHexColorString(value);
|
|
722
841
|
}
|
|
723
842
|
/**
|
|
724
843
|
* Creates new Color object
|
|
@@ -731,89 +850,13 @@ class Color {
|
|
|
731
850
|
* @param alpha number from 0 (transparent) to 255 (opaque)
|
|
732
851
|
*/
|
|
733
852
|
constructor(red, green, blue, alpha = 255) {
|
|
734
|
-
|
|
735
|
-
this.green = green;
|
|
736
|
-
this.blue = blue;
|
|
737
|
-
this.alpha = alpha;
|
|
738
|
-
checkChannelValue('Red', red);
|
|
739
|
-
checkChannelValue('Green', green);
|
|
740
|
-
checkChannelValue('Blue', blue);
|
|
741
|
-
checkChannelValue('Alpha', alpha);
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Shortcut for `red` property
|
|
745
|
-
* Number from 0 to 255
|
|
746
|
-
* @alias red
|
|
747
|
-
*/
|
|
748
|
-
get r() {
|
|
749
|
-
return this.red;
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Shortcut for `green` property
|
|
753
|
-
* Number from 0 to 255
|
|
754
|
-
* @alias green
|
|
755
|
-
*/
|
|
756
|
-
get g() {
|
|
757
|
-
return this.green;
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Shortcut for `blue` property
|
|
761
|
-
* Number from 0 to 255
|
|
762
|
-
* @alias blue
|
|
763
|
-
*/
|
|
764
|
-
get b() {
|
|
765
|
-
return this.blue;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Shortcut for `alpha` property
|
|
769
|
-
* Number from 0 (transparent) to 255 (opaque)
|
|
770
|
-
* @alias alpha
|
|
771
|
-
*/
|
|
772
|
-
get a() {
|
|
773
|
-
return this.alpha;
|
|
774
|
-
}
|
|
775
|
-
/**
|
|
776
|
-
* Shortcut for `alpha` property
|
|
777
|
-
* Number from 0 (transparent) to 255 (opaque)
|
|
778
|
-
* @alias alpha
|
|
779
|
-
*/
|
|
780
|
-
get opacity() {
|
|
781
|
-
return this.alpha;
|
|
782
|
-
}
|
|
783
|
-
/**
|
|
784
|
-
* Shortcut for 1-`alpha` property
|
|
785
|
-
*/
|
|
786
|
-
get transparency() {
|
|
787
|
-
return 255 - this.alpha;
|
|
788
|
-
}
|
|
789
|
-
clone() {
|
|
790
|
-
return take(new Color(this.red, this.green, this.blue, this.alpha));
|
|
791
|
-
}
|
|
792
|
-
toString() {
|
|
793
|
-
return this.toHex();
|
|
794
|
-
}
|
|
795
|
-
toHex() {
|
|
796
|
-
if (this.alpha === 255) {
|
|
797
|
-
return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
|
|
798
|
-
.toString(16)
|
|
799
|
-
.padStart(2, '0')}`;
|
|
800
|
-
}
|
|
801
|
-
else {
|
|
802
|
-
return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
|
|
803
|
-
.toString(16)
|
|
804
|
-
.padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
|
|
805
|
-
}
|
|
853
|
+
super(red, green, blue, alpha);
|
|
806
854
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
return `rgb(${this.red}, ${this.green}, ${this.blue})`;
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
|
|
813
|
-
}
|
|
855
|
+
createColor(red, green, blue, alpha) {
|
|
856
|
+
return new Color(red, green, blue, alpha);
|
|
814
857
|
}
|
|
815
|
-
|
|
816
|
-
|
|
858
|
+
static fromColorChannels({ red, green, blue, alpha }) {
|
|
859
|
+
return take(new Color(red, green, blue, alpha));
|
|
817
860
|
}
|
|
818
861
|
}
|
|
819
862
|
|
|
@@ -2032,6 +2075,60 @@ function validatePipelineString(pipelineString) {
|
|
|
2032
2075
|
}
|
|
2033
2076
|
// TODO: [🧠][🈴] Where is the best location for this file
|
|
2034
2077
|
|
|
2078
|
+
/**
|
|
2079
|
+
* Appends one markdown block to an existing markdown document.
|
|
2080
|
+
*
|
|
2081
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2082
|
+
*/
|
|
2083
|
+
function appendMarkdownBlock(pipelineString, markdownBlock) {
|
|
2084
|
+
return spaceTrim$1((block) => `
|
|
2085
|
+
${block(pipelineString)}
|
|
2086
|
+
|
|
2087
|
+
${block(markdownBlock)}
|
|
2088
|
+
`);
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
/**
|
|
2092
|
+
* Collects pipeline-level commands in the existing serialization order.
|
|
2093
|
+
*
|
|
2094
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2095
|
+
*/
|
|
2096
|
+
function createPipelineCommands(pipelineJson) {
|
|
2097
|
+
const { pipelineUrl, bookVersion, parameters } = pipelineJson;
|
|
2098
|
+
const commands = [];
|
|
2099
|
+
if (pipelineUrl) {
|
|
2100
|
+
commands.push(`PIPELINE URL ${pipelineUrl}`);
|
|
2101
|
+
}
|
|
2102
|
+
if (bookVersion !== `undefined`) {
|
|
2103
|
+
commands.push(`BOOK VERSION ${bookVersion}`);
|
|
2104
|
+
}
|
|
2105
|
+
commands.push(...createParameterCommands(parameters, 'INPUT PARAMETER', ({ isInput }) => isInput));
|
|
2106
|
+
commands.push(...createParameterCommands(parameters, 'OUTPUT PARAMETER', ({ isOutput }) => isOutput));
|
|
2107
|
+
return commands;
|
|
2108
|
+
}
|
|
2109
|
+
/**
|
|
2110
|
+
* Builds one group of parameter commands while preserving the original parameter order.
|
|
2111
|
+
*
|
|
2112
|
+
* @private internal utility of `createPipelineCommands`
|
|
2113
|
+
*/
|
|
2114
|
+
function createParameterCommands(parameters, commandPrefix, isIncluded) {
|
|
2115
|
+
return parameters
|
|
2116
|
+
.filter((parameter) => isIncluded(parameter))
|
|
2117
|
+
.map((parameter) => `${commandPrefix} ${parameterJsonToString(parameter)}`);
|
|
2118
|
+
}
|
|
2119
|
+
/**
|
|
2120
|
+
* Converts one parameter JSON declaration to the serialized inline form.
|
|
2121
|
+
*
|
|
2122
|
+
* @private internal utility of `createPipelineCommands`
|
|
2123
|
+
*/
|
|
2124
|
+
function parameterJsonToString(parameterJson) {
|
|
2125
|
+
const { name, description } = parameterJson;
|
|
2126
|
+
if (!description) {
|
|
2127
|
+
return `{${name}}`;
|
|
2128
|
+
}
|
|
2129
|
+
return `{${name}} ${description}`;
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2035
2132
|
/**
|
|
2036
2133
|
* Prettify the html code
|
|
2037
2134
|
*
|
|
@@ -2046,152 +2143,222 @@ function prettifyMarkdown(content) {
|
|
|
2046
2143
|
}
|
|
2047
2144
|
|
|
2048
2145
|
/**
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2051
|
-
* Note: [🔂] This function is idempotent.
|
|
2146
|
+
* Creates the initial markdown heading and description of a pipeline.
|
|
2052
2147
|
*
|
|
2053
|
-
* @
|
|
2148
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2054
2149
|
*/
|
|
2055
|
-
function
|
|
2056
|
-
|
|
2150
|
+
function createPipelineIntroduction(pipelineJson) {
|
|
2151
|
+
const { title, description } = pipelineJson;
|
|
2152
|
+
const pipelineIntroduction = spaceTrim$1((block) => `
|
|
2153
|
+
# ${title}
|
|
2154
|
+
|
|
2155
|
+
${block(description || '')}
|
|
2156
|
+
`);
|
|
2157
|
+
// TODO: [main] !!5 This increases size of the bundle and is probably not necessary
|
|
2158
|
+
return prettifyMarkdown(pipelineIntroduction);
|
|
2057
2159
|
}
|
|
2058
2160
|
|
|
2059
2161
|
/**
|
|
2060
|
-
*
|
|
2162
|
+
* Renders commands as markdown bullet items.
|
|
2061
2163
|
*
|
|
2062
|
-
* @
|
|
2063
|
-
|
|
2064
|
-
|
|
2164
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2165
|
+
*/
|
|
2166
|
+
function stringifyCommands(commands) {
|
|
2167
|
+
return commands.map((command) => `- ${command}`).join('\n');
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
/**
|
|
2171
|
+
* Makes first letter of a string uppercase
|
|
2065
2172
|
*
|
|
2066
|
-
*
|
|
2173
|
+
* Note: [🔂] This function is idempotent.
|
|
2174
|
+
*
|
|
2175
|
+
* @public exported from `@promptbook/utils`
|
|
2067
2176
|
*/
|
|
2068
|
-
function
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
# ${title}
|
|
2177
|
+
function capitalize(word) {
|
|
2178
|
+
return word.substring(0, 1).toUpperCase() + word.substring(1);
|
|
2179
|
+
}
|
|
2072
2180
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2181
|
+
/**
|
|
2182
|
+
* Collects all task-specific serialization details.
|
|
2183
|
+
*
|
|
2184
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2185
|
+
*/
|
|
2186
|
+
function createTaskSerialization(task) {
|
|
2187
|
+
const taskTypeSerialization = createTaskTypeSerialization(task);
|
|
2188
|
+
return {
|
|
2189
|
+
commands: [
|
|
2190
|
+
...taskTypeSerialization.commands,
|
|
2191
|
+
...createJokerCommands(task),
|
|
2192
|
+
...createPostprocessingCommands(task),
|
|
2193
|
+
...createExpectationCommands(task),
|
|
2194
|
+
...createFormatCommands(task),
|
|
2195
|
+
],
|
|
2196
|
+
contentLanguage: taskTypeSerialization.contentLanguage,
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2200
|
+
* Collects commands and content language driven by the task type.
|
|
2201
|
+
*
|
|
2202
|
+
* @private internal utility of `createTaskSerialization`
|
|
2203
|
+
*/
|
|
2204
|
+
function createTaskTypeSerialization(task) {
|
|
2205
|
+
if (task.taskType === 'PROMPT_TASK') {
|
|
2206
|
+
return {
|
|
2207
|
+
commands: createPromptTaskCommands(task),
|
|
2208
|
+
contentLanguage: 'text',
|
|
2209
|
+
};
|
|
2210
|
+
}
|
|
2211
|
+
if (task.taskType === 'SIMPLE_TASK') {
|
|
2212
|
+
return {
|
|
2213
|
+
commands: ['SIMPLE TEMPLATE'],
|
|
2214
|
+
contentLanguage: 'text',
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
2217
|
+
if (task.taskType === 'SCRIPT_TASK') {
|
|
2218
|
+
return {
|
|
2219
|
+
commands: ['SCRIPT'],
|
|
2220
|
+
contentLanguage: task.contentLanguage || '',
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
if (task.taskType === 'DIALOG_TASK') {
|
|
2224
|
+
return {
|
|
2225
|
+
commands: ['DIALOG'],
|
|
2226
|
+
contentLanguage: 'text',
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
return {
|
|
2230
|
+
commands: [],
|
|
2231
|
+
contentLanguage: 'text',
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
/**
|
|
2235
|
+
* Collects prompt-task-specific commands.
|
|
2236
|
+
*
|
|
2237
|
+
* @private internal utility of `createTaskSerialization`
|
|
2238
|
+
*/
|
|
2239
|
+
function createPromptTaskCommands(task) {
|
|
2240
|
+
const { modelName, modelVariant } = task.modelRequirements || {};
|
|
2075
2241
|
const commands = [];
|
|
2076
|
-
|
|
2077
|
-
|
|
2242
|
+
// Note: Do nothing, it is default
|
|
2243
|
+
// commands.push(`PROMPT`);
|
|
2244
|
+
if (modelVariant) {
|
|
2245
|
+
commands.push(`MODEL VARIANT ${capitalize(modelVariant)}`);
|
|
2078
2246
|
}
|
|
2079
|
-
if (
|
|
2080
|
-
commands.push(`
|
|
2247
|
+
if (modelName) {
|
|
2248
|
+
commands.push(`MODEL NAME \`${modelName}\``);
|
|
2081
2249
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2250
|
+
return commands;
|
|
2251
|
+
}
|
|
2252
|
+
/**
|
|
2253
|
+
* Collects joker commands.
|
|
2254
|
+
*
|
|
2255
|
+
* @private internal utility of `createTaskSerialization`
|
|
2256
|
+
*/
|
|
2257
|
+
function createJokerCommands(task) {
|
|
2258
|
+
var _a;
|
|
2259
|
+
return ((_a = task.jokerParameterNames) === null || _a === void 0 ? void 0 : _a.map((joker) => `JOKER {${joker}}`)) || [];
|
|
2260
|
+
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Collects postprocessing commands.
|
|
2263
|
+
*
|
|
2264
|
+
* @private internal utility of `createTaskSerialization`
|
|
2265
|
+
*/
|
|
2266
|
+
function createPostprocessingCommands(task) {
|
|
2267
|
+
var _a;
|
|
2268
|
+
return (((_a = task.postprocessingFunctionNames) === null || _a === void 0 ? void 0 : _a.map((postprocessingFunctionName) => `POSTPROCESSING \`${postprocessingFunctionName}\``)) || []);
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* Collects expectation commands.
|
|
2272
|
+
*
|
|
2273
|
+
* @private internal utility of `createTaskSerialization`
|
|
2274
|
+
*/
|
|
2275
|
+
function createExpectationCommands(task) {
|
|
2276
|
+
if (!task.expectations) {
|
|
2277
|
+
return [];
|
|
2086
2278
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2279
|
+
return Object.entries(task.expectations).flatMap(([unit, expectation]) => createExpectationCommandsForUnit(unit, expectation.min, expectation.max));
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Collects expectation commands for a single unit.
|
|
2283
|
+
*
|
|
2284
|
+
* @private internal utility of `createTaskSerialization`
|
|
2285
|
+
*/
|
|
2286
|
+
function createExpectationCommandsForUnit(unit, min, max) {
|
|
2287
|
+
if (min === max) {
|
|
2288
|
+
return [`EXPECT EXACTLY ${min} ${formatExpectationUnit(unit, min)}`];
|
|
2089
2289
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
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)}
|
|
2290
|
+
const commands = [];
|
|
2291
|
+
if (min !== undefined) {
|
|
2292
|
+
commands.push(`EXPECT MIN ${min} ${formatExpectationUnit(unit, min)}`);
|
|
2293
|
+
}
|
|
2294
|
+
if (max !== undefined) {
|
|
2295
|
+
commands.push(`EXPECT MAX ${max} ${formatExpectationUnit(unit, max)}`);
|
|
2296
|
+
}
|
|
2297
|
+
return commands;
|
|
2298
|
+
}
|
|
2299
|
+
/**
|
|
2300
|
+
* Formats the expectation unit exactly as the legacy serializer does.
|
|
2301
|
+
*
|
|
2302
|
+
* @private internal utility of `createTaskSerialization`
|
|
2303
|
+
*/
|
|
2304
|
+
function formatExpectationUnit(unit, amount) {
|
|
2305
|
+
return capitalize(unit + (amount > 1 ? 's' : ''));
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Collects format commands.
|
|
2309
|
+
*
|
|
2310
|
+
* @private internal utility of `createTaskSerialization`
|
|
2311
|
+
*/
|
|
2312
|
+
function createFormatCommands(task) {
|
|
2313
|
+
if (task.format === 'JSON') {
|
|
2314
|
+
// TODO: @deprecated remove
|
|
2315
|
+
return ['FORMAT JSON'];
|
|
2316
|
+
}
|
|
2317
|
+
return [];
|
|
2318
|
+
}
|
|
2165
2319
|
|
|
2166
|
-
|
|
2320
|
+
/**
|
|
2321
|
+
* Stringifies one task section of the pipeline.
|
|
2322
|
+
*
|
|
2323
|
+
* @private internal utility of `pipelineJsonToString`
|
|
2324
|
+
*/
|
|
2325
|
+
function stringifyTask(task) {
|
|
2326
|
+
const { title, description, content, resultingParameterName } = task;
|
|
2327
|
+
const { commands, contentLanguage } = createTaskSerialization(task);
|
|
2328
|
+
return spaceTrim$1((block) => `
|
|
2329
|
+
## ${title}
|
|
2167
2330
|
|
|
2168
|
-
|
|
2331
|
+
${block(description || '')}
|
|
2169
2332
|
|
|
2170
|
-
|
|
2333
|
+
${block(stringifyCommands(commands))}
|
|
2171
2334
|
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2335
|
+
\`\`\`${contentLanguage}
|
|
2336
|
+
${block(spaceTrim$1(content))}
|
|
2337
|
+
\`\`\`
|
|
2175
2338
|
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
}
|
|
2181
|
-
return validatePipelineString(pipelineString);
|
|
2339
|
+
\`-> {${resultingParameterName}}\`
|
|
2340
|
+
`); // <- TODO: [main] !!3 If the parameter here has description, add it and use taskParameterJsonToString
|
|
2341
|
+
// <- TODO: [main] !!3 Escape
|
|
2342
|
+
// <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
|
|
2182
2343
|
}
|
|
2344
|
+
|
|
2183
2345
|
/**
|
|
2184
|
-
*
|
|
2346
|
+
* Converts promptbook in JSON format to string format
|
|
2185
2347
|
*
|
|
2186
|
-
* @
|
|
2348
|
+
* @deprecated TODO: [🥍][🧠] Backup original files in `PipelineJson` same as in Promptbook.studio
|
|
2349
|
+
* @param pipelineJson Promptbook in JSON format (.bookc)
|
|
2350
|
+
* @returns Promptbook in string format (.book.md)
|
|
2351
|
+
*
|
|
2352
|
+
* @public exported from `@promptbook/core`
|
|
2187
2353
|
*/
|
|
2188
|
-
function
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2354
|
+
function pipelineJsonToString(pipelineJson) {
|
|
2355
|
+
let pipelineString = createPipelineIntroduction(pipelineJson);
|
|
2356
|
+
const pipelineCommands = createPipelineCommands(pipelineJson);
|
|
2357
|
+
pipelineString = appendMarkdownBlock(pipelineString, stringifyCommands(pipelineCommands));
|
|
2358
|
+
for (const task of pipelineJson.tasks) {
|
|
2359
|
+
pipelineString = appendMarkdownBlock(pipelineString, stringifyTask(task));
|
|
2193
2360
|
}
|
|
2194
|
-
return
|
|
2361
|
+
return validatePipelineString(pipelineString);
|
|
2195
2362
|
}
|
|
2196
2363
|
// TODO: [🛋] Implement new features and commands into `pipelineJsonToString` + `taskParameterJsonToString` , use `stringifyCommand`
|
|
2197
2364
|
// TODO: [🧠] Is there a way to auto-detect missing features in pipelineJsonToString
|
|
@@ -2263,120 +2430,183 @@ function $deepFreeze(objectValue) {
|
|
|
2263
2430
|
* @public exported from `@promptbook/utils`
|
|
2264
2431
|
*/
|
|
2265
2432
|
function checkSerializableAsJson(options) {
|
|
2266
|
-
|
|
2433
|
+
checkSerializableValue(options);
|
|
2434
|
+
}
|
|
2435
|
+
// TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
|
|
2436
|
+
// TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
2437
|
+
// Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
|
|
2438
|
+
/**
|
|
2439
|
+
* Checks one value and dispatches to the appropriate specialized validator.
|
|
2440
|
+
*
|
|
2441
|
+
* @private function of `checkSerializableAsJson`
|
|
2442
|
+
*/
|
|
2443
|
+
function checkSerializableValue(options) {
|
|
2444
|
+
const { value } = options;
|
|
2445
|
+
if (isSerializablePrimitive(value)) {
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2267
2448
|
if (value === undefined) {
|
|
2268
|
-
throw new UnexpectedError(`${name} is undefined`);
|
|
2449
|
+
throw new UnexpectedError(`${options.name} is undefined`);
|
|
2269
2450
|
}
|
|
2270
|
-
|
|
2271
|
-
|
|
2451
|
+
if (typeof value === 'symbol') {
|
|
2452
|
+
throw new UnexpectedError(`${options.name} is symbol`);
|
|
2272
2453
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2454
|
+
if (typeof value === 'function') {
|
|
2455
|
+
throw new UnexpectedError(`${options.name} is function`);
|
|
2275
2456
|
}
|
|
2276
|
-
|
|
2457
|
+
if (Array.isArray(value)) {
|
|
2458
|
+
checkSerializableArray(options, value);
|
|
2277
2459
|
return;
|
|
2278
2460
|
}
|
|
2279
|
-
|
|
2461
|
+
if (value !== null && typeof value === 'object') {
|
|
2462
|
+
checkSerializableObject(options, value);
|
|
2280
2463
|
return;
|
|
2281
2464
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2465
|
+
throwUnknownTypeError(options);
|
|
2466
|
+
}
|
|
2467
|
+
/**
|
|
2468
|
+
* Checks the primitive values that are directly JSON serializable.
|
|
2469
|
+
*
|
|
2470
|
+
* @private function of `checkSerializableAsJson`
|
|
2471
|
+
*/
|
|
2472
|
+
function isSerializablePrimitive(value) {
|
|
2473
|
+
return (value === null ||
|
|
2474
|
+
typeof value === 'boolean' ||
|
|
2475
|
+
(typeof value === 'number' && !isNaN(value)) ||
|
|
2476
|
+
typeof value === 'string');
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Recursively checks JSON array items.
|
|
2480
|
+
*
|
|
2481
|
+
* @private function of `checkSerializableAsJson`
|
|
2482
|
+
*/
|
|
2483
|
+
function checkSerializableArray(context, arrayValue) {
|
|
2484
|
+
for (let index = 0; index < arrayValue.length; index++) {
|
|
2485
|
+
checkSerializableAsJson({
|
|
2486
|
+
...context,
|
|
2487
|
+
name: `${context.name}[${index}]`,
|
|
2488
|
+
value: arrayValue[index],
|
|
2489
|
+
});
|
|
2292
2490
|
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Checks object-like values and dispatches special unsupported built-ins.
|
|
2494
|
+
*
|
|
2495
|
+
* @private function of `checkSerializableAsJson`
|
|
2496
|
+
*/
|
|
2497
|
+
function checkSerializableObject(context, objectValue) {
|
|
2498
|
+
checkUnsupportedObjectType(context, objectValue);
|
|
2499
|
+
checkSerializableObjectEntries(context, objectValue);
|
|
2500
|
+
assertJsonStringificationSucceeds(context, objectValue);
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Rejects built-in objects that must be converted before JSON serialization.
|
|
2504
|
+
*
|
|
2505
|
+
* @private function of `checkSerializableAsJson`
|
|
2506
|
+
*/
|
|
2507
|
+
function checkUnsupportedObjectType(context, objectValue) {
|
|
2508
|
+
if (objectValue instanceof Date) {
|
|
2509
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
2510
|
+
\`${context.name}\` is Date
|
|
2297
2511
|
|
|
2298
|
-
|
|
2512
|
+
Use \`string_date_iso8601\` instead
|
|
2299
2513
|
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2514
|
+
Additional message for \`${context.name}\`:
|
|
2515
|
+
${block(context.message || '(nothing)')}
|
|
2516
|
+
`));
|
|
2517
|
+
}
|
|
2518
|
+
if (objectValue instanceof Map) {
|
|
2519
|
+
throw new UnexpectedError(`${context.name} is Map`);
|
|
2520
|
+
}
|
|
2521
|
+
if (objectValue instanceof Set) {
|
|
2522
|
+
throw new UnexpectedError(`${context.name} is Set`);
|
|
2523
|
+
}
|
|
2524
|
+
if (objectValue instanceof RegExp) {
|
|
2525
|
+
throw new UnexpectedError(`${context.name} is RegExp`);
|
|
2526
|
+
}
|
|
2527
|
+
if (objectValue instanceof Error) {
|
|
2528
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
2529
|
+
\`${context.name}\` is unserialized Error
|
|
2316
2530
|
|
|
2317
|
-
|
|
2531
|
+
Use function \`serializeError\`
|
|
2318
2532
|
|
|
2319
|
-
|
|
2320
|
-
|
|
2533
|
+
Additional message for \`${context.name}\`:
|
|
2534
|
+
${block(context.message || '(nothing)')}
|
|
2321
2535
|
|
|
2322
|
-
|
|
2536
|
+
`));
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
/**
|
|
2540
|
+
* Recursively checks object properties while preserving omitted `undefined` keys.
|
|
2541
|
+
*
|
|
2542
|
+
* @private function of `checkSerializableAsJson`
|
|
2543
|
+
*/
|
|
2544
|
+
function checkSerializableObjectEntries(context, objectValue) {
|
|
2545
|
+
for (const [subName, subValue] of Object.entries(objectValue)) {
|
|
2546
|
+
if (subValue === undefined) {
|
|
2547
|
+
// Note: undefined in object is serializable - it is just omitted
|
|
2548
|
+
continue;
|
|
2323
2549
|
}
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2550
|
+
checkSerializableAsJson({
|
|
2551
|
+
...context,
|
|
2552
|
+
name: `${context.name}.${subName}`,
|
|
2553
|
+
value: subValue,
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
/**
|
|
2558
|
+
* Uses `JSON.stringify` as the final guard for cases like circular references.
|
|
2559
|
+
*
|
|
2560
|
+
* @private function of `checkSerializableAsJson`
|
|
2561
|
+
*/
|
|
2562
|
+
function assertJsonStringificationSucceeds(context, objectValue) {
|
|
2563
|
+
try {
|
|
2564
|
+
JSON.stringify(objectValue); // <- TODO: [0]
|
|
2565
|
+
}
|
|
2566
|
+
catch (error) {
|
|
2567
|
+
assertsError(error);
|
|
2568
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
2569
|
+
\`${context.name}\` is not serializable
|
|
2339
2570
|
|
|
2340
|
-
|
|
2571
|
+
${block(error.stack || error.message)}
|
|
2341
2572
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2573
|
+
Additional message for \`${context.name}\`:
|
|
2574
|
+
${block(context.message || '(nothing)')}
|
|
2575
|
+
`));
|
|
2576
|
+
}
|
|
2577
|
+
/*
|
|
2578
|
+
TODO: [0] Is there some more elegant way to check circular references?
|
|
2579
|
+
const seen = new Set();
|
|
2580
|
+
const stack = [{ value }];
|
|
2581
|
+
while (stack.length > 0) {
|
|
2582
|
+
const { value } = stack.pop()!;
|
|
2583
|
+
if (typeof value === 'object' && value !== null) {
|
|
2584
|
+
if (seen.has(value)) {
|
|
2585
|
+
throw new UnexpectedError(`${name} has circular reference`);
|
|
2345
2586
|
}
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
const { value } = stack.pop()!;
|
|
2352
|
-
if (typeof value === 'object' && value !== null) {
|
|
2353
|
-
if (seen.has(value)) {
|
|
2354
|
-
throw new UnexpectedError(`${name} has circular reference`);
|
|
2355
|
-
}
|
|
2356
|
-
seen.add(value);
|
|
2357
|
-
if (Array.isArray(value)) {
|
|
2358
|
-
stack.push(...value.map((value) => ({ value })));
|
|
2359
|
-
} else {
|
|
2360
|
-
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2587
|
+
seen.add(value);
|
|
2588
|
+
if (Array.isArray(value)) {
|
|
2589
|
+
stack.push(...value.map((value) => ({ value })));
|
|
2590
|
+
} else {
|
|
2591
|
+
stack.push(...Object.values(value).map((value) => ({ value })));
|
|
2363
2592
|
}
|
|
2364
|
-
*/
|
|
2365
|
-
return;
|
|
2366
2593
|
}
|
|
2367
2594
|
}
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2595
|
+
*/
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* Throws the fallback error for unsupported value types like `bigint` and `NaN`.
|
|
2599
|
+
*
|
|
2600
|
+
* @private function of `checkSerializableAsJson`
|
|
2601
|
+
*/
|
|
2602
|
+
function throwUnknownTypeError(context) {
|
|
2603
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
2604
|
+
\`${context.name}\` is unknown type
|
|
2371
2605
|
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
}
|
|
2606
|
+
Additional message for \`${context.name}\`:
|
|
2607
|
+
${block(context.message || '(nothing)')}
|
|
2608
|
+
`));
|
|
2376
2609
|
}
|
|
2377
|
-
// TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
|
|
2378
|
-
// TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
|
|
2379
|
-
// Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
|
|
2380
2610
|
|
|
2381
2611
|
/**
|
|
2382
2612
|
* Creates a deep clone of the given object
|
|
@@ -2631,233 +2861,518 @@ function validatePipeline(pipeline) {
|
|
|
2631
2861
|
*/
|
|
2632
2862
|
function validatePipeline_InnerFunction(pipeline) {
|
|
2633
2863
|
// TODO: [🧠] Maybe test if promptbook is a promise and make specific error case for that
|
|
2634
|
-
const
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2864
|
+
const context = createPipelineValidationContext(pipeline);
|
|
2865
|
+
validatePipelineMetadata(context);
|
|
2866
|
+
validatePipelineCollectionsStructure(context);
|
|
2867
|
+
validatePipelineParameters(context);
|
|
2868
|
+
validatePipelineTasks(context);
|
|
2869
|
+
validatePipelineDependencyResolution(context);
|
|
2870
|
+
// Note: Check that formfactor is corresponding to the pipeline interface
|
|
2871
|
+
// TODO: !!6 Implement this
|
|
2872
|
+
// pipeline.formfactorName
|
|
2873
|
+
}
|
|
2874
|
+
/**
|
|
2875
|
+
* Creates the shared validation context for one pipeline.
|
|
2876
|
+
*
|
|
2877
|
+
* @private internal utility of `validatePipeline`
|
|
2878
|
+
*/
|
|
2879
|
+
function createPipelineValidationContext(pipeline) {
|
|
2880
|
+
return {
|
|
2881
|
+
pipeline,
|
|
2882
|
+
pipelineIdentification: getPipelineIdentification(pipeline),
|
|
2883
|
+
};
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Builds a short file/url identification block for validation errors.
|
|
2887
|
+
*
|
|
2888
|
+
* @private internal utility of `validatePipeline`
|
|
2889
|
+
*/
|
|
2890
|
+
function getPipelineIdentification(pipeline) {
|
|
2891
|
+
// Note: This is a 😐 implementation of [🚞]
|
|
2892
|
+
const pipelineIdentificationParts = [];
|
|
2893
|
+
if (pipeline.sourceFile !== undefined) {
|
|
2894
|
+
pipelineIdentificationParts.push(`File: ${pipeline.sourceFile}`);
|
|
2895
|
+
}
|
|
2896
|
+
if (pipeline.pipelineUrl !== undefined) {
|
|
2897
|
+
pipelineIdentificationParts.push(`Url: ${pipeline.pipelineUrl}`);
|
|
2898
|
+
}
|
|
2899
|
+
return pipelineIdentificationParts.join('\n');
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* Validates pipeline-level metadata fields.
|
|
2903
|
+
*
|
|
2904
|
+
* @private internal step of `validatePipeline`
|
|
2905
|
+
*/
|
|
2906
|
+
function validatePipelineMetadata({ pipeline, pipelineIdentification }) {
|
|
2907
|
+
validatePipelineUrl(pipeline, pipelineIdentification);
|
|
2908
|
+
validatePipelineBookVersion(pipeline, pipelineIdentification);
|
|
2909
|
+
}
|
|
2910
|
+
/**
|
|
2911
|
+
* Validates that the expected top-level collections have array structure.
|
|
2912
|
+
*
|
|
2913
|
+
* @private internal step of `validatePipeline`
|
|
2914
|
+
*/
|
|
2915
|
+
function validatePipelineCollectionsStructure({ pipeline, pipelineIdentification }) {
|
|
2916
|
+
validatePipelineParametersCollection(pipeline, pipelineIdentification);
|
|
2917
|
+
validatePipelineTasksCollection(pipeline, pipelineIdentification);
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Validates all pipeline parameter declarations.
|
|
2921
|
+
*
|
|
2922
|
+
* @private internal step of `validatePipeline`
|
|
2923
|
+
*/
|
|
2924
|
+
function validatePipelineParameters({ pipeline, pipelineIdentification }) {
|
|
2925
|
+
for (const parameter of pipeline.parameters) {
|
|
2926
|
+
validatePipelineParameter(parameter, pipeline, pipelineIdentification);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
/**
|
|
2930
|
+
* Validates all pipeline tasks and their per-task invariants.
|
|
2931
|
+
*
|
|
2932
|
+
* @private internal step of `validatePipeline`
|
|
2933
|
+
*/
|
|
2934
|
+
function validatePipelineTasks({ pipeline, pipelineIdentification }) {
|
|
2935
|
+
// Note: All input parameters are defined - so that they can be used as result of some task
|
|
2936
|
+
const definedParameters = createInitiallyDefinedParameters(pipeline);
|
|
2937
|
+
for (const task of pipeline.tasks) {
|
|
2938
|
+
validatePipelineTask(task, definedParameters, pipelineIdentification);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* Validates that task dependencies can be resolved without cycles or missing definitions.
|
|
2943
|
+
*
|
|
2944
|
+
* @private internal step of `validatePipeline`
|
|
2945
|
+
*/
|
|
2946
|
+
function validatePipelineDependencyResolution({ pipeline, pipelineIdentification }) {
|
|
2947
|
+
let dependencyResolutionState = createInitialDependencyResolutionState(pipeline);
|
|
2948
|
+
let loopLimit = LOOP_LIMIT;
|
|
2949
|
+
while (hasUnresolvedTasks(dependencyResolutionState)) {
|
|
2950
|
+
if (loopLimit-- < 0) {
|
|
2951
|
+
throw createDependencyResolutionLoopLimitError(pipelineIdentification);
|
|
2642
2952
|
}
|
|
2643
|
-
|
|
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
|
-
`));
|
|
2953
|
+
dependencyResolutionState = resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification);
|
|
2652
2954
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2955
|
+
}
|
|
2956
|
+
/**
|
|
2957
|
+
* Validates one pipeline parameter declaration.
|
|
2958
|
+
*
|
|
2959
|
+
* @private internal step of `validatePipeline`
|
|
2960
|
+
*/
|
|
2961
|
+
function validatePipelineParameter(parameter, pipeline, pipelineIdentification) {
|
|
2962
|
+
validateParameterDirection(parameter, pipelineIdentification);
|
|
2963
|
+
validateParameterUsage(parameter, pipeline, pipelineIdentification);
|
|
2964
|
+
validateParameterDefinition(parameter, pipeline, pipelineIdentification);
|
|
2965
|
+
}
|
|
2966
|
+
/**
|
|
2967
|
+
* Validates one pipeline task and its invariants.
|
|
2968
|
+
*
|
|
2969
|
+
* @private internal step of `validatePipeline`
|
|
2970
|
+
*/
|
|
2971
|
+
function validatePipelineTask(task, definedParameters, pipelineIdentification) {
|
|
2972
|
+
validateTaskResultingParameter(task, definedParameters, pipelineIdentification);
|
|
2973
|
+
validateTaskJokers(task, pipelineIdentification);
|
|
2974
|
+
validateTaskExpectations(task, pipelineIdentification);
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Validates the pipeline URL, when present.
|
|
2978
|
+
*
|
|
2979
|
+
* @private internal utility of `validatePipeline`
|
|
2980
|
+
*/
|
|
2981
|
+
function validatePipelineUrl(pipeline, pipelineIdentification) {
|
|
2982
|
+
if (pipeline.pipelineUrl === undefined || isValidPipelineUrl(pipeline.pipelineUrl)) {
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2985
|
+
// <- Note: [🚲]
|
|
2986
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
2987
|
+
Invalid promptbook URL "${pipeline.pipelineUrl}"
|
|
2657
2988
|
|
|
2658
|
-
|
|
2659
|
-
|
|
2989
|
+
${block(pipelineIdentification)}
|
|
2990
|
+
`));
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Validates the Promptbook version, when present.
|
|
2994
|
+
*
|
|
2995
|
+
* @private internal utility of `validatePipeline`
|
|
2996
|
+
*/
|
|
2997
|
+
function validatePipelineBookVersion(pipeline, pipelineIdentification) {
|
|
2998
|
+
if (pipeline.bookVersion === undefined || isValidPromptbookVersion(pipeline.bookVersion)) {
|
|
2999
|
+
return;
|
|
2660
3000
|
}
|
|
3001
|
+
// <- Note: [🚲]
|
|
3002
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3003
|
+
Invalid Promptbook Version "${pipeline.bookVersion}"
|
|
3004
|
+
|
|
3005
|
+
${block(pipelineIdentification)}
|
|
3006
|
+
`));
|
|
3007
|
+
}
|
|
3008
|
+
/**
|
|
3009
|
+
* Validates that `pipeline.parameters` is an array.
|
|
3010
|
+
*
|
|
3011
|
+
* @private internal utility of `validatePipeline`
|
|
3012
|
+
*/
|
|
3013
|
+
function validatePipelineParametersCollection(pipeline, pipelineIdentification) {
|
|
2661
3014
|
// TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
|
|
2662
|
-
if (
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
3015
|
+
if (Array.isArray(pipeline.parameters)) {
|
|
3016
|
+
return;
|
|
3017
|
+
}
|
|
3018
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
3019
|
+
throw new ParseError(spaceTrim$1((block) => `
|
|
3020
|
+
Pipeline is valid JSON but with wrong structure
|
|
2666
3021
|
|
|
2667
|
-
|
|
3022
|
+
\`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
|
|
2668
3023
|
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
3024
|
+
${block(pipelineIdentification)}
|
|
3025
|
+
`));
|
|
3026
|
+
}
|
|
3027
|
+
/**
|
|
3028
|
+
* Validates that `pipeline.tasks` is an array.
|
|
3029
|
+
*
|
|
3030
|
+
* @private internal utility of `validatePipeline`
|
|
3031
|
+
*/
|
|
3032
|
+
function validatePipelineTasksCollection(pipeline, pipelineIdentification) {
|
|
2672
3033
|
// TODO: [🧠] Maybe do here some proper JSON-schema / ZOD checking
|
|
2673
|
-
if (
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
3034
|
+
if (Array.isArray(pipeline.tasks)) {
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
// TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
|
|
3038
|
+
throw new ParseError(spaceTrim$1((block) => `
|
|
3039
|
+
Pipeline is valid JSON but with wrong structure
|
|
2677
3040
|
|
|
2678
|
-
|
|
3041
|
+
\`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
|
|
2679
3042
|
|
|
2680
|
-
|
|
2681
|
-
|
|
3043
|
+
${block(pipelineIdentification)}
|
|
3044
|
+
`));
|
|
3045
|
+
}
|
|
3046
|
+
/**
|
|
3047
|
+
* Validates that one parameter does not declare incompatible directions.
|
|
3048
|
+
*
|
|
3049
|
+
* @private internal utility of `validatePipeline`
|
|
3050
|
+
*/
|
|
3051
|
+
function validateParameterDirection(parameter, pipelineIdentification) {
|
|
3052
|
+
if (!parameter.isInput || !parameter.isOutput) {
|
|
3053
|
+
return;
|
|
2682
3054
|
}
|
|
2683
|
-
|
|
2684
|
-
|
|
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
|
|
3055
|
+
const parameterName = parameter.name;
|
|
3056
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
2691
3057
|
|
|
2692
|
-
|
|
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) => `
|
|
3058
|
+
Parameter \`{${parameterName}}\` can not be both input and output
|
|
2702
3059
|
|
|
2703
|
-
|
|
3060
|
+
${block(pipelineIdentification)}
|
|
3061
|
+
`));
|
|
3062
|
+
}
|
|
3063
|
+
/**
|
|
3064
|
+
* Validates that one intermediate parameter is actually consumed by at least one task.
|
|
3065
|
+
*
|
|
3066
|
+
* @private internal utility of `validatePipeline`
|
|
3067
|
+
*/
|
|
3068
|
+
function validateParameterUsage(parameter, pipeline, pipelineIdentification) {
|
|
3069
|
+
if (parameter.isInput || parameter.isOutput || isParameterUsedByAnyTask(parameter, pipeline.tasks)) {
|
|
3070
|
+
return;
|
|
3071
|
+
}
|
|
3072
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3073
|
+
Parameter \`{${parameter.name}}\` is created but not used
|
|
2704
3074
|
|
|
2705
|
-
|
|
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
|
|
3075
|
+
You can declare {${parameter.name}} as output parameter by adding in the header:
|
|
3076
|
+
- OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
|
|
2714
3077
|
|
|
2715
|
-
|
|
2716
|
-
- OUTPUT PARAMETER \`{${parameter.name}}\` ${parameter.description || ''}
|
|
3078
|
+
${block(pipelineIdentification)}
|
|
2717
3079
|
|
|
2718
|
-
|
|
3080
|
+
`));
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* Validates that one non-input parameter is produced by at least one task.
|
|
3084
|
+
*
|
|
3085
|
+
* @private internal utility of `validatePipeline`
|
|
3086
|
+
*/
|
|
3087
|
+
function validateParameterDefinition(parameter, pipeline, pipelineIdentification) {
|
|
3088
|
+
if (parameter.isInput || isParameterDefinedByAnyTask(parameter, pipeline.tasks)) {
|
|
3089
|
+
return;
|
|
3090
|
+
}
|
|
3091
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3092
|
+
Parameter \`{${parameter.name}}\` is declared but not defined
|
|
2719
3093
|
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
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
|
|
3094
|
+
You can do one of these:
|
|
3095
|
+
1) Remove declaration of \`{${parameter.name}}\`
|
|
3096
|
+
2) Add task that results in \`-> {${parameter.name}}\`
|
|
2726
3097
|
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
3098
|
+
${block(pipelineIdentification)}
|
|
3099
|
+
`));
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Checks whether one parameter is consumed by at least one task dependency list.
|
|
3103
|
+
*
|
|
3104
|
+
* @private internal utility of `validatePipeline`
|
|
3105
|
+
*/
|
|
3106
|
+
function isParameterUsedByAnyTask(parameter, tasks) {
|
|
3107
|
+
return tasks.some((task) => task.dependentParameterNames.includes(parameter.name));
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Checks whether one parameter is produced by at least one task.
|
|
3111
|
+
*
|
|
3112
|
+
* @private internal utility of `validatePipeline`
|
|
3113
|
+
*/
|
|
3114
|
+
function isParameterDefinedByAnyTask(parameter, tasks) {
|
|
3115
|
+
return tasks.some((task) => task.resultingParameterName === parameter.name);
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Collects the parameter names that are already defined before task validation starts.
|
|
3119
|
+
*
|
|
3120
|
+
* @private internal utility of `validatePipeline`
|
|
3121
|
+
*/
|
|
3122
|
+
function createInitiallyDefinedParameters(pipeline) {
|
|
3123
|
+
return new Set(pipeline.parameters.filter(({ isInput }) => isInput).map(({ name }) => name));
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Validates one task result parameter declaration and marks it as defined.
|
|
3127
|
+
*
|
|
3128
|
+
* @private internal utility of `validatePipeline`
|
|
3129
|
+
*/
|
|
3130
|
+
function validateTaskResultingParameter(task, definedParameters, pipelineIdentification) {
|
|
3131
|
+
if (definedParameters.has(task.resultingParameterName)) {
|
|
3132
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3133
|
+
Parameter \`{${task.resultingParameterName}}\` is defined multiple times
|
|
2730
3134
|
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
}
|
|
3135
|
+
${block(pipelineIdentification)}
|
|
3136
|
+
`));
|
|
2734
3137
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
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
|
|
3138
|
+
if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
|
|
3139
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3140
|
+
Parameter name {${task.resultingParameterName}} is reserved, please use different name
|
|
2742
3141
|
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
3142
|
+
${block(pipelineIdentification)}
|
|
3143
|
+
`));
|
|
3144
|
+
}
|
|
3145
|
+
definedParameters.add(task.resultingParameterName);
|
|
3146
|
+
}
|
|
3147
|
+
/**
|
|
3148
|
+
* Validates joker parameters for one task.
|
|
3149
|
+
*
|
|
3150
|
+
* @private internal utility of `validatePipeline`
|
|
3151
|
+
*/
|
|
3152
|
+
function validateTaskJokers(task, pipelineIdentification) {
|
|
3153
|
+
if (!hasTaskJokers(task)) {
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
validateTaskSupportsJokers(task, pipelineIdentification);
|
|
3157
|
+
validateTaskJokerDependencies(task, pipelineIdentification);
|
|
3158
|
+
}
|
|
3159
|
+
/**
|
|
3160
|
+
* Checks whether one task declares any joker parameters.
|
|
3161
|
+
*
|
|
3162
|
+
* @private internal utility of `validatePipeline`
|
|
3163
|
+
*/
|
|
3164
|
+
function hasTaskJokers(task) {
|
|
3165
|
+
return !!task.jokerParameterNames && task.jokerParameterNames.length > 0;
|
|
3166
|
+
}
|
|
3167
|
+
/**
|
|
3168
|
+
* Validates that a task has the required supporting features when using jokers.
|
|
3169
|
+
*
|
|
3170
|
+
* @private internal utility of `validatePipeline`
|
|
3171
|
+
*/
|
|
3172
|
+
function validateTaskSupportsJokers(task, pipelineIdentification) {
|
|
3173
|
+
if (task.format || task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
|
|
3174
|
+
return;
|
|
3175
|
+
}
|
|
3176
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3177
|
+
Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
|
|
2749
3178
|
|
|
2750
|
-
|
|
2751
|
-
|
|
3179
|
+
${block(pipelineIdentification)}
|
|
3180
|
+
`));
|
|
3181
|
+
}
|
|
3182
|
+
/**
|
|
3183
|
+
* Validates that every joker parameter is also listed among task dependencies.
|
|
3184
|
+
*
|
|
3185
|
+
* @private internal utility of `validatePipeline`
|
|
3186
|
+
*/
|
|
3187
|
+
function validateTaskJokerDependencies(task, pipelineIdentification) {
|
|
3188
|
+
for (const joker of task.jokerParameterNames) {
|
|
3189
|
+
if (task.dependentParameterNames.includes(joker)) {
|
|
3190
|
+
continue;
|
|
2752
3191
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
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\`
|
|
3192
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3193
|
+
Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
|
|
2767
3194
|
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3195
|
+
${block(pipelineIdentification)}
|
|
3196
|
+
`));
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
/**
|
|
3200
|
+
* Validates all expectation bounds configured on one task.
|
|
3201
|
+
*
|
|
3202
|
+
* @private internal utility of `validatePipeline`
|
|
3203
|
+
*/
|
|
3204
|
+
function validateTaskExpectations(task, pipelineIdentification) {
|
|
3205
|
+
if (!task.expectations) {
|
|
3206
|
+
return;
|
|
3207
|
+
}
|
|
3208
|
+
for (const [unit, { min, max }] of Object.entries(task.expectations)) {
|
|
3209
|
+
validateTaskExpectationRange(unit, min, max, pipelineIdentification);
|
|
3210
|
+
validateTaskExpectationMin(unit, min, pipelineIdentification);
|
|
3211
|
+
validateTaskExpectationMax(unit, max, pipelineIdentification);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
/**
|
|
3215
|
+
* Validates the minimum and maximum expectation ordering for one unit.
|
|
3216
|
+
*
|
|
3217
|
+
* @private internal utility of `validatePipeline`
|
|
3218
|
+
*/
|
|
3219
|
+
function validateTaskExpectationRange(unit, min, max, pipelineIdentification) {
|
|
3220
|
+
if (min === undefined || max === undefined || min <= max) {
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3224
|
+
Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
|
|
2778
3225
|
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
3226
|
+
${block(pipelineIdentification)}
|
|
3227
|
+
`));
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Validates the minimum expectation bound for one unit.
|
|
3231
|
+
*
|
|
3232
|
+
* @private internal utility of `validatePipeline`
|
|
3233
|
+
*/
|
|
3234
|
+
function validateTaskExpectationMin(unit, min, pipelineIdentification) {
|
|
3235
|
+
if (min === undefined || min >= 0) {
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3239
|
+
Min expectation of ${unit} must be zero or positive
|
|
2785
3240
|
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
3241
|
+
${block(pipelineIdentification)}
|
|
3242
|
+
`));
|
|
3243
|
+
}
|
|
3244
|
+
/**
|
|
3245
|
+
* Validates the maximum expectation bound for one unit.
|
|
3246
|
+
*
|
|
3247
|
+
* @private internal utility of `validatePipeline`
|
|
3248
|
+
*/
|
|
3249
|
+
function validateTaskExpectationMax(unit, max, pipelineIdentification) {
|
|
3250
|
+
if (max === undefined || max > 0) {
|
|
3251
|
+
return;
|
|
3252
|
+
}
|
|
3253
|
+
throw new PipelineLogicError(spaceTrim$1((block) => `
|
|
3254
|
+
Max expectation of ${unit} must be positive
|
|
2792
3255
|
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
3256
|
+
${block(pipelineIdentification)}
|
|
3257
|
+
`));
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Collects the parameter names that are already resolvable before dependency traversal starts.
|
|
3261
|
+
*
|
|
3262
|
+
* @private internal utility of `validatePipeline`
|
|
3263
|
+
*/
|
|
3264
|
+
function createInitialDependencyResolutionState(pipeline) {
|
|
3265
|
+
return {
|
|
3266
|
+
resolvedParameterNames: createInitiallyResolvedParameterNames(pipeline),
|
|
3267
|
+
unresolvedTasks: [...pipeline.tasks],
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
/**
|
|
3271
|
+
* Checks whether dependency resolution still has tasks left to process.
|
|
3272
|
+
*
|
|
3273
|
+
* @private internal utility of `validatePipeline`
|
|
3274
|
+
*/
|
|
3275
|
+
function hasUnresolvedTasks({ unresolvedTasks }) {
|
|
3276
|
+
return unresolvedTasks.length > 0;
|
|
3277
|
+
}
|
|
3278
|
+
/**
|
|
3279
|
+
* Resolves the next batch of currently satisfiable tasks.
|
|
3280
|
+
*
|
|
3281
|
+
* @private internal utility of `validatePipeline`
|
|
3282
|
+
*/
|
|
3283
|
+
function resolveNextDependencyResolutionState(dependencyResolutionState, pipelineIdentification) {
|
|
3284
|
+
const currentlyResolvedTasks = getCurrentlyResolvedTasks(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames);
|
|
3285
|
+
if (currentlyResolvedTasks.length === 0) {
|
|
3286
|
+
throw createUnresolvedTasksError(dependencyResolutionState.unresolvedTasks, dependencyResolutionState.resolvedParameterNames, pipelineIdentification);
|
|
2798
3287
|
}
|
|
2799
|
-
|
|
2800
|
-
|
|
3288
|
+
return {
|
|
3289
|
+
resolvedParameterNames: appendResolvedTaskParameterNames(dependencyResolutionState.resolvedParameterNames, currentlyResolvedTasks),
|
|
3290
|
+
unresolvedTasks: dependencyResolutionState.unresolvedTasks.filter((task) => !currentlyResolvedTasks.includes(task)),
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
/**
|
|
3294
|
+
* Collects the parameter names that are already resolvable before dependency traversal starts.
|
|
3295
|
+
*
|
|
3296
|
+
* @private internal utility of `validatePipeline`
|
|
3297
|
+
*/
|
|
3298
|
+
function createInitiallyResolvedParameterNames(pipeline) {
|
|
3299
|
+
let resolvedParameterNames = pipeline.parameters
|
|
2801
3300
|
.filter(({ isInput }) => isInput)
|
|
2802
3301
|
.map(({ name }) => name);
|
|
2803
|
-
// Note: All reserved parameters are resolved
|
|
2804
3302
|
for (const reservedParameterName of RESERVED_PARAMETER_NAMES) {
|
|
2805
|
-
|
|
3303
|
+
resolvedParameterNames = [...resolvedParameterNames, reservedParameterName];
|
|
2806
3304
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
3305
|
+
return resolvedParameterNames;
|
|
3306
|
+
}
|
|
3307
|
+
/**
|
|
3308
|
+
* Adds newly resolved task outputs to the resolved parameter list.
|
|
3309
|
+
*
|
|
3310
|
+
* @private internal utility of `validatePipeline`
|
|
3311
|
+
*/
|
|
3312
|
+
function appendResolvedTaskParameterNames(resolvedParameterNames, currentlyResolvedTasks) {
|
|
3313
|
+
return [
|
|
3314
|
+
...resolvedParameterNames,
|
|
3315
|
+
...currentlyResolvedTasks.map(({ resultingParameterName }) => resultingParameterName),
|
|
3316
|
+
];
|
|
3317
|
+
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Selects tasks whose dependencies are already resolved.
|
|
3320
|
+
*
|
|
3321
|
+
* @private internal utility of `validatePipeline`
|
|
3322
|
+
*/
|
|
3323
|
+
function getCurrentlyResolvedTasks(unresolvedTasks, resolvedParameterNames) {
|
|
3324
|
+
return unresolvedTasks.filter((task) => task.dependentParameterNames.every((name) => resolvedParameterNames.includes(name)));
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Creates the unexpected loop-limit error for dependency resolution.
|
|
3328
|
+
*
|
|
3329
|
+
* @private internal utility of `validatePipeline`
|
|
3330
|
+
*/
|
|
3331
|
+
function createDependencyResolutionLoopLimitError(pipelineIdentification) {
|
|
3332
|
+
// Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
|
|
3333
|
+
return new UnexpectedError(spaceTrim$1((block) => `
|
|
3334
|
+
Loop limit reached during detection of circular dependencies in \`validatePipeline\`
|
|
2814
3335
|
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
3336
|
+
${block(pipelineIdentification)}
|
|
3337
|
+
`));
|
|
3338
|
+
}
|
|
3339
|
+
/**
|
|
3340
|
+
* Creates the detailed error for unresolved or circular task dependencies.
|
|
3341
|
+
*
|
|
3342
|
+
* @private internal utility of `validatePipeline`
|
|
3343
|
+
*/
|
|
3344
|
+
function createUnresolvedTasksError(unresolvedTasks, resolvedParameterNames, pipelineIdentification) {
|
|
3345
|
+
return new PipelineLogicError(
|
|
3346
|
+
// TODO: [🐎] DRY
|
|
3347
|
+
spaceTrim$1((block) => `
|
|
2823
3348
|
|
|
2824
|
-
|
|
2825
|
-
|
|
3349
|
+
Can not resolve some parameters:
|
|
3350
|
+
Either you are using a parameter that is not defined, or there are some circular dependencies.
|
|
2826
3351
|
|
|
2827
|
-
|
|
3352
|
+
${block(pipelineIdentification)}
|
|
2828
3353
|
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3354
|
+
**Can not resolve:**
|
|
3355
|
+
${block(unresolvedTasks
|
|
3356
|
+
.map(({ resultingParameterName, dependentParameterNames }) => `- Parameter \`{${resultingParameterName}}\` which depends on ${dependentParameterNames
|
|
3357
|
+
.map((dependentParameterName) => `\`{${dependentParameterName}}\``)
|
|
3358
|
+
.join(' and ')}`)
|
|
3359
|
+
.join('\n'))}
|
|
2835
3360
|
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
3361
|
+
**Resolved:**
|
|
3362
|
+
${block(resolvedParameterNames
|
|
3363
|
+
.filter((name) => !RESERVED_PARAMETER_NAMES.includes(name))
|
|
3364
|
+
.map((name) => `- Parameter \`{${name}}\``)
|
|
3365
|
+
.join('\n'))}
|
|
2841
3366
|
|
|
2842
3367
|
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
3368
|
+
**Reserved (which are available):**
|
|
3369
|
+
${block(resolvedParameterNames
|
|
3370
|
+
.filter((name) => RESERVED_PARAMETER_NAMES.includes(name))
|
|
3371
|
+
.map((name) => `- Parameter \`{${name}}\``)
|
|
3372
|
+
.join('\n'))}
|
|
2848
3373
|
|
|
2849
3374
|
|
|
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
|
|
3375
|
+
`));
|
|
2861
3376
|
}
|
|
2862
3377
|
/**
|
|
2863
3378
|
* TODO: [🧞♀️] Do not allow joker + foreach
|
|
@@ -3553,70 +4068,281 @@ function assertsTaskSuccessful(executionResult) {
|
|
|
3553
4068
|
// TODO: [🧠] Can this return type be better typed than void
|
|
3554
4069
|
|
|
3555
4070
|
/**
|
|
3556
|
-
*
|
|
4071
|
+
* Resolves the short task summary shown in the UI.
|
|
4072
|
+
*
|
|
4073
|
+
* @private internal helper function of `ExecutionTask`
|
|
4074
|
+
*/
|
|
4075
|
+
function resolveTaskTldr(options) {
|
|
4076
|
+
const { customTldr } = options;
|
|
4077
|
+
if (customTldr) {
|
|
4078
|
+
return customTldr;
|
|
4079
|
+
}
|
|
4080
|
+
return {
|
|
4081
|
+
percent: resolveTaskPercent(options),
|
|
4082
|
+
message: `${resolveTaskMessage(options)} (!!!fallback)`,
|
|
4083
|
+
};
|
|
4084
|
+
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Resolves the best progress percentage for the current task state.
|
|
4087
|
+
*
|
|
4088
|
+
* @private internal helper function of `ExecutionTask`
|
|
4089
|
+
*/
|
|
4090
|
+
function resolveTaskPercent(options) {
|
|
4091
|
+
const explicitPercent = getExplicitTaskPercent(options.currentValue);
|
|
4092
|
+
if (typeof explicitPercent === 'number') {
|
|
4093
|
+
return normalizeTaskPercent(explicitPercent);
|
|
4094
|
+
}
|
|
4095
|
+
return normalizeTaskPercent(calculateSimulatedTaskPercent(options));
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* Picks a directly reported progress percentage from the task result snapshot.
|
|
4099
|
+
*
|
|
4100
|
+
* @private internal helper function of `ExecutionTask`
|
|
4101
|
+
*/
|
|
4102
|
+
function getExplicitTaskPercent(currentValue) {
|
|
4103
|
+
var _a, _b, _c, _d, _e, _f;
|
|
4104
|
+
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);
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Simulates progress when the task result does not expose an explicit percentage.
|
|
4108
|
+
*
|
|
4109
|
+
* @private internal helper function of `ExecutionTask`
|
|
4110
|
+
*/
|
|
4111
|
+
function calculateSimulatedTaskPercent(options) {
|
|
4112
|
+
const { currentValue, status, createdAt } = options;
|
|
4113
|
+
const elapsedMs = new Date().getTime() - createdAt.getTime();
|
|
4114
|
+
const timeProgress = Math.min(elapsedMs / DEFAULT_TASK_SIMULATED_DURATION_MS, 1);
|
|
4115
|
+
const { subtaskCount, completedSubtasks } = summarizeTaskSubtasks(currentValue);
|
|
4116
|
+
if (status === 'FINISHED') {
|
|
4117
|
+
return 1;
|
|
4118
|
+
}
|
|
4119
|
+
if (status === 'ERROR') {
|
|
4120
|
+
return 0;
|
|
4121
|
+
}
|
|
4122
|
+
return Math.min(completedSubtasks / subtaskCount + (1 / subtaskCount) * timeProgress, 1);
|
|
4123
|
+
}
|
|
4124
|
+
/**
|
|
4125
|
+
* Counts total and completed subtasks used by the fallback progress simulation.
|
|
4126
|
+
*
|
|
4127
|
+
* @private internal helper function of `ExecutionTask`
|
|
4128
|
+
*/
|
|
4129
|
+
function summarizeTaskSubtasks(currentValue) {
|
|
4130
|
+
if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks)) {
|
|
4131
|
+
return { subtaskCount: 1, completedSubtasks: 0 };
|
|
4132
|
+
}
|
|
4133
|
+
return {
|
|
4134
|
+
subtaskCount: currentValue.subtasks.length || 1,
|
|
4135
|
+
completedSubtasks: currentValue.subtasks.filter(isTaskSubtaskCompleted).length,
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
/**
|
|
4139
|
+
* Tells whether a task subtask is already finished.
|
|
4140
|
+
*
|
|
4141
|
+
* @private internal helper function of `ExecutionTask`
|
|
4142
|
+
*/
|
|
4143
|
+
function isTaskSubtaskCompleted(subtask) {
|
|
4144
|
+
return subtask.done || subtask.completed || false;
|
|
4145
|
+
}
|
|
4146
|
+
/**
|
|
4147
|
+
* Normalizes a progress percentage into the expected `0..1` range.
|
|
4148
|
+
*
|
|
4149
|
+
* @private internal helper function of `ExecutionTask`
|
|
4150
|
+
*/
|
|
4151
|
+
function normalizeTaskPercent(percentRaw) {
|
|
4152
|
+
let percent = Number(percentRaw) || 0;
|
|
4153
|
+
if (percent < 0) {
|
|
4154
|
+
percent = 0;
|
|
4155
|
+
}
|
|
4156
|
+
if (percent > 1) {
|
|
4157
|
+
percent = 1;
|
|
4158
|
+
}
|
|
4159
|
+
return percent;
|
|
4160
|
+
}
|
|
4161
|
+
/**
|
|
4162
|
+
* Resolves the best human-readable status message for the current task state.
|
|
4163
|
+
*
|
|
4164
|
+
* @private internal helper function of `ExecutionTask`
|
|
4165
|
+
*/
|
|
4166
|
+
function resolveTaskMessage(options) {
|
|
4167
|
+
return (getCurrentValueMessage(options.currentValue) ||
|
|
4168
|
+
getCurrentSubtaskMessage(options.currentValue) ||
|
|
4169
|
+
getLatestIssueMessage(options.errors, 'Error') ||
|
|
4170
|
+
getLatestIssueMessage(options.warnings, 'Warning') ||
|
|
4171
|
+
getStatusMessage(options.status));
|
|
4172
|
+
}
|
|
4173
|
+
/**
|
|
4174
|
+
* Picks a message already reported by the current task result snapshot.
|
|
4175
|
+
*
|
|
4176
|
+
* @private internal helper function of `ExecutionTask`
|
|
4177
|
+
*/
|
|
4178
|
+
function getCurrentValueMessage(currentValue) {
|
|
4179
|
+
var _a, _b, _c, _d;
|
|
4180
|
+
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;
|
|
4181
|
+
}
|
|
4182
|
+
/**
|
|
4183
|
+
* Builds a fallback message from the first unfinished subtask title.
|
|
4184
|
+
*
|
|
4185
|
+
* @private internal helper function of `ExecutionTask`
|
|
4186
|
+
*/
|
|
4187
|
+
function getCurrentSubtaskMessage(currentValue) {
|
|
4188
|
+
if (!Array.isArray(currentValue === null || currentValue === void 0 ? void 0 : currentValue.subtasks) || currentValue.subtasks.length === 0) {
|
|
4189
|
+
return undefined;
|
|
4190
|
+
}
|
|
4191
|
+
const currentSubtask = currentValue.subtasks.find((subtask) => !isTaskSubtaskCompleted(subtask));
|
|
4192
|
+
if (!(currentSubtask === null || currentSubtask === void 0 ? void 0 : currentSubtask.title)) {
|
|
4193
|
+
return undefined;
|
|
4194
|
+
}
|
|
4195
|
+
return `Working on ${currentSubtask.title}`;
|
|
4196
|
+
}
|
|
4197
|
+
/**
|
|
4198
|
+
* Picks the latest error or warning message, with the legacy generic fallback label.
|
|
4199
|
+
*
|
|
4200
|
+
* @private internal helper function of `ExecutionTask`
|
|
4201
|
+
*/
|
|
4202
|
+
function getLatestIssueMessage(issues, fallbackMessage) {
|
|
4203
|
+
if (issues.length === 0) {
|
|
4204
|
+
return undefined;
|
|
4205
|
+
}
|
|
4206
|
+
return issues[issues.length - 1].message || fallbackMessage;
|
|
4207
|
+
}
|
|
4208
|
+
/**
|
|
4209
|
+
* Builds the final status-based fallback message.
|
|
4210
|
+
*
|
|
4211
|
+
* @private internal helper function of `ExecutionTask`
|
|
4212
|
+
*/
|
|
4213
|
+
function getStatusMessage(status) {
|
|
4214
|
+
if (status === 'FINISHED') {
|
|
4215
|
+
return 'Finished';
|
|
4216
|
+
}
|
|
4217
|
+
if (status === 'ERROR') {
|
|
4218
|
+
return 'Error';
|
|
4219
|
+
}
|
|
4220
|
+
return 'Running';
|
|
4221
|
+
}
|
|
4222
|
+
|
|
4223
|
+
/**
|
|
4224
|
+
* Creates the initial mutable state for a task.
|
|
3557
4225
|
*
|
|
3558
4226
|
* @private internal helper function
|
|
3559
4227
|
*/
|
|
3560
|
-
function
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
4228
|
+
function createTaskState(title, createdAt) {
|
|
4229
|
+
return {
|
|
4230
|
+
title,
|
|
4231
|
+
status: 'RUNNING',
|
|
4232
|
+
updatedAt: createdAt,
|
|
4233
|
+
errors: [],
|
|
4234
|
+
warnings: [],
|
|
4235
|
+
llmCalls: [],
|
|
4236
|
+
currentValue: {},
|
|
4237
|
+
customTldr: null,
|
|
4238
|
+
};
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* Creates the partial-result updater passed into the task process callback.
|
|
4242
|
+
*
|
|
4243
|
+
* @private internal helper function
|
|
4244
|
+
*/
|
|
4245
|
+
function createOngoingResultUpdater(taskState, partialResultSubject) {
|
|
4246
|
+
return (newOngoingResult) => {
|
|
3576
4247
|
if (newOngoingResult.title) {
|
|
3577
|
-
title = newOngoingResult.title;
|
|
4248
|
+
taskState.title = newOngoingResult.title;
|
|
3578
4249
|
}
|
|
3579
|
-
updatedAt = new Date();
|
|
3580
|
-
Object.assign(currentValue, newOngoingResult);
|
|
4250
|
+
taskState.updatedAt = new Date();
|
|
4251
|
+
Object.assign(taskState.currentValue, newOngoingResult);
|
|
3581
4252
|
// <- TODO: assign deep
|
|
3582
4253
|
partialResultSubject.next(newOngoingResult);
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
/**
|
|
4257
|
+
* Creates the custom-TLDR updater passed into the task process callback.
|
|
4258
|
+
*
|
|
4259
|
+
* @private internal helper function
|
|
4260
|
+
*/
|
|
4261
|
+
function createTldrUpdater(taskState) {
|
|
4262
|
+
return (tldrInfo) => {
|
|
4263
|
+
taskState.customTldr = tldrInfo;
|
|
4264
|
+
taskState.updatedAt = new Date();
|
|
4265
|
+
};
|
|
4266
|
+
}
|
|
4267
|
+
/**
|
|
4268
|
+
* Creates the LLM call logger passed into the task process callback.
|
|
4269
|
+
*
|
|
4270
|
+
* @private internal helper function
|
|
4271
|
+
*/
|
|
4272
|
+
function createLlmCallLogger(taskState) {
|
|
4273
|
+
return (llmCall) => {
|
|
4274
|
+
taskState.llmCalls.push(llmCall);
|
|
4275
|
+
taskState.updatedAt = new Date();
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
/**
|
|
4279
|
+
* Wires the task promise into the observable/error lifecycle.
|
|
4280
|
+
*
|
|
4281
|
+
* @private internal helper function
|
|
4282
|
+
*/
|
|
4283
|
+
function settleTaskPromise(finalResultPromise, taskState, partialResultSubject) {
|
|
3590
4284
|
finalResultPromise
|
|
3591
4285
|
.catch((error) => {
|
|
3592
|
-
errors.push(error);
|
|
4286
|
+
taskState.errors.push(error);
|
|
3593
4287
|
partialResultSubject.error(error);
|
|
3594
4288
|
})
|
|
3595
4289
|
.then((executionResult) => {
|
|
3596
4290
|
if (executionResult) {
|
|
3597
4291
|
try {
|
|
3598
|
-
|
|
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);
|
|
4292
|
+
finalizeTaskResult(executionResult, taskState, partialResultSubject);
|
|
3610
4293
|
}
|
|
3611
4294
|
catch (error) {
|
|
3612
|
-
|
|
3613
|
-
status = 'ERROR';
|
|
3614
|
-
errors.push(error);
|
|
3615
|
-
partialResultSubject.error(error);
|
|
4295
|
+
failTaskResult(error, taskState, partialResultSubject);
|
|
3616
4296
|
}
|
|
3617
4297
|
}
|
|
3618
4298
|
partialResultSubject.complete();
|
|
3619
4299
|
});
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* Applies the final successful task result into the mutable task state.
|
|
4303
|
+
*
|
|
4304
|
+
* @private internal helper function
|
|
4305
|
+
*/
|
|
4306
|
+
function finalizeTaskResult(executionResult, taskState, partialResultSubject) {
|
|
4307
|
+
taskState.updatedAt = new Date();
|
|
4308
|
+
taskState.errors.push(...executionResult.errors);
|
|
4309
|
+
taskState.warnings.push(...executionResult.warnings);
|
|
4310
|
+
// <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
|
|
4311
|
+
// TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
|
|
4312
|
+
// Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
|
|
4313
|
+
// And delete `ExecutionTask.currentValue.preparedPipeline`
|
|
4314
|
+
assertsTaskSuccessful(executionResult);
|
|
4315
|
+
taskState.status = 'FINISHED';
|
|
4316
|
+
taskState.currentValue = jsonStringsToJsons(executionResult);
|
|
4317
|
+
// <- TODO: [🧠] Is this a good idea to convert JSON strins to JSONs?
|
|
4318
|
+
partialResultSubject.next(executionResult);
|
|
4319
|
+
}
|
|
4320
|
+
/**
|
|
4321
|
+
* Records a final-result failure after the task promise itself resolved.
|
|
4322
|
+
*
|
|
4323
|
+
* @private internal helper function
|
|
4324
|
+
*/
|
|
4325
|
+
function failTaskResult(error, taskState, partialResultSubject) {
|
|
4326
|
+
assertsError(error);
|
|
4327
|
+
taskState.status = 'ERROR';
|
|
4328
|
+
taskState.errors.push(error);
|
|
4329
|
+
partialResultSubject.error(error);
|
|
4330
|
+
}
|
|
4331
|
+
/**
|
|
4332
|
+
* Helper to create a new task
|
|
4333
|
+
*
|
|
4334
|
+
* @private internal helper function
|
|
4335
|
+
*/
|
|
4336
|
+
function createTask(options) {
|
|
4337
|
+
const { taskType, title, taskProcessCallback } = options;
|
|
4338
|
+
// TODO: [🐙] DRY
|
|
4339
|
+
const taskId = `${taskType.toLowerCase().substring(0, 4)}-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
|
|
4340
|
+
const createdAt = new Date();
|
|
4341
|
+
const taskState = createTaskState(title, createdAt);
|
|
4342
|
+
const partialResultSubject = new Subject();
|
|
4343
|
+
// <- Note: Not using `BehaviorSubject` because on error we can't access the last value
|
|
4344
|
+
const finalResultPromise = /* not await */ taskProcessCallback(createOngoingResultUpdater(taskState, partialResultSubject), createTldrUpdater(taskState), createLlmCallLogger(taskState));
|
|
4345
|
+
settleTaskPromise(finalResultPromise, taskState, partialResultSubject);
|
|
3620
4346
|
async function asPromise(options) {
|
|
3621
4347
|
const { isCrashedOnError = true } = options || {};
|
|
3622
4348
|
const finalResult = await finalResultPromise;
|
|
@@ -3632,91 +4358,29 @@ function createTask(options) {
|
|
|
3632
4358
|
return PROMPTBOOK_ENGINE_VERSION;
|
|
3633
4359
|
},
|
|
3634
4360
|
get title() {
|
|
3635
|
-
return title;
|
|
4361
|
+
return taskState.title;
|
|
3636
4362
|
// <- Note: [1] These must be getters to allow changing the value in the future
|
|
3637
4363
|
},
|
|
3638
4364
|
get status() {
|
|
3639
|
-
return status;
|
|
4365
|
+
return taskState.status;
|
|
3640
4366
|
// <- Note: [1] --||--
|
|
3641
4367
|
},
|
|
3642
4368
|
get tldr() {
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
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
|
-
};
|
|
4369
|
+
return resolveTaskTldr({
|
|
4370
|
+
customTldr: taskState.customTldr,
|
|
4371
|
+
currentValue: taskState.currentValue,
|
|
4372
|
+
status: taskState.status,
|
|
4373
|
+
createdAt,
|
|
4374
|
+
errors: taskState.errors,
|
|
4375
|
+
warnings: taskState.warnings,
|
|
4376
|
+
});
|
|
3713
4377
|
},
|
|
3714
4378
|
get createdAt() {
|
|
3715
4379
|
return createdAt;
|
|
3716
4380
|
// <- Note: [1] --||--
|
|
3717
4381
|
},
|
|
3718
4382
|
get updatedAt() {
|
|
3719
|
-
return updatedAt;
|
|
4383
|
+
return taskState.updatedAt;
|
|
3720
4384
|
// <- Note: [1] --||--
|
|
3721
4385
|
},
|
|
3722
4386
|
asPromise,
|
|
@@ -3724,19 +4388,19 @@ function createTask(options) {
|
|
|
3724
4388
|
return partialResultSubject.asObservable();
|
|
3725
4389
|
},
|
|
3726
4390
|
get errors() {
|
|
3727
|
-
return errors;
|
|
4391
|
+
return taskState.errors;
|
|
3728
4392
|
// <- Note: [1] --||--
|
|
3729
4393
|
},
|
|
3730
4394
|
get warnings() {
|
|
3731
|
-
return warnings;
|
|
4395
|
+
return taskState.warnings;
|
|
3732
4396
|
// <- Note: [1] --||--
|
|
3733
4397
|
},
|
|
3734
4398
|
get llmCalls() {
|
|
3735
|
-
return [...llmCalls, { foo: '!!! bar' }];
|
|
4399
|
+
return [...taskState.llmCalls, { foo: '!!! bar' }];
|
|
3736
4400
|
// <- Note: [1] --||--
|
|
3737
4401
|
},
|
|
3738
4402
|
get currentValue() {
|
|
3739
|
-
return currentValue;
|
|
4403
|
+
return taskState.currentValue;
|
|
3740
4404
|
// <- Note: [1] --||--
|
|
3741
4405
|
},
|
|
3742
4406
|
};
|
|
@@ -4654,210 +5318,275 @@ const promptbookFetch = async (urlOrRequest, init) => {
|
|
|
4654
5318
|
* @public exported from `@promptbook/core`
|
|
4655
5319
|
*/
|
|
4656
5320
|
async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
|
|
5321
|
+
const { knowledgeSourceContent } = knowledgeSource;
|
|
5322
|
+
const name = knowledgeSource.name || knowledgeSourceContentToName(knowledgeSourceContent);
|
|
5323
|
+
const { rootDirname = null, isVerbose = DEFAULT_IS_VERBOSE } = options || {};
|
|
5324
|
+
if (isValidUrl(knowledgeSourceContent)) {
|
|
5325
|
+
return makeUrlKnowledgeSourceHandler(knowledgeSourceContent, name, tools, options, isVerbose);
|
|
5326
|
+
}
|
|
5327
|
+
if (isValidFilePath(knowledgeSourceContent)) {
|
|
5328
|
+
return makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose);
|
|
5329
|
+
}
|
|
5330
|
+
return makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose);
|
|
5331
|
+
}
|
|
5332
|
+
/**
|
|
5333
|
+
* Creates a source handler for URL-based knowledge.
|
|
5334
|
+
*
|
|
5335
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5336
|
+
*/
|
|
5337
|
+
async function makeUrlKnowledgeSourceHandler(url, name, tools, options, isVerbose) {
|
|
4657
5338
|
var _a;
|
|
4658
5339
|
const { fetch = promptbookFetch } = tools;
|
|
4659
|
-
|
|
4660
|
-
|
|
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);
|
|
5340
|
+
if (isVerbose) {
|
|
5341
|
+
console.info(`📄 [1] "${name}" is available at "${url}"`);
|
|
4666
5342
|
}
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
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
|
-
}
|
|
4767
|
-
}
|
|
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)}"`);
|
|
4772
|
-
}
|
|
4773
|
-
return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
|
|
4774
|
-
...options,
|
|
4775
|
-
rootDirname,
|
|
4776
|
-
});
|
|
5343
|
+
const response = await fetch(url); // <- TODO: [🧠] Scraping and fetch proxy
|
|
5344
|
+
const mimeType = ((_a = response.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.split(';')[0]) || 'text/html';
|
|
5345
|
+
if (tools.fs === undefined || !url.endsWith('.pdf' /* <- TODO: [💵] */)) {
|
|
5346
|
+
return makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose);
|
|
4777
5347
|
}
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
5348
|
+
return cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose);
|
|
5349
|
+
}
|
|
5350
|
+
/**
|
|
5351
|
+
* Creates a source handler that reads directly from a fetched response.
|
|
5352
|
+
*
|
|
5353
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5354
|
+
*/
|
|
5355
|
+
function makeRemoteResponseKnowledgeSourceHandler(name, url, mimeType, response, isVerbose) {
|
|
5356
|
+
if (isVerbose) {
|
|
5357
|
+
console.info(`📄 [2] "${name}" tools.fs is not available or URL is not a PDF.`);
|
|
5358
|
+
}
|
|
5359
|
+
return {
|
|
5360
|
+
source: name,
|
|
5361
|
+
filename: null,
|
|
5362
|
+
url,
|
|
5363
|
+
mimeType,
|
|
5364
|
+
/*
|
|
5365
|
+
TODO: [🥽]
|
|
5366
|
+
> async asBlob() {
|
|
5367
|
+
> // TODO: [👨🏻🤝👨🏻] This can be called multiple times BUT when called second time, response in already consumed
|
|
5368
|
+
> const content = await response.blob();
|
|
5369
|
+
> return content;
|
|
5370
|
+
> },
|
|
5371
|
+
*/
|
|
5372
|
+
async asJson() {
|
|
5373
|
+
// TODO: [👨🏻🤝👨🏻]
|
|
5374
|
+
const content = await response.json();
|
|
5375
|
+
return content;
|
|
5376
|
+
},
|
|
5377
|
+
async asText() {
|
|
5378
|
+
// TODO: [👨🏻🤝👨🏻]
|
|
5379
|
+
const content = await response.text();
|
|
5380
|
+
return content;
|
|
5381
|
+
},
|
|
5382
|
+
};
|
|
5383
|
+
}
|
|
5384
|
+
/**
|
|
5385
|
+
* Downloads a PDF knowledge source into cache when possible and falls back to in-memory content otherwise.
|
|
5386
|
+
*
|
|
5387
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5388
|
+
*/
|
|
5389
|
+
async function cachePdfKnowledgeSourceHandler(url, name, mimeType, response, tools, options, isVerbose) {
|
|
5390
|
+
const rootDirname = join(process.cwd(), DEFAULT_DOWNLOAD_CACHE_DIRNAME);
|
|
5391
|
+
const filepath = createDownloadedKnowledgeSourceFilepath(url, mimeType);
|
|
5392
|
+
const fullFilepath = join(rootDirname, filepath);
|
|
5393
|
+
await createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose);
|
|
5394
|
+
const fileContent = Buffer.from(await response.arrayBuffer());
|
|
5395
|
+
if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
|
|
5396
|
+
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.`);
|
|
5397
|
+
}
|
|
5398
|
+
const isCached = await writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose);
|
|
5399
|
+
if (!isCached) {
|
|
5400
|
+
return makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent);
|
|
5401
|
+
}
|
|
5402
|
+
// TODO: [💵] Check the file security
|
|
5403
|
+
// TODO: [🧹][🧠] Delete the file after the scraping is done
|
|
5404
|
+
if (isVerbose) {
|
|
5405
|
+
console.info(`📄 [5] "${name}" cached at "${fullFilepath}"`);
|
|
5406
|
+
}
|
|
5407
|
+
return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
|
|
5408
|
+
...options,
|
|
5409
|
+
rootDirname,
|
|
5410
|
+
});
|
|
5411
|
+
}
|
|
5412
|
+
/**
|
|
5413
|
+
* Builds a stable cache filepath for a downloaded knowledge source.
|
|
5414
|
+
*
|
|
5415
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5416
|
+
*/
|
|
5417
|
+
function createDownloadedKnowledgeSourceFilepath(url, mimeType) {
|
|
5418
|
+
const basename = url.split('/').pop() || titleToName(url);
|
|
5419
|
+
const hash = sha256(hexEncoder.parse(url)).toString( /* hex */);
|
|
5420
|
+
// <- TODO: [🥬] Encapsulate sha256 to some private utility function
|
|
5421
|
+
return join(...nameToSubfolderPath(hash /* <- TODO: [🎎] Maybe add some SHA256 prefix */), `${basename.substring(0, MAX_FILENAME_LENGTH)}.${mimeTypeToExtension(mimeType)}`);
|
|
5422
|
+
}
|
|
5423
|
+
/**
|
|
5424
|
+
* Tries to create the cache directory needed for a downloaded knowledge source.
|
|
5425
|
+
*
|
|
5426
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5427
|
+
*/
|
|
5428
|
+
async function createCacheDirectoryIfPossible(name, fullFilepath, tools, isVerbose) {
|
|
5429
|
+
try {
|
|
5430
|
+
await tools.fs.mkdir(dirname(fullFilepath), { recursive: true });
|
|
5431
|
+
}
|
|
5432
|
+
catch (error) {
|
|
5433
|
+
if (isVerbose) {
|
|
5434
|
+
console.info(`📄 [3] "${name}" error creating cache directory`);
|
|
4782
5435
|
}
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
5436
|
+
// Note: If we can't create cache directory, we'll handle it when trying to write the file
|
|
5437
|
+
// This handles read-only filesystems, permission issues, and missing parent directories
|
|
5438
|
+
if (!isIgnorableCacheFilesystemError(error)) {
|
|
5439
|
+
throw error;
|
|
4786
5440
|
}
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
5441
|
+
}
|
|
5442
|
+
}
|
|
5443
|
+
/**
|
|
5444
|
+
* Tries to write downloaded content into cache and reports whether the cache was usable.
|
|
5445
|
+
*
|
|
5446
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5447
|
+
*/
|
|
5448
|
+
async function writeCacheFileIfPossible(name, fullFilepath, fileContent, tools, isVerbose) {
|
|
5449
|
+
// Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
|
|
5450
|
+
try {
|
|
5451
|
+
await tools.fs.writeFile(fullFilepath, fileContent);
|
|
5452
|
+
return true;
|
|
5453
|
+
}
|
|
5454
|
+
catch (error) {
|
|
4790
5455
|
if (isVerbose) {
|
|
4791
|
-
console.info(`📄 [
|
|
5456
|
+
console.info(`📄 [4] "${name}" error writing cache file`);
|
|
4792
5457
|
}
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
if (
|
|
4796
|
-
|
|
4797
|
-
|
|
5458
|
+
// Note: If we can't write to cache, we'll process the file directly from memory
|
|
5459
|
+
// This handles read-only filesystems like Vercel
|
|
5460
|
+
if (isIgnorableCacheFilesystemError(error)) {
|
|
5461
|
+
return false;
|
|
5462
|
+
}
|
|
5463
|
+
throw error;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
/**
|
|
5467
|
+
* Detects filesystem errors that should not fail optional caching.
|
|
5468
|
+
*
|
|
5469
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5470
|
+
*/
|
|
5471
|
+
function isIgnorableCacheFilesystemError(error) {
|
|
5472
|
+
return (error instanceof Error &&
|
|
5473
|
+
(error.message.includes('EROFS') ||
|
|
5474
|
+
error.message.includes('read-only') ||
|
|
5475
|
+
error.message.includes('EACCES') ||
|
|
5476
|
+
error.message.includes('EPERM') ||
|
|
5477
|
+
error.message.includes('ENOENT')));
|
|
5478
|
+
}
|
|
5479
|
+
/**
|
|
5480
|
+
* Creates a source handler backed by already downloaded file content kept in memory.
|
|
5481
|
+
*
|
|
5482
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5483
|
+
*/
|
|
5484
|
+
function makeBufferedKnowledgeSourceHandler(name, url, mimeType, fileContent) {
|
|
5485
|
+
return {
|
|
5486
|
+
source: name,
|
|
5487
|
+
filename: null,
|
|
5488
|
+
url,
|
|
5489
|
+
mimeType,
|
|
5490
|
+
async asJson() {
|
|
5491
|
+
return JSON.parse(fileContent.toString('utf-8'));
|
|
5492
|
+
},
|
|
5493
|
+
async asText() {
|
|
5494
|
+
return fileContent.toString('utf-8');
|
|
5495
|
+
},
|
|
5496
|
+
};
|
|
5497
|
+
}
|
|
5498
|
+
/**
|
|
5499
|
+
* Creates a source handler for file-based knowledge.
|
|
5500
|
+
*
|
|
5501
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5502
|
+
*/
|
|
5503
|
+
async function makeFileKnowledgeSourceHandler(knowledgeSourceContent, name, tools, rootDirname, isVerbose) {
|
|
5504
|
+
if (tools.fs === undefined) {
|
|
5505
|
+
throw new EnvironmentMismatchError('Can not import file knowledge without filesystem tools');
|
|
5506
|
+
// <- TODO: [🧠] What is the best error type here`
|
|
5507
|
+
}
|
|
5508
|
+
if (rootDirname === null) {
|
|
5509
|
+
throw new EnvironmentMismatchError('Can not import file knowledge in non-file pipeline');
|
|
5510
|
+
// <- TODO: [🧠] What is the best error type here`
|
|
5511
|
+
}
|
|
5512
|
+
const filename = isAbsolute(knowledgeSourceContent)
|
|
5513
|
+
? knowledgeSourceContent
|
|
5514
|
+
: join(rootDirname, knowledgeSourceContent).split('\\').join('/');
|
|
5515
|
+
if (isVerbose) {
|
|
5516
|
+
console.info(`📄 [6] "${name}" is a valid file "${filename}"`);
|
|
5517
|
+
}
|
|
5518
|
+
const fileExtension = getFileExtension(filename);
|
|
5519
|
+
const mimeType = extensionToMimeType(fileExtension || '');
|
|
5520
|
+
if (!(await isFileExisting(filename, tools.fs))) {
|
|
5521
|
+
throw new NotFoundError(spaceTrim$1((block) => `
|
|
5522
|
+
Can not make source handler for file which does not exist:
|
|
4798
5523
|
|
|
4799
|
-
|
|
4800
|
-
|
|
5524
|
+
File:
|
|
5525
|
+
${block(knowledgeSourceContent)}
|
|
4801
5526
|
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
}
|
|
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
|
-
};
|
|
5527
|
+
Full file path:
|
|
5528
|
+
${block(filename)}
|
|
5529
|
+
`));
|
|
4832
5530
|
}
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
filename
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
},
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
5531
|
+
// TODO: [🧠][😿] Test security file - file is scoped to the project (BUT maybe do this in `filesystemTools`)
|
|
5532
|
+
return {
|
|
5533
|
+
source: name,
|
|
5534
|
+
filename,
|
|
5535
|
+
url: null,
|
|
5536
|
+
mimeType,
|
|
5537
|
+
/*
|
|
5538
|
+
TODO: [🥽]
|
|
5539
|
+
> async asBlob() {
|
|
5540
|
+
> const content = await tools.fs!.readFile(filename);
|
|
5541
|
+
> return new Blob(
|
|
5542
|
+
> [
|
|
5543
|
+
> content,
|
|
5544
|
+
> // <- TODO: [🥽] This is NOT tested, test it
|
|
5545
|
+
> ],
|
|
5546
|
+
> { type: mimeType },
|
|
5547
|
+
> );
|
|
5548
|
+
> },
|
|
5549
|
+
*/
|
|
5550
|
+
async asJson() {
|
|
5551
|
+
return jsonParse(await tools.fs.readFile(filename, 'utf-8'));
|
|
5552
|
+
},
|
|
5553
|
+
async asText() {
|
|
5554
|
+
return await tools.fs.readFile(filename, 'utf-8');
|
|
5555
|
+
},
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
/**
|
|
5559
|
+
* Creates a source handler for inline text knowledge.
|
|
5560
|
+
*
|
|
5561
|
+
* @private internal utility of `makeKnowledgeSourceHandler`
|
|
5562
|
+
*/
|
|
5563
|
+
function makeInlineTextKnowledgeSourceHandler(knowledgeSourceContent, name, isVerbose) {
|
|
5564
|
+
if (isVerbose) {
|
|
5565
|
+
console.info(`📄 [7] "${name}" is just a explicit string text with a knowledge source`);
|
|
5566
|
+
console.info('---');
|
|
5567
|
+
console.info(knowledgeSourceContent);
|
|
5568
|
+
console.info('---');
|
|
4860
5569
|
}
|
|
5570
|
+
return {
|
|
5571
|
+
source: name,
|
|
5572
|
+
filename: null,
|
|
5573
|
+
url: null,
|
|
5574
|
+
mimeType: 'text/markdown',
|
|
5575
|
+
asText() {
|
|
5576
|
+
return knowledgeSourceContent;
|
|
5577
|
+
},
|
|
5578
|
+
asJson() {
|
|
5579
|
+
throw new UnexpectedError('Did not expect that `markdownScraper` would need to get the content `asJson`');
|
|
5580
|
+
},
|
|
5581
|
+
/*
|
|
5582
|
+
TODO: [🥽]
|
|
5583
|
+
> asBlob() {
|
|
5584
|
+
> throw new UnexpectedError(
|
|
5585
|
+
> 'Did not expect that `markdownScraper` would need to get the content `asBlob`',
|
|
5586
|
+
> );
|
|
5587
|
+
> },
|
|
5588
|
+
*/
|
|
5589
|
+
};
|
|
4861
5590
|
}
|
|
4862
5591
|
|
|
4863
5592
|
/**
|
|
@@ -6050,204 +6779,102 @@ const CountUtils = {
|
|
|
6050
6779
|
// TODO: [🧠][🤠] This should be probably as part of `TextFormatParser`
|
|
6051
6780
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6052
6781
|
|
|
6053
|
-
/**
|
|
6054
|
-
* Function checkExpectations will check if the expectations on given value are met
|
|
6055
|
-
*
|
|
6056
|
-
* Note: There are two similar functions:
|
|
6057
|
-
* - `checkExpectations` which throws an error if the expectations are not met
|
|
6058
|
-
* - `isPassingExpectations` which returns a boolean
|
|
6059
|
-
*
|
|
6060
|
-
* @throws {ExpectError} if the expectations are not met
|
|
6061
|
-
* @returns {void} Nothing
|
|
6062
|
-
*
|
|
6063
|
-
* @private internal function of `createPipelineExecutor`
|
|
6064
|
-
*/
|
|
6065
|
-
function checkExpectations(expectations, value) {
|
|
6066
|
-
for (const [unit, { max, min }] of Object.entries(expectations)) {
|
|
6067
|
-
const amount = CountUtils[unit.toUpperCase()](value);
|
|
6068
|
-
if (min && amount < min) {
|
|
6069
|
-
throw new ExpectError(`Expected at least ${min} ${unit} but got ${amount}`);
|
|
6070
|
-
} /* not else */
|
|
6071
|
-
if (max && amount > max) {
|
|
6072
|
-
throw new ExpectError(`Expected at most ${max} ${unit} but got ${amount}`);
|
|
6073
|
-
}
|
|
6074
|
-
}
|
|
6075
|
-
}
|
|
6076
|
-
// TODO: [💝] Unite object for expecting amount and format
|
|
6077
|
-
// TODO: [🧠][🤠] This should be part of `TextFormatParser`
|
|
6078
|
-
// Note: [💝] and [🤠] are interconnected together
|
|
6079
|
-
|
|
6080
|
-
/**
|
|
6081
|
-
* Validates a prompt result against expectations and format requirements.
|
|
6082
|
-
* This function provides a common abstraction for result validation that can be used
|
|
6083
|
-
* by both execution logic and caching logic to ensure consistency.
|
|
6084
|
-
*
|
|
6085
|
-
* Note: [🔂] This function is idempotent.
|
|
6086
|
-
*
|
|
6087
|
-
* @param options - The validation options including result string, expectations, and format
|
|
6088
|
-
* @returns Validation result with processed string and validity status
|
|
6089
|
-
*
|
|
6090
|
-
* @private internal function of `createPipelineExecutor` and `cacheLlmTools`
|
|
6091
|
-
*/
|
|
6092
|
-
function validatePromptResult(options) {
|
|
6093
|
-
const { resultString, expectations, format } = options;
|
|
6094
|
-
let processedResultString = resultString;
|
|
6095
|
-
let validationError;
|
|
6096
|
-
try {
|
|
6097
|
-
// TODO: [💝] Unite object for expecting amount and format
|
|
6098
|
-
if (format) {
|
|
6099
|
-
if (format === 'JSON') {
|
|
6100
|
-
if (!isValidJsonString(processedResultString)) {
|
|
6101
|
-
// TODO: [🏢] Do more universally via `FormatParser`
|
|
6102
|
-
try {
|
|
6103
|
-
processedResultString = extractJsonBlock(processedResultString);
|
|
6104
|
-
}
|
|
6105
|
-
catch (error) {
|
|
6106
|
-
keepUnused(error);
|
|
6107
|
-
throw new ExpectError(spaceTrim$1((block) => `
|
|
6108
|
-
Expected valid JSON string
|
|
6109
|
-
|
|
6110
|
-
The expected JSON text:
|
|
6111
|
-
${block(processedResultString)}
|
|
6112
|
-
`));
|
|
6113
|
-
}
|
|
6114
|
-
}
|
|
6115
|
-
}
|
|
6116
|
-
else {
|
|
6117
|
-
throw new UnexpectedError(`Unknown format "${format}"`);
|
|
6118
|
-
}
|
|
6119
|
-
}
|
|
6120
|
-
// TODO: [💝] Unite object for expecting amount and format
|
|
6121
|
-
if (expectations) {
|
|
6122
|
-
checkExpectations(expectations, processedResultString);
|
|
6123
|
-
}
|
|
6124
|
-
return {
|
|
6125
|
-
isValid: true,
|
|
6126
|
-
processedResultString,
|
|
6127
|
-
};
|
|
6128
|
-
}
|
|
6129
|
-
catch (error) {
|
|
6130
|
-
if (error instanceof ExpectError) {
|
|
6131
|
-
validationError = error;
|
|
6132
|
-
}
|
|
6133
|
-
else {
|
|
6134
|
-
// Re-throw non-ExpectError errors (like UnexpectedError)
|
|
6135
|
-
throw error;
|
|
6136
|
-
}
|
|
6137
|
-
return {
|
|
6138
|
-
isValid: false,
|
|
6139
|
-
processedResultString,
|
|
6140
|
-
error: validationError,
|
|
6141
|
-
};
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
|
|
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
|
-
`));
|
|
6782
|
+
/**
|
|
6783
|
+
* Function checkExpectations will check if the expectations on given value are met
|
|
6784
|
+
*
|
|
6785
|
+
* Note: There are two similar functions:
|
|
6786
|
+
* - `checkExpectations` which throws an error if the expectations are not met
|
|
6787
|
+
* - `isPassingExpectations` which returns a boolean
|
|
6788
|
+
*
|
|
6789
|
+
* @throws {ExpectError} if the expectations are not met
|
|
6790
|
+
* @returns {void} Nothing
|
|
6791
|
+
*
|
|
6792
|
+
* @private internal function of `createPipelineExecutor`
|
|
6793
|
+
*/
|
|
6794
|
+
function checkExpectations(expectations, value) {
|
|
6795
|
+
for (const [unit, { max, min }] of Object.entries(expectations)) {
|
|
6796
|
+
const amount = CountUtils[unit.toUpperCase()](value);
|
|
6797
|
+
if (min && amount < min) {
|
|
6798
|
+
throw new ExpectError(`Expected at least ${min} ${unit} but got ${amount}`);
|
|
6799
|
+
} /* not else */
|
|
6800
|
+
if (max && amount > max) {
|
|
6801
|
+
throw new ExpectError(`Expected at most ${max} ${unit} but got ${amount}`);
|
|
6802
|
+
}
|
|
6234
6803
|
}
|
|
6235
|
-
return {
|
|
6236
|
-
attemptIndex,
|
|
6237
|
-
isJokerAttempt,
|
|
6238
|
-
jokerParameterName,
|
|
6239
|
-
};
|
|
6240
6804
|
}
|
|
6805
|
+
// TODO: [💝] Unite object for expecting amount and format
|
|
6806
|
+
// TODO: [🧠][🤠] This should be part of `TextFormatParser`
|
|
6807
|
+
// Note: [💝] and [🤠] are interconnected together
|
|
6808
|
+
|
|
6241
6809
|
/**
|
|
6242
|
-
*
|
|
6810
|
+
* Validates a prompt result against expectations and format requirements.
|
|
6811
|
+
* This function provides a common abstraction for result validation that can be used
|
|
6812
|
+
* by both execution logic and caching logic to ensure consistency.
|
|
6813
|
+
*
|
|
6814
|
+
* Note: [🔂] This function is idempotent.
|
|
6815
|
+
*
|
|
6816
|
+
* @param options - The validation options including result string, expectations, and format
|
|
6817
|
+
* @returns Validation result with processed string and validity status
|
|
6818
|
+
*
|
|
6819
|
+
* @private internal function of `createPipelineExecutor` and `cacheLlmTools`
|
|
6243
6820
|
*/
|
|
6244
|
-
function
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6821
|
+
function validatePromptResult(options) {
|
|
6822
|
+
const { resultString, expectations, format } = options;
|
|
6823
|
+
let processedResultString = resultString;
|
|
6824
|
+
let validationError;
|
|
6825
|
+
try {
|
|
6826
|
+
// TODO: [💝] Unite object for expecting amount and format
|
|
6827
|
+
if (format) {
|
|
6828
|
+
if (format === 'JSON') {
|
|
6829
|
+
if (!isValidJsonString(processedResultString)) {
|
|
6830
|
+
// TODO: [🏢] Do more universally via `FormatParser`
|
|
6831
|
+
try {
|
|
6832
|
+
processedResultString = extractJsonBlock(processedResultString);
|
|
6833
|
+
}
|
|
6834
|
+
catch (error) {
|
|
6835
|
+
keepUnused(error);
|
|
6836
|
+
throw new ExpectError(spaceTrim$1((block) => `
|
|
6837
|
+
Expected valid JSON string
|
|
6838
|
+
|
|
6839
|
+
The expected JSON text:
|
|
6840
|
+
${block(processedResultString)}
|
|
6841
|
+
`));
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
6844
|
+
}
|
|
6845
|
+
else {
|
|
6846
|
+
throw new UnexpectedError(`Unknown format "${format}"`);
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
// TODO: [💝] Unite object for expecting amount and format
|
|
6850
|
+
if (expectations) {
|
|
6851
|
+
checkExpectations(expectations, processedResultString);
|
|
6852
|
+
}
|
|
6853
|
+
return {
|
|
6854
|
+
isValid: true,
|
|
6855
|
+
processedResultString,
|
|
6856
|
+
};
|
|
6857
|
+
}
|
|
6858
|
+
catch (error) {
|
|
6859
|
+
if (error instanceof ExpectError) {
|
|
6860
|
+
validationError = error;
|
|
6861
|
+
}
|
|
6862
|
+
else {
|
|
6863
|
+
// Re-throw non-ExpectError errors (like UnexpectedError)
|
|
6864
|
+
throw error;
|
|
6865
|
+
}
|
|
6866
|
+
return {
|
|
6867
|
+
isValid: false,
|
|
6868
|
+
processedResultString,
|
|
6869
|
+
error: validationError,
|
|
6870
|
+
};
|
|
6871
|
+
}
|
|
6248
6872
|
}
|
|
6873
|
+
|
|
6249
6874
|
/**
|
|
6250
6875
|
* Executes one loop iteration, from joker resolution or task execution through validation.
|
|
6876
|
+
*
|
|
6877
|
+
* @private function of `executeAttempts`
|
|
6251
6878
|
*/
|
|
6252
6879
|
async function executeSingleAttempt(options) {
|
|
6253
6880
|
const { attempt, options: executeAttemptsOptions, llmTools, $ongoingTaskResult } = options;
|
|
@@ -6562,11 +7189,15 @@ function validateAttemptResult(options) {
|
|
|
6562
7189
|
// Update the result string in case format processing modified it (e.g., JSON extraction)
|
|
6563
7190
|
$ongoingTaskResult.$resultString = validationResult.processedResultString;
|
|
6564
7191
|
}
|
|
7192
|
+
|
|
6565
7193
|
/**
|
|
6566
|
-
* Stores one failed attempt
|
|
7194
|
+
* Stores one failed attempt, reports the expectation error, and throws the aggregated retry error after the final
|
|
7195
|
+
* regular attempt.
|
|
7196
|
+
*
|
|
7197
|
+
* @private function of `executeAttempts`
|
|
6567
7198
|
*/
|
|
6568
|
-
function
|
|
6569
|
-
const { error, attemptIndex, onProgress, $ongoingTaskResult } = options;
|
|
7199
|
+
function handleAttemptFailure(options) {
|
|
7200
|
+
const { error, attemptIndex, maxAttempts, maxExecutionAttempts, onProgress, pipelineIdentification, $ongoingTaskResult, } = options;
|
|
6570
7201
|
$ongoingTaskResult.$expectError = error;
|
|
6571
7202
|
$ongoingTaskResult.$failedResults.push({
|
|
6572
7203
|
attemptIndex,
|
|
@@ -6576,39 +7207,7 @@ function recordFailedAttempt(options) {
|
|
|
6576
7207
|
onProgress({
|
|
6577
7208
|
errors: [error],
|
|
6578
7209
|
});
|
|
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) {
|
|
7210
|
+
if (attemptIndex !== maxAttempts - 1) {
|
|
6612
7211
|
return;
|
|
6613
7212
|
}
|
|
6614
7213
|
throw new PipelineExecutionError(spaceTrim$1((block) => {
|
|
@@ -6653,6 +7252,136 @@ function quoteMultilineText(text) {
|
|
|
6653
7252
|
.map((line) => `> ${line}`)
|
|
6654
7253
|
.join('\n');
|
|
6655
7254
|
}
|
|
7255
|
+
|
|
7256
|
+
/**
|
|
7257
|
+
* Appends the prompt execution report for prompt-task attempts.
|
|
7258
|
+
*
|
|
7259
|
+
* @private function of `executeAttempts`
|
|
7260
|
+
*/
|
|
7261
|
+
function reportPromptExecution(options) {
|
|
7262
|
+
const { attempt, task, $executionReport, logLlmCall, $ongoingTaskResult } = options;
|
|
7263
|
+
if (attempt.isJokerAttempt || task.taskType !== 'PROMPT_TASK' || !$ongoingTaskResult.$prompt) {
|
|
7264
|
+
return;
|
|
7265
|
+
}
|
|
7266
|
+
// Note: [2] When some expected parameter is not defined, error will occur in templateParameters
|
|
7267
|
+
// In that case we don’t want to make a report about it because it’s not a llm execution error
|
|
7268
|
+
const executionPromptReport = {
|
|
7269
|
+
prompt: {
|
|
7270
|
+
...$ongoingTaskResult.$prompt,
|
|
7271
|
+
// <- TODO: [🧠] How to pick everyhing except `pipelineUrl`
|
|
7272
|
+
},
|
|
7273
|
+
result: $ongoingTaskResult.$result || undefined,
|
|
7274
|
+
error: $ongoingTaskResult.$expectError === null ? undefined : serializeError($ongoingTaskResult.$expectError),
|
|
7275
|
+
};
|
|
7276
|
+
$executionReport.promptExecutions.push(executionPromptReport);
|
|
7277
|
+
if (logLlmCall) {
|
|
7278
|
+
logLlmCall({
|
|
7279
|
+
modelName: 'model' /* <- TODO: How to get model name from the report */,
|
|
7280
|
+
report: executionPromptReport,
|
|
7281
|
+
});
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
|
|
7285
|
+
/**
|
|
7286
|
+
* Executes a pipeline task with multiple attempts, including joker and retry logic. Handles different task types
|
|
7287
|
+
* (prompt, script, dialog, etc.), applies postprocessing, checks expectations, and updates the execution report.
|
|
7288
|
+
* Throws errors if execution fails after all attempts.
|
|
7289
|
+
*
|
|
7290
|
+
* @param options - The options for execution, including task, parameters, pipeline, and configuration.
|
|
7291
|
+
* @returns The result string of the executed task.
|
|
7292
|
+
*
|
|
7293
|
+
* @private internal utility of `createPipelineExecutor`
|
|
7294
|
+
*/
|
|
7295
|
+
async function executeAttempts(options) {
|
|
7296
|
+
const $ongoingTaskResult = createOngoingTaskResult();
|
|
7297
|
+
const llmTools = getSingleLlmExecutionTools(options.tools.llm);
|
|
7298
|
+
attempts: for (let attemptIndex = -options.jokerParameterNames.length; attemptIndex < options.maxAttempts; attemptIndex++) {
|
|
7299
|
+
const attempt = createAttemptDescriptor({
|
|
7300
|
+
attemptIndex,
|
|
7301
|
+
jokerParameterNames: options.jokerParameterNames,
|
|
7302
|
+
pipelineIdentification: options.pipelineIdentification,
|
|
7303
|
+
});
|
|
7304
|
+
resetAttemptExecutionState($ongoingTaskResult);
|
|
7305
|
+
try {
|
|
7306
|
+
await executeSingleAttempt({
|
|
7307
|
+
attempt,
|
|
7308
|
+
options,
|
|
7309
|
+
llmTools,
|
|
7310
|
+
$ongoingTaskResult,
|
|
7311
|
+
});
|
|
7312
|
+
break attempts;
|
|
7313
|
+
}
|
|
7314
|
+
catch (error) {
|
|
7315
|
+
if (!(error instanceof ExpectError)) {
|
|
7316
|
+
throw error;
|
|
7317
|
+
}
|
|
7318
|
+
handleAttemptFailure({
|
|
7319
|
+
error,
|
|
7320
|
+
attemptIndex,
|
|
7321
|
+
maxAttempts: options.maxAttempts,
|
|
7322
|
+
maxExecutionAttempts: options.maxExecutionAttempts,
|
|
7323
|
+
onProgress: options.onProgress,
|
|
7324
|
+
pipelineIdentification: options.pipelineIdentification,
|
|
7325
|
+
$ongoingTaskResult,
|
|
7326
|
+
});
|
|
7327
|
+
}
|
|
7328
|
+
finally {
|
|
7329
|
+
reportPromptExecution({
|
|
7330
|
+
attempt,
|
|
7331
|
+
task: options.task,
|
|
7332
|
+
$executionReport: options.$executionReport,
|
|
7333
|
+
logLlmCall: options.logLlmCall,
|
|
7334
|
+
$ongoingTaskResult,
|
|
7335
|
+
});
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
return getSuccessfulResultString({
|
|
7339
|
+
pipelineIdentification: options.pipelineIdentification,
|
|
7340
|
+
$ongoingTaskResult,
|
|
7341
|
+
});
|
|
7342
|
+
}
|
|
7343
|
+
/**
|
|
7344
|
+
* Creates mutable attempt state for one task execution lifecycle.
|
|
7345
|
+
*/
|
|
7346
|
+
function createOngoingTaskResult() {
|
|
7347
|
+
return {
|
|
7348
|
+
$result: null,
|
|
7349
|
+
$resultString: null,
|
|
7350
|
+
$expectError: null,
|
|
7351
|
+
$scriptPipelineExecutionErrors: [],
|
|
7352
|
+
$failedResults: [],
|
|
7353
|
+
};
|
|
7354
|
+
}
|
|
7355
|
+
/**
|
|
7356
|
+
* Resolves the bookkeeping for one loop iteration, including joker lookup.
|
|
7357
|
+
*/
|
|
7358
|
+
function createAttemptDescriptor(options) {
|
|
7359
|
+
const { attemptIndex, jokerParameterNames, pipelineIdentification } = options;
|
|
7360
|
+
const isJokerAttempt = attemptIndex < 0;
|
|
7361
|
+
const jokerParameterName = isJokerAttempt
|
|
7362
|
+
? jokerParameterNames[jokerParameterNames.length + attemptIndex]
|
|
7363
|
+
: undefined;
|
|
7364
|
+
if (isJokerAttempt && !jokerParameterName) {
|
|
7365
|
+
throw new UnexpectedError(spaceTrim$1((block) => `
|
|
7366
|
+
Joker not found in attempt ${attemptIndex}
|
|
7367
|
+
|
|
7368
|
+
${block(pipelineIdentification)}
|
|
7369
|
+
`));
|
|
7370
|
+
}
|
|
7371
|
+
return {
|
|
7372
|
+
attemptIndex,
|
|
7373
|
+
isJokerAttempt,
|
|
7374
|
+
jokerParameterName,
|
|
7375
|
+
};
|
|
7376
|
+
}
|
|
7377
|
+
/**
|
|
7378
|
+
* Clears the per-attempt result slots while preserving cumulative failure history.
|
|
7379
|
+
*/
|
|
7380
|
+
function resetAttemptExecutionState($ongoingTaskResult) {
|
|
7381
|
+
$ongoingTaskResult.$result = null;
|
|
7382
|
+
$ongoingTaskResult.$resultString = null;
|
|
7383
|
+
$ongoingTaskResult.$expectError = null;
|
|
7384
|
+
}
|
|
6656
7385
|
/**
|
|
6657
7386
|
* Returns the successful result string or raises an unexpected internal-state error.
|
|
6658
7387
|
*/
|