@zerowidth/workbench-sdk 2.6.0 → 2.7.1
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.
|
@@ -19,6 +19,32 @@ const renderVariable = (value) => {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* A token's key and its fallback: `name`, or `name:"Unknown Name"` for the
|
|
24
|
+
* text to use when `name` is missing or empty. Inside the quotes, `\"` is a
|
|
25
|
+
* quote, `\\` a backslash and `\n` a newline. Same rules as the System
|
|
26
|
+
* Prompt node.
|
|
27
|
+
*/
|
|
28
|
+
const TOKEN = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*"((?:[^"\\]|\\.)*)")?\s*$/;
|
|
29
|
+
|
|
30
|
+
const isEmpty = (value) =>
|
|
31
|
+
value === null || value === undefined || (typeof value === "string" && value.trim() === "");
|
|
32
|
+
|
|
33
|
+
/** The text a `{{…}}` token becomes, or null to leave it as written. */
|
|
34
|
+
const resolveToken = (variables, inner) => {
|
|
35
|
+
const exact = variables.find((v) => Object.keys(v).includes(inner));
|
|
36
|
+
if (exact) return renderVariable(exact[inner]);
|
|
37
|
+
const m = TOKEN.exec(inner);
|
|
38
|
+
if (!m) return null;
|
|
39
|
+
const key = m[1];
|
|
40
|
+
const fallback = m[2] === undefined ? undefined : m[2].replace(/\\(.)/g, (_, c) => (c === "n" ? "\n" : c));
|
|
41
|
+
const found = variables.find((v) => Object.keys(v).includes(key));
|
|
42
|
+
const value = found ? found[key] : undefined;
|
|
43
|
+
if (!isEmpty(value)) return renderVariable(value);
|
|
44
|
+
if (fallback !== undefined) return fallback;
|
|
45
|
+
return found ? renderVariable(value) : null;
|
|
46
|
+
};
|
|
47
|
+
|
|
22
48
|
export default async ({inputs, settings, config}) => {
|
|
23
49
|
|
|
24
50
|
// If an input value is provided, use it; otherwise use the value from settings
|
|
@@ -45,15 +71,10 @@ export default async ({inputs, settings, config}) => {
|
|
|
45
71
|
// do we have a text content item and what index is it
|
|
46
72
|
let textContentIndex = message.content.findIndex(item => item.type === 'text');
|
|
47
73
|
if(textContentIndex !== -1) {
|
|
48
|
-
message.content[textContentIndex].text = message.content[textContentIndex].text.replace(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if(variable) {
|
|
53
|
-
return renderVariable(variable[p1]);
|
|
54
|
-
}
|
|
55
|
-
return match;
|
|
56
|
-
});
|
|
74
|
+
message.content[textContentIndex].text = message.content[textContentIndex].text.replace(
|
|
75
|
+
/\{\{(.*?)\}\}/g,
|
|
76
|
+
(match, p1) => resolveToken(variables, p1) ?? match,
|
|
77
|
+
);
|
|
57
78
|
}
|
|
58
79
|
|
|
59
80
|
// Return the string value
|
|
@@ -19,6 +19,44 @@ const renderVariable = (value) => {
|
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* A token's key and its fallback: `name`, or `name:"Unknown Name"` for the
|
|
24
|
+
* text to use when `name` is missing or empty. Inside the quotes, `\"` is a
|
|
25
|
+
* quote, `\\` a backslash and `\n` a newline. Spaces around the parts are
|
|
26
|
+
* allowed. A fallback can't hold `}}`, since that ends the token. Null when
|
|
27
|
+
* the token isn't in either form.
|
|
28
|
+
*/
|
|
29
|
+
const TOKEN = /^\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?::\s*"((?:[^"\\]|\\.)*)")?\s*$/;
|
|
30
|
+
const parseToken = (inner) => {
|
|
31
|
+
const m = TOKEN.exec(inner);
|
|
32
|
+
if (!m) return null;
|
|
33
|
+
return {
|
|
34
|
+
key: m[1],
|
|
35
|
+
fallback: m[2] === undefined ? undefined : m[2].replace(/\\(.)/g, (_, c) => (c === "n" ? "\n" : c)),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const isEmpty = (value) =>
|
|
40
|
+
value === null || value === undefined || (typeof value === "string" && value.trim() === "");
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The text a `{{…}}` token becomes, or null to leave it as written: a
|
|
44
|
+
* variable's value, else the token's fallback when the value is missing or
|
|
45
|
+
* empty. A token naming nothing, with no fallback, stays in the text.
|
|
46
|
+
*/
|
|
47
|
+
const resolveToken = (variables, inner) => {
|
|
48
|
+
// An exact key first, so keys that aren't plain names keep working.
|
|
49
|
+
const exact = variables.find((v) => Object.keys(v).includes(inner));
|
|
50
|
+
if (exact) return renderVariable(exact[inner]);
|
|
51
|
+
const token = parseToken(inner);
|
|
52
|
+
if (!token) return null;
|
|
53
|
+
const found = variables.find((v) => Object.keys(v).includes(token.key));
|
|
54
|
+
const value = found ? found[token.key] : undefined;
|
|
55
|
+
if (!isEmpty(value)) return renderVariable(value);
|
|
56
|
+
if (token.fallback !== undefined) return token.fallback;
|
|
57
|
+
return found ? renderVariable(value) : null;
|
|
58
|
+
};
|
|
59
|
+
|
|
22
60
|
export default async ({inputs, settings, config}) => {
|
|
23
61
|
// Initialize variables array if not provided
|
|
24
62
|
if(!inputs.variables) {
|
|
@@ -57,14 +95,7 @@ export default async ({inputs, settings, config}) => {
|
|
|
57
95
|
let fullContent = chainedContent ? `${chainedContent}\n\n${baseContent}` : baseContent;
|
|
58
96
|
|
|
59
97
|
const fill = (text) =>
|
|
60
|
-
text.replace(/\{\{(.*?)\}\}/g, (match, p1) =>
|
|
61
|
-
// look for a variable with the key p1
|
|
62
|
-
let variable = variables.find(variable => Object.keys(variable).find(key => key === p1));
|
|
63
|
-
if(variable) {
|
|
64
|
-
return renderVariable(variable[p1]);
|
|
65
|
-
}
|
|
66
|
-
return match;
|
|
67
|
-
});
|
|
98
|
+
text.replace(/\{\{(.*?)\}\}/g, (match, p1) => resolveToken(variables, p1) ?? match);
|
|
68
99
|
|
|
69
100
|
// Values filled in can change from run to run (the time, memory, search
|
|
70
101
|
// results); the text before the first of them doesn't. The block records
|
|
@@ -73,7 +104,8 @@ export default async ({inputs, settings, config}) => {
|
|
|
73
104
|
// removes the hint before any model sees it.
|
|
74
105
|
let firstFilled = -1;
|
|
75
106
|
for (const m of fullContent.matchAll(/\{\{(.*?)\}\}/g)) {
|
|
76
|
-
|
|
107
|
+
// A fallback is filled in too, so it ends the fixed part as a value does.
|
|
108
|
+
if (resolveToken(variables, m[1]) !== null) {
|
|
77
109
|
firstFilled = m.index;
|
|
78
110
|
break;
|
|
79
111
|
}
|
package/package.json
CHANGED
package/src/utilities/typers.js
CHANGED
|
@@ -229,12 +229,17 @@ export function convertImportToNodeType(importDef) {
|
|
|
229
229
|
requestedSnapshot: processedImportDef.requestedSnapshot,
|
|
230
230
|
inputs: inputNodes.map(node => {
|
|
231
231
|
if (node.type === 'input-data') {
|
|
232
|
+
// An input with a default doesn't need a connection: left
|
|
233
|
+
// unconnected (or left out by a tool call), it gets the default,
|
|
234
|
+
// the same as running the imported flow on its own.
|
|
235
|
+
const defaultValue = node.settings?.default_value;
|
|
232
236
|
return {
|
|
233
237
|
// Use the node's settings.key as the input name to match port connections
|
|
234
238
|
name: node.settings?.key || 'data',
|
|
235
|
-
display_name:
|
|
239
|
+
display_name: node.settings?.display_name || node.settings?.key || 'Data',
|
|
236
240
|
type: node.settings?.type || 'any',
|
|
237
|
-
required:
|
|
241
|
+
required: defaultValue === undefined,
|
|
242
|
+
...(defaultValue !== undefined ? { default: defaultValue } : {}),
|
|
238
243
|
is_data_input: true,
|
|
239
244
|
description: node.settings?.description || 'Imported data input'
|
|
240
245
|
};
|