@promptbook/node 0.112.0-70 → 0.112.0-72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/index.es.js +1403 -1272
- package/esm/index.es.js.map +1 -1
- package/esm/src/book-3.0/Book.d.ts +6 -0
- package/esm/src/book-components/Chat/utils/getToolCallChipletInfo.test.d.ts +1 -0
- package/esm/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +12 -2
- package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -0
- package/esm/src/cli/cli-commands/run/prepareRunCommandResources.d.ts +20 -0
- package/esm/src/cli/cli-commands/run/resolveRunInputParameters.d.ts +12 -0
- package/esm/src/cli/cli-commands/run/runCommandAction.d.ts +21 -0
- package/esm/src/cli/cli-commands/run/runPipelineExecution.d.ts +14 -0
- package/esm/src/cli/cli-commands/run.d.ts +1 -1
- package/esm/src/conversion/parsePipeline/applyPipelineHead.d.ts +8 -0
- package/esm/src/conversion/parsePipeline/createInitialPipelineJson.d.ts +8 -0
- package/esm/src/conversion/parsePipeline/createUniqueSectionNameResolver.d.ts +14 -0
- package/esm/src/conversion/parsePipeline/defineParameter.d.ts +8 -0
- package/esm/src/conversion/parsePipeline/extractPipelineDescription.d.ts +6 -0
- package/esm/src/conversion/parsePipeline/finalizeParsedPipeline.d.ts +8 -0
- package/esm/src/conversion/parsePipeline/getPipelineIdentification.d.ts +7 -0
- package/esm/src/conversion/parsePipeline/parsePreparedPipelineSections.d.ts +18 -0
- package/esm/src/conversion/parsePipeline/preparePipelineString.d.ts +8 -0
- package/esm/src/conversion/parsePipeline/processPipelineSection.d.ts +9 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +1403 -1272
- package/umd/index.umd.js.map +1 -1
- package/umd/src/book-3.0/Book.d.ts +6 -0
- package/umd/src/book-components/Chat/utils/getToolCallChipletInfo.test.d.ts +1 -0
- package/umd/src/cli/cli-commands/agent/agentRunCliOptions.d.ts +12 -2
- package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -0
- package/umd/src/cli/cli-commands/run/prepareRunCommandResources.d.ts +20 -0
- package/umd/src/cli/cli-commands/run/resolveRunInputParameters.d.ts +12 -0
- package/umd/src/cli/cli-commands/run/runCommandAction.d.ts +21 -0
- package/umd/src/cli/cli-commands/run/runPipelineExecution.d.ts +14 -0
- package/umd/src/cli/cli-commands/run.d.ts +1 -1
- package/umd/src/conversion/parsePipeline/applyPipelineHead.d.ts +8 -0
- package/umd/src/conversion/parsePipeline/createInitialPipelineJson.d.ts +8 -0
- package/umd/src/conversion/parsePipeline/createUniqueSectionNameResolver.d.ts +14 -0
- package/umd/src/conversion/parsePipeline/defineParameter.d.ts +8 -0
- package/umd/src/conversion/parsePipeline/extractPipelineDescription.d.ts +6 -0
- package/umd/src/conversion/parsePipeline/finalizeParsedPipeline.d.ts +8 -0
- package/umd/src/conversion/parsePipeline/getPipelineIdentification.d.ts +7 -0
- package/umd/src/conversion/parsePipeline/parsePreparedPipelineSections.d.ts +18 -0
- package/umd/src/conversion/parsePipeline/preparePipelineString.d.ts +8 -0
- package/umd/src/conversion/parsePipeline/processPipelineSection.d.ts +9 -0
- package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* @generated
|
|
49
49
|
* @see https://github.com/webgptorg/promptbook
|
|
50
50
|
*/
|
|
51
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
51
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-72';
|
|
52
52
|
/**
|
|
53
53
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
54
54
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -7621,118 +7621,75 @@
|
|
|
7621
7621
|
*/
|
|
7622
7622
|
|
|
7623
7623
|
/**
|
|
7624
|
-
*
|
|
7625
|
-
*
|
|
7626
|
-
* There is is distinction between task types and section types
|
|
7627
|
-
* - Every section in markdown has its SectionType
|
|
7628
|
-
* - Some sections are tasks but other can be non-task sections
|
|
7629
|
-
*
|
|
7630
|
-
* @public exported from `@promptbook/core`
|
|
7631
|
-
*/
|
|
7632
|
-
const TaskTypes = [
|
|
7633
|
-
'PROMPT',
|
|
7634
|
-
'SIMPLE',
|
|
7635
|
-
'SCRIPT',
|
|
7636
|
-
'DIALOG',
|
|
7637
|
-
// <- [🅱]
|
|
7638
|
-
];
|
|
7639
|
-
|
|
7640
|
-
/**
|
|
7641
|
-
* All available sections which are not tasks
|
|
7642
|
-
*
|
|
7643
|
-
* @public exported from `@promptbook/core`
|
|
7644
|
-
*/
|
|
7645
|
-
const NonTaskSectionTypes = ['EXAMPLE', 'KNOWLEDGE', 'INSTRUMENT', 'ACTION'];
|
|
7646
|
-
/**
|
|
7647
|
-
* All available section types
|
|
7648
|
-
*
|
|
7649
|
-
* There is is distinction between task types and section types
|
|
7650
|
-
* - Every section in markdown has its SectionType
|
|
7651
|
-
* - Some sections are tasks but other can be non-task sections
|
|
7624
|
+
* Parses the boilerplate command
|
|
7652
7625
|
*
|
|
7653
|
-
*
|
|
7654
|
-
*/
|
|
7655
|
-
const SectionTypes = [
|
|
7656
|
-
...TaskTypes.map((TaskType) => `${TaskType}_TASK`),
|
|
7657
|
-
...NonTaskSectionTypes,
|
|
7658
|
-
];
|
|
7659
|
-
|
|
7660
|
-
/**
|
|
7661
|
-
* Parses the knowledge command
|
|
7626
|
+
* Note: @@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
|
|
7662
7627
|
*
|
|
7663
7628
|
* @see `documentationUrl` for more details
|
|
7664
7629
|
*
|
|
7665
|
-
* @
|
|
7630
|
+
* @private within the commands folder
|
|
7666
7631
|
*/
|
|
7667
|
-
const
|
|
7632
|
+
const boilerplateCommandParser = {
|
|
7668
7633
|
/**
|
|
7669
7634
|
* Name of the command
|
|
7670
7635
|
*/
|
|
7671
|
-
name: '
|
|
7636
|
+
name: 'BOILERPLATE',
|
|
7637
|
+
/**
|
|
7638
|
+
* Aliases for the BOILERPLATE command
|
|
7639
|
+
*/
|
|
7640
|
+
aliasNames: ['BP'],
|
|
7672
7641
|
/**
|
|
7673
7642
|
* BOILERPLATE command can be used in:
|
|
7674
7643
|
*/
|
|
7675
7644
|
isUsedInPipelineHead: true,
|
|
7676
|
-
isUsedInPipelineTask:
|
|
7645
|
+
isUsedInPipelineTask: true,
|
|
7677
7646
|
/**
|
|
7678
|
-
* Description of the
|
|
7647
|
+
* Description of the BOILERPLATE command
|
|
7679
7648
|
*/
|
|
7680
|
-
description:
|
|
7649
|
+
description: `@@`,
|
|
7681
7650
|
/**
|
|
7682
7651
|
* Link to documentation
|
|
7683
7652
|
*/
|
|
7684
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions
|
|
7653
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
7685
7654
|
/**
|
|
7686
|
-
* Example usages of the
|
|
7655
|
+
* Example usages of the BOILERPLATE command
|
|
7687
7656
|
*/
|
|
7688
|
-
examples: [
|
|
7689
|
-
'KNOWLEDGE https://www.pavolhejny.com/',
|
|
7690
|
-
'KNOWLEDGE ./hejny-cv.txt',
|
|
7691
|
-
'KNOWLEDGE ./hejny-cv.md',
|
|
7692
|
-
'KNOWLEDGE ./hejny-cv.pdf',
|
|
7693
|
-
'KNOWLEDGE ./hejny-cv.docx',
|
|
7694
|
-
// <- TODO: [😿] Allow ONLY files scoped in the (sub)directory NOT ../ and test it
|
|
7695
|
-
],
|
|
7657
|
+
examples: ['BOILERPLATE foo', 'BOILERPLATE bar', 'BP foo', 'BP bar'],
|
|
7696
7658
|
/**
|
|
7697
|
-
* Parses the
|
|
7659
|
+
* Parses the BOILERPLATE command
|
|
7698
7660
|
*/
|
|
7699
7661
|
parse(input) {
|
|
7700
7662
|
const { args } = input;
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
throw new ParseError(`Source is not defined`);
|
|
7704
|
-
}
|
|
7705
|
-
// TODO: [main] !!4 Following checks should be applied every link in the `sourceContent`
|
|
7706
|
-
if (knowledgeSourceContent.startsWith('http://')) {
|
|
7707
|
-
throw new ParseError(`Source is not secure`);
|
|
7708
|
-
}
|
|
7709
|
-
if (!(isValidFilePath(knowledgeSourceContent) || isValidUrl(knowledgeSourceContent))) {
|
|
7710
|
-
throw new ParseError(`Source not valid`);
|
|
7663
|
+
if (args.length !== 1) {
|
|
7664
|
+
throw new ParseError(`BOILERPLATE command requires exactly one argument`);
|
|
7711
7665
|
}
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
throw new ParseError(`Source cannot be outside of the .book.md folder`);
|
|
7666
|
+
const value = args[0].toLowerCase();
|
|
7667
|
+
if (value.includes('brr')) {
|
|
7668
|
+
throw new ParseError(`BOILERPLATE value can not contain brr`);
|
|
7716
7669
|
}
|
|
7717
7670
|
return {
|
|
7718
|
-
type: '
|
|
7719
|
-
|
|
7671
|
+
type: 'BOILERPLATE',
|
|
7672
|
+
value,
|
|
7720
7673
|
};
|
|
7721
7674
|
},
|
|
7722
7675
|
/**
|
|
7723
|
-
* Apply the
|
|
7676
|
+
* Apply the BOILERPLATE command to the `pipelineJson`
|
|
7724
7677
|
*
|
|
7725
7678
|
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
7726
7679
|
*/
|
|
7727
7680
|
$applyToPipelineJson(command, $pipelineJson) {
|
|
7728
|
-
|
|
7729
|
-
$pipelineJson.knowledgeSources.push({
|
|
7730
|
-
name: knowledgeSourceContentToName(knowledgeSourceContent),
|
|
7731
|
-
knowledgeSourceContent,
|
|
7732
|
-
});
|
|
7681
|
+
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
7733
7682
|
},
|
|
7734
7683
|
/**
|
|
7735
|
-
*
|
|
7684
|
+
* Apply the BOILERPLATE command to the `pipelineJson`
|
|
7685
|
+
*
|
|
7686
|
+
* Note: `$` is used to indicate that this function mutates given `taskJson`
|
|
7687
|
+
*/
|
|
7688
|
+
$applyToTaskJson(command, $taskJson, $pipelineJson) {
|
|
7689
|
+
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
7690
|
+
},
|
|
7691
|
+
/**
|
|
7692
|
+
* Converts the BOILERPLATE command back to string
|
|
7736
7693
|
*
|
|
7737
7694
|
* Note: This is used in `pipelineJsonToString` utility
|
|
7738
7695
|
*/
|
|
@@ -7740,354 +7697,77 @@
|
|
|
7740
7697
|
return `---`; // <- TODO: [🛋] Implement
|
|
7741
7698
|
},
|
|
7742
7699
|
/**
|
|
7743
|
-
* Reads the
|
|
7700
|
+
* Reads the BOILERPLATE command from the `PipelineJson`
|
|
7744
7701
|
*
|
|
7745
7702
|
* Note: This is used in `pipelineJsonToString` utility
|
|
7746
7703
|
*/
|
|
7747
7704
|
takeFromPipelineJson(pipelineJson) {
|
|
7748
|
-
throw new
|
|
7705
|
+
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
7706
|
+
},
|
|
7707
|
+
/**
|
|
7708
|
+
* Reads the BOILERPLATE command from the `TaskJson`
|
|
7709
|
+
*
|
|
7710
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
7711
|
+
*/
|
|
7712
|
+
takeFromTaskJson($taskJson) {
|
|
7713
|
+
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
7749
7714
|
},
|
|
7750
7715
|
};
|
|
7751
|
-
// Note: [⛱] There are two types of KNOWLEDGE commands *...(read more in [⛱])*
|
|
7752
7716
|
|
|
7753
7717
|
/**
|
|
7754
|
-
* Parses the
|
|
7718
|
+
* Parses the BOOK_VERSION command
|
|
7755
7719
|
*
|
|
7756
7720
|
* @see `documentationUrl` for more details
|
|
7757
7721
|
*
|
|
7758
7722
|
* @public exported from `@promptbook/editable`
|
|
7759
7723
|
*/
|
|
7760
|
-
const
|
|
7724
|
+
const bookVersionCommandParser = {
|
|
7761
7725
|
/**
|
|
7762
7726
|
* Name of the command
|
|
7763
7727
|
*/
|
|
7764
|
-
name: '
|
|
7765
|
-
|
|
7766
|
-
* Aliases for the SECTION command
|
|
7767
|
-
*/
|
|
7768
|
-
aliasNames: [
|
|
7769
|
-
'PROMPT',
|
|
7770
|
-
'SIMPLE',
|
|
7771
|
-
'SCRIPT',
|
|
7772
|
-
'DIALOG',
|
|
7773
|
-
'SAMPLE',
|
|
7774
|
-
'EXAMPLE',
|
|
7775
|
-
'KNOWLEDGE',
|
|
7776
|
-
'INSTRUMENT',
|
|
7777
|
-
'ACTION', // <- Note: [⛱]
|
|
7778
|
-
],
|
|
7779
|
-
/**
|
|
7780
|
-
* Aliases for the SECTION command
|
|
7781
|
-
*/
|
|
7782
|
-
deprecatedNames: ['TEMPLATE', 'BLOCK', 'EXECUTE'],
|
|
7728
|
+
name: 'BOOK_VERSION',
|
|
7729
|
+
aliasNames: ['PTBK_VERSION', 'PROMPTBOOK_VERSION', 'BOOK'],
|
|
7783
7730
|
/**
|
|
7784
7731
|
* BOILERPLATE command can be used in:
|
|
7785
7732
|
*/
|
|
7786
|
-
isUsedInPipelineHead:
|
|
7787
|
-
isUsedInPipelineTask:
|
|
7733
|
+
isUsedInPipelineHead: true,
|
|
7734
|
+
isUsedInPipelineTask: false,
|
|
7788
7735
|
/**
|
|
7789
|
-
* Description of the
|
|
7736
|
+
* Description of the BOOK_VERSION command
|
|
7790
7737
|
*/
|
|
7791
|
-
description: `
|
|
7738
|
+
description: `Which version of the Book language is the .book.md using`,
|
|
7792
7739
|
/**
|
|
7793
7740
|
* Link to documentation
|
|
7794
7741
|
*/
|
|
7795
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/
|
|
7742
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/69',
|
|
7796
7743
|
/**
|
|
7797
|
-
* Example usages of the
|
|
7744
|
+
* Example usages of the BOOK_VERSION command
|
|
7798
7745
|
*/
|
|
7799
|
-
examples: [
|
|
7800
|
-
// Short form:
|
|
7801
|
-
'PROMPT',
|
|
7802
|
-
'SIMPLE',
|
|
7803
|
-
'SCRIPT',
|
|
7804
|
-
'DIALOG',
|
|
7805
|
-
// <- [🅱]
|
|
7806
|
-
'EXAMPLE',
|
|
7807
|
-
'KNOWLEDGE',
|
|
7808
|
-
'INSTRUMENT',
|
|
7809
|
-
'ACTION',
|
|
7810
|
-
// -----------------
|
|
7811
|
-
// Recommended (reversed) form:
|
|
7812
|
-
'PROMPT SECTION',
|
|
7813
|
-
'SIMPLE SECTION',
|
|
7814
|
-
'SCRIPT SECTION',
|
|
7815
|
-
'DIALOG SECTION',
|
|
7816
|
-
// <- [🅱]
|
|
7817
|
-
'EXAMPLE SECTION',
|
|
7818
|
-
'KNOWLEDGE SECTION',
|
|
7819
|
-
'INSTRUMENT SECTION',
|
|
7820
|
-
'ACTION SECTION',
|
|
7821
|
-
// -----------------
|
|
7822
|
-
// Standard form:
|
|
7823
|
-
'SECTION PROMPT',
|
|
7824
|
-
'SECTION SIMPLE',
|
|
7825
|
-
'SECTION SCRIPT',
|
|
7826
|
-
'SECTION DIALOG',
|
|
7827
|
-
// <- [🅱]
|
|
7828
|
-
'SECTION EXAMPLE',
|
|
7829
|
-
'SECTION KNOWLEDGE',
|
|
7830
|
-
'SECTION INSTRUMENT',
|
|
7831
|
-
'SECTION ACTION',
|
|
7832
|
-
],
|
|
7833
|
-
// TODO: [♓️] order: -10 /* <- Note: Putting before other commands */
|
|
7746
|
+
examples: [`BOOK VERSION ${BOOK_LANGUAGE_VERSION}`, `BOOK ${BOOK_LANGUAGE_VERSION}`],
|
|
7834
7747
|
/**
|
|
7835
|
-
* Parses the
|
|
7748
|
+
* Parses the BOOK_VERSION command
|
|
7836
7749
|
*/
|
|
7837
7750
|
parse(input) {
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
if (
|
|
7844
|
-
throw new ParseError(
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
${block(SectionTypes.join(', '))}
|
|
7849
|
-
`));
|
|
7751
|
+
const { args } = input;
|
|
7752
|
+
const bookVersion = args.pop();
|
|
7753
|
+
if (bookVersion === undefined) {
|
|
7754
|
+
throw new ParseError(`Version is required`);
|
|
7755
|
+
}
|
|
7756
|
+
if (!isValidPromptbookVersion(bookVersion)) {
|
|
7757
|
+
throw new ParseError(`Invalid Promptbook version "${bookVersion}"`);
|
|
7758
|
+
}
|
|
7759
|
+
if (args.length > 0 && !(((args.length === 1 && args[0]) || '').toUpperCase() === 'VERSION')) {
|
|
7760
|
+
throw new ParseError(`Can not have more than one Promptbook version`);
|
|
7850
7761
|
}
|
|
7851
|
-
const taskType = taskTypes[0];
|
|
7852
7762
|
return {
|
|
7853
|
-
type: '
|
|
7854
|
-
|
|
7763
|
+
type: 'BOOK_VERSION',
|
|
7764
|
+
bookVersion: bookVersion,
|
|
7855
7765
|
};
|
|
7856
7766
|
},
|
|
7857
7767
|
/**
|
|
7858
|
-
* Apply the
|
|
7768
|
+
* Apply the BOOK_VERSION command to the `pipelineJson`
|
|
7859
7769
|
*
|
|
7860
|
-
* Note: `$` is used to indicate that this function mutates given `
|
|
7861
|
-
*/
|
|
7862
|
-
$applyToTaskJson(command, $taskJson, $pipelineJson) {
|
|
7863
|
-
if ($taskJson.isSectionTypeSet === true) {
|
|
7864
|
-
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
7865
|
-
Section type is already defined in the section.
|
|
7866
|
-
It can be defined only once.
|
|
7867
|
-
`));
|
|
7868
|
-
}
|
|
7869
|
-
$taskJson.isSectionTypeSet = true;
|
|
7870
|
-
// TODO: [🍧][💩] Rearrange better - but at bottom and unwrap from function
|
|
7871
|
-
const expectResultingParameterName = () => {
|
|
7872
|
-
if ($taskJson.resultingParameterName) {
|
|
7873
|
-
return;
|
|
7874
|
-
}
|
|
7875
|
-
throw new ParseError(`Task section and example section must end with return statement -> {parameterName}`);
|
|
7876
|
-
};
|
|
7877
|
-
if ($taskJson.content === undefined) {
|
|
7878
|
-
throw new UnexpectedError(`Content is missing in the taskJson - probably commands are applied in wrong order`);
|
|
7879
|
-
}
|
|
7880
|
-
if (command.taskType === 'EXAMPLE') {
|
|
7881
|
-
expectResultingParameterName();
|
|
7882
|
-
const parameter = $pipelineJson.parameters.find((param) => param.name === $taskJson.resultingParameterName);
|
|
7883
|
-
if (parameter === undefined) {
|
|
7884
|
-
// TODO: !!6 Change to logic error for higher level abstraction of chatbot to work
|
|
7885
|
-
throw new ParseError(`Parameter \`{${$taskJson.resultingParameterName}}\` is not defined so can not define example value of it`);
|
|
7886
|
-
}
|
|
7887
|
-
parameter.exampleValues = parameter.exampleValues || [];
|
|
7888
|
-
parameter.exampleValues.push($taskJson.content);
|
|
7889
|
-
$taskJson.isTask = false;
|
|
7890
|
-
return;
|
|
7891
|
-
}
|
|
7892
|
-
if (command.taskType === 'KNOWLEDGE') {
|
|
7893
|
-
knowledgeCommandParser.$applyToPipelineJson({
|
|
7894
|
-
type: 'KNOWLEDGE',
|
|
7895
|
-
knowledgeSourceContent: $taskJson.content, // <- TODO: [🐝][main] !!3 Work with KNOWLEDGE which not referring to the source file or website, but its content itself
|
|
7896
|
-
}, $pipelineJson);
|
|
7897
|
-
$taskJson.isTask = false;
|
|
7898
|
-
return;
|
|
7899
|
-
}
|
|
7900
|
-
if (command.taskType === 'ACTION') {
|
|
7901
|
-
console.error(new NotYetImplementedError('Actions are not implemented yet'));
|
|
7902
|
-
$taskJson.isTask = false;
|
|
7903
|
-
return;
|
|
7904
|
-
}
|
|
7905
|
-
if (command.taskType === 'INSTRUMENT') {
|
|
7906
|
-
console.error(new NotYetImplementedError('Instruments are not implemented yet'));
|
|
7907
|
-
$taskJson.isTask = false;
|
|
7908
|
-
return;
|
|
7909
|
-
}
|
|
7910
|
-
expectResultingParameterName();
|
|
7911
|
-
$taskJson.taskType = command.taskType;
|
|
7912
|
-
$taskJson.isTask = true;
|
|
7913
|
-
},
|
|
7914
|
-
/**
|
|
7915
|
-
* Converts the SECTION command back to string
|
|
7916
|
-
*
|
|
7917
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
7918
|
-
*/
|
|
7919
|
-
stringify(command) {
|
|
7920
|
-
return `---`; // <- TODO: [🛋] Implement
|
|
7921
|
-
},
|
|
7922
|
-
/**
|
|
7923
|
-
* Reads the SECTION command from the `TaskJson`
|
|
7924
|
-
*
|
|
7925
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
7926
|
-
*/
|
|
7927
|
-
takeFromTaskJson($taskJson) {
|
|
7928
|
-
throw new NotYetImplementedError(`[🛋] Not implemented yet`); // <- TODO: [🛋] Implement
|
|
7929
|
-
},
|
|
7930
|
-
};
|
|
7931
|
-
/**
|
|
7932
|
-
* Note: [⛱] There are two types of KNOWLEDGE, ACTION and INSTRUMENT commands:
|
|
7933
|
-
* 1) There are commands `KNOWLEDGE`, `ACTION` and `INSTRUMENT` used in the pipeline head, they just define the knowledge, action or instrument as single line after the command
|
|
7934
|
-
* - KNOWLEDGE Look at https://en.wikipedia.org/wiki/Artificial_intelligence
|
|
7935
|
-
* 2) `KNOWLEDGE SECTION` which has short form `KNOWLEDGE` is used in the sectiom, does not refer the line itself, but the content of the section block
|
|
7936
|
-
* - KNOWLEDGE SECTION
|
|
7937
|
-
*
|
|
7938
|
-
* ```
|
|
7939
|
-
* Look at https://en.wikipedia.org/wiki/Artificial_intelligence
|
|
7940
|
-
* ```
|
|
7941
|
-
*/
|
|
7942
|
-
|
|
7943
|
-
/**
|
|
7944
|
-
* Parses the boilerplate command
|
|
7945
|
-
*
|
|
7946
|
-
* Note: @@ This command is used as boilerplate for new commands - it should NOT be used in any `.book` file
|
|
7947
|
-
*
|
|
7948
|
-
* @see `documentationUrl` for more details
|
|
7949
|
-
*
|
|
7950
|
-
* @private within the commands folder
|
|
7951
|
-
*/
|
|
7952
|
-
const boilerplateCommandParser = {
|
|
7953
|
-
/**
|
|
7954
|
-
* Name of the command
|
|
7955
|
-
*/
|
|
7956
|
-
name: 'BOILERPLATE',
|
|
7957
|
-
/**
|
|
7958
|
-
* Aliases for the BOILERPLATE command
|
|
7959
|
-
*/
|
|
7960
|
-
aliasNames: ['BP'],
|
|
7961
|
-
/**
|
|
7962
|
-
* BOILERPLATE command can be used in:
|
|
7963
|
-
*/
|
|
7964
|
-
isUsedInPipelineHead: true,
|
|
7965
|
-
isUsedInPipelineTask: true,
|
|
7966
|
-
/**
|
|
7967
|
-
* Description of the BOILERPLATE command
|
|
7968
|
-
*/
|
|
7969
|
-
description: `@@`,
|
|
7970
|
-
/**
|
|
7971
|
-
* Link to documentation
|
|
7972
|
-
*/
|
|
7973
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/@@',
|
|
7974
|
-
/**
|
|
7975
|
-
* Example usages of the BOILERPLATE command
|
|
7976
|
-
*/
|
|
7977
|
-
examples: ['BOILERPLATE foo', 'BOILERPLATE bar', 'BP foo', 'BP bar'],
|
|
7978
|
-
/**
|
|
7979
|
-
* Parses the BOILERPLATE command
|
|
7980
|
-
*/
|
|
7981
|
-
parse(input) {
|
|
7982
|
-
const { args } = input;
|
|
7983
|
-
if (args.length !== 1) {
|
|
7984
|
-
throw new ParseError(`BOILERPLATE command requires exactly one argument`);
|
|
7985
|
-
}
|
|
7986
|
-
const value = args[0].toLowerCase();
|
|
7987
|
-
if (value.includes('brr')) {
|
|
7988
|
-
throw new ParseError(`BOILERPLATE value can not contain brr`);
|
|
7989
|
-
}
|
|
7990
|
-
return {
|
|
7991
|
-
type: 'BOILERPLATE',
|
|
7992
|
-
value,
|
|
7993
|
-
};
|
|
7994
|
-
},
|
|
7995
|
-
/**
|
|
7996
|
-
* Apply the BOILERPLATE command to the `pipelineJson`
|
|
7997
|
-
*
|
|
7998
|
-
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
7999
|
-
*/
|
|
8000
|
-
$applyToPipelineJson(command, $pipelineJson) {
|
|
8001
|
-
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
8002
|
-
},
|
|
8003
|
-
/**
|
|
8004
|
-
* Apply the BOILERPLATE command to the `pipelineJson`
|
|
8005
|
-
*
|
|
8006
|
-
* Note: `$` is used to indicate that this function mutates given `taskJson`
|
|
8007
|
-
*/
|
|
8008
|
-
$applyToTaskJson(command, $taskJson, $pipelineJson) {
|
|
8009
|
-
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
8010
|
-
},
|
|
8011
|
-
/**
|
|
8012
|
-
* Converts the BOILERPLATE command back to string
|
|
8013
|
-
*
|
|
8014
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
8015
|
-
*/
|
|
8016
|
-
stringify(command) {
|
|
8017
|
-
return `---`; // <- TODO: [🛋] Implement
|
|
8018
|
-
},
|
|
8019
|
-
/**
|
|
8020
|
-
* Reads the BOILERPLATE command from the `PipelineJson`
|
|
8021
|
-
*
|
|
8022
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
8023
|
-
*/
|
|
8024
|
-
takeFromPipelineJson(pipelineJson) {
|
|
8025
|
-
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
8026
|
-
},
|
|
8027
|
-
/**
|
|
8028
|
-
* Reads the BOILERPLATE command from the `TaskJson`
|
|
8029
|
-
*
|
|
8030
|
-
* Note: This is used in `pipelineJsonToString` utility
|
|
8031
|
-
*/
|
|
8032
|
-
takeFromTaskJson($taskJson) {
|
|
8033
|
-
throw new ParseError(`BOILERPLATE command is only for testing purposes and should not be used in the .book.md file`);
|
|
8034
|
-
},
|
|
8035
|
-
};
|
|
8036
|
-
|
|
8037
|
-
/**
|
|
8038
|
-
* Parses the BOOK_VERSION command
|
|
8039
|
-
*
|
|
8040
|
-
* @see `documentationUrl` for more details
|
|
8041
|
-
*
|
|
8042
|
-
* @public exported from `@promptbook/editable`
|
|
8043
|
-
*/
|
|
8044
|
-
const bookVersionCommandParser = {
|
|
8045
|
-
/**
|
|
8046
|
-
* Name of the command
|
|
8047
|
-
*/
|
|
8048
|
-
name: 'BOOK_VERSION',
|
|
8049
|
-
aliasNames: ['PTBK_VERSION', 'PROMPTBOOK_VERSION', 'BOOK'],
|
|
8050
|
-
/**
|
|
8051
|
-
* BOILERPLATE command can be used in:
|
|
8052
|
-
*/
|
|
8053
|
-
isUsedInPipelineHead: true,
|
|
8054
|
-
isUsedInPipelineTask: false,
|
|
8055
|
-
/**
|
|
8056
|
-
* Description of the BOOK_VERSION command
|
|
8057
|
-
*/
|
|
8058
|
-
description: `Which version of the Book language is the .book.md using`,
|
|
8059
|
-
/**
|
|
8060
|
-
* Link to documentation
|
|
8061
|
-
*/
|
|
8062
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/69',
|
|
8063
|
-
/**
|
|
8064
|
-
* Example usages of the BOOK_VERSION command
|
|
8065
|
-
*/
|
|
8066
|
-
examples: [`BOOK VERSION ${BOOK_LANGUAGE_VERSION}`, `BOOK ${BOOK_LANGUAGE_VERSION}`],
|
|
8067
|
-
/**
|
|
8068
|
-
* Parses the BOOK_VERSION command
|
|
8069
|
-
*/
|
|
8070
|
-
parse(input) {
|
|
8071
|
-
const { args } = input;
|
|
8072
|
-
const bookVersion = args.pop();
|
|
8073
|
-
if (bookVersion === undefined) {
|
|
8074
|
-
throw new ParseError(`Version is required`);
|
|
8075
|
-
}
|
|
8076
|
-
if (!isValidPromptbookVersion(bookVersion)) {
|
|
8077
|
-
throw new ParseError(`Invalid Promptbook version "${bookVersion}"`);
|
|
8078
|
-
}
|
|
8079
|
-
if (args.length > 0 && !(((args.length === 1 && args[0]) || '').toUpperCase() === 'VERSION')) {
|
|
8080
|
-
throw new ParseError(`Can not have more than one Promptbook version`);
|
|
8081
|
-
}
|
|
8082
|
-
return {
|
|
8083
|
-
type: 'BOOK_VERSION',
|
|
8084
|
-
bookVersion: bookVersion,
|
|
8085
|
-
};
|
|
8086
|
-
},
|
|
8087
|
-
/**
|
|
8088
|
-
* Apply the BOOK_VERSION command to the `pipelineJson`
|
|
8089
|
-
*
|
|
8090
|
-
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
7770
|
+
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
8091
7771
|
*/
|
|
8092
7772
|
$applyToPipelineJson(command, $pipelineJson) {
|
|
8093
7773
|
// TODO: Warn if the version is overridden
|
|
@@ -9170,63 +8850,156 @@
|
|
|
9170
8850
|
};
|
|
9171
8851
|
|
|
9172
8852
|
/**
|
|
9173
|
-
*
|
|
9174
|
-
*
|
|
9175
|
-
* @see {@link ModelVariant}
|
|
9176
|
-
*
|
|
9177
|
-
* @public exported from `@promptbook/core`
|
|
9178
|
-
*/
|
|
9179
|
-
const MODEL_VARIANTS = [
|
|
9180
|
-
'COMPLETION',
|
|
9181
|
-
'CHAT',
|
|
9182
|
-
'IMAGE_GENERATION',
|
|
9183
|
-
'EMBEDDING' /* <- TODO [🏳] */ /* <- [🤖] */,
|
|
9184
|
-
];
|
|
9185
|
-
|
|
9186
|
-
/**
|
|
9187
|
-
* Parses the model command
|
|
8853
|
+
* Parses the knowledge command
|
|
9188
8854
|
*
|
|
9189
8855
|
* @see `documentationUrl` for more details
|
|
9190
|
-
* @deprecated Option to manually set the model requirements is not recommended to use, use `PERSONA` instead
|
|
9191
8856
|
*
|
|
9192
8857
|
* @public exported from `@promptbook/editable`
|
|
9193
8858
|
*/
|
|
9194
|
-
const
|
|
8859
|
+
const knowledgeCommandParser = {
|
|
9195
8860
|
/**
|
|
9196
8861
|
* Name of the command
|
|
9197
8862
|
*/
|
|
9198
|
-
name: '
|
|
8863
|
+
name: 'KNOWLEDGE',
|
|
9199
8864
|
/**
|
|
9200
8865
|
* BOILERPLATE command can be used in:
|
|
9201
8866
|
*/
|
|
9202
8867
|
isUsedInPipelineHead: true,
|
|
9203
|
-
isUsedInPipelineTask:
|
|
8868
|
+
isUsedInPipelineTask: false,
|
|
9204
8869
|
/**
|
|
9205
|
-
* Description of the
|
|
8870
|
+
* Description of the KNOWLEDGE command
|
|
9206
8871
|
*/
|
|
9207
|
-
description: `Tells which
|
|
8872
|
+
description: `Tells promptbook which external knowledge to use`,
|
|
9208
8873
|
/**
|
|
9209
8874
|
* Link to documentation
|
|
9210
8875
|
*/
|
|
9211
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/
|
|
8876
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/41',
|
|
9212
8877
|
/**
|
|
9213
|
-
* Example usages of the
|
|
8878
|
+
* Example usages of the KNOWLEDGE command
|
|
9214
8879
|
*/
|
|
9215
|
-
examples: [
|
|
8880
|
+
examples: [
|
|
8881
|
+
'KNOWLEDGE https://www.pavolhejny.com/',
|
|
8882
|
+
'KNOWLEDGE ./hejny-cv.txt',
|
|
8883
|
+
'KNOWLEDGE ./hejny-cv.md',
|
|
8884
|
+
'KNOWLEDGE ./hejny-cv.pdf',
|
|
8885
|
+
'KNOWLEDGE ./hejny-cv.docx',
|
|
8886
|
+
// <- TODO: [😿] Allow ONLY files scoped in the (sub)directory NOT ../ and test it
|
|
8887
|
+
],
|
|
9216
8888
|
/**
|
|
9217
|
-
* Parses the
|
|
8889
|
+
* Parses the KNOWLEDGE command
|
|
9218
8890
|
*/
|
|
9219
8891
|
parse(input) {
|
|
9220
|
-
const { args
|
|
9221
|
-
const
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
// TODO:
|
|
9226
|
-
if (
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
8892
|
+
const { args } = input;
|
|
8893
|
+
const knowledgeSourceContent = _spaceTrim.spaceTrim(args[0] || '');
|
|
8894
|
+
if (knowledgeSourceContent === '') {
|
|
8895
|
+
throw new ParseError(`Source is not defined`);
|
|
8896
|
+
}
|
|
8897
|
+
// TODO: [main] !!4 Following checks should be applied every link in the `sourceContent`
|
|
8898
|
+
if (knowledgeSourceContent.startsWith('http://')) {
|
|
8899
|
+
throw new ParseError(`Source is not secure`);
|
|
8900
|
+
}
|
|
8901
|
+
if (!(isValidFilePath(knowledgeSourceContent) || isValidUrl(knowledgeSourceContent))) {
|
|
8902
|
+
throw new ParseError(`Source not valid`);
|
|
8903
|
+
}
|
|
8904
|
+
if (knowledgeSourceContent.startsWith('../') ||
|
|
8905
|
+
knowledgeSourceContent.startsWith('/') ||
|
|
8906
|
+
/^[A-Z]:[\\/]+/i.test(knowledgeSourceContent)) {
|
|
8907
|
+
throw new ParseError(`Source cannot be outside of the .book.md folder`);
|
|
8908
|
+
}
|
|
8909
|
+
return {
|
|
8910
|
+
type: 'KNOWLEDGE',
|
|
8911
|
+
knowledgeSourceContent,
|
|
8912
|
+
};
|
|
8913
|
+
},
|
|
8914
|
+
/**
|
|
8915
|
+
* Apply the KNOWLEDGE command to the `pipelineJson`
|
|
8916
|
+
*
|
|
8917
|
+
* Note: `$` is used to indicate that this function mutates given `pipelineJson`
|
|
8918
|
+
*/
|
|
8919
|
+
$applyToPipelineJson(command, $pipelineJson) {
|
|
8920
|
+
const { knowledgeSourceContent } = command;
|
|
8921
|
+
$pipelineJson.knowledgeSources.push({
|
|
8922
|
+
name: knowledgeSourceContentToName(knowledgeSourceContent),
|
|
8923
|
+
knowledgeSourceContent,
|
|
8924
|
+
});
|
|
8925
|
+
},
|
|
8926
|
+
/**
|
|
8927
|
+
* Converts the KNOWLEDGE command back to string
|
|
8928
|
+
*
|
|
8929
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
8930
|
+
*/
|
|
8931
|
+
stringify(command) {
|
|
8932
|
+
return `---`; // <- TODO: [🛋] Implement
|
|
8933
|
+
},
|
|
8934
|
+
/**
|
|
8935
|
+
* Reads the KNOWLEDGE command from the `PipelineJson`
|
|
8936
|
+
*
|
|
8937
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
8938
|
+
*/
|
|
8939
|
+
takeFromPipelineJson(pipelineJson) {
|
|
8940
|
+
throw new NotYetImplementedError(`[🛋] Not implemented yet`); // <- TODO: [🛋] Implement
|
|
8941
|
+
},
|
|
8942
|
+
};
|
|
8943
|
+
// Note: [⛱] There are two types of KNOWLEDGE commands *...(read more in [⛱])*
|
|
8944
|
+
|
|
8945
|
+
/**
|
|
8946
|
+
* Constant for model variants.
|
|
8947
|
+
*
|
|
8948
|
+
* @see {@link ModelVariant}
|
|
8949
|
+
*
|
|
8950
|
+
* @public exported from `@promptbook/core`
|
|
8951
|
+
*/
|
|
8952
|
+
const MODEL_VARIANTS = [
|
|
8953
|
+
'COMPLETION',
|
|
8954
|
+
'CHAT',
|
|
8955
|
+
'IMAGE_GENERATION',
|
|
8956
|
+
'EMBEDDING' /* <- TODO [🏳] */ /* <- [🤖] */,
|
|
8957
|
+
];
|
|
8958
|
+
|
|
8959
|
+
/**
|
|
8960
|
+
* Parses the model command
|
|
8961
|
+
*
|
|
8962
|
+
* @see `documentationUrl` for more details
|
|
8963
|
+
* @deprecated Option to manually set the model requirements is not recommended to use, use `PERSONA` instead
|
|
8964
|
+
*
|
|
8965
|
+
* @public exported from `@promptbook/editable`
|
|
8966
|
+
*/
|
|
8967
|
+
const modelCommandParser = {
|
|
8968
|
+
/**
|
|
8969
|
+
* Name of the command
|
|
8970
|
+
*/
|
|
8971
|
+
name: 'MODEL',
|
|
8972
|
+
/**
|
|
8973
|
+
* BOILERPLATE command can be used in:
|
|
8974
|
+
*/
|
|
8975
|
+
isUsedInPipelineHead: true,
|
|
8976
|
+
isUsedInPipelineTask: true,
|
|
8977
|
+
/**
|
|
8978
|
+
* Description of the MODEL command
|
|
8979
|
+
*/
|
|
8980
|
+
description: `Tells which \`modelRequirements\` (for example which model) to use for the prompt task execution`,
|
|
8981
|
+
/**
|
|
8982
|
+
* Link to documentation
|
|
8983
|
+
*/
|
|
8984
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/67',
|
|
8985
|
+
/**
|
|
8986
|
+
* Example usages of the MODEL command
|
|
8987
|
+
*/
|
|
8988
|
+
examples: ['MODEL VARIANT Chat', 'MODEL NAME `gpt-4`'],
|
|
8989
|
+
/**
|
|
8990
|
+
* Parses the MODEL command
|
|
8991
|
+
*/
|
|
8992
|
+
parse(input) {
|
|
8993
|
+
const { args, normalized } = input;
|
|
8994
|
+
const availableVariantsMessage = _spaceTrim.spaceTrim((block) => `
|
|
8995
|
+
Available variants are:
|
|
8996
|
+
${block(MODEL_VARIANTS.map((variantName) => `- ${variantName}${variantName !== 'EMBEDDING' ? '' : ' (Not available in pipeline)'}`).join('\n'))}
|
|
8997
|
+
`);
|
|
8998
|
+
// TODO: Make this more elegant and dynamically
|
|
8999
|
+
if (normalized.startsWith('MODEL_VARIANT')) {
|
|
9000
|
+
if (normalized === 'MODEL_VARIANT_CHAT') {
|
|
9001
|
+
return {
|
|
9002
|
+
type: 'MODEL',
|
|
9230
9003
|
key: 'modelVariant',
|
|
9231
9004
|
value: 'CHAT',
|
|
9232
9005
|
};
|
|
@@ -9703,69 +9476,296 @@
|
|
|
9703
9476
|
};
|
|
9704
9477
|
|
|
9705
9478
|
/**
|
|
9706
|
-
*
|
|
9479
|
+
* All available task types
|
|
9480
|
+
*
|
|
9481
|
+
* There is is distinction between task types and section types
|
|
9482
|
+
* - Every section in markdown has its SectionType
|
|
9483
|
+
* - Some sections are tasks but other can be non-task sections
|
|
9484
|
+
*
|
|
9485
|
+
* @public exported from `@promptbook/core`
|
|
9486
|
+
*/
|
|
9487
|
+
const TaskTypes = [
|
|
9488
|
+
'PROMPT',
|
|
9489
|
+
'SIMPLE',
|
|
9490
|
+
'SCRIPT',
|
|
9491
|
+
'DIALOG',
|
|
9492
|
+
// <- [🅱]
|
|
9493
|
+
];
|
|
9494
|
+
|
|
9495
|
+
/**
|
|
9496
|
+
* All available sections which are not tasks
|
|
9497
|
+
*
|
|
9498
|
+
* @public exported from `@promptbook/core`
|
|
9499
|
+
*/
|
|
9500
|
+
const NonTaskSectionTypes = ['EXAMPLE', 'KNOWLEDGE', 'INSTRUMENT', 'ACTION'];
|
|
9501
|
+
/**
|
|
9502
|
+
* All available section types
|
|
9503
|
+
*
|
|
9504
|
+
* There is is distinction between task types and section types
|
|
9505
|
+
* - Every section in markdown has its SectionType
|
|
9506
|
+
* - Some sections are tasks but other can be non-task sections
|
|
9507
|
+
*
|
|
9508
|
+
* @public exported from `@promptbook/core`
|
|
9509
|
+
*/
|
|
9510
|
+
const SectionTypes = [
|
|
9511
|
+
...TaskTypes.map((TaskType) => `${TaskType}_TASK`),
|
|
9512
|
+
...NonTaskSectionTypes,
|
|
9513
|
+
];
|
|
9514
|
+
|
|
9515
|
+
/**
|
|
9516
|
+
* Parses the section command
|
|
9707
9517
|
*
|
|
9708
9518
|
* @see `documentationUrl` for more details
|
|
9709
9519
|
*
|
|
9710
9520
|
* @public exported from `@promptbook/editable`
|
|
9711
9521
|
*/
|
|
9712
|
-
const
|
|
9522
|
+
const sectionCommandParser = {
|
|
9713
9523
|
/**
|
|
9714
9524
|
* Name of the command
|
|
9715
9525
|
*/
|
|
9716
|
-
name: '
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
aliasNames: [
|
|
9721
|
-
|
|
9526
|
+
name: 'SECTION',
|
|
9527
|
+
/**
|
|
9528
|
+
* Aliases for the SECTION command
|
|
9529
|
+
*/
|
|
9530
|
+
aliasNames: [
|
|
9531
|
+
'PROMPT',
|
|
9532
|
+
'SIMPLE',
|
|
9533
|
+
'SCRIPT',
|
|
9534
|
+
'DIALOG',
|
|
9535
|
+
'SAMPLE',
|
|
9536
|
+
'EXAMPLE',
|
|
9537
|
+
'KNOWLEDGE',
|
|
9538
|
+
'INSTRUMENT',
|
|
9539
|
+
'ACTION', // <- Note: [⛱]
|
|
9540
|
+
],
|
|
9541
|
+
/**
|
|
9542
|
+
* Aliases for the SECTION command
|
|
9543
|
+
*/
|
|
9544
|
+
deprecatedNames: ['TEMPLATE', 'BLOCK', 'EXECUTE'],
|
|
9722
9545
|
/**
|
|
9723
9546
|
* BOILERPLATE command can be used in:
|
|
9724
9547
|
*/
|
|
9725
|
-
isUsedInPipelineHead:
|
|
9726
|
-
isUsedInPipelineTask:
|
|
9548
|
+
isUsedInPipelineHead: false,
|
|
9549
|
+
isUsedInPipelineTask: true,
|
|
9727
9550
|
/**
|
|
9728
|
-
* Description of the
|
|
9551
|
+
* Description of the SECTION command
|
|
9729
9552
|
*/
|
|
9730
|
-
description: `
|
|
9553
|
+
description: `Defines the purpose of the markdown section - if its a task and which type or something else`,
|
|
9731
9554
|
/**
|
|
9732
9555
|
* Link to documentation
|
|
9733
9556
|
*/
|
|
9734
|
-
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/
|
|
9557
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/64',
|
|
9735
9558
|
/**
|
|
9736
|
-
* Example usages of the
|
|
9559
|
+
* Example usages of the SECTION command
|
|
9737
9560
|
*/
|
|
9738
9561
|
examples: [
|
|
9739
|
-
|
|
9740
|
-
'
|
|
9741
|
-
'
|
|
9562
|
+
// Short form:
|
|
9563
|
+
'PROMPT',
|
|
9564
|
+
'SIMPLE',
|
|
9565
|
+
'SCRIPT',
|
|
9566
|
+
'DIALOG',
|
|
9567
|
+
// <- [🅱]
|
|
9568
|
+
'EXAMPLE',
|
|
9569
|
+
'KNOWLEDGE',
|
|
9570
|
+
'INSTRUMENT',
|
|
9571
|
+
'ACTION',
|
|
9572
|
+
// -----------------
|
|
9573
|
+
// Recommended (reversed) form:
|
|
9574
|
+
'PROMPT SECTION',
|
|
9575
|
+
'SIMPLE SECTION',
|
|
9576
|
+
'SCRIPT SECTION',
|
|
9577
|
+
'DIALOG SECTION',
|
|
9578
|
+
// <- [🅱]
|
|
9579
|
+
'EXAMPLE SECTION',
|
|
9580
|
+
'KNOWLEDGE SECTION',
|
|
9581
|
+
'INSTRUMENT SECTION',
|
|
9582
|
+
'ACTION SECTION',
|
|
9583
|
+
// -----------------
|
|
9584
|
+
// Standard form:
|
|
9585
|
+
'SECTION PROMPT',
|
|
9586
|
+
'SECTION SIMPLE',
|
|
9587
|
+
'SECTION SCRIPT',
|
|
9588
|
+
'SECTION DIALOG',
|
|
9589
|
+
// <- [🅱]
|
|
9590
|
+
'SECTION EXAMPLE',
|
|
9591
|
+
'SECTION KNOWLEDGE',
|
|
9592
|
+
'SECTION INSTRUMENT',
|
|
9593
|
+
'SECTION ACTION',
|
|
9742
9594
|
],
|
|
9595
|
+
// TODO: [♓️] order: -10 /* <- Note: Putting before other commands */
|
|
9743
9596
|
/**
|
|
9744
|
-
* Parses the
|
|
9597
|
+
* Parses the SECTION command
|
|
9745
9598
|
*/
|
|
9746
9599
|
parse(input) {
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
}
|
|
9756
|
-
if (args.length > 0) {
|
|
9757
|
-
throw new ParseError(`Can not have more than one pipeline URL`);
|
|
9758
|
-
}
|
|
9759
|
-
/*
|
|
9760
|
-
TODO: [🐠 Maybe more info from `isValidPipelineUrl`:
|
|
9761
|
-
if (pipelineUrl.protocol !== 'https:') {
|
|
9762
|
-
throw new ParseError(`Protocol must be HTTPS`);
|
|
9763
|
-
}
|
|
9600
|
+
let { normalized } = input;
|
|
9601
|
+
normalized = normalized.split('SAMPLE').join('EXAMPLE');
|
|
9602
|
+
normalized = normalized.split('EXECUTE_').join('');
|
|
9603
|
+
normalized = normalized.split('DIALOGUE').join('DIALOG');
|
|
9604
|
+
const taskTypes = SectionTypes.filter((sectionType) => normalized.includes(sectionType.split('_TASK').join('')));
|
|
9605
|
+
if (taskTypes.length !== 1) {
|
|
9606
|
+
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
9607
|
+
Unknown section type "${normalized}"
|
|
9764
9608
|
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9609
|
+
Supported section types are:
|
|
9610
|
+
${block(SectionTypes.join(', '))}
|
|
9611
|
+
`));
|
|
9612
|
+
}
|
|
9613
|
+
const taskType = taskTypes[0];
|
|
9614
|
+
return {
|
|
9615
|
+
type: 'SECTION',
|
|
9616
|
+
taskType,
|
|
9617
|
+
};
|
|
9618
|
+
},
|
|
9619
|
+
/**
|
|
9620
|
+
* Apply the SECTION command to the `pipelineJson`
|
|
9621
|
+
*
|
|
9622
|
+
* Note: `$` is used to indicate that this function mutates given `taskJson`
|
|
9623
|
+
*/
|
|
9624
|
+
$applyToTaskJson(command, $taskJson, $pipelineJson) {
|
|
9625
|
+
if ($taskJson.isSectionTypeSet === true) {
|
|
9626
|
+
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
9627
|
+
Section type is already defined in the section.
|
|
9628
|
+
It can be defined only once.
|
|
9629
|
+
`));
|
|
9630
|
+
}
|
|
9631
|
+
$taskJson.isSectionTypeSet = true;
|
|
9632
|
+
// TODO: [🍧][💩] Rearrange better - but at bottom and unwrap from function
|
|
9633
|
+
const expectResultingParameterName = () => {
|
|
9634
|
+
if ($taskJson.resultingParameterName) {
|
|
9635
|
+
return;
|
|
9636
|
+
}
|
|
9637
|
+
throw new ParseError(`Task section and example section must end with return statement -> {parameterName}`);
|
|
9638
|
+
};
|
|
9639
|
+
if ($taskJson.content === undefined) {
|
|
9640
|
+
throw new UnexpectedError(`Content is missing in the taskJson - probably commands are applied in wrong order`);
|
|
9641
|
+
}
|
|
9642
|
+
if (command.taskType === 'EXAMPLE') {
|
|
9643
|
+
expectResultingParameterName();
|
|
9644
|
+
const parameter = $pipelineJson.parameters.find((param) => param.name === $taskJson.resultingParameterName);
|
|
9645
|
+
if (parameter === undefined) {
|
|
9646
|
+
// TODO: !!6 Change to logic error for higher level abstraction of chatbot to work
|
|
9647
|
+
throw new ParseError(`Parameter \`{${$taskJson.resultingParameterName}}\` is not defined so can not define example value of it`);
|
|
9648
|
+
}
|
|
9649
|
+
parameter.exampleValues = parameter.exampleValues || [];
|
|
9650
|
+
parameter.exampleValues.push($taskJson.content);
|
|
9651
|
+
$taskJson.isTask = false;
|
|
9652
|
+
return;
|
|
9653
|
+
}
|
|
9654
|
+
if (command.taskType === 'KNOWLEDGE') {
|
|
9655
|
+
knowledgeCommandParser.$applyToPipelineJson({
|
|
9656
|
+
type: 'KNOWLEDGE',
|
|
9657
|
+
knowledgeSourceContent: $taskJson.content, // <- TODO: [🐝][main] !!3 Work with KNOWLEDGE which not referring to the source file or website, but its content itself
|
|
9658
|
+
}, $pipelineJson);
|
|
9659
|
+
$taskJson.isTask = false;
|
|
9660
|
+
return;
|
|
9661
|
+
}
|
|
9662
|
+
if (command.taskType === 'ACTION') {
|
|
9663
|
+
console.error(new NotYetImplementedError('Actions are not implemented yet'));
|
|
9664
|
+
$taskJson.isTask = false;
|
|
9665
|
+
return;
|
|
9666
|
+
}
|
|
9667
|
+
if (command.taskType === 'INSTRUMENT') {
|
|
9668
|
+
console.error(new NotYetImplementedError('Instruments are not implemented yet'));
|
|
9669
|
+
$taskJson.isTask = false;
|
|
9670
|
+
return;
|
|
9671
|
+
}
|
|
9672
|
+
expectResultingParameterName();
|
|
9673
|
+
$taskJson.taskType = command.taskType;
|
|
9674
|
+
$taskJson.isTask = true;
|
|
9675
|
+
},
|
|
9676
|
+
/**
|
|
9677
|
+
* Converts the SECTION command back to string
|
|
9678
|
+
*
|
|
9679
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
9680
|
+
*/
|
|
9681
|
+
stringify(command) {
|
|
9682
|
+
return `---`; // <- TODO: [🛋] Implement
|
|
9683
|
+
},
|
|
9684
|
+
/**
|
|
9685
|
+
* Reads the SECTION command from the `TaskJson`
|
|
9686
|
+
*
|
|
9687
|
+
* Note: This is used in `pipelineJsonToString` utility
|
|
9688
|
+
*/
|
|
9689
|
+
takeFromTaskJson($taskJson) {
|
|
9690
|
+
throw new NotYetImplementedError(`[🛋] Not implemented yet`); // <- TODO: [🛋] Implement
|
|
9691
|
+
},
|
|
9692
|
+
};
|
|
9693
|
+
/**
|
|
9694
|
+
* Note: [⛱] There are two types of KNOWLEDGE, ACTION and INSTRUMENT commands:
|
|
9695
|
+
* 1) There are commands `KNOWLEDGE`, `ACTION` and `INSTRUMENT` used in the pipeline head, they just define the knowledge, action or instrument as single line after the command
|
|
9696
|
+
* - KNOWLEDGE Look at https://en.wikipedia.org/wiki/Artificial_intelligence
|
|
9697
|
+
* 2) `KNOWLEDGE SECTION` which has short form `KNOWLEDGE` is used in the sectiom, does not refer the line itself, but the content of the section block
|
|
9698
|
+
* - KNOWLEDGE SECTION
|
|
9699
|
+
*
|
|
9700
|
+
* ```
|
|
9701
|
+
* Look at https://en.wikipedia.org/wiki/Artificial_intelligence
|
|
9702
|
+
* ```
|
|
9703
|
+
*/
|
|
9704
|
+
|
|
9705
|
+
/**
|
|
9706
|
+
* Parses the url command
|
|
9707
|
+
*
|
|
9708
|
+
* @see `documentationUrl` for more details
|
|
9709
|
+
*
|
|
9710
|
+
* @public exported from `@promptbook/editable`
|
|
9711
|
+
*/
|
|
9712
|
+
const urlCommandParser = {
|
|
9713
|
+
/**
|
|
9714
|
+
* Name of the command
|
|
9715
|
+
*/
|
|
9716
|
+
name: 'URL',
|
|
9717
|
+
aliasNames: ['PIPELINE_URL'],
|
|
9718
|
+
/*
|
|
9719
|
+
Note: [🛵] No need for this alias name because it is already preprocessed
|
|
9720
|
+
aliasNames: ['HTTPS'],
|
|
9721
|
+
*/
|
|
9722
|
+
/**
|
|
9723
|
+
* BOILERPLATE command can be used in:
|
|
9724
|
+
*/
|
|
9725
|
+
isUsedInPipelineHead: true,
|
|
9726
|
+
isUsedInPipelineTask: false,
|
|
9727
|
+
/**
|
|
9728
|
+
* Description of the URL command
|
|
9729
|
+
*/
|
|
9730
|
+
description: `Declares unique URL for the pipeline`,
|
|
9731
|
+
/**
|
|
9732
|
+
* Link to documentation
|
|
9733
|
+
*/
|
|
9734
|
+
documentationUrl: 'https://github.com/webgptorg/promptbook/discussions/70',
|
|
9735
|
+
/**
|
|
9736
|
+
* Example usages of the URL command
|
|
9737
|
+
*/
|
|
9738
|
+
examples: [
|
|
9739
|
+
'PIPELINE URL https://promptbook.studio/library/write-cv.book',
|
|
9740
|
+
'URL https://promptbook.studio/library/write-cv.book',
|
|
9741
|
+
'https://promptbook.studio/library/write-cv.book',
|
|
9742
|
+
],
|
|
9743
|
+
/**
|
|
9744
|
+
* Parses the URL command
|
|
9745
|
+
*/
|
|
9746
|
+
parse(input) {
|
|
9747
|
+
const { args } = input;
|
|
9748
|
+
const pipelineUrl = args.pop();
|
|
9749
|
+
if (pipelineUrl === undefined) {
|
|
9750
|
+
throw new ParseError(`URL is required`);
|
|
9751
|
+
}
|
|
9752
|
+
// TODO: [🧠][🚲] This should be maybe tested as logic not parse
|
|
9753
|
+
if (!isValidPipelineUrl(pipelineUrl)) {
|
|
9754
|
+
throw new ParseError(`Invalid pipeline URL "${pipelineUrl}"`);
|
|
9755
|
+
}
|
|
9756
|
+
if (args.length > 0) {
|
|
9757
|
+
throw new ParseError(`Can not have more than one pipeline URL`);
|
|
9758
|
+
}
|
|
9759
|
+
/*
|
|
9760
|
+
TODO: [🐠 Maybe more info from `isValidPipelineUrl`:
|
|
9761
|
+
if (pipelineUrl.protocol !== 'https:') {
|
|
9762
|
+
throw new ParseError(`Protocol must be HTTPS`);
|
|
9763
|
+
}
|
|
9764
|
+
|
|
9765
|
+
if (pipelineUrl.hash !== '') {
|
|
9766
|
+
throw new ParseError(
|
|
9767
|
+
spaceTrim(
|
|
9768
|
+
`
|
|
9769
9769
|
URL must not contain hash
|
|
9770
9770
|
Hash is used for identification of the section of the pipeline
|
|
9771
9771
|
`,
|
|
@@ -10156,85 +10156,321 @@
|
|
|
10156
10156
|
}
|
|
10157
10157
|
|
|
10158
10158
|
/**
|
|
10159
|
-
*
|
|
10159
|
+
* Utility function to extract all list items from markdown
|
|
10160
10160
|
*
|
|
10161
|
-
*
|
|
10162
|
-
*
|
|
10161
|
+
* Note: It works with both ul and ol
|
|
10162
|
+
* Note: It omits list items in code blocks
|
|
10163
|
+
* Note: It flattens nested lists
|
|
10164
|
+
* Note: It can not work with html syntax and comments
|
|
10163
10165
|
*
|
|
10164
|
-
* @
|
|
10166
|
+
* @param markdown any valid markdown
|
|
10167
|
+
* @returns An array of strings, each representing an individual list item found in the markdown
|
|
10168
|
+
*
|
|
10169
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
10165
10170
|
*/
|
|
10166
|
-
function
|
|
10167
|
-
const
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
pipelineInterface.inputParameters.push(deepClone(parameter));
|
|
10171
|
+
function extractAllListItemsFromMarkdown(markdown) {
|
|
10172
|
+
const lines = markdown.split(/\r?\n/);
|
|
10173
|
+
const listItems = [];
|
|
10174
|
+
let isInCodeBlock = false;
|
|
10175
|
+
for (const line of lines) {
|
|
10176
|
+
const trimmedLine = line.trim();
|
|
10177
|
+
if (trimmedLine.startsWith('```')) {
|
|
10178
|
+
isInCodeBlock = !isInCodeBlock;
|
|
10175
10179
|
}
|
|
10176
|
-
if (
|
|
10177
|
-
|
|
10180
|
+
if (!isInCodeBlock && (trimmedLine.startsWith('-') || trimmedLine.match(/^\d+\./))) {
|
|
10181
|
+
const listItem = trimmedLine.replace(/^-|\d+\./, '').trim();
|
|
10182
|
+
listItems.push(listItem);
|
|
10178
10183
|
}
|
|
10179
10184
|
}
|
|
10180
|
-
|
|
10181
|
-
pipelineInterface[key].sort(({ name: name1 }, { name: name2 }) => name1.localeCompare(name2));
|
|
10182
|
-
}
|
|
10183
|
-
return exportJson({
|
|
10184
|
-
name: `pipelineInterface`,
|
|
10185
|
-
message: `Result of \`getPipelineInterface\``,
|
|
10186
|
-
order: ['inputParameters', 'outputParameters'],
|
|
10187
|
-
value: pipelineInterface,
|
|
10188
|
-
});
|
|
10185
|
+
return listItems;
|
|
10189
10186
|
}
|
|
10190
10187
|
|
|
10191
10188
|
/**
|
|
10192
|
-
*
|
|
10193
|
-
*
|
|
10194
|
-
* @deprecated https://github.com/webgptorg/promptbook/pull/186
|
|
10195
|
-
* @see https://github.com/webgptorg/promptbook/discussions/171
|
|
10189
|
+
* Builds a short file/url identification block for parse errors.
|
|
10196
10190
|
*
|
|
10197
|
-
* @
|
|
10191
|
+
* @private internal utility of `parsePipeline`
|
|
10198
10192
|
*/
|
|
10199
|
-
function
|
|
10200
|
-
|
|
10201
|
-
|
|
10202
|
-
|
|
10203
|
-
|
|
10204
|
-
return false;
|
|
10205
|
-
}
|
|
10206
|
-
for (const parameter of parameters1) {
|
|
10207
|
-
const matchingParameter = parameters2.find(({ name }) => name === parameter.name);
|
|
10208
|
-
if (!matchingParameter) {
|
|
10209
|
-
return false;
|
|
10210
|
-
}
|
|
10211
|
-
// Note: Do not compare description, it is not relevant for compatibility
|
|
10212
|
-
if (matchingParameter.isInput !== parameter.isInput) {
|
|
10213
|
-
return false;
|
|
10214
|
-
}
|
|
10215
|
-
if (matchingParameter.isOutput !== parameter.isOutput) {
|
|
10216
|
-
return false;
|
|
10217
|
-
}
|
|
10218
|
-
}
|
|
10193
|
+
function getPipelineIdentification($pipelineJson) {
|
|
10194
|
+
// Note: This is a 😐 implementation of [🚞]
|
|
10195
|
+
const pipelineIdentificationParts = [];
|
|
10196
|
+
if ($pipelineJson.sourceFile !== undefined) {
|
|
10197
|
+
pipelineIdentificationParts.push(`File: ${$pipelineJson.sourceFile}`);
|
|
10219
10198
|
}
|
|
10220
|
-
|
|
10199
|
+
if ($pipelineJson.pipelineUrl !== undefined) {
|
|
10200
|
+
pipelineIdentificationParts.push(`Url: ${$pipelineJson.pipelineUrl}`);
|
|
10201
|
+
}
|
|
10202
|
+
return pipelineIdentificationParts.join('\n');
|
|
10221
10203
|
}
|
|
10222
10204
|
|
|
10223
10205
|
/**
|
|
10224
|
-
*
|
|
10225
|
-
*
|
|
10226
|
-
* @deprecated https://github.com/webgptorg/promptbook/pull/186
|
|
10227
|
-
* @see https://github.com/webgptorg/promptbook/discussions/171
|
|
10228
|
-
* @returns `true` if the pipeline implements the interface, `false` otherwise.
|
|
10206
|
+
* Merges one parameter declaration into the mutable pipeline parameter list.
|
|
10229
10207
|
*
|
|
10230
|
-
* @
|
|
10208
|
+
* @private internal utility of `parsePipeline`
|
|
10231
10209
|
*/
|
|
10232
|
-
function
|
|
10233
|
-
const {
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10210
|
+
function defineParameter($pipelineJson, parameterCommand) {
|
|
10211
|
+
const { parameterName, parameterDescription, isInput, isOutput } = parameterCommand;
|
|
10212
|
+
if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
10213
|
+
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10214
|
+
Parameter name {${parameterName}} is reserved and cannot be used as resulting parameter name
|
|
10215
|
+
|
|
10216
|
+
${block(getPipelineIdentification($pipelineJson))}
|
|
10217
|
+
`) /* <- TODO: [🚞] */);
|
|
10218
|
+
}
|
|
10219
|
+
const existingParameter = $pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
|
|
10220
|
+
if (existingParameter &&
|
|
10221
|
+
existingParameter.description &&
|
|
10222
|
+
existingParameter.description !== parameterDescription &&
|
|
10223
|
+
parameterDescription) {
|
|
10224
|
+
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10225
|
+
Parameter \`{${parameterName}}\` is defined multiple times with different description:
|
|
10226
|
+
|
|
10227
|
+
${block(getPipelineIdentification($pipelineJson))}
|
|
10228
|
+
|
|
10229
|
+
First definition:
|
|
10230
|
+
${block(existingParameter.description || '[undefined]')}
|
|
10231
|
+
|
|
10232
|
+
Second definition:
|
|
10233
|
+
${block(parameterDescription || '[undefined]')}
|
|
10234
|
+
`));
|
|
10235
|
+
}
|
|
10236
|
+
if (existingParameter) {
|
|
10237
|
+
if (parameterDescription) {
|
|
10238
|
+
existingParameter.description = parameterDescription;
|
|
10239
|
+
}
|
|
10240
|
+
existingParameter.isInput = existingParameter.isInput || isInput;
|
|
10241
|
+
existingParameter.isOutput = existingParameter.isOutput || isOutput;
|
|
10242
|
+
return;
|
|
10243
|
+
}
|
|
10244
|
+
$pipelineJson.parameters.push({
|
|
10245
|
+
name: parameterName,
|
|
10246
|
+
description: parameterDescription || undefined,
|
|
10247
|
+
isInput,
|
|
10248
|
+
isOutput,
|
|
10249
|
+
});
|
|
10250
|
+
}
|
|
10251
|
+
|
|
10252
|
+
/**
|
|
10253
|
+
* Removes fenced code blocks when deriving human-readable section descriptions.
|
|
10254
|
+
*
|
|
10255
|
+
* @private internal utility of `extractPipelineDescription`
|
|
10256
|
+
*/
|
|
10257
|
+
const DESCRIPTION_CODE_BLOCK_REGEXP = /^```.*^```/gms;
|
|
10258
|
+
/**
|
|
10259
|
+
* Removes blockquote lines when deriving human-readable section descriptions.
|
|
10260
|
+
*
|
|
10261
|
+
* @private internal utility of `extractPipelineDescription`
|
|
10262
|
+
*/
|
|
10263
|
+
const DESCRIPTION_BLOCKQUOTE_REGEXP = /^>.*$/gm;
|
|
10264
|
+
/**
|
|
10265
|
+
* Removes list items and return statements when deriving human-readable section descriptions.
|
|
10266
|
+
*
|
|
10267
|
+
* @private internal utility of `extractPipelineDescription`
|
|
10268
|
+
*/
|
|
10269
|
+
const DESCRIPTION_LIST_ITEM_REGEXP = /^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm;
|
|
10270
|
+
/**
|
|
10271
|
+
* Extracts the plain-text description from a head or task section body.
|
|
10272
|
+
*
|
|
10273
|
+
* @private internal utility of `parsePipeline`
|
|
10274
|
+
*/
|
|
10275
|
+
function extractPipelineDescription(sectionContent) {
|
|
10276
|
+
let description = sectionContent;
|
|
10277
|
+
description = description.split(DESCRIPTION_CODE_BLOCK_REGEXP).join('');
|
|
10278
|
+
description = description.split(DESCRIPTION_BLOCKQUOTE_REGEXP).join('');
|
|
10279
|
+
description = description.split(DESCRIPTION_LIST_ITEM_REGEXP).join('');
|
|
10280
|
+
description = _spaceTrim.spaceTrim(description);
|
|
10281
|
+
if (description === '') {
|
|
10282
|
+
return undefined;
|
|
10283
|
+
}
|
|
10284
|
+
return description;
|
|
10285
|
+
}
|
|
10286
|
+
|
|
10287
|
+
/**
|
|
10288
|
+
* Applies the pipeline head title, description, and head-level commands.
|
|
10289
|
+
*
|
|
10290
|
+
* @private internal utility of `parsePipeline`
|
|
10291
|
+
*/
|
|
10292
|
+
function applyPipelineHead(pipelineHead, $pipelineJson) {
|
|
10293
|
+
$pipelineJson.title = pipelineHead.title;
|
|
10294
|
+
$pipelineJson.description = extractPipelineDescription(pipelineHead.content);
|
|
10295
|
+
for (const listItem of extractAllListItemsFromMarkdown(pipelineHead.content)) {
|
|
10296
|
+
applyPipelineHeadCommand(listItem, $pipelineJson);
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
/**
|
|
10300
|
+
* Parses and applies one command declared in the pipeline head.
|
|
10301
|
+
*
|
|
10302
|
+
* @private internal utility of `applyPipelineHead`
|
|
10303
|
+
*/
|
|
10304
|
+
function applyPipelineHeadCommand(listItem, $pipelineJson) {
|
|
10305
|
+
const command = parseCommand(listItem, 'PIPELINE_HEAD');
|
|
10306
|
+
const commandParser = getParserForCommand(command);
|
|
10307
|
+
if (commandParser.isUsedInPipelineHead !== true /* <- Note: [🦦][4] */) {
|
|
10308
|
+
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10309
|
+
Command \`${command.type}\` is not allowed in the head of the pipeline ONLY at the pipeline task
|
|
10310
|
+
|
|
10311
|
+
${block(getPipelineIdentification($pipelineJson))}
|
|
10312
|
+
`)); // <- TODO: [🚞]
|
|
10313
|
+
}
|
|
10314
|
+
try {
|
|
10315
|
+
commandParser.$applyToPipelineJson(command, $pipelineJson);
|
|
10316
|
+
// <- Note: [🦦] Its strange that this assertion must be here, [🦦][4] should do this assertion implicitly
|
|
10317
|
+
}
|
|
10318
|
+
catch (error) {
|
|
10319
|
+
if (!(error instanceof ParseError)) {
|
|
10320
|
+
throw error;
|
|
10321
|
+
}
|
|
10322
|
+
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10323
|
+
Command ${command.type} failed to apply to the pipeline
|
|
10324
|
+
|
|
10325
|
+
The error:
|
|
10326
|
+
${block(error.message)}
|
|
10327
|
+
|
|
10328
|
+
Raw command:
|
|
10329
|
+
- ${listItem}
|
|
10330
|
+
|
|
10331
|
+
Usage of ${command.type}:
|
|
10332
|
+
${block(commandParser.examples.map((example) => `- ${example}`).join('\n'))}
|
|
10333
|
+
|
|
10334
|
+
${block(getPipelineIdentification($pipelineJson))}
|
|
10335
|
+
`)); // <- TODO: [🚞]
|
|
10336
|
+
}
|
|
10337
|
+
if (command.type === 'PARAMETER') {
|
|
10338
|
+
defineParameter($pipelineJson, command);
|
|
10339
|
+
// <- Note: [🍣]
|
|
10340
|
+
}
|
|
10341
|
+
}
|
|
10342
|
+
|
|
10343
|
+
/**
|
|
10344
|
+
* Creates the mutable pipeline JSON structure used throughout parsing.
|
|
10345
|
+
*
|
|
10346
|
+
* @private internal utility of `parsePipeline`
|
|
10347
|
+
*/
|
|
10348
|
+
function createInitialPipelineJson(pipelineString) {
|
|
10349
|
+
return {
|
|
10350
|
+
title: DEFAULT_BOOK_TITLE,
|
|
10351
|
+
parameters: [],
|
|
10352
|
+
tasks: [],
|
|
10353
|
+
knowledgeSources: [],
|
|
10354
|
+
knowledgePieces: [],
|
|
10355
|
+
personas: [],
|
|
10356
|
+
preparations: [],
|
|
10357
|
+
sources: [
|
|
10358
|
+
{
|
|
10359
|
+
type: 'BOOK',
|
|
10360
|
+
path: null,
|
|
10361
|
+
// <- TODO: !!6 Pass here path of the file
|
|
10362
|
+
content: pipelineString,
|
|
10363
|
+
},
|
|
10364
|
+
],
|
|
10365
|
+
};
|
|
10366
|
+
}
|
|
10367
|
+
|
|
10368
|
+
/**
|
|
10369
|
+
* Creates stable unique task names for duplicate section titles.
|
|
10370
|
+
*
|
|
10371
|
+
* @private internal utility of `parsePipeline`
|
|
10372
|
+
*/
|
|
10373
|
+
function createUniqueSectionNameResolver(pipelineSections) {
|
|
10374
|
+
const sectionCounts = {};
|
|
10375
|
+
for (const pipelineSection of pipelineSections) {
|
|
10376
|
+
const sectionName = titleToName(pipelineSection.title);
|
|
10377
|
+
if (sectionCounts[sectionName] === undefined) {
|
|
10378
|
+
sectionCounts[sectionName] = { count: 0, currentIndex: 0 };
|
|
10379
|
+
}
|
|
10380
|
+
sectionCounts[sectionName].count++;
|
|
10381
|
+
}
|
|
10382
|
+
return (title) => {
|
|
10383
|
+
const sectionName = titleToName(title);
|
|
10384
|
+
const sectionCount = sectionCounts[sectionName];
|
|
10385
|
+
if (sectionCount.count === 1) {
|
|
10386
|
+
return sectionName;
|
|
10387
|
+
}
|
|
10388
|
+
const nameWithSuffix = `${sectionName}-${sectionCount.currentIndex}`;
|
|
10389
|
+
sectionCount.currentIndex++;
|
|
10390
|
+
return nameWithSuffix;
|
|
10391
|
+
};
|
|
10392
|
+
}
|
|
10393
|
+
|
|
10394
|
+
/**
|
|
10395
|
+
* Extracts the interface (input and output parameters) from a pipeline.
|
|
10396
|
+
*
|
|
10397
|
+
* @deprecated https://github.com/webgptorg/promptbook/pull/186
|
|
10398
|
+
* @see https://github.com/webgptorg/promptbook/discussions/171
|
|
10399
|
+
*
|
|
10400
|
+
* @public exported from `@promptbook/core`
|
|
10401
|
+
*/
|
|
10402
|
+
function getPipelineInterface(pipeline) {
|
|
10403
|
+
const pipelineInterface = {
|
|
10404
|
+
inputParameters: [],
|
|
10405
|
+
outputParameters: [],
|
|
10406
|
+
};
|
|
10407
|
+
for (const parameter of pipeline.parameters) {
|
|
10408
|
+
const { isInput, isOutput } = parameter;
|
|
10409
|
+
if (isInput) {
|
|
10410
|
+
pipelineInterface.inputParameters.push(deepClone(parameter));
|
|
10411
|
+
}
|
|
10412
|
+
if (isOutput) {
|
|
10413
|
+
pipelineInterface.outputParameters.push(deepClone(parameter));
|
|
10414
|
+
}
|
|
10415
|
+
}
|
|
10416
|
+
for (const key of ['inputParameters', 'outputParameters']) {
|
|
10417
|
+
pipelineInterface[key].sort(({ name: name1 }, { name: name2 }) => name1.localeCompare(name2));
|
|
10418
|
+
}
|
|
10419
|
+
return exportJson({
|
|
10420
|
+
name: `pipelineInterface`,
|
|
10421
|
+
message: `Result of \`getPipelineInterface\``,
|
|
10422
|
+
order: ['inputParameters', 'outputParameters'],
|
|
10423
|
+
value: pipelineInterface,
|
|
10424
|
+
});
|
|
10425
|
+
}
|
|
10426
|
+
|
|
10427
|
+
/**
|
|
10428
|
+
* Checks if two pipeline interfaces are structurally identical.
|
|
10429
|
+
*
|
|
10430
|
+
* @deprecated https://github.com/webgptorg/promptbook/pull/186
|
|
10431
|
+
* @see https://github.com/webgptorg/promptbook/discussions/171
|
|
10432
|
+
*
|
|
10433
|
+
* @public exported from `@promptbook/core`
|
|
10434
|
+
*/
|
|
10435
|
+
function isPipelineInterfacesEqual(pipelineInterface1, pipelineInterface2) {
|
|
10436
|
+
for (const whichParameters of ['inputParameters', 'outputParameters']) {
|
|
10437
|
+
const parameters1 = pipelineInterface1[whichParameters]; // <- Note: `isPipelineInterfacesEqual` is just temporary solution, no need to fix this
|
|
10438
|
+
const parameters2 = pipelineInterface2[whichParameters];
|
|
10439
|
+
if (parameters1.length !== parameters2.length) {
|
|
10440
|
+
return false;
|
|
10441
|
+
}
|
|
10442
|
+
for (const parameter of parameters1) {
|
|
10443
|
+
const matchingParameter = parameters2.find(({ name }) => name === parameter.name);
|
|
10444
|
+
if (!matchingParameter) {
|
|
10445
|
+
return false;
|
|
10446
|
+
}
|
|
10447
|
+
// Note: Do not compare description, it is not relevant for compatibility
|
|
10448
|
+
if (matchingParameter.isInput !== parameter.isInput) {
|
|
10449
|
+
return false;
|
|
10450
|
+
}
|
|
10451
|
+
if (matchingParameter.isOutput !== parameter.isOutput) {
|
|
10452
|
+
return false;
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
return true;
|
|
10457
|
+
}
|
|
10458
|
+
|
|
10459
|
+
/**
|
|
10460
|
+
* Checks if a given pipeline satisfies the requirements of a specified pipeline interface.
|
|
10461
|
+
*
|
|
10462
|
+
* @deprecated https://github.com/webgptorg/promptbook/pull/186
|
|
10463
|
+
* @see https://github.com/webgptorg/promptbook/discussions/171
|
|
10464
|
+
* @returns `true` if the pipeline implements the interface, `false` otherwise.
|
|
10465
|
+
*
|
|
10466
|
+
* @public exported from `@promptbook/core`
|
|
10467
|
+
*/
|
|
10468
|
+
function isPipelineImplementingInterface(options) {
|
|
10469
|
+
const { pipeline, pipelineInterface } = options;
|
|
10470
|
+
return isPipelineInterfacesEqual(getPipelineInterface(pipeline), pipelineInterface);
|
|
10471
|
+
}
|
|
10472
|
+
|
|
10473
|
+
/**
|
|
10238
10474
|
* Set formfactor based on the pipeline interface e
|
|
10239
10475
|
*
|
|
10240
10476
|
* @private
|
|
@@ -10416,197 +10652,125 @@
|
|
|
10416
10652
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
10417
10653
|
|
|
10418
10654
|
/**
|
|
10419
|
-
*
|
|
10655
|
+
* Applies postprocessing and exports the parsed pipeline JSON.
|
|
10420
10656
|
*
|
|
10421
|
-
* @private internal
|
|
10657
|
+
* @private internal utility of `parsePipeline`
|
|
10422
10658
|
*/
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10659
|
+
function finalizeParsedPipeline($pipelineJson) {
|
|
10660
|
+
applyImplicitParameterDirections($pipelineJson);
|
|
10661
|
+
removeUndefinedValuesFromPipeline($pipelineJson);
|
|
10662
|
+
applySyncHighLevelAbstractions($pipelineJson);
|
|
10663
|
+
ensurePipelineFormfactor($pipelineJson);
|
|
10664
|
+
return exportParsedPipelineJson($pipelineJson);
|
|
10665
|
+
}
|
|
10426
10666
|
/**
|
|
10427
|
-
*
|
|
10667
|
+
* Applies default INPUT/OUTPUT flags when the author did not specify them explicitly.
|
|
10428
10668
|
*
|
|
10429
|
-
* @
|
|
10669
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10430
10670
|
*/
|
|
10431
|
-
|
|
10671
|
+
function applyImplicitParameterDirections($pipelineJson) {
|
|
10672
|
+
markImplicitInputParameters($pipelineJson);
|
|
10673
|
+
markImplicitOutputParameters($pipelineJson);
|
|
10674
|
+
}
|
|
10432
10675
|
/**
|
|
10433
|
-
*
|
|
10434
|
-
*
|
|
10435
|
-
* Note: [🔂] This function is idempotent.
|
|
10676
|
+
* Marks non-result parameters as pipeline inputs when no input was declared.
|
|
10436
10677
|
*
|
|
10437
|
-
* @
|
|
10678
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10438
10679
|
*/
|
|
10439
|
-
function
|
|
10440
|
-
if (
|
|
10441
|
-
return
|
|
10680
|
+
function markImplicitInputParameters($pipelineJson) {
|
|
10681
|
+
if ($pipelineJson.parameters.some((parameter) => parameter.isInput)) {
|
|
10682
|
+
return;
|
|
10442
10683
|
}
|
|
10443
|
-
const
|
|
10444
|
-
|
|
10445
|
-
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
//
|
|
10449
|
-
|
|
10450
|
-
|
|
10451
|
-
if (line.trim() === '') {
|
|
10452
|
-
trailingEmptyLines++;
|
|
10453
|
-
}
|
|
10454
|
-
else {
|
|
10455
|
-
break;
|
|
10684
|
+
for (const parameter of $pipelineJson.parameters) {
|
|
10685
|
+
const isThisParameterResulting = $pipelineJson.tasks.some((task) => task.resultingParameterName === parameter.name);
|
|
10686
|
+
if (!isThisParameterResulting) {
|
|
10687
|
+
parameter.isInput = true;
|
|
10688
|
+
// <- TODO: [💔] Why this is making typescript error in vscode but not in cli
|
|
10689
|
+
// > Type 'true' is not assignable to type 'false'.ts(2322)
|
|
10690
|
+
// > (property) isInput: false
|
|
10691
|
+
// > The parameter is input of the pipeline The parameter is NOT input of the pipeline
|
|
10456
10692
|
}
|
|
10457
10693
|
}
|
|
10458
|
-
if (trailingEmptyLines >= PADDING_LINES) {
|
|
10459
|
-
return content;
|
|
10460
|
-
}
|
|
10461
|
-
const linesToAdd = PADDING_LINES - trailingEmptyLines;
|
|
10462
|
-
return (content + '\n'.repeat(linesToAdd));
|
|
10463
10694
|
}
|
|
10464
|
-
// TODO: [🧠] Maybe export
|
|
10465
|
-
|
|
10466
10695
|
/**
|
|
10467
|
-
*
|
|
10468
|
-
*
|
|
10469
|
-
* @param {string} content - The string to remove comments from.
|
|
10470
|
-
* @returns {string} The input string with all comments removed.
|
|
10471
|
-
*
|
|
10472
|
-
* @public exported from `@promptbook/markdown-utils`
|
|
10473
|
-
*/
|
|
10474
|
-
function removeMarkdownComments(content) {
|
|
10475
|
-
return _spaceTrim.spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
|
|
10476
|
-
}
|
|
10477
|
-
|
|
10478
|
-
/**
|
|
10479
|
-
* Utility to determine if a pipeline string is in flat format.
|
|
10480
|
-
* A flat pipeline is a simple text without proper structure (headers, blocks, etc).
|
|
10481
|
-
*
|
|
10482
|
-
* @public exported from `@promptbook/editable`
|
|
10483
|
-
*/
|
|
10484
|
-
function isFlatPipeline(pipelineString) {
|
|
10485
|
-
pipelineString = removeMarkdownComments(pipelineString);
|
|
10486
|
-
pipelineString = _spaceTrim.spaceTrim(pipelineString);
|
|
10487
|
-
const isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
|
|
10488
|
-
//const isLastLineReturnStatement = pipelineString.split(/\r?\n/).pop()!.split('`').join('').startsWith('->');
|
|
10489
|
-
const isBacktickBlockUsed = pipelineString.includes('```');
|
|
10490
|
-
const isQuoteBlocksUsed = /^>\s+/m.test(pipelineString);
|
|
10491
|
-
const isBlocksUsed = isBacktickBlockUsed || isQuoteBlocksUsed;
|
|
10492
|
-
// TODO: [🧉] Also (double)check
|
|
10493
|
-
// > const usedCommands
|
|
10494
|
-
// > const isBlocksUsed
|
|
10495
|
-
// > const returnStatementCount
|
|
10496
|
-
const isFlat = !isMarkdownBeginningWithHeadline && !isBlocksUsed; /* && isLastLineReturnStatement */
|
|
10497
|
-
return isFlat;
|
|
10498
|
-
}
|
|
10499
|
-
|
|
10500
|
-
/**
|
|
10501
|
-
* Converts a pipeline structure to its string representation.
|
|
10502
|
-
*
|
|
10503
|
-
* Transforms a flat, simple pipeline into a properly formatted pipeline string
|
|
10504
|
-
* with sections for title, prompt, and return statement.
|
|
10696
|
+
* Marks every non-input parameter as output when no output was declared.
|
|
10505
10697
|
*
|
|
10506
|
-
* @
|
|
10698
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10507
10699
|
*/
|
|
10508
|
-
function
|
|
10509
|
-
if (
|
|
10510
|
-
return
|
|
10511
|
-
}
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
// Note: Last line is return statement
|
|
10518
|
-
returnStatement = potentialReturnStatement;
|
|
10519
|
-
}
|
|
10520
|
-
else {
|
|
10521
|
-
// Note: Last line is not a return statement
|
|
10522
|
-
returnStatement = `-> {${DEFAULT_BOOK_OUTPUT_PARAMETER_NAME}}`;
|
|
10523
|
-
pipelineStringLines.push(potentialReturnStatement);
|
|
10524
|
-
}
|
|
10525
|
-
const prompt = _spaceTrim.spaceTrim(pipelineStringLines.join('\n'));
|
|
10526
|
-
let quotedPrompt;
|
|
10527
|
-
if (prompt.split(/\r?\n/).length <= 1) {
|
|
10528
|
-
quotedPrompt = `> ${prompt}`;
|
|
10529
|
-
}
|
|
10530
|
-
else {
|
|
10531
|
-
quotedPrompt = _spaceTrim.spaceTrim((block) => `
|
|
10532
|
-
\`\`\`
|
|
10533
|
-
${block(prompt.split('`').join('\\`'))}
|
|
10534
|
-
\`\`\`
|
|
10535
|
-
`);
|
|
10700
|
+
function markImplicitOutputParameters($pipelineJson) {
|
|
10701
|
+
if ($pipelineJson.parameters.some((parameter) => parameter.isOutput)) {
|
|
10702
|
+
return;
|
|
10703
|
+
}
|
|
10704
|
+
for (const parameter of $pipelineJson.parameters) {
|
|
10705
|
+
if (!parameter.isInput) {
|
|
10706
|
+
parameter.isOutput = true;
|
|
10707
|
+
// <- TODO: [💔]
|
|
10708
|
+
}
|
|
10536
10709
|
}
|
|
10537
|
-
pipelineString = validatePipelineString(_spaceTrim.spaceTrim((block) => `
|
|
10538
|
-
# ${DEFAULT_BOOK_TITLE}
|
|
10539
|
-
|
|
10540
|
-
## Prompt
|
|
10541
|
-
|
|
10542
|
-
${block(quotedPrompt)}
|
|
10543
|
-
|
|
10544
|
-
${returnStatement}
|
|
10545
|
-
`));
|
|
10546
|
-
// <- TODO: Maybe use book` notation
|
|
10547
|
-
return padBook(pipelineString);
|
|
10548
10710
|
}
|
|
10549
|
-
// TODO: Unit test
|
|
10550
|
-
|
|
10551
10711
|
/**
|
|
10552
|
-
*
|
|
10553
|
-
*
|
|
10554
|
-
* Note: It works with both ul and ol
|
|
10555
|
-
* Note: It omits list items in code blocks
|
|
10556
|
-
* Note: It flattens nested lists
|
|
10557
|
-
* Note: It can not work with html syntax and comments
|
|
10712
|
+
* Removes `undefined` properties from serialized tasks and parameters.
|
|
10558
10713
|
*
|
|
10559
|
-
* @
|
|
10560
|
-
|
|
10714
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10715
|
+
*/
|
|
10716
|
+
function removeUndefinedValuesFromPipeline($pipelineJson) {
|
|
10717
|
+
$pipelineJson.tasks.forEach(removeUndefinedProperties);
|
|
10718
|
+
$pipelineJson.parameters.forEach(removeUndefinedProperties);
|
|
10719
|
+
}
|
|
10720
|
+
/**
|
|
10721
|
+
* Deletes all own properties with `undefined` values from a mutable JSON entity.
|
|
10561
10722
|
*
|
|
10562
|
-
* @
|
|
10723
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10563
10724
|
*/
|
|
10564
|
-
function
|
|
10565
|
-
const
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
for (const line of lines) {
|
|
10569
|
-
const trimmedLine = line.trim();
|
|
10570
|
-
if (trimmedLine.startsWith('```')) {
|
|
10571
|
-
isInCodeBlock = !isInCodeBlock;
|
|
10572
|
-
}
|
|
10573
|
-
if (!isInCodeBlock && (trimmedLine.startsWith('-') || trimmedLine.match(/^\d+\./))) {
|
|
10574
|
-
const listItem = trimmedLine.replace(/^-|\d+\./, '').trim();
|
|
10575
|
-
listItems.push(listItem);
|
|
10725
|
+
function removeUndefinedProperties(entity) {
|
|
10726
|
+
for (const [key, value] of Object.entries(entity)) {
|
|
10727
|
+
if (value === undefined) {
|
|
10728
|
+
delete entity[key];
|
|
10576
10729
|
}
|
|
10577
10730
|
}
|
|
10578
|
-
return listItems;
|
|
10579
10731
|
}
|
|
10580
|
-
|
|
10581
10732
|
/**
|
|
10582
|
-
*
|
|
10583
|
-
*
|
|
10584
|
-
* - When there are multiple or no code blocks the function throws a `ParseError`
|
|
10585
|
-
*
|
|
10586
|
-
* Note: There are multiple similar functions:
|
|
10587
|
-
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
10588
|
-
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
10589
|
-
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
10590
|
-
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
10733
|
+
* Applies all sync-only high-level abstractions after parsing.
|
|
10591
10734
|
*
|
|
10592
|
-
* @
|
|
10593
|
-
|
|
10594
|
-
|
|
10735
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10736
|
+
*/
|
|
10737
|
+
function applySyncHighLevelAbstractions($pipelineJson) {
|
|
10738
|
+
for (const highLevelAbstraction of HIGH_LEVEL_ABSTRACTIONS.filter(({ type }) => type === 'SYNC')) {
|
|
10739
|
+
highLevelAbstraction.$applyToPipelineJson($pipelineJson);
|
|
10740
|
+
}
|
|
10741
|
+
}
|
|
10742
|
+
/**
|
|
10743
|
+
* Ensures parsed pipelines always have the default `GENERIC` formfactor.
|
|
10595
10744
|
*
|
|
10596
|
-
* @
|
|
10745
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10597
10746
|
*/
|
|
10598
|
-
function
|
|
10599
|
-
|
|
10600
|
-
if (
|
|
10601
|
-
|
|
10602
|
-
There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
|
|
10603
|
-
|
|
10604
|
-
${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
|
|
10605
|
-
`));
|
|
10747
|
+
function ensurePipelineFormfactor($pipelineJson) {
|
|
10748
|
+
// Note: [🔆] If formfactor is still not set, set it to 'GENERIC'
|
|
10749
|
+
if ($pipelineJson.formfactorName === undefined) {
|
|
10750
|
+
$pipelineJson.formfactorName = 'GENERIC';
|
|
10606
10751
|
}
|
|
10607
|
-
return codeBlocks[0];
|
|
10608
10752
|
}
|
|
10609
|
-
|
|
10753
|
+
/**
|
|
10754
|
+
* Finalizes ordering and exports the parsed pipeline JSON.
|
|
10755
|
+
*
|
|
10756
|
+
* @private internal utility of `finalizeParsedPipeline`
|
|
10757
|
+
*/
|
|
10758
|
+
function exportParsedPipelineJson($pipelineJson) {
|
|
10759
|
+
return exportJson({
|
|
10760
|
+
name: 'pipelineJson',
|
|
10761
|
+
message: `Result of \`parsePipeline\``,
|
|
10762
|
+
order: ORDER_OF_PIPELINE_JSON,
|
|
10763
|
+
value: {
|
|
10764
|
+
formfactorName: 'GENERIC',
|
|
10765
|
+
// <- Note: [🔆] Setting `formfactorName` is redundant to satisfy the typescript
|
|
10766
|
+
...$pipelineJson,
|
|
10767
|
+
},
|
|
10768
|
+
});
|
|
10769
|
+
}
|
|
10770
|
+
// TODO: Use spaceTrim more effectively
|
|
10771
|
+
// TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
10772
|
+
// TODO: [♈] Probably move expectations from tasks to parameters
|
|
10773
|
+
// TODO: [🍙] Make some standard order of json properties
|
|
10610
10774
|
|
|
10611
10775
|
/**
|
|
10612
10776
|
* Parses markdown section to title its level and content
|
|
@@ -10678,213 +10842,24 @@
|
|
|
10678
10842
|
currentType = 'MARKDOWN';
|
|
10679
10843
|
}
|
|
10680
10844
|
}
|
|
10681
|
-
else if (currentType === 'COMMENT') {
|
|
10682
|
-
buffer.push(line);
|
|
10683
|
-
if (line.includes('-->')) {
|
|
10684
|
-
currentType = 'MARKDOWN';
|
|
10685
|
-
}
|
|
10686
|
-
}
|
|
10687
|
-
}
|
|
10688
|
-
finishSection();
|
|
10689
|
-
return sections;
|
|
10690
|
-
}
|
|
10691
|
-
/**
|
|
10692
|
-
* TODO: [🏛] This can be part of markdown builder
|
|
10693
|
-
* Note: [🕞] In past (commit 42086e1603cbed506482997c00a8ee979af0a247) there was much more
|
|
10694
|
-
* sophisticated implementation of this function through parsing markdown into JSON structure
|
|
10695
|
-
* and flattening the actual structure
|
|
10696
|
-
* NOW we are working just with markdown string and its good enough
|
|
10697
|
-
*/
|
|
10698
|
-
|
|
10699
|
-
/**
|
|
10700
|
-
* Normalizes the markdown by flattening the structure
|
|
10701
|
-
*
|
|
10702
|
-
* - It always have h1 - if there is no h1 in the markdown, it will be added `DEFAULT_BOOK_TITLE`
|
|
10703
|
-
* - All other headings are normalized to h2
|
|
10704
|
-
*
|
|
10705
|
-
* @public exported from `@promptbook/markdown-utils`
|
|
10706
|
-
*/
|
|
10707
|
-
function flattenMarkdown(markdown) {
|
|
10708
|
-
const sections = splitMarkdownIntoSections(markdown);
|
|
10709
|
-
if (sections.length === 0) {
|
|
10710
|
-
return `# ${DEFAULT_BOOK_TITLE}`;
|
|
10711
|
-
}
|
|
10712
|
-
let flattenedMarkdown = '';
|
|
10713
|
-
const parsedSections = sections.map(parseMarkdownSection);
|
|
10714
|
-
const firstSection = parsedSections.shift();
|
|
10715
|
-
if (firstSection.level === 1) {
|
|
10716
|
-
flattenedMarkdown += `# ${firstSection.title}` + `\n\n`;
|
|
10717
|
-
flattenedMarkdown += firstSection.content + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
10718
|
-
}
|
|
10719
|
-
else {
|
|
10720
|
-
parsedSections.unshift(firstSection);
|
|
10721
|
-
flattenedMarkdown += `# ${DEFAULT_BOOK_TITLE}` + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
10722
|
-
}
|
|
10723
|
-
for (const { title, content } of parsedSections) {
|
|
10724
|
-
flattenedMarkdown += `## ${title}` + `\n\n`;
|
|
10725
|
-
flattenedMarkdown += content + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
10726
|
-
}
|
|
10727
|
-
return _spaceTrim.spaceTrim(flattenedMarkdown);
|
|
10728
|
-
}
|
|
10729
|
-
/**
|
|
10730
|
-
* TODO: [🏛] This can be part of markdown builder
|
|
10731
|
-
* Note: [🕞] In past (commit 42086e1603cbed506482997c00a8ee979af0a247) there was much more
|
|
10732
|
-
* sophisticated implementation of this function through parsing markdown into JSON structure
|
|
10733
|
-
* and flattening the actual structure
|
|
10734
|
-
* NOW we are working just with markdown string and its good enough
|
|
10735
|
-
*/
|
|
10736
|
-
|
|
10737
|
-
/**
|
|
10738
|
-
* Normalizes inline parameter mentions wrapped in code spans before markdown flattening.
|
|
10739
|
-
*
|
|
10740
|
-
* @private internal utility of `parsePipeline`
|
|
10741
|
-
*/
|
|
10742
|
-
const INLINE_CODE_PARAMETER_REGEXP = /`\{(?<parameterName>[a-z0-9_]+)\}`/gi;
|
|
10743
|
-
/**
|
|
10744
|
-
* Normalizes inline return statements wrapped in code spans before markdown flattening.
|
|
10745
|
-
*
|
|
10746
|
-
* @private internal utility of `parsePipeline`
|
|
10747
|
-
*/
|
|
10748
|
-
const INLINE_CODE_RETURN_PARAMETER_REGEXP = /`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi;
|
|
10749
|
-
/**
|
|
10750
|
-
* Matches the trailing return statement of a task section.
|
|
10751
|
-
*
|
|
10752
|
-
* @private internal utility of `parsePipeline`
|
|
10753
|
-
*/
|
|
10754
|
-
const RESULTING_PARAMETER_LINE_REGEXP = /^->\s*\{(?<resultingParamName>[a-z0-9_]+)\}/im;
|
|
10755
|
-
/**
|
|
10756
|
-
* Removes fenced code blocks when deriving human-readable section descriptions.
|
|
10757
|
-
*
|
|
10758
|
-
* @private internal utility of `parsePipeline`
|
|
10759
|
-
*/
|
|
10760
|
-
const DESCRIPTION_CODE_BLOCK_REGEXP = /^```.*^```/gms;
|
|
10761
|
-
/**
|
|
10762
|
-
* Removes blockquote lines when deriving human-readable section descriptions.
|
|
10763
|
-
*
|
|
10764
|
-
* @private internal utility of `parsePipeline`
|
|
10765
|
-
*/
|
|
10766
|
-
const DESCRIPTION_BLOCKQUOTE_REGEXP = /^>.*$/gm;
|
|
10767
|
-
/**
|
|
10768
|
-
* Removes list items and return statements when deriving human-readable section descriptions.
|
|
10769
|
-
*
|
|
10770
|
-
* @private internal utility of `parsePipeline`
|
|
10771
|
-
*/
|
|
10772
|
-
const DESCRIPTION_LIST_ITEM_REGEXP = /^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm;
|
|
10773
|
-
/**
|
|
10774
|
-
* Compile pipeline from string (markdown) format to JSON format synchronously
|
|
10775
|
-
*
|
|
10776
|
-
* Note: There are 3 similar functions:
|
|
10777
|
-
* - `compilePipeline` **(preferred)** - which properly compiles the promptbook and uses embedding for external knowledge
|
|
10778
|
-
* - `parsePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
|
|
10779
|
-
* - `preparePipeline` - just one step in the compilation process
|
|
10780
|
-
*
|
|
10781
|
-
* Note: This function does not validate logic of the pipeline only the parsing
|
|
10782
|
-
* Note: This function acts as compilation process
|
|
10783
|
-
*
|
|
10784
|
-
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
10785
|
-
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
10786
|
-
* @throws {ParseError} if the promptbook string is not valid
|
|
10787
|
-
*
|
|
10788
|
-
* @public exported from `@promptbook/core`
|
|
10789
|
-
*/
|
|
10790
|
-
function parsePipeline(pipelineString) {
|
|
10791
|
-
const $pipelineJson = createInitialPipelineJson(pipelineString);
|
|
10792
|
-
const preparedPipelineString = preparePipelineString(pipelineString, $pipelineJson);
|
|
10793
|
-
const { pipelineHead, pipelineSections } = parsePreparedPipelineSections(preparedPipelineString, $pipelineJson);
|
|
10794
|
-
const getUniqueSectionName = createUniqueSectionNameResolver(pipelineSections);
|
|
10795
|
-
applyPipelineHead(pipelineHead, $pipelineJson);
|
|
10796
|
-
for (const pipelineSection of pipelineSections) {
|
|
10797
|
-
processPipelineSection(pipelineSection, $pipelineJson, getUniqueSectionName);
|
|
10798
|
-
}
|
|
10799
|
-
applyImplicitParameterDirections($pipelineJson);
|
|
10800
|
-
removeUndefinedValuesFromPipeline($pipelineJson);
|
|
10801
|
-
applySyncHighLevelAbstractions($pipelineJson);
|
|
10802
|
-
ensurePipelineFormfactor($pipelineJson);
|
|
10803
|
-
return exportParsedPipelineJson($pipelineJson);
|
|
10804
|
-
}
|
|
10805
|
-
/**
|
|
10806
|
-
* Creates the mutable pipeline JSON structure used throughout parsing.
|
|
10807
|
-
*
|
|
10808
|
-
* @private internal utility of `parsePipeline`
|
|
10809
|
-
*/
|
|
10810
|
-
function createInitialPipelineJson(pipelineString) {
|
|
10811
|
-
return {
|
|
10812
|
-
title: DEFAULT_BOOK_TITLE,
|
|
10813
|
-
parameters: [],
|
|
10814
|
-
tasks: [],
|
|
10815
|
-
knowledgeSources: [],
|
|
10816
|
-
knowledgePieces: [],
|
|
10817
|
-
personas: [],
|
|
10818
|
-
preparations: [],
|
|
10819
|
-
sources: [
|
|
10820
|
-
{
|
|
10821
|
-
type: 'BOOK',
|
|
10822
|
-
path: null,
|
|
10823
|
-
// <- TODO: !!6 Pass here path of the file
|
|
10824
|
-
content: pipelineString,
|
|
10825
|
-
},
|
|
10826
|
-
],
|
|
10827
|
-
};
|
|
10828
|
-
}
|
|
10829
|
-
/**
|
|
10830
|
-
* Builds a short file/url identification block for parse errors.
|
|
10831
|
-
*
|
|
10832
|
-
* @private internal utility of `parsePipeline`
|
|
10833
|
-
*/
|
|
10834
|
-
function getPipelineIdentification($pipelineJson) {
|
|
10835
|
-
// Note: This is a 😐 implementation of [🚞]
|
|
10836
|
-
const pipelineIdentificationParts = [];
|
|
10837
|
-
if ($pipelineJson.sourceFile !== undefined) {
|
|
10838
|
-
pipelineIdentificationParts.push(`File: ${$pipelineJson.sourceFile}`);
|
|
10839
|
-
}
|
|
10840
|
-
if ($pipelineJson.pipelineUrl !== undefined) {
|
|
10841
|
-
pipelineIdentificationParts.push(`Url: ${$pipelineJson.pipelineUrl}`);
|
|
10845
|
+
else if (currentType === 'COMMENT') {
|
|
10846
|
+
buffer.push(line);
|
|
10847
|
+
if (line.includes('-->')) {
|
|
10848
|
+
currentType = 'MARKDOWN';
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10842
10851
|
}
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
/**
|
|
10846
|
-
* Removes shebang/comments and normalizes markdown into a parseable pipeline form.
|
|
10847
|
-
*
|
|
10848
|
-
* @private internal utility of `parsePipeline`
|
|
10849
|
-
*/
|
|
10850
|
-
function preparePipelineString(pipelineString, $pipelineJson) {
|
|
10851
|
-
pipelineString = removePipelineShebang(pipelineString, $pipelineJson);
|
|
10852
|
-
pipelineString = removeMarkdownComments(pipelineString);
|
|
10853
|
-
pipelineString = _spaceTrim.spaceTrim(pipelineString);
|
|
10854
|
-
// <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
|
|
10855
|
-
pipelineString = deflatePipeline(pipelineString);
|
|
10856
|
-
pipelineString = flattenMarkdown(pipelineString);
|
|
10857
|
-
pipelineString = pipelineString.replaceAll(INLINE_CODE_PARAMETER_REGEXP, '{$<parameterName>}');
|
|
10858
|
-
pipelineString = pipelineString.replaceAll(INLINE_CODE_RETURN_PARAMETER_REGEXP, '-> {$<parameterName>}');
|
|
10859
|
-
return pipelineString;
|
|
10852
|
+
finishSection();
|
|
10853
|
+
return sections;
|
|
10860
10854
|
}
|
|
10861
10855
|
/**
|
|
10862
|
-
*
|
|
10863
|
-
*
|
|
10864
|
-
*
|
|
10856
|
+
* TODO: [🏛] This can be part of markdown builder
|
|
10857
|
+
* Note: [🕞] In past (commit 42086e1603cbed506482997c00a8ee979af0a247) there was much more
|
|
10858
|
+
* sophisticated implementation of this function through parsing markdown into JSON structure
|
|
10859
|
+
* and flattening the actual structure
|
|
10860
|
+
* NOW we are working just with markdown string and its good enough
|
|
10865
10861
|
*/
|
|
10866
|
-
function removePipelineShebang(pipelineString, $pipelineJson) {
|
|
10867
|
-
if (!pipelineString.startsWith('#!')) {
|
|
10868
|
-
return pipelineString;
|
|
10869
|
-
}
|
|
10870
|
-
const [shebangLine, ...restLines] = pipelineString.split(/\r?\n/);
|
|
10871
|
-
const isBookShebang = (shebangLine || '').includes('ptbk');
|
|
10872
|
-
if (!isBookShebang) {
|
|
10873
|
-
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10874
|
-
It seems that you try to parse a book file which has non-standard shebang line for book files:
|
|
10875
|
-
Shebang line must contain 'ptbk'
|
|
10876
|
-
|
|
10877
|
-
You have:
|
|
10878
|
-
${block(shebangLine || '(empty line)')}
|
|
10879
|
-
|
|
10880
|
-
It should look like this:
|
|
10881
|
-
#!/usr/bin/env ptbk
|
|
10882
10862
|
|
|
10883
|
-
${block(getPipelineIdentification($pipelineJson))}
|
|
10884
|
-
`));
|
|
10885
|
-
}
|
|
10886
|
-
return validatePipelineString(restLines.join('\n'));
|
|
10887
|
-
}
|
|
10888
10863
|
/**
|
|
10889
10864
|
* Splits the prepared markdown into the pipeline head and task sections.
|
|
10890
10865
|
*
|
|
@@ -10901,7 +10876,7 @@
|
|
|
10901
10876
|
/**
|
|
10902
10877
|
* Ensures the flattened markdown has exactly one h1 head followed by only h2 sections.
|
|
10903
10878
|
*
|
|
10904
|
-
* @private internal utility of `
|
|
10879
|
+
* @private internal utility of `parsePreparedPipelineSections`
|
|
10905
10880
|
*/
|
|
10906
10881
|
function assertPipelineSectionsStructure(pipelineHead, pipelineSections, $pipelineJson) {
|
|
10907
10882
|
if (pipelineHead === undefined) {
|
|
@@ -10932,148 +10907,270 @@
|
|
|
10932
10907
|
`));
|
|
10933
10908
|
}
|
|
10934
10909
|
}
|
|
10910
|
+
|
|
10935
10911
|
/**
|
|
10936
|
-
*
|
|
10912
|
+
* Number of padding lines to add at the end of the book content
|
|
10937
10913
|
*
|
|
10938
|
-
* @
|
|
10914
|
+
* @public exported from `@promptbook/core`
|
|
10939
10915
|
*/
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
|
|
10944
|
-
|
|
10916
|
+
const PADDING_LINES = 11;
|
|
10917
|
+
/**
|
|
10918
|
+
* A function that adds padding to the book content
|
|
10919
|
+
*
|
|
10920
|
+
* Note: [🔂] This function is idempotent.
|
|
10921
|
+
*
|
|
10922
|
+
* @public exported from `@promptbook/core`
|
|
10923
|
+
*/
|
|
10924
|
+
function padBook(content) {
|
|
10925
|
+
if (!content) {
|
|
10926
|
+
return '\n'.repeat(PADDING_LINES);
|
|
10927
|
+
}
|
|
10928
|
+
const lines = content.split(/\r?\n/);
|
|
10929
|
+
let trailingEmptyLines = 0;
|
|
10930
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
10931
|
+
const line = lines[i];
|
|
10932
|
+
if (line === undefined) {
|
|
10933
|
+
// Note: This should not happen in reality, but it's here to satisfy TypeScript's noUncheckedIndexedAccess option
|
|
10934
|
+
continue;
|
|
10935
|
+
}
|
|
10936
|
+
if (line.trim() === '') {
|
|
10937
|
+
trailingEmptyLines++;
|
|
10938
|
+
}
|
|
10939
|
+
else {
|
|
10940
|
+
break;
|
|
10941
|
+
}
|
|
10942
|
+
}
|
|
10943
|
+
if (trailingEmptyLines >= PADDING_LINES) {
|
|
10944
|
+
return content;
|
|
10945
10945
|
}
|
|
10946
|
+
const linesToAdd = PADDING_LINES - trailingEmptyLines;
|
|
10947
|
+
return (content + '\n'.repeat(linesToAdd));
|
|
10946
10948
|
}
|
|
10949
|
+
// TODO: [🧠] Maybe export
|
|
10950
|
+
|
|
10947
10951
|
/**
|
|
10948
|
-
*
|
|
10952
|
+
* Removes Markdown (or HTML) comments
|
|
10949
10953
|
*
|
|
10950
|
-
* @
|
|
10954
|
+
* @param {string} content - The string to remove comments from.
|
|
10955
|
+
* @returns {string} The input string with all comments removed.
|
|
10956
|
+
*
|
|
10957
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
10951
10958
|
*/
|
|
10952
|
-
function
|
|
10953
|
-
|
|
10954
|
-
description = description.split(DESCRIPTION_CODE_BLOCK_REGEXP).join('');
|
|
10955
|
-
description = description.split(DESCRIPTION_BLOCKQUOTE_REGEXP).join('');
|
|
10956
|
-
description = description.split(DESCRIPTION_LIST_ITEM_REGEXP).join('');
|
|
10957
|
-
description = _spaceTrim.spaceTrim(description);
|
|
10958
|
-
if (description === '') {
|
|
10959
|
-
return undefined;
|
|
10960
|
-
}
|
|
10961
|
-
return description;
|
|
10959
|
+
function removeMarkdownComments(content) {
|
|
10960
|
+
return _spaceTrim.spaceTrim(content.replace(/<!--(.*?)-->/gs, ''));
|
|
10962
10961
|
}
|
|
10962
|
+
|
|
10963
10963
|
/**
|
|
10964
|
-
*
|
|
10964
|
+
* Utility to determine if a pipeline string is in flat format.
|
|
10965
|
+
* A flat pipeline is a simple text without proper structure (headers, blocks, etc).
|
|
10965
10966
|
*
|
|
10966
|
-
* @
|
|
10967
|
+
* @public exported from `@promptbook/editable`
|
|
10967
10968
|
*/
|
|
10968
|
-
function
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
|
|
10969
|
+
function isFlatPipeline(pipelineString) {
|
|
10970
|
+
pipelineString = removeMarkdownComments(pipelineString);
|
|
10971
|
+
pipelineString = _spaceTrim.spaceTrim(pipelineString);
|
|
10972
|
+
const isMarkdownBeginningWithHeadline = pipelineString.startsWith('# ');
|
|
10973
|
+
//const isLastLineReturnStatement = pipelineString.split(/\r?\n/).pop()!.split('`').join('').startsWith('->');
|
|
10974
|
+
const isBacktickBlockUsed = pipelineString.includes('```');
|
|
10975
|
+
const isQuoteBlocksUsed = /^>\s+/m.test(pipelineString);
|
|
10976
|
+
const isBlocksUsed = isBacktickBlockUsed || isQuoteBlocksUsed;
|
|
10977
|
+
// TODO: [🧉] Also (double)check
|
|
10978
|
+
// > const usedCommands
|
|
10979
|
+
// > const isBlocksUsed
|
|
10980
|
+
// > const returnStatementCount
|
|
10981
|
+
const isFlat = !isMarkdownBeginningWithHeadline && !isBlocksUsed; /* && isLastLineReturnStatement */
|
|
10982
|
+
return isFlat;
|
|
10983
|
+
}
|
|
10974
10984
|
|
|
10975
|
-
|
|
10976
|
-
|
|
10985
|
+
/**
|
|
10986
|
+
* Converts a pipeline structure to its string representation.
|
|
10987
|
+
*
|
|
10988
|
+
* Transforms a flat, simple pipeline into a properly formatted pipeline string
|
|
10989
|
+
* with sections for title, prompt, and return statement.
|
|
10990
|
+
*
|
|
10991
|
+
* @public exported from `@promptbook/editable`
|
|
10992
|
+
*/
|
|
10993
|
+
function deflatePipeline(pipelineString) {
|
|
10994
|
+
if (!isFlatPipeline(pipelineString)) {
|
|
10995
|
+
return pipelineString;
|
|
10977
10996
|
}
|
|
10978
|
-
|
|
10979
|
-
|
|
10980
|
-
|
|
10997
|
+
pipelineString = _spaceTrim.spaceTrim(pipelineString);
|
|
10998
|
+
const pipelineStringLines = pipelineString.split(/\r?\n/);
|
|
10999
|
+
const potentialReturnStatement = pipelineStringLines.pop();
|
|
11000
|
+
let returnStatement;
|
|
11001
|
+
if (/(-|=)>\s*\{.*\}/.test(potentialReturnStatement)) {
|
|
11002
|
+
// Note: Last line is return statement
|
|
11003
|
+
returnStatement = potentialReturnStatement;
|
|
11004
|
+
}
|
|
11005
|
+
else {
|
|
11006
|
+
// Note: Last line is not a return statement
|
|
11007
|
+
returnStatement = `-> {${DEFAULT_BOOK_OUTPUT_PARAMETER_NAME}}`;
|
|
11008
|
+
pipelineStringLines.push(potentialReturnStatement);
|
|
11009
|
+
}
|
|
11010
|
+
const prompt = _spaceTrim.spaceTrim(pipelineStringLines.join('\n'));
|
|
11011
|
+
let quotedPrompt;
|
|
11012
|
+
if (prompt.split(/\r?\n/).length <= 1) {
|
|
11013
|
+
quotedPrompt = `> ${prompt}`;
|
|
11014
|
+
}
|
|
11015
|
+
else {
|
|
11016
|
+
quotedPrompt = _spaceTrim.spaceTrim((block) => `
|
|
11017
|
+
\`\`\`
|
|
11018
|
+
${block(prompt.split('`').join('\\`'))}
|
|
11019
|
+
\`\`\`
|
|
11020
|
+
`);
|
|
11021
|
+
}
|
|
11022
|
+
pipelineString = validatePipelineString(_spaceTrim.spaceTrim((block) => `
|
|
11023
|
+
# ${DEFAULT_BOOK_TITLE}
|
|
11024
|
+
|
|
11025
|
+
## Prompt
|
|
11026
|
+
|
|
11027
|
+
${block(quotedPrompt)}
|
|
11028
|
+
|
|
11029
|
+
${returnStatement}
|
|
11030
|
+
`));
|
|
11031
|
+
// <- TODO: Maybe use book` notation
|
|
11032
|
+
return padBook(pipelineString);
|
|
11033
|
+
}
|
|
11034
|
+
// TODO: Unit test
|
|
11035
|
+
|
|
11036
|
+
/**
|
|
11037
|
+
* Normalizes the markdown by flattening the structure
|
|
11038
|
+
*
|
|
11039
|
+
* - It always have h1 - if there is no h1 in the markdown, it will be added `DEFAULT_BOOK_TITLE`
|
|
11040
|
+
* - All other headings are normalized to h2
|
|
11041
|
+
*
|
|
11042
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
11043
|
+
*/
|
|
11044
|
+
function flattenMarkdown(markdown) {
|
|
11045
|
+
const sections = splitMarkdownIntoSections(markdown);
|
|
11046
|
+
if (sections.length === 0) {
|
|
11047
|
+
return `# ${DEFAULT_BOOK_TITLE}`;
|
|
11048
|
+
}
|
|
11049
|
+
let flattenedMarkdown = '';
|
|
11050
|
+
const parsedSections = sections.map(parseMarkdownSection);
|
|
11051
|
+
const firstSection = parsedSections.shift();
|
|
11052
|
+
if (firstSection.level === 1) {
|
|
11053
|
+
flattenedMarkdown += `# ${firstSection.title}` + `\n\n`;
|
|
11054
|
+
flattenedMarkdown += firstSection.content + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
11055
|
+
}
|
|
11056
|
+
else {
|
|
11057
|
+
parsedSections.unshift(firstSection);
|
|
11058
|
+
flattenedMarkdown += `# ${DEFAULT_BOOK_TITLE}` + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
11059
|
+
}
|
|
11060
|
+
for (const { title, content } of parsedSections) {
|
|
11061
|
+
flattenedMarkdown += `## ${title}` + `\n\n`;
|
|
11062
|
+
flattenedMarkdown += content + `\n\n`; // <- [🧠] Maybe 3 new lines?
|
|
11063
|
+
}
|
|
11064
|
+
return _spaceTrim.spaceTrim(flattenedMarkdown);
|
|
11065
|
+
}
|
|
11066
|
+
/**
|
|
11067
|
+
* TODO: [🏛] This can be part of markdown builder
|
|
11068
|
+
* Note: [🕞] In past (commit 42086e1603cbed506482997c00a8ee979af0a247) there was much more
|
|
11069
|
+
* sophisticated implementation of this function through parsing markdown into JSON structure
|
|
11070
|
+
* and flattening the actual structure
|
|
11071
|
+
* NOW we are working just with markdown string and its good enough
|
|
11072
|
+
*/
|
|
11073
|
+
|
|
11074
|
+
/**
|
|
11075
|
+
* Normalizes inline parameter mentions wrapped in code spans before markdown flattening.
|
|
11076
|
+
*
|
|
11077
|
+
* @private internal utility of `preparePipelineString`
|
|
11078
|
+
*/
|
|
11079
|
+
const INLINE_CODE_PARAMETER_REGEXP = /`\{(?<parameterName>[a-z0-9_]+)\}`/gi;
|
|
11080
|
+
/**
|
|
11081
|
+
* Normalizes inline return statements wrapped in code spans before markdown flattening.
|
|
11082
|
+
*
|
|
11083
|
+
* @private internal utility of `preparePipelineString`
|
|
11084
|
+
*/
|
|
11085
|
+
const INLINE_CODE_RETURN_PARAMETER_REGEXP = /`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi;
|
|
11086
|
+
/**
|
|
11087
|
+
* Removes shebang/comments and normalizes markdown into a parseable pipeline form.
|
|
11088
|
+
*
|
|
11089
|
+
* @private internal utility of `parsePipeline`
|
|
11090
|
+
*/
|
|
11091
|
+
function preparePipelineString(pipelineString, $pipelineJson) {
|
|
11092
|
+
pipelineString = removePipelineShebang(pipelineString, $pipelineJson);
|
|
11093
|
+
pipelineString = removeMarkdownComments(pipelineString);
|
|
11094
|
+
pipelineString = _spaceTrim.spaceTrim(pipelineString);
|
|
11095
|
+
// <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
|
|
11096
|
+
pipelineString = deflatePipeline(pipelineString);
|
|
11097
|
+
pipelineString = flattenMarkdown(pipelineString);
|
|
11098
|
+
pipelineString = pipelineString.replaceAll(INLINE_CODE_PARAMETER_REGEXP, '{$<parameterName>}');
|
|
11099
|
+
pipelineString = pipelineString.replaceAll(INLINE_CODE_RETURN_PARAMETER_REGEXP, '-> {$<parameterName>}');
|
|
11100
|
+
return pipelineString;
|
|
11101
|
+
}
|
|
11102
|
+
/**
|
|
11103
|
+
* Validates and removes the optional `#!` shebang line for `.book` files.
|
|
11104
|
+
*
|
|
11105
|
+
* @private internal utility of `preparePipelineString`
|
|
11106
|
+
*/
|
|
11107
|
+
function removePipelineShebang(pipelineString, $pipelineJson) {
|
|
11108
|
+
if (!pipelineString.startsWith('#!')) {
|
|
11109
|
+
return pipelineString;
|
|
10981
11110
|
}
|
|
10982
|
-
|
|
10983
|
-
|
|
10984
|
-
|
|
10985
|
-
}
|
|
11111
|
+
const [shebangLine, ...restLines] = pipelineString.split(/\r?\n/);
|
|
11112
|
+
const isBookShebang = (shebangLine || '').includes('ptbk');
|
|
11113
|
+
if (!isBookShebang) {
|
|
10986
11114
|
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
10987
|
-
|
|
10988
|
-
|
|
10989
|
-
The error:
|
|
10990
|
-
${block(error.message)}
|
|
11115
|
+
It seems that you try to parse a book file which has non-standard shebang line for book files:
|
|
11116
|
+
Shebang line must contain 'ptbk'
|
|
10991
11117
|
|
|
10992
|
-
|
|
10993
|
-
|
|
11118
|
+
You have:
|
|
11119
|
+
${block(shebangLine || '(empty line)')}
|
|
10994
11120
|
|
|
10995
|
-
|
|
10996
|
-
|
|
11121
|
+
It should look like this:
|
|
11122
|
+
#!/usr/bin/env ptbk
|
|
10997
11123
|
|
|
10998
11124
|
${block(getPipelineIdentification($pipelineJson))}
|
|
10999
|
-
|
|
11000
|
-
}
|
|
11001
|
-
if (command.type === 'PARAMETER') {
|
|
11002
|
-
defineParameter($pipelineJson, command);
|
|
11003
|
-
// <- Note: [🍣]
|
|
11125
|
+
`));
|
|
11004
11126
|
}
|
|
11127
|
+
return validatePipelineString(restLines.join('\n'));
|
|
11005
11128
|
}
|
|
11129
|
+
|
|
11006
11130
|
/**
|
|
11007
|
-
*
|
|
11131
|
+
* Supported script languages
|
|
11008
11132
|
*
|
|
11009
|
-
* @private internal
|
|
11133
|
+
* @private internal base for `ScriptLanguage`
|
|
11010
11134
|
*/
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
|
|
11014
|
-
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
11015
|
-
Parameter name {${parameterName}} is reserved and cannot be used as resulting parameter name
|
|
11135
|
+
const SUPPORTED_SCRIPT_LANGUAGES = ['javascript', 'typescript', 'python'];
|
|
11136
|
+
// <- TODO: [🏥] DRY
|
|
11016
11137
|
|
|
11017
|
-
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11138
|
+
/**
|
|
11139
|
+
* Extracts exactly ONE code block from markdown.
|
|
11140
|
+
*
|
|
11141
|
+
* - When there are multiple or no code blocks the function throws a `ParseError`
|
|
11142
|
+
*
|
|
11143
|
+
* Note: There are multiple similar functions:
|
|
11144
|
+
* - `extractBlock` just extracts the content of the code block which is also used as built-in function for postprocessing
|
|
11145
|
+
* - `extractJsonBlock` extracts exactly one valid JSON code block
|
|
11146
|
+
* - `extractOneBlockFromMarkdown` extracts exactly one code block with language of the code block
|
|
11147
|
+
* - `extractAllBlocksFromMarkdown` extracts all code blocks with language of the code block
|
|
11148
|
+
*
|
|
11149
|
+
* @param markdown any valid markdown
|
|
11150
|
+
* @returns code block with language and content
|
|
11151
|
+
* @throws {ParseError} if there is not exactly one code block in the markdown
|
|
11152
|
+
*
|
|
11153
|
+
* @public exported from `@promptbook/markdown-utils`
|
|
11154
|
+
*/
|
|
11155
|
+
function extractOneBlockFromMarkdown(markdown) {
|
|
11156
|
+
const codeBlocks = extractAllBlocksFromMarkdown(markdown);
|
|
11157
|
+
if (codeBlocks.length !== 1) {
|
|
11025
11158
|
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
${block(getPipelineIdentification($pipelineJson))}
|
|
11029
|
-
|
|
11030
|
-
First definition:
|
|
11031
|
-
${block(existingParameter.description || '[undefined]')}
|
|
11159
|
+
There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
|
|
11032
11160
|
|
|
11033
|
-
|
|
11034
|
-
${block(parameterDescription || '[undefined]')}
|
|
11161
|
+
${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
|
|
11035
11162
|
`));
|
|
11036
11163
|
}
|
|
11037
|
-
|
|
11038
|
-
if (parameterDescription) {
|
|
11039
|
-
existingParameter.description = parameterDescription;
|
|
11040
|
-
}
|
|
11041
|
-
existingParameter.isInput = existingParameter.isInput || isInput;
|
|
11042
|
-
existingParameter.isOutput = existingParameter.isOutput || isOutput;
|
|
11043
|
-
return;
|
|
11044
|
-
}
|
|
11045
|
-
$pipelineJson.parameters.push({
|
|
11046
|
-
name: parameterName,
|
|
11047
|
-
description: parameterDescription || undefined,
|
|
11048
|
-
isInput,
|
|
11049
|
-
isOutput,
|
|
11050
|
-
});
|
|
11164
|
+
return codeBlocks[0];
|
|
11051
11165
|
}
|
|
11166
|
+
// TODO: [🍓][🌻] Decide of this is internal utility, external util OR validator/postprocessor
|
|
11167
|
+
|
|
11052
11168
|
/**
|
|
11053
|
-
*
|
|
11169
|
+
* Matches the trailing return statement of a task section.
|
|
11054
11170
|
*
|
|
11055
|
-
* @private internal utility of `
|
|
11171
|
+
* @private internal utility of `processPipelineSection`
|
|
11056
11172
|
*/
|
|
11057
|
-
|
|
11058
|
-
const sectionCounts = {};
|
|
11059
|
-
for (const pipelineSection of pipelineSections) {
|
|
11060
|
-
const sectionName = titleToName(pipelineSection.title);
|
|
11061
|
-
if (sectionCounts[sectionName] === undefined) {
|
|
11062
|
-
sectionCounts[sectionName] = { count: 0, currentIndex: 0 };
|
|
11063
|
-
}
|
|
11064
|
-
sectionCounts[sectionName].count++;
|
|
11065
|
-
}
|
|
11066
|
-
return (title) => {
|
|
11067
|
-
const sectionName = titleToName(title);
|
|
11068
|
-
const sectionCount = sectionCounts[sectionName];
|
|
11069
|
-
if (sectionCount.count === 1) {
|
|
11070
|
-
return sectionName;
|
|
11071
|
-
}
|
|
11072
|
-
const nameWithSuffix = `${sectionName}-${sectionCount.currentIndex}`;
|
|
11073
|
-
sectionCount.currentIndex++;
|
|
11074
|
-
return nameWithSuffix;
|
|
11075
|
-
};
|
|
11076
|
-
}
|
|
11173
|
+
const RESULTING_PARAMETER_LINE_REGEXP = /^->\s*\{(?<resultingParamName>[a-z0-9_]+)\}/im;
|
|
11077
11174
|
/**
|
|
11078
11175
|
* Parses, applies, and persists one h2 task section.
|
|
11079
11176
|
*
|
|
@@ -11093,7 +11190,7 @@
|
|
|
11093
11190
|
/**
|
|
11094
11191
|
* Creates the mutable task JSON shell from one markdown section.
|
|
11095
11192
|
*
|
|
11096
|
-
* @private internal utility of `
|
|
11193
|
+
* @private internal utility of `processPipelineSection`
|
|
11097
11194
|
*/
|
|
11098
11195
|
function createTaskJsonFromSection(pipelineSection, getUniqueSectionName) {
|
|
11099
11196
|
const { language, content } = extractOneBlockFromMarkdown(pipelineSection.content);
|
|
@@ -11121,7 +11218,7 @@
|
|
|
11121
11218
|
/**
|
|
11122
11219
|
* Extracts the optional trailing `-> {parameter}` statement from a section body.
|
|
11123
11220
|
*
|
|
11124
|
-
* @private internal utility of `
|
|
11221
|
+
* @private internal utility of `processPipelineSection`
|
|
11125
11222
|
*/
|
|
11126
11223
|
function extractResultingParameterName(sectionContent) {
|
|
11127
11224
|
var _a;
|
|
@@ -11132,7 +11229,7 @@
|
|
|
11132
11229
|
/**
|
|
11133
11230
|
* Parses all list-item commands declared inside one task section.
|
|
11134
11231
|
*
|
|
11135
|
-
* @private internal utility of `
|
|
11232
|
+
* @private internal utility of `processPipelineSection`
|
|
11136
11233
|
*/
|
|
11137
11234
|
function parsePipelineTaskCommands(sectionContent) {
|
|
11138
11235
|
return extractAllListItemsFromMarkdown(sectionContent).map((listItem) => ({
|
|
@@ -11143,7 +11240,7 @@
|
|
|
11143
11240
|
/**
|
|
11144
11241
|
* Applies the implicit default `PROMPT_TASK` section type when no SECTION command is present.
|
|
11145
11242
|
*
|
|
11146
|
-
* @private internal utility of `
|
|
11243
|
+
* @private internal utility of `processPipelineSection`
|
|
11147
11244
|
*/
|
|
11148
11245
|
function applyDefaultTaskSectionType($taskJson, commands, $pipelineJson) {
|
|
11149
11246
|
const isSectionCommandPresent = commands.some(({ command }) => command.type === 'SECTION');
|
|
@@ -11155,7 +11252,7 @@
|
|
|
11155
11252
|
/**
|
|
11156
11253
|
* Parses and applies one command declared inside a task section.
|
|
11157
11254
|
*
|
|
11158
|
-
* @private internal utility of `
|
|
11255
|
+
* @private internal utility of `processPipelineSection`
|
|
11159
11256
|
*/
|
|
11160
11257
|
function applyPipelineTaskCommand(commandItem, $taskJson, $pipelineJson) {
|
|
11161
11258
|
const { listItem, command } = commandItem;
|
|
@@ -11206,7 +11303,7 @@
|
|
|
11206
11303
|
/**
|
|
11207
11304
|
* Validates and stores the language for SCRIPT tasks.
|
|
11208
11305
|
*
|
|
11209
|
-
* @private internal utility of `
|
|
11306
|
+
* @private internal utility of `processPipelineSection`
|
|
11210
11307
|
*/
|
|
11211
11308
|
function applyScriptTaskLanguage($taskJson, language, $pipelineJson) {
|
|
11212
11309
|
const isScriptTask = $taskJson.taskType === 'SCRIPT_TASK';
|
|
@@ -11223,7 +11320,6 @@
|
|
|
11223
11320
|
if (!SUPPORTED_SCRIPT_LANGUAGES.includes(language)) {
|
|
11224
11321
|
throw new ParseError(_spaceTrim.spaceTrim((block) => `
|
|
11225
11322
|
Script language ${language} is not supported.
|
|
11226
|
-
|
|
11227
11323
|
Supported languages are:
|
|
11228
11324
|
${block(SUPPORTED_SCRIPT_LANGUAGES.join(', '))}
|
|
11229
11325
|
|
|
@@ -11232,168 +11328,87 @@
|
|
|
11232
11328
|
$taskJson.contentLanguage = language;
|
|
11233
11329
|
}
|
|
11234
11330
|
/**
|
|
11235
|
-
* Extracts task dependencies and ensures referenced parameters exist.
|
|
11236
|
-
*
|
|
11237
|
-
* @private internal utility of `parsePipeline`
|
|
11238
|
-
*/
|
|
11239
|
-
function registerTaskDependentParameters($taskJson, $pipelineJson) {
|
|
11240
|
-
$taskJson.dependentParameterNames = Array.from(extractParameterNamesFromTask($taskJson));
|
|
11241
|
-
for (const parameterName of $taskJson.dependentParameterNames) {
|
|
11242
|
-
// TODO: [🧠] This definition should be made first in the task
|
|
11243
|
-
defineParameter($pipelineJson, {
|
|
11244
|
-
parameterName,
|
|
11245
|
-
parameterDescription: null,
|
|
11246
|
-
isInput: false,
|
|
11247
|
-
isOutput: false,
|
|
11248
|
-
// <- Note: In this case null+false+false means that we do not know yet if it is input or output and we will set it later
|
|
11249
|
-
});
|
|
11250
|
-
}
|
|
11251
|
-
}
|
|
11252
|
-
/**
|
|
11253
|
-
* Removes transient parsing flags and persists real tasks into the pipeline JSON.
|
|
11254
|
-
*
|
|
11255
|
-
* @private internal utility of `parsePipeline`
|
|
11256
|
-
*/
|
|
11257
|
-
function persistTaskIfNeeded($taskJson, $pipelineJson) {
|
|
11258
|
-
/*
|
|
11259
|
-
// TODO: [🍧] This should be checked in `MODEL` command + better error message
|
|
11260
|
-
if ($taskJson.taskType !== 'PROMPT_TASK' && $taskJson.modelRequirements !== undefined) {
|
|
11261
|
-
throw new UnexpectedError(
|
|
11262
|
-
spaceTrim(
|
|
11263
|
-
(block) => `
|
|
11264
|
-
Model requirements are defined for the block type ${
|
|
11265
|
-
$taskJson.taskType
|
|
11266
|
-
} which is not a \`PROMPT\` task
|
|
11267
|
-
|
|
11268
|
-
This should be avoided by the \`modelCommandParser\`
|
|
11269
|
-
|
|
11270
|
-
${block(getPipelineIdentification($pipelineJson))}
|
|
11271
|
-
`,
|
|
11272
|
-
),
|
|
11273
|
-
);
|
|
11274
|
-
}
|
|
11275
|
-
*/
|
|
11276
|
-
if (!$taskJson.isTask) {
|
|
11277
|
-
return;
|
|
11278
|
-
}
|
|
11279
|
-
delete $taskJson.isSectionTypeSet;
|
|
11280
|
-
delete $taskJson.isTask;
|
|
11281
|
-
// TODO: [🍙] Maybe do reorder of `$taskJson` here
|
|
11282
|
-
$pipelineJson.tasks.push($taskJson);
|
|
11283
|
-
}
|
|
11284
|
-
/**
|
|
11285
|
-
* Applies default INPUT/OUTPUT flags when the author did not specify them explicitly.
|
|
11286
|
-
*
|
|
11287
|
-
* @private internal utility of `parsePipeline`
|
|
11288
|
-
*/
|
|
11289
|
-
function applyImplicitParameterDirections($pipelineJson) {
|
|
11290
|
-
markImplicitInputParameters($pipelineJson);
|
|
11291
|
-
markImplicitOutputParameters($pipelineJson);
|
|
11292
|
-
}
|
|
11293
|
-
/**
|
|
11294
|
-
* Marks non-result parameters as pipeline inputs when no input was declared.
|
|
11295
|
-
*
|
|
11296
|
-
* @private internal utility of `parsePipeline`
|
|
11297
|
-
*/
|
|
11298
|
-
function markImplicitInputParameters($pipelineJson) {
|
|
11299
|
-
if ($pipelineJson.parameters.some((parameter) => parameter.isInput)) {
|
|
11300
|
-
return;
|
|
11301
|
-
}
|
|
11302
|
-
for (const parameter of $pipelineJson.parameters) {
|
|
11303
|
-
const isThisParameterResulting = $pipelineJson.tasks.some((task) => task.resultingParameterName === parameter.name);
|
|
11304
|
-
if (!isThisParameterResulting) {
|
|
11305
|
-
parameter.isInput = true;
|
|
11306
|
-
// <- TODO: [💔] Why this is making typescript error in vscode but not in cli
|
|
11307
|
-
// > Type 'true' is not assignable to type 'false'.ts(2322)
|
|
11308
|
-
// > (property) isInput: false
|
|
11309
|
-
// > The parameter is input of the pipeline The parameter is NOT input of the pipeline
|
|
11310
|
-
}
|
|
11311
|
-
}
|
|
11312
|
-
}
|
|
11313
|
-
/**
|
|
11314
|
-
* Marks every non-input parameter as output when no output was declared.
|
|
11315
|
-
*
|
|
11316
|
-
* @private internal utility of `parsePipeline`
|
|
11317
|
-
*/
|
|
11318
|
-
function markImplicitOutputParameters($pipelineJson) {
|
|
11319
|
-
if ($pipelineJson.parameters.some((parameter) => parameter.isOutput)) {
|
|
11320
|
-
return;
|
|
11321
|
-
}
|
|
11322
|
-
for (const parameter of $pipelineJson.parameters) {
|
|
11323
|
-
if (!parameter.isInput) {
|
|
11324
|
-
parameter.isOutput = true;
|
|
11325
|
-
// <- TODO: [💔]
|
|
11326
|
-
}
|
|
11327
|
-
}
|
|
11328
|
-
}
|
|
11329
|
-
/**
|
|
11330
|
-
* Removes `undefined` properties from serialized tasks and parameters.
|
|
11331
|
-
*
|
|
11332
|
-
* @private internal utility of `parsePipeline`
|
|
11333
|
-
*/
|
|
11334
|
-
function removeUndefinedValuesFromPipeline($pipelineJson) {
|
|
11335
|
-
$pipelineJson.tasks.forEach(removeUndefinedProperties);
|
|
11336
|
-
$pipelineJson.parameters.forEach(removeUndefinedProperties);
|
|
11337
|
-
}
|
|
11338
|
-
/**
|
|
11339
|
-
* Deletes all own properties with `undefined` values from a mutable JSON entity.
|
|
11340
|
-
*
|
|
11341
|
-
* @private internal utility of `parsePipeline`
|
|
11342
|
-
*/
|
|
11343
|
-
function removeUndefinedProperties(entity) {
|
|
11344
|
-
for (const [key, value] of Object.entries(entity)) {
|
|
11345
|
-
if (value === undefined) {
|
|
11346
|
-
delete entity[key];
|
|
11347
|
-
}
|
|
11348
|
-
}
|
|
11349
|
-
}
|
|
11350
|
-
/**
|
|
11351
|
-
* Applies all sync-only high-level abstractions after parsing.
|
|
11331
|
+
* Extracts task dependencies and ensures referenced parameters exist.
|
|
11352
11332
|
*
|
|
11353
|
-
* @private internal utility of `
|
|
11333
|
+
* @private internal utility of `processPipelineSection`
|
|
11354
11334
|
*/
|
|
11355
|
-
function
|
|
11356
|
-
|
|
11357
|
-
|
|
11335
|
+
function registerTaskDependentParameters($taskJson, $pipelineJson) {
|
|
11336
|
+
$taskJson.dependentParameterNames = Array.from(extractParameterNamesFromTask($taskJson));
|
|
11337
|
+
for (const parameterName of $taskJson.dependentParameterNames) {
|
|
11338
|
+
// TODO: [🧠] This definition should be made first in the task
|
|
11339
|
+
defineParameter($pipelineJson, {
|
|
11340
|
+
parameterName,
|
|
11341
|
+
parameterDescription: null,
|
|
11342
|
+
isInput: false,
|
|
11343
|
+
isOutput: false,
|
|
11344
|
+
// <- Note: In this case null+false+false means that we do not know yet if it is input or output and we will set it later
|
|
11345
|
+
});
|
|
11358
11346
|
}
|
|
11359
11347
|
}
|
|
11360
11348
|
/**
|
|
11361
|
-
*
|
|
11349
|
+
* Removes transient parsing flags and persists real tasks into the pipeline JSON.
|
|
11362
11350
|
*
|
|
11363
|
-
* @private internal utility of `
|
|
11351
|
+
* @private internal utility of `processPipelineSection`
|
|
11364
11352
|
*/
|
|
11365
|
-
function
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11353
|
+
function persistTaskIfNeeded($taskJson, $pipelineJson) {
|
|
11354
|
+
/*
|
|
11355
|
+
// TODO: [🍧] This should be checked in `MODEL` command + better error message
|
|
11356
|
+
if ($taskJson.taskType !== 'PROMPT_TASK' && $taskJson.modelRequirements !== undefined) {
|
|
11357
|
+
throw new UnexpectedError(
|
|
11358
|
+
spaceTrim(
|
|
11359
|
+
(block) => `
|
|
11360
|
+
Model requirements are defined for the block type ${
|
|
11361
|
+
$taskJson.taskType
|
|
11362
|
+
} which is not a \`PROMPT\` task
|
|
11363
|
+
|
|
11364
|
+
This should be avoided by the \`modelCommandParser\`
|
|
11365
|
+
|
|
11366
|
+
${block(getPipelineIdentification($pipelineJson))}
|
|
11367
|
+
`,
|
|
11368
|
+
),
|
|
11369
|
+
);
|
|
11370
|
+
}
|
|
11371
|
+
*/
|
|
11372
|
+
if (!$taskJson.isTask) {
|
|
11373
|
+
return;
|
|
11369
11374
|
}
|
|
11375
|
+
delete $taskJson.isSectionTypeSet;
|
|
11376
|
+
delete $taskJson.isTask;
|
|
11377
|
+
// TODO: [🍙] Maybe do reorder of `$taskJson` here
|
|
11378
|
+
$pipelineJson.tasks.push($taskJson);
|
|
11370
11379
|
}
|
|
11380
|
+
|
|
11371
11381
|
/**
|
|
11372
|
-
*
|
|
11382
|
+
* Compile pipeline from string (markdown) format to JSON format synchronously
|
|
11373
11383
|
*
|
|
11374
|
-
*
|
|
11384
|
+
* Note: There are 3 similar functions:
|
|
11385
|
+
* - `compilePipeline` **(preferred)** - which properly compiles the promptbook and uses embedding for external knowledge
|
|
11386
|
+
* - `parsePipeline` - use only if you need to compile promptbook synchronously and it contains NO external knowledge
|
|
11387
|
+
* - `preparePipeline` - just one step in the compilation process
|
|
11388
|
+
*
|
|
11389
|
+
* Note: This function does not validate logic of the pipeline only the parsing
|
|
11390
|
+
* Note: This function acts as compilation process
|
|
11391
|
+
*
|
|
11392
|
+
* @param pipelineString {Promptbook} in string markdown format (.book.md)
|
|
11393
|
+
* @returns {Promptbook} compiled in JSON format (.bookc)
|
|
11394
|
+
* @throws {ParseError} if the promptbook string is not valid
|
|
11395
|
+
*
|
|
11396
|
+
* @public exported from `@promptbook/core`
|
|
11375
11397
|
*/
|
|
11376
|
-
function
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
});
|
|
11398
|
+
function parsePipeline(pipelineString) {
|
|
11399
|
+
const $pipelineJson = createInitialPipelineJson(pipelineString);
|
|
11400
|
+
const preparedPipelineString = preparePipelineString(pipelineString, $pipelineJson);
|
|
11401
|
+
const { pipelineHead, pipelineSections } = parsePreparedPipelineSections(preparedPipelineString, $pipelineJson);
|
|
11402
|
+
const getUniqueSectionName = createUniqueSectionNameResolver(pipelineSections);
|
|
11403
|
+
applyPipelineHead(pipelineHead, $pipelineJson);
|
|
11404
|
+
for (const pipelineSection of pipelineSections) {
|
|
11405
|
+
processPipelineSection(pipelineSection, $pipelineJson, getUniqueSectionName);
|
|
11406
|
+
}
|
|
11407
|
+
return finalizeParsedPipeline($pipelineJson);
|
|
11387
11408
|
}
|
|
11388
11409
|
// TODO: [🧠] Maybe more things here can be refactored as high-level abstractions
|
|
11389
11410
|
// TODO: [main] !!4 Warn if used only sync version
|
|
11390
11411
|
// TODO: [🚞] Report here line/column of error
|
|
11391
|
-
// TODO: Use spaceTrim more effectively
|
|
11392
|
-
// TODO: [🧠] Parameter flags - isInput, isOutput, isInternal
|
|
11393
|
-
// TODO: [🥞] Not optimal parsing because `splitMarkdownIntoSections` is executed twice with same string, once through `flattenMarkdown` and second directly here
|
|
11394
|
-
// TODO: [♈] Probably move expectations from tasks to parameters
|
|
11395
|
-
// TODO: [🛠] Actions, instruments (and maybe knowledge) => Functions and tools
|
|
11396
|
-
// TODO: [🍙] Make some standard order of json properties
|
|
11397
11412
|
|
|
11398
11413
|
/**
|
|
11399
11414
|
* Compile pipeline from string (markdown) format to JSON format
|
|
@@ -18141,7 +18156,9 @@
|
|
|
18141
18156
|
};
|
|
18142
18157
|
const mantleRadiusX = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.horizontalStretch;
|
|
18143
18158
|
const mantleRadiusY = size * morphologyProfile.body.bodyRadiusRatio * morphologyProfile.body.verticalStretch * 1.1;
|
|
18144
|
-
const mantleRadiusZ = size *
|
|
18159
|
+
const mantleRadiusZ = size *
|
|
18160
|
+
morphologyProfile.body.bodyRadiusRatio *
|
|
18161
|
+
(0.9 + (morphologyProfile.body.horizontalStretch - 1) * 0.3);
|
|
18145
18162
|
const underbodyRadiusX = mantleRadiusX * (0.9 + (morphologyProfile.tentacles.rootSpreadScale - 1) * 0.08);
|
|
18146
18163
|
const underbodyRadiusY = mantleRadiusY * (0.44 + morphologyProfile.body.lowerDropRatio * 3.1);
|
|
18147
18164
|
const underbodyRadiusZ = mantleRadiusZ * 0.78;
|
|
@@ -18227,7 +18244,11 @@
|
|
|
18227
18244
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, faceEyeSpacing, faceEyeYOffset),
|
|
18228
18245
|
}, faceEyeRadiusX, faceEyeRadiusY, mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, size, palette, timeMs, animationPhase + 0.7 + eyeRandom() * 0.6, interaction, morphologyProfile.face.eyeStyle);
|
|
18229
18246
|
drawProjectedMouth(context, [
|
|
18230
|
-
{
|
|
18247
|
+
{
|
|
18248
|
+
x: -mouthHalfWidth,
|
|
18249
|
+
y: mouthY,
|
|
18250
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, -mouthHalfWidth, mouthY),
|
|
18251
|
+
},
|
|
18231
18252
|
{
|
|
18232
18253
|
x: size * morphologyProfile.face.mouthCenterOffsetRatio,
|
|
18233
18254
|
y: mouthY +
|
|
@@ -18236,7 +18257,11 @@
|
|
|
18236
18257
|
interaction.gazeY * size * 0.01,
|
|
18237
18258
|
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, size * morphologyProfile.face.mouthCenterOffsetRatio, mouthY),
|
|
18238
18259
|
},
|
|
18239
|
-
{
|
|
18260
|
+
{
|
|
18261
|
+
x: mouthHalfWidth,
|
|
18262
|
+
y: mouthY,
|
|
18263
|
+
z: resolveEllipsoidSurfaceDepth(mantleRadiusX, mantleRadiusY, mantleRadiusZ, mouthHalfWidth, mouthY),
|
|
18264
|
+
},
|
|
18240
18265
|
], mantleCenter, headPitch, headYaw, sceneCenterX, sceneCenterY, palette, size);
|
|
18241
18266
|
},
|
|
18242
18267
|
};
|
|
@@ -18397,7 +18422,8 @@
|
|
|
18397
18422
|
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.3 + sway * size * 0.012,
|
|
18398
18423
|
};
|
|
18399
18424
|
const controlPointTwo = {
|
|
18400
|
-
x: anchorPoint.x +
|
|
18425
|
+
x: anchorPoint.x +
|
|
18426
|
+
Math.sin(orbitAngle) * lateralReach * (0.82 + morphologyProfile.tentacles.swayScale * 0.12),
|
|
18401
18427
|
y: anchorPoint.y + flowLength * 0.66,
|
|
18402
18428
|
z: anchorPoint.z + Math.cos(orbitAngle) * depthReach * 0.72 + sway * size * 0.02,
|
|
18403
18429
|
};
|
|
@@ -18462,8 +18488,7 @@
|
|
|
18462
18488
|
context.beginPath();
|
|
18463
18489
|
context.moveTo(startPoint.x, startPoint.y);
|
|
18464
18490
|
context.lineTo(endPoint.x, endPoint.y);
|
|
18465
|
-
context.strokeStyle =
|
|
18466
|
-
tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
|
|
18491
|
+
context.strokeStyle = tentacleStroke.colorBias > 0.6 ? `${palette.secondary}f0` : `${palette.primary}f0`;
|
|
18467
18492
|
context.lineWidth = width;
|
|
18468
18493
|
context.lineCap = 'round';
|
|
18469
18494
|
context.stroke();
|
|
@@ -26658,6 +26683,212 @@
|
|
|
26658
26683
|
* @private internal USE TIMEOUT constant
|
|
26659
26684
|
*/
|
|
26660
26685
|
const MAX_LIST_TIMEOUTS_LIMIT = 100;
|
|
26686
|
+
/**
|
|
26687
|
+
* Creates one formatted timeout-argument validation error.
|
|
26688
|
+
*
|
|
26689
|
+
* @private internal utility of USE TIMEOUT
|
|
26690
|
+
*/
|
|
26691
|
+
function createTimeoutToolArgsError(message) {
|
|
26692
|
+
return new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26693
|
+
${message}
|
|
26694
|
+
`));
|
|
26695
|
+
}
|
|
26696
|
+
/**
|
|
26697
|
+
* Normalizes one optional timeout id string.
|
|
26698
|
+
*
|
|
26699
|
+
* @private internal utility of USE TIMEOUT
|
|
26700
|
+
*/
|
|
26701
|
+
function normalizeOptionalTimeoutId(value) {
|
|
26702
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
26703
|
+
}
|
|
26704
|
+
/**
|
|
26705
|
+
* Parses timeout target selection for tools that accept either `timeoutId` or `allActive: true`.
|
|
26706
|
+
*
|
|
26707
|
+
* @private internal utility of USE TIMEOUT
|
|
26708
|
+
*/
|
|
26709
|
+
function parseTimeoutTargetSelection(args, options) {
|
|
26710
|
+
const timeoutId = normalizeOptionalTimeoutId(args.timeoutId);
|
|
26711
|
+
const allActive = args.allActive === true;
|
|
26712
|
+
if (timeoutId && allActive) {
|
|
26713
|
+
throw createTimeoutToolArgsError(options.bothMessage);
|
|
26714
|
+
}
|
|
26715
|
+
if (allActive) {
|
|
26716
|
+
return { allActive: true };
|
|
26717
|
+
}
|
|
26718
|
+
if (!timeoutId) {
|
|
26719
|
+
throw createTimeoutToolArgsError(options.missingMessage);
|
|
26720
|
+
}
|
|
26721
|
+
return {
|
|
26722
|
+
timeoutId,
|
|
26723
|
+
allActive: false,
|
|
26724
|
+
};
|
|
26725
|
+
}
|
|
26726
|
+
/**
|
|
26727
|
+
* Parses one explicit `dueAt` update value.
|
|
26728
|
+
*
|
|
26729
|
+
* @private internal utility of USE TIMEOUT
|
|
26730
|
+
*/
|
|
26731
|
+
function parseOptionalTimeoutDueAt(value) {
|
|
26732
|
+
if (typeof value !== 'string' || value.trim().length === 0) {
|
|
26733
|
+
return undefined;
|
|
26734
|
+
}
|
|
26735
|
+
const normalizedDueAt = value.trim();
|
|
26736
|
+
const dueAtTimestamp = Date.parse(normalizedDueAt);
|
|
26737
|
+
if (!Number.isFinite(dueAtTimestamp)) {
|
|
26738
|
+
throw createTimeoutToolArgsError('Timeout `dueAt` must be one valid ISO timestamp.');
|
|
26739
|
+
}
|
|
26740
|
+
return new Date(dueAtTimestamp).toISOString();
|
|
26741
|
+
}
|
|
26742
|
+
/**
|
|
26743
|
+
* Parses one explicit `extendByMs` update value.
|
|
26744
|
+
*
|
|
26745
|
+
* @private internal utility of USE TIMEOUT
|
|
26746
|
+
*/
|
|
26747
|
+
function parseOptionalTimeoutExtendByMs(value) {
|
|
26748
|
+
if (typeof value !== 'number') {
|
|
26749
|
+
return undefined;
|
|
26750
|
+
}
|
|
26751
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
26752
|
+
throw createTimeoutToolArgsError('Timeout `extendByMs` must be a positive number of milliseconds.');
|
|
26753
|
+
}
|
|
26754
|
+
return Math.floor(value);
|
|
26755
|
+
}
|
|
26756
|
+
/**
|
|
26757
|
+
* Parses one explicit `recurrenceIntervalMs` update value.
|
|
26758
|
+
*
|
|
26759
|
+
* @private internal utility of USE TIMEOUT
|
|
26760
|
+
*/
|
|
26761
|
+
function parseOptionalTimeoutRecurrenceInterval(value) {
|
|
26762
|
+
if (value === null) {
|
|
26763
|
+
return null;
|
|
26764
|
+
}
|
|
26765
|
+
if (typeof value !== 'number') {
|
|
26766
|
+
return undefined;
|
|
26767
|
+
}
|
|
26768
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
26769
|
+
throw createTimeoutToolArgsError('Timeout `recurrenceIntervalMs` must be a positive number of milliseconds or `null`.');
|
|
26770
|
+
}
|
|
26771
|
+
return Math.floor(value);
|
|
26772
|
+
}
|
|
26773
|
+
/**
|
|
26774
|
+
* Parses one explicit `message` update value.
|
|
26775
|
+
*
|
|
26776
|
+
* @private internal utility of USE TIMEOUT
|
|
26777
|
+
*/
|
|
26778
|
+
function parseOptionalTimeoutMessage(value) {
|
|
26779
|
+
if (value === null) {
|
|
26780
|
+
return null;
|
|
26781
|
+
}
|
|
26782
|
+
if (typeof value !== 'string') {
|
|
26783
|
+
return undefined;
|
|
26784
|
+
}
|
|
26785
|
+
const normalizedMessage = value.trim();
|
|
26786
|
+
return normalizedMessage.length > 0 ? normalizedMessage : null;
|
|
26787
|
+
}
|
|
26788
|
+
/**
|
|
26789
|
+
* Parses one explicit `parameters` update value.
|
|
26790
|
+
*
|
|
26791
|
+
* @private internal utility of USE TIMEOUT
|
|
26792
|
+
*/
|
|
26793
|
+
function parseOptionalTimeoutParameters(value) {
|
|
26794
|
+
if (value === undefined) {
|
|
26795
|
+
return undefined;
|
|
26796
|
+
}
|
|
26797
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
26798
|
+
throw createTimeoutToolArgsError('Timeout `parameters` must be one JSON object.');
|
|
26799
|
+
}
|
|
26800
|
+
return value;
|
|
26801
|
+
}
|
|
26802
|
+
/**
|
|
26803
|
+
* Parses one explicit `paused` update value.
|
|
26804
|
+
*
|
|
26805
|
+
* @private internal utility of USE TIMEOUT
|
|
26806
|
+
*/
|
|
26807
|
+
function parseOptionalTimeoutPaused(value) {
|
|
26808
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
26809
|
+
}
|
|
26810
|
+
/**
|
|
26811
|
+
* Parses patch fields for `update_timeout`.
|
|
26812
|
+
*
|
|
26813
|
+
* @private internal utility of USE TIMEOUT
|
|
26814
|
+
*/
|
|
26815
|
+
function parseTimeoutUpdatePatch(args) {
|
|
26816
|
+
const patch = {};
|
|
26817
|
+
const dueAt = parseOptionalTimeoutDueAt(args.dueAt);
|
|
26818
|
+
const extendByMs = parseOptionalTimeoutExtendByMs(args.extendByMs);
|
|
26819
|
+
const recurrenceIntervalMs = parseOptionalTimeoutRecurrenceInterval(args.recurrenceIntervalMs);
|
|
26820
|
+
const message = parseOptionalTimeoutMessage(args.message);
|
|
26821
|
+
const parameters = parseOptionalTimeoutParameters(args.parameters);
|
|
26822
|
+
const paused = parseOptionalTimeoutPaused(args.paused);
|
|
26823
|
+
if (dueAt !== undefined) {
|
|
26824
|
+
patch.dueAt = dueAt;
|
|
26825
|
+
}
|
|
26826
|
+
if (extendByMs !== undefined) {
|
|
26827
|
+
patch.extendByMs = extendByMs;
|
|
26828
|
+
}
|
|
26829
|
+
if (patch.dueAt !== undefined && patch.extendByMs !== undefined) {
|
|
26830
|
+
throw createTimeoutToolArgsError('Timeout update cannot include both `dueAt` and `extendByMs`.');
|
|
26831
|
+
}
|
|
26832
|
+
if (recurrenceIntervalMs !== undefined) {
|
|
26833
|
+
patch.recurrenceIntervalMs = recurrenceIntervalMs;
|
|
26834
|
+
}
|
|
26835
|
+
if (message !== undefined) {
|
|
26836
|
+
patch.message = message;
|
|
26837
|
+
}
|
|
26838
|
+
if (parameters !== undefined) {
|
|
26839
|
+
patch.parameters = parameters;
|
|
26840
|
+
}
|
|
26841
|
+
if (paused !== undefined) {
|
|
26842
|
+
patch.paused = paused;
|
|
26843
|
+
}
|
|
26844
|
+
return patch;
|
|
26845
|
+
}
|
|
26846
|
+
/**
|
|
26847
|
+
* Determines whether the patch contains fields that are only supported for single-timeout updates.
|
|
26848
|
+
*
|
|
26849
|
+
* @private internal utility of USE TIMEOUT
|
|
26850
|
+
*/
|
|
26851
|
+
function hasSingleTimeoutOnlyPatchFields(patch) {
|
|
26852
|
+
return (patch.dueAt !== undefined ||
|
|
26853
|
+
patch.extendByMs !== undefined ||
|
|
26854
|
+
patch.recurrenceIntervalMs !== undefined ||
|
|
26855
|
+
patch.message !== undefined ||
|
|
26856
|
+
patch.parameters !== undefined);
|
|
26857
|
+
}
|
|
26858
|
+
/**
|
|
26859
|
+
* Parses bulk timeout update arguments.
|
|
26860
|
+
*
|
|
26861
|
+
* @private internal utility of USE TIMEOUT
|
|
26862
|
+
*/
|
|
26863
|
+
function parseBulkTimeoutUpdateArgs(patch) {
|
|
26864
|
+
if (patch.paused === undefined) {
|
|
26865
|
+
throw createTimeoutToolArgsError('Bulk timeout update with `allActive: true` requires `paused` to be explicitly set.');
|
|
26866
|
+
}
|
|
26867
|
+
if (hasSingleTimeoutOnlyPatchFields(patch)) {
|
|
26868
|
+
throw createTimeoutToolArgsError('Bulk timeout update only supports the `paused` field.');
|
|
26869
|
+
}
|
|
26870
|
+
return {
|
|
26871
|
+
allActive: true,
|
|
26872
|
+
paused: patch.paused,
|
|
26873
|
+
};
|
|
26874
|
+
}
|
|
26875
|
+
/**
|
|
26876
|
+
* Parses single-timeout update arguments.
|
|
26877
|
+
*
|
|
26878
|
+
* @private internal utility of USE TIMEOUT
|
|
26879
|
+
*/
|
|
26880
|
+
function parseSingleTimeoutUpdateArgs(timeoutId, patch) {
|
|
26881
|
+
if (!timeoutId) {
|
|
26882
|
+
throw createTimeoutToolArgsError('Timeout `timeoutId` is required for single-timeout updates.');
|
|
26883
|
+
}
|
|
26884
|
+
if (Object.keys(patch).length === 0) {
|
|
26885
|
+
throw createTimeoutToolArgsError('Timeout update must include at least one editable field.');
|
|
26886
|
+
}
|
|
26887
|
+
return {
|
|
26888
|
+
timeoutId,
|
|
26889
|
+
patch,
|
|
26890
|
+
};
|
|
26891
|
+
}
|
|
26661
26892
|
/**
|
|
26662
26893
|
* Parses and validates `USE TIMEOUT` tool arguments.
|
|
26663
26894
|
*
|
|
@@ -26684,22 +26915,14 @@
|
|
|
26684
26915
|
* Parses `cancel_timeout` input.
|
|
26685
26916
|
*/
|
|
26686
26917
|
cancel(args) {
|
|
26687
|
-
const
|
|
26688
|
-
|
|
26689
|
-
|
|
26690
|
-
|
|
26691
|
-
|
|
26692
|
-
`));
|
|
26693
|
-
}
|
|
26694
|
-
if (allActive) {
|
|
26918
|
+
const target = parseTimeoutTargetSelection(args, {
|
|
26919
|
+
bothMessage: 'Timeout cancellation must target either one `timeoutId` or `allActive: true`, not both.',
|
|
26920
|
+
missingMessage: 'Timeout `timeoutId` is required unless you pass `allActive: true`.',
|
|
26921
|
+
});
|
|
26922
|
+
if (target.allActive) {
|
|
26695
26923
|
return { allActive: true };
|
|
26696
26924
|
}
|
|
26697
|
-
|
|
26698
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26699
|
-
Timeout \`timeoutId\` is required unless you pass \`allActive: true\`.
|
|
26700
|
-
`));
|
|
26701
|
-
}
|
|
26702
|
-
return { timeoutId };
|
|
26925
|
+
return { timeoutId: target.timeoutId };
|
|
26703
26926
|
},
|
|
26704
26927
|
/**
|
|
26705
26928
|
* Parses `list_timeouts` input.
|
|
@@ -26730,106 +26953,14 @@
|
|
|
26730
26953
|
* Parses `update_timeout` input.
|
|
26731
26954
|
*/
|
|
26732
26955
|
update(args) {
|
|
26733
|
-
const
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26742
|
-
Timeout update requires one \`timeoutId\` or \`allActive: true\`.
|
|
26743
|
-
`));
|
|
26744
|
-
}
|
|
26745
|
-
const patch = {};
|
|
26746
|
-
if (typeof args.dueAt === 'string' && args.dueAt.trim().length > 0) {
|
|
26747
|
-
const normalizedDueAt = args.dueAt.trim();
|
|
26748
|
-
const dueAtTimestamp = Date.parse(normalizedDueAt);
|
|
26749
|
-
if (!Number.isFinite(dueAtTimestamp)) {
|
|
26750
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26751
|
-
Timeout \`dueAt\` must be one valid ISO timestamp.
|
|
26752
|
-
`));
|
|
26753
|
-
}
|
|
26754
|
-
patch.dueAt = new Date(dueAtTimestamp).toISOString();
|
|
26755
|
-
}
|
|
26756
|
-
if (typeof args.extendByMs === 'number') {
|
|
26757
|
-
if (!Number.isFinite(args.extendByMs) || args.extendByMs <= 0) {
|
|
26758
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26759
|
-
Timeout \`extendByMs\` must be a positive number of milliseconds.
|
|
26760
|
-
`));
|
|
26761
|
-
}
|
|
26762
|
-
patch.extendByMs = Math.floor(args.extendByMs);
|
|
26763
|
-
}
|
|
26764
|
-
if (patch.dueAt !== undefined && patch.extendByMs !== undefined) {
|
|
26765
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26766
|
-
Timeout update cannot include both \`dueAt\` and \`extendByMs\`.
|
|
26767
|
-
`));
|
|
26768
|
-
}
|
|
26769
|
-
if (args.recurrenceIntervalMs === null) {
|
|
26770
|
-
patch.recurrenceIntervalMs = null;
|
|
26771
|
-
}
|
|
26772
|
-
else if (typeof args.recurrenceIntervalMs === 'number') {
|
|
26773
|
-
if (!Number.isFinite(args.recurrenceIntervalMs) || args.recurrenceIntervalMs <= 0) {
|
|
26774
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26775
|
-
Timeout \`recurrenceIntervalMs\` must be a positive number of milliseconds or \`null\`.
|
|
26776
|
-
`));
|
|
26777
|
-
}
|
|
26778
|
-
patch.recurrenceIntervalMs = Math.floor(args.recurrenceIntervalMs);
|
|
26779
|
-
}
|
|
26780
|
-
if (args.message === null) {
|
|
26781
|
-
patch.message = null;
|
|
26782
|
-
}
|
|
26783
|
-
else if (typeof args.message === 'string') {
|
|
26784
|
-
const normalizedMessage = args.message.trim();
|
|
26785
|
-
patch.message = normalizedMessage.length > 0 ? normalizedMessage : null;
|
|
26786
|
-
}
|
|
26787
|
-
if (args.parameters !== undefined) {
|
|
26788
|
-
if (!args.parameters || typeof args.parameters !== 'object' || Array.isArray(args.parameters)) {
|
|
26789
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26790
|
-
Timeout \`parameters\` must be one JSON object.
|
|
26791
|
-
`));
|
|
26792
|
-
}
|
|
26793
|
-
patch.parameters = args.parameters;
|
|
26794
|
-
}
|
|
26795
|
-
if (typeof args.paused === 'boolean') {
|
|
26796
|
-
patch.paused = args.paused;
|
|
26797
|
-
}
|
|
26798
|
-
if (allActive) {
|
|
26799
|
-
if (patch.paused === undefined) {
|
|
26800
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26801
|
-
Bulk timeout update with \`allActive: true\` requires \`paused\` to be explicitly set.
|
|
26802
|
-
`));
|
|
26803
|
-
}
|
|
26804
|
-
const hasSingleOnlyPatch = patch.dueAt !== undefined ||
|
|
26805
|
-
patch.extendByMs !== undefined ||
|
|
26806
|
-
patch.recurrenceIntervalMs !== undefined ||
|
|
26807
|
-
patch.message !== undefined ||
|
|
26808
|
-
patch.parameters !== undefined;
|
|
26809
|
-
if (hasSingleOnlyPatch) {
|
|
26810
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26811
|
-
Bulk timeout update only supports the \`paused\` field.
|
|
26812
|
-
`));
|
|
26813
|
-
}
|
|
26814
|
-
return {
|
|
26815
|
-
allActive: true,
|
|
26816
|
-
paused: patch.paused,
|
|
26817
|
-
};
|
|
26818
|
-
}
|
|
26819
|
-
if (!timeoutId) {
|
|
26820
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26821
|
-
Timeout \`timeoutId\` is required for single-timeout updates.
|
|
26822
|
-
`));
|
|
26823
|
-
}
|
|
26824
|
-
if (Object.keys(patch).length === 0) {
|
|
26825
|
-
throw new PipelineExecutionError(_spaceTrim.spaceTrim(`
|
|
26826
|
-
Timeout update must include at least one editable field.
|
|
26827
|
-
`));
|
|
26828
|
-
}
|
|
26829
|
-
return {
|
|
26830
|
-
timeoutId,
|
|
26831
|
-
patch,
|
|
26832
|
-
};
|
|
26956
|
+
const target = parseTimeoutTargetSelection(args, {
|
|
26957
|
+
bothMessage: 'Timeout update must target either one `timeoutId` or `allActive: true`, not both.',
|
|
26958
|
+
missingMessage: 'Timeout update requires one `timeoutId` or `allActive: true`.',
|
|
26959
|
+
});
|
|
26960
|
+
const patch = parseTimeoutUpdatePatch(args);
|
|
26961
|
+
return target.allActive
|
|
26962
|
+
? parseBulkTimeoutUpdateArgs(patch)
|
|
26963
|
+
: parseSingleTimeoutUpdateArgs(target.timeoutId, patch);
|
|
26833
26964
|
},
|
|
26834
26965
|
};
|
|
26835
26966
|
|