langflower 0.0.10 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -70
- package/docs/public/README.md +2 -2
- package/docs/public/configuration.md +4 -0
- package/docs/public/extending.md +18 -2
- package/docs/public/getting-started.md +7 -0
- package/docs/public/how-it-works.md +18 -11
- package/docs/public/product.md +35 -25
- package/package.json +1 -1
- package/ui-dist/chunk-QI4IYILJ.js +4 -0
- package/ui-dist/{chunk-7UMVYN3A.js → chunk-UG3GFV4Y.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/main-ODIDCCNX.js +315 -0
- package/ui-dist/styles-2JMBAQC3.css +1 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +2 -2
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +39 -7
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.d.ts +11 -0
- package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.js +2 -2
- package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +10 -0
- package/vendor/common-nodes/dist/ai/nodes/critique/node.js +5 -1
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +7 -1
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +5 -1
- package/vendor/common-nodes/dist/ai/nodes/review/node.js +7 -1
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +62 -22
- package/vendor/common-nodes/dist/catalog.js +10 -0
- package/vendor/common-nodes/dist/crawl/crawl/node.js +11 -3
- package/vendor/common-nodes/dist/crawl/crawl-tools/node.js +5 -1
- package/vendor/common-nodes/dist/crawl/extract-links/node.js +7 -1
- package/vendor/common-nodes/dist/crawl/fetch-url/node.js +13 -4
- package/vendor/common-nodes/dist/crawl/save-page/node.js +11 -3
- package/vendor/common-nodes/dist/embeddings/create-embedding.d.ts +39 -0
- package/vendor/common-nodes/dist/embeddings/create-embedding.js +154 -0
- package/vendor/common-nodes/dist/embeddings/embed-provider/node.d.ts +53 -0
- package/vendor/common-nodes/dist/embeddings/embed-provider/node.js +126 -0
- package/vendor/common-nodes/dist/embeddings/embed-similarity/node.d.ts +20 -0
- package/vendor/common-nodes/dist/embeddings/embed-similarity/node.js +81 -0
- package/vendor/common-nodes/dist/embeddings/embed-text/node.d.ts +53 -0
- package/vendor/common-nodes/dist/embeddings/embed-text/node.js +91 -0
- package/vendor/common-nodes/dist/embeddings/from-embedding.d.ts +8 -0
- package/vendor/common-nodes/dist/embeddings/from-embedding.js +24 -0
- package/vendor/common-nodes/dist/embeddings/resolve-embedding-provider-model.d.ts +11 -0
- package/vendor/common-nodes/dist/embeddings/resolve-embedding-provider-model.js +8 -0
- package/vendor/common-nodes/dist/flow/checkpoint/node.js +7 -3
- package/vendor/common-nodes/dist/flow/delay/node.d.ts +2 -6
- package/vendor/common-nodes/dist/flow/delay/node.js +13 -9
- package/vendor/common-nodes/dist/flow/loop/node.js +7 -1
- package/vendor/common-nodes/dist/flow/merge/node.js +7 -1
- package/vendor/common-nodes/dist/flow/repeat/node.js +5 -1
- package/vendor/common-nodes/dist/flow/router/node.js +7 -1
- package/vendor/common-nodes/dist/hitl/chat-input/node.d.ts +2 -2
- package/vendor/common-nodes/dist/hitl/chat-input/node.js +8 -4
- package/vendor/common-nodes/dist/hitl/review-gate/node.js +11 -5
- package/vendor/common-nodes/dist/langflower-tools/node.js +5 -1
- package/vendor/common-nodes/dist/logic/assert/node.js +7 -1
- package/vendor/common-nodes/dist/logic/compare/node.js +7 -1
- package/vendor/common-nodes/dist/logic/gate/node.js +7 -1
- package/vendor/common-nodes/dist/logic/if/node.js +7 -1
- package/vendor/common-nodes/dist/logic/switch/node.js +7 -1
- package/vendor/common-nodes/dist/mcp/mcp-http/node.js +55 -53
- package/vendor/common-nodes/dist/mcp/mcp-stdio/node.js +52 -50
- package/vendor/common-nodes/dist/memory/memory-tools/node.js +5 -1
- package/vendor/common-nodes/dist/output/finish/node.js +13 -1
- package/vendor/common-nodes/dist/output/preview/node.js +9 -2
- package/vendor/common-nodes/dist/primitives/boolean/node.js +7 -1
- package/vendor/common-nodes/dist/primitives/number/node.js +7 -1
- package/vendor/common-nodes/dist/primitives/string/node.js +7 -1
- package/vendor/common-nodes/dist/primitives/string-multiline/node.d.ts +17 -0
- package/vendor/common-nodes/dist/primitives/string-multiline/node.js +31 -0
- package/vendor/common-nodes/dist/text/append-file/node.js +11 -3
- package/vendor/common-nodes/dist/text/concat/node.js +5 -1
- package/vendor/common-nodes/dist/text/read-file/node.js +9 -3
- package/vendor/common-nodes/dist/text/split-paced/node.d.ts +21 -0
- package/vendor/common-nodes/dist/text/split-paced/node.js +93 -0
- package/vendor/common-nodes/dist/text/write-file/node.js +11 -3
- package/vendor/common-nodes/dist/tools/tool-collection/node.js +5 -2
- package/vendor/common-nodes/package.json +4 -0
- package/vendor/node-sdk/dist/node-factory/define-embed/embed-handle.d.ts +25 -0
- package/vendor/node-sdk/dist/node-factory/define-embed/embed-handle.js +25 -0
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.d.ts +1 -0
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.js +1 -0
- package/vendor/node-sdk/dist/node-factory/define-node/define-node.d.ts +2 -0
- package/vendor/node-sdk/dist/node-factory/define-node/define-node.js +4 -1
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/define-reactive-node.d.ts +3 -1
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/define-reactive-node.js +2 -1
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/io-helpers.d.ts +15 -0
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/io-helpers.js +17 -1
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/port-meta.d.ts +17 -4
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/test/samples/delay-node.js +4 -2
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/types.d.ts +4 -3
- package/vendor/node-sdk/dist/testing/create-node-harness.d.ts +30 -0
- package/vendor/node-sdk/dist/testing/create-node-harness.js +68 -0
- package/vendor/node-sdk/package.json +4 -4
- package/vendor/runtime/dist/types.d.ts +1 -1
- package/vendor/server/dist/bootstrap/project-bootstrap.service.d.ts +1 -1
- package/vendor/server/dist/bootstrap/seed-skeleton.d.ts +2 -2
- package/vendor/server/dist/bootstrap/seed-skeleton.js +17 -2
- package/vendor/server/dist/bridge/bind-embed-context.d.ts +7 -0
- package/vendor/server/dist/bridge/bind-embed-context.js +16 -0
- package/vendor/server/dist/bridge/build-execution-context.d.ts +2 -2
- package/vendor/server/dist/bridge/build-execution-context.js +6 -2
- package/vendor/server/dist/bridge/emit-bootstrap.js +4 -1
- package/vendor/server/dist/bridge/settings-draft-controller.js +7 -3
- package/vendor/server/dist/bridge/wire-config-handlers.js +2 -0
- package/vendor/server/dist/bridge/wire-editor-handlers.js +14 -0
- package/vendor/server/dist/config/langflower-config.service.d.ts +2 -0
- package/vendor/server/dist/config/langflower-config.service.js +30 -0
- package/vendor/server/dist/session/build-session-bootstrap.js +1 -0
- package/vendor/server/dist/session/langflower-session.d.ts +3 -1
- package/vendor/server/dist/session/langflower-session.js +3 -1
- package/vendor/server/dist/workflow/apply-editor-mutation.js +6 -0
- package/vendor/server/dist/workflow/workflow-persisted-inputs.js +8 -6
- package/vendor/server/skeleton/instructions.md +15 -3
- package/vendor/server/skeleton/nodes/hello-embed/README.md +185 -0
- package/vendor/server/skeleton/nodes/hello-embed/hello-embed.nodes.test.ts +181 -0
- package/vendor/server/skeleton/nodes/hello-embed/ingest.ts +126 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/chunk-markdown.test.ts +40 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/chunk-markdown.ts +123 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/ingest-search.test.ts +180 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/ingest.ts +128 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/paths.test.ts +20 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/paths.ts +70 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/schema.ts +21 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/search.ts +254 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/text-embedder.ts +14 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/vectors.ts +126 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/walk-markdown.test.ts +62 -0
- package/vendor/server/skeleton/nodes/hello-embed/lib/walk-markdown.ts +67 -0
- package/vendor/server/skeleton/nodes/hello-embed/package.json +11 -0
- package/vendor/server/skeleton/nodes/hello-embed/search-handle.ts +119 -0
- package/vendor/server/skeleton/nodes/hello-embed/search.ts +98 -0
- package/vendor/server/skeleton/nodes/hello-embed/tsconfig.json +14 -0
- package/vendor/server/skeleton/nodes/my-nodes/README.md +13 -3
- package/vendor/server/skeleton/schemas/langflower-config.schema.json +4 -0
- package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +99 -26
- package/vendor/server/skeleton/skills/langflower-helper/architecture.md +11 -6
- package/vendor/server/skeleton/skills/langflower-helper/layout.md +24 -18
- package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +8 -1
- package/vendor/server/skeleton/workflows/kb-ingest.json +88 -0
- package/vendor/server/skeleton/workflows/kb-manual-search.json +105 -0
- package/vendor/server/skeleton/workflows/kb-rag.json +192 -0
- package/vendor/server/skeleton/workflows/kb-tool.json +111 -0
- package/vendor/shared/dist/langflower-bus-config.d.ts +22 -6
- package/vendor/shared/dist/langflower-bus-config.js +23 -7
- package/vendor/shared/dist/langflower-config/merge-langflower-config-layers.test.js +8 -0
- package/vendor/shared/dist/langflower-config/settings-draft.d.ts +5 -0
- package/vendor/shared/dist/langflower-config/settings-draft.js +15 -7
- package/vendor/shared/dist/langflower.d.ts +1 -1
- package/vendor/shared/dist/langflower.js +1 -1
- package/vendor/shared/dist/types/langflower-bootstrap.d.ts +6 -0
- package/vendor/shared/dist/types/langflower-config.d.ts +17 -0
- package/vendor/shared/dist/types/langflower-palette.d.ts +2 -1
- package/vendor/tools/dist/harness-types.d.ts +1 -1
- package/vendor/tools/dist/mcp/build-mcp-handle.d.ts +3 -21
- package/vendor/tools/dist/mcp/build-mcp-handle.js +2 -11
- package/vendor/tools/dist/mcp/create-system-mcp-handles.d.ts +9 -6
- package/vendor/tools/dist/mcp/create-system-mcp-handles.js +21 -17
- package/vendor/tools/package.json +1 -0
- package/ui-dist/chunk-MDGXX73Z.js +0 -4
- package/ui-dist/main-Z7CPFXFA.js +0 -313
- package/ui-dist/styles-2TMDAUD7.css +0 -1
|
@@ -4,7 +4,13 @@ export const booleanNode = defineReactiveNode({
|
|
|
4
4
|
type: 'common-boolean',
|
|
5
5
|
displayName: 'Boolean',
|
|
6
6
|
category: 'Primitives',
|
|
7
|
-
description:
|
|
7
|
+
description: `
|
|
8
|
+
Put a constant true/false on the canvas.
|
|
9
|
+
|
|
10
|
+
Typical uses:
|
|
11
|
+
- Drive IF, Gate, or Assert without another node
|
|
12
|
+
- A toggle you can flip while authoring
|
|
13
|
+
`.trim(),
|
|
8
14
|
uiSchema: [],
|
|
9
15
|
bind(_ctx, { makeInput, configureOutput }) {
|
|
10
16
|
const value$ = makeInput('value', {
|
|
@@ -4,7 +4,13 @@ export const numberNode = defineReactiveNode({
|
|
|
4
4
|
type: 'common-number',
|
|
5
5
|
displayName: 'Number',
|
|
6
6
|
category: 'Primitives',
|
|
7
|
-
description:
|
|
7
|
+
description: `
|
|
8
|
+
Put a constant number on the canvas and wire it onward.
|
|
9
|
+
|
|
10
|
+
Typical uses:
|
|
11
|
+
- A Repeat **count** or Delay milliseconds
|
|
12
|
+
- A threshold for Compare or Loop
|
|
13
|
+
`.trim(),
|
|
8
14
|
uiSchema: [],
|
|
9
15
|
bind(_ctx, { makeInput, configureOutput }) {
|
|
10
16
|
const value$ = makeInput('value', {
|
|
@@ -4,7 +4,13 @@ export const stringNode = defineReactiveNode({
|
|
|
4
4
|
type: 'common-string',
|
|
5
5
|
displayName: 'String',
|
|
6
6
|
category: 'Primitives',
|
|
7
|
-
description:
|
|
7
|
+
description: `
|
|
8
|
+
Put a short constant string on the canvas and wire it onward.
|
|
9
|
+
|
|
10
|
+
Typical uses:
|
|
11
|
+
- A file path for Read File
|
|
12
|
+
- A short label or prompt fragment
|
|
13
|
+
`.trim(),
|
|
8
14
|
uiSchema: [],
|
|
9
15
|
bind(_ctx, { makeInput, configureOutput }) {
|
|
10
16
|
const value$ = makeInput('value', {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Multiline string literal — inline `value` textarea, no wire required. */
|
|
2
|
+
export declare const stringMultilineNode: {
|
|
3
|
+
readonly emitOncePerActivation: boolean;
|
|
4
|
+
readonly stopsRun: boolean;
|
|
5
|
+
readonly chatEntry: boolean;
|
|
6
|
+
readonly uiSchema: readonly [];
|
|
7
|
+
readonly bypassPorts: Record<string, import("@langflower/node-sdk").WireType>;
|
|
8
|
+
readonly inputsConfigs: readonly [import("@langflower/node-sdk").InputPortMeta<import("@langflower/node-sdk").ExecutionContext<readonly [], Record<string, never>>>, ...import("@langflower/node-sdk").InputPortMeta<unknown>[]];
|
|
9
|
+
readonly outputsConfigs: (import("@langflower/node-sdk").OutputParams & import("@langflower/node-sdk").PortMeta)[];
|
|
10
|
+
readonly getInstance: () => import("@langflower/node-sdk").ReactiveNodeInstance<readonly [], Record<string, never>>;
|
|
11
|
+
readonly paletteSecondary?: true;
|
|
12
|
+
readonly description?: string;
|
|
13
|
+
readonly type: string;
|
|
14
|
+
readonly displayName: string;
|
|
15
|
+
readonly category: string | undefined;
|
|
16
|
+
readonly icon: string | undefined;
|
|
17
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
2
|
+
/** Multiline string literal — inline `value` textarea, no wire required. */
|
|
3
|
+
export const stringMultilineNode = defineReactiveNode({
|
|
4
|
+
type: 'common-string-multiline',
|
|
5
|
+
displayName: 'String (multiline)',
|
|
6
|
+
category: 'Primitives',
|
|
7
|
+
description: `
|
|
8
|
+
Paste longer text on the node — several lines, not a single field.
|
|
9
|
+
|
|
10
|
+
Typical uses:
|
|
11
|
+
- A prompt or email draft
|
|
12
|
+
- CSV or log text to feed into Split (paced)
|
|
13
|
+
`.trim(),
|
|
14
|
+
uiSchema: [],
|
|
15
|
+
bind(_ctx, { makeInput, configureOutput }) {
|
|
16
|
+
const value$ = makeInput('value', {
|
|
17
|
+
name: 'value',
|
|
18
|
+
wireType: 'string',
|
|
19
|
+
inline: 'text-multiline',
|
|
20
|
+
defaultValue: '',
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
inputs: [value$],
|
|
24
|
+
outputs: [
|
|
25
|
+
configureOutput('value', value$, {
|
|
26
|
+
wireType: 'string',
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createProjectFilesContext } from '@langflower/tools/create-project-files-context';
|
|
3
3
|
import { from, map, mergeMap, throwError } from 'rxjs';
|
|
4
4
|
import { getRunHostServices } from '../../ai/features/run-host-services.js';
|
|
@@ -10,7 +10,13 @@ export const appendFileNode = defineReactiveNode({
|
|
|
10
10
|
type: 'common-append-file',
|
|
11
11
|
displayName: 'Append File',
|
|
12
12
|
category: 'Text',
|
|
13
|
-
description:
|
|
13
|
+
description: `
|
|
14
|
+
Add **content** to the end of a project file. If the file already has text, **delimiter** is inserted first.
|
|
15
|
+
|
|
16
|
+
Typical uses:
|
|
17
|
+
- Append a log line
|
|
18
|
+
- Grow a notes file across loop iterations
|
|
19
|
+
`.trim(),
|
|
14
20
|
uiSchema: [],
|
|
15
21
|
bind(ctx, { makeInput, configureOutput, combineInputs }) {
|
|
16
22
|
const pathInput = makeInput('path', {
|
|
@@ -37,7 +43,9 @@ export const appendFileNode = defineReactiveNode({
|
|
|
37
43
|
delimiter: String(rawDelimiter ?? ''),
|
|
38
44
|
content: String(rawContent ?? ''),
|
|
39
45
|
ec,
|
|
40
|
-
}))
|
|
46
|
+
}))
|
|
47
|
+
.pipe(withLoading())
|
|
48
|
+
.pipeValue(mergeMap(({ path: filePath, delimiter: fileDelimiter, content: fileContent, ec, }) => {
|
|
41
49
|
if (filePath.length === 0) {
|
|
42
50
|
return throwError(() => new Error('Append File requires a non-empty path.'));
|
|
43
51
|
}
|
|
@@ -4,7 +4,11 @@ export const concatNode = defineReactiveNode({
|
|
|
4
4
|
type: 'common-concat',
|
|
5
5
|
displayName: 'Concat',
|
|
6
6
|
category: 'Text',
|
|
7
|
-
description:
|
|
7
|
+
description: `
|
|
8
|
+
Join several strings with a **separator**. Emits only when every wired string has a fresh value.
|
|
9
|
+
|
|
10
|
+
Type \`\\n\` in the separator for a line break.
|
|
11
|
+
`.trim(),
|
|
8
12
|
uiSchema: [],
|
|
9
13
|
bind(_ctx, { makeInput, configureOutput, combineInputs }) {
|
|
10
14
|
const separator$ = makeInput('separator', {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createProjectFilesContext } from '@langflower/tools/create-project-files-context';
|
|
3
3
|
import { from, mergeMap, throwError } from 'rxjs';
|
|
4
4
|
import { getRunHostServices } from '../../ai/features/run-host-services.js';
|
|
@@ -10,7 +10,11 @@ export const readFileNode = defineReactiveNode({
|
|
|
10
10
|
type: 'common-read-file',
|
|
11
11
|
displayName: 'Read File',
|
|
12
12
|
category: 'Text',
|
|
13
|
-
description:
|
|
13
|
+
description: `
|
|
14
|
+
Read a file inside the project into **content**. Absolute paths are refused.
|
|
15
|
+
|
|
16
|
+
Pulse **update** to read the same path again after it changes on disk.
|
|
17
|
+
`.trim(),
|
|
14
18
|
uiSchema: [],
|
|
15
19
|
bind(ctx, { makeInput, configureOutput, combineInputs }) {
|
|
16
20
|
const pathInput = makeInput('path', {
|
|
@@ -28,7 +32,9 @@ export const readFileNode = defineReactiveNode({
|
|
|
28
32
|
const content$ = combineInputs([pathInput, update, ctx], ([rawPath, _update, ec]) => ({
|
|
29
33
|
path: String(rawPath ?? '').trim(),
|
|
30
34
|
ec,
|
|
31
|
-
}))
|
|
35
|
+
}))
|
|
36
|
+
.pipe(withLoading())
|
|
37
|
+
.pipeValue(mergeMap(({ path: filePath, ec }) => {
|
|
32
38
|
if (filePath.length === 0) {
|
|
33
39
|
return throwError(() => new Error('Read File requires a non-empty path.'));
|
|
34
40
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits `text` on `divider` and emits one non-empty chunk per pace slot:
|
|
3
|
+
* first ASAP, later on `trigger`. After the last chunk, the next slot emits
|
|
4
|
+
* `finish: true`. Use for CSV / logs line-by-line; not the one-shot Split array.
|
|
5
|
+
*/
|
|
6
|
+
export declare const splitPacedNode: {
|
|
7
|
+
readonly emitOncePerActivation: boolean;
|
|
8
|
+
readonly stopsRun: boolean;
|
|
9
|
+
readonly chatEntry: boolean;
|
|
10
|
+
readonly uiSchema: readonly [];
|
|
11
|
+
readonly bypassPorts: Record<string, import("@langflower/node-sdk").WireType>;
|
|
12
|
+
readonly inputsConfigs: readonly [import("@langflower/node-sdk").InputPortMeta<import("@langflower/node-sdk").ExecutionContext<readonly [], Record<string, never>>>, ...import("@langflower/node-sdk").InputPortMeta<unknown>[]];
|
|
13
|
+
readonly outputsConfigs: (import("@langflower/node-sdk").OutputParams & import("@langflower/node-sdk").PortMeta)[];
|
|
14
|
+
readonly getInstance: () => import("@langflower/node-sdk").ReactiveNodeInstance<readonly [], Record<string, never>>;
|
|
15
|
+
readonly paletteSecondary?: true;
|
|
16
|
+
readonly description?: string;
|
|
17
|
+
readonly type: string;
|
|
18
|
+
readonly displayName: string;
|
|
19
|
+
readonly category: string | undefined;
|
|
20
|
+
readonly icon: string | undefined;
|
|
21
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
2
|
+
import { asapScheduler, filter, map, observeOn, of, startWith, switchMap, take, } from 'rxjs';
|
|
3
|
+
const remainingChunks = (text, divider, startFrom) => {
|
|
4
|
+
const splitDivider = divider.replaceAll('\\n', '\n');
|
|
5
|
+
const from = Math.max(0, Math.floor(Number(startFrom) || 0));
|
|
6
|
+
const pieces = String(text)
|
|
7
|
+
.split(splitDivider)
|
|
8
|
+
.map((piece) => piece.replace(/\r$/, ''))
|
|
9
|
+
.filter((piece) => piece.length > 0);
|
|
10
|
+
return pieces.slice(from).map((chunk, offset) => ({
|
|
11
|
+
text: chunk,
|
|
12
|
+
index: from + offset,
|
|
13
|
+
}));
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Splits `text` on `divider` and emits one non-empty chunk per pace slot:
|
|
17
|
+
* first ASAP, later on `trigger`. After the last chunk, the next slot emits
|
|
18
|
+
* `finish: true`. Use for CSV / logs line-by-line; not the one-shot Split array.
|
|
19
|
+
*/
|
|
20
|
+
export const splitPacedNode = defineReactiveNode({
|
|
21
|
+
type: 'common-split-paced',
|
|
22
|
+
displayName: 'Split (paced)',
|
|
23
|
+
category: 'Text',
|
|
24
|
+
description: `
|
|
25
|
+
Walk a large text one piece at a time — CSV rows, log lines, paragraphs.
|
|
26
|
+
|
|
27
|
+
The first piece starts immediately. Pulse **trigger** for the next. After the last piece, **finish** fires so you can stop the loop.
|
|
28
|
+
|
|
29
|
+
Skip ahead with **start from**. Empty pieces are ignored. In **divider**, type \`\\n\` for a line break.
|
|
30
|
+
`.trim(),
|
|
31
|
+
uiSchema: [],
|
|
32
|
+
bind(_ctx, { makeInput, configureOutput, combineInputs }) {
|
|
33
|
+
const text = makeInput('text', {
|
|
34
|
+
name: 'text',
|
|
35
|
+
wireType: 'string',
|
|
36
|
+
inline: 'text-multiline',
|
|
37
|
+
required: true,
|
|
38
|
+
defaultValue: '',
|
|
39
|
+
});
|
|
40
|
+
const divider = makeInput('divider', {
|
|
41
|
+
name: 'divider',
|
|
42
|
+
wireType: 'string',
|
|
43
|
+
inline: 'text',
|
|
44
|
+
defaultValue: '\\n',
|
|
45
|
+
description: '`\\n` is replaced with a line break.',
|
|
46
|
+
});
|
|
47
|
+
const startFrom = makeInput('startFrom', {
|
|
48
|
+
name: 'start from',
|
|
49
|
+
wireType: 'number',
|
|
50
|
+
inline: { type: 'number', min: 0, step: 1 },
|
|
51
|
+
defaultValue: 0,
|
|
52
|
+
});
|
|
53
|
+
const trigger = makeInput('trigger', {
|
|
54
|
+
name: 'trigger',
|
|
55
|
+
dynamic: true,
|
|
56
|
+
required: true,
|
|
57
|
+
});
|
|
58
|
+
const session$ = combineInputs([text, divider, startFrom], ([rawText, rawDivider, rawStart]) => remainingChunks(String(rawText ?? ''), String(rawDivider ?? ''), Number(rawStart))).pipeValue(switchMap((chunks) => {
|
|
59
|
+
const n = chunks.length;
|
|
60
|
+
if (n <= 0) {
|
|
61
|
+
return of({ kind: 'finish' });
|
|
62
|
+
}
|
|
63
|
+
return trigger.value$.pipe(startWith(undefined), take(n + 1), observeOn(asapScheduler), map((_, slot) => {
|
|
64
|
+
const chunk = chunks[slot];
|
|
65
|
+
return slot < n && chunk !== undefined
|
|
66
|
+
? {
|
|
67
|
+
kind: 'chunk',
|
|
68
|
+
text: chunk.text,
|
|
69
|
+
index: chunk.index,
|
|
70
|
+
}
|
|
71
|
+
: { kind: 'finish' };
|
|
72
|
+
}));
|
|
73
|
+
}));
|
|
74
|
+
const textOut$ = session$.pipeValue(filter((e) => e.kind === 'chunk'), map((e) => e.text));
|
|
75
|
+
const indexOut$ = session$.pipeValue(filter((e) => e.kind === 'chunk'), map((e) => e.index));
|
|
76
|
+
const finishOut$ = session$.pipeValue(filter((e) => e.kind === 'finish'), map(() => true));
|
|
77
|
+
return {
|
|
78
|
+
inputs: [text, divider, startFrom, trigger],
|
|
79
|
+
outputs: [
|
|
80
|
+
configureOutput('text', textOut$, {
|
|
81
|
+
wireType: 'string',
|
|
82
|
+
}),
|
|
83
|
+
configureOutput('index', indexOut$, {
|
|
84
|
+
wireType: 'number',
|
|
85
|
+
}),
|
|
86
|
+
configureOutput('finish', finishOut$, {
|
|
87
|
+
wireType: 'boolean',
|
|
88
|
+
feed: { role: 'none' },
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createProjectFilesContext } from '@langflower/tools/create-project-files-context';
|
|
3
3
|
import { from, map, mergeMap, throwError } from 'rxjs';
|
|
4
4
|
import { getRunHostServices } from '../../ai/features/run-host-services.js';
|
|
@@ -9,7 +9,13 @@ export const writeFileNode = defineReactiveNode({
|
|
|
9
9
|
type: 'common-write-file',
|
|
10
10
|
displayName: 'Write File',
|
|
11
11
|
category: 'Text',
|
|
12
|
-
description:
|
|
12
|
+
description: `
|
|
13
|
+
Write **content** to a file inside the project (creates folders if needed). Absolute paths are refused.
|
|
14
|
+
|
|
15
|
+
Typical uses:
|
|
16
|
+
- Save an LLM draft
|
|
17
|
+
- Persist a generated config
|
|
18
|
+
`.trim(),
|
|
13
19
|
uiSchema: [],
|
|
14
20
|
bind(ctx, { makeInput, configureOutput, combineInputs }) {
|
|
15
21
|
const pathInput = makeInput('path', {
|
|
@@ -29,7 +35,9 @@ export const writeFileNode = defineReactiveNode({
|
|
|
29
35
|
path: String(rawPath ?? '').trim(),
|
|
30
36
|
content: String(rawContent ?? ''),
|
|
31
37
|
ec,
|
|
32
|
-
}))
|
|
38
|
+
}))
|
|
39
|
+
.pipe(withLoading())
|
|
40
|
+
.pipeValue(mergeMap(({ path: filePath, content: fileContent, ec }) => {
|
|
33
41
|
if (filePath.length === 0) {
|
|
34
42
|
return throwError(() => new Error('Write File requires a non-empty path.'));
|
|
35
43
|
}
|
|
@@ -19,8 +19,11 @@ export const toolCollectionNode = defineReactiveNode({
|
|
|
19
19
|
type: 'common-tool-collection',
|
|
20
20
|
displayName: 'Tool collection',
|
|
21
21
|
category: 'Tools',
|
|
22
|
-
description:
|
|
23
|
-
|
|
22
|
+
description: `
|
|
23
|
+
Combine several tool wires into one before an agent. Duplicate names: the last wire wins.
|
|
24
|
+
|
|
25
|
+
Optional — you can still plug many tool packs straight into the agent.
|
|
26
|
+
`.trim(),
|
|
24
27
|
uiSchema: [],
|
|
25
28
|
bind(_ctx, { makeInput, configureOutput }) {
|
|
26
29
|
const tools = makeInput('tools', {
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"./ai/openai/list-provider-models": {
|
|
29
29
|
"types": "./dist/ai/features/openai/list-provider-models.d.ts",
|
|
30
30
|
"import": "./dist/ai/features/openai/list-provider-models.js"
|
|
31
|
+
},
|
|
32
|
+
"./embeddings/create-embedding": {
|
|
33
|
+
"types": "./dist/embeddings/create-embedding.d.ts",
|
|
34
|
+
"import": "./dist/embeddings/create-embedding.js"
|
|
31
35
|
}
|
|
32
36
|
},
|
|
33
37
|
"dependencies": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas capability for batch embeddings. Parallel to {@link ToolHandle}
|
|
3
|
+
* but **not** agent inventory: `embedTexts` returns float vectors, never
|
|
4
|
+
* LLM-facing strings.
|
|
5
|
+
*
|
|
6
|
+
* Wire: provider `embed` out → consumer `embed` in, both
|
|
7
|
+
* {@link EMBED_HANDLE_WIRE_TYPE}. Live closure (not JSON-serializable).
|
|
8
|
+
*/
|
|
9
|
+
export declare const EMBED_HANDLE_WIRE_TYPE = "embed-handle";
|
|
10
|
+
export type EmbedTextRole = 'document' | 'query';
|
|
11
|
+
export type EmbedTextsOptions = {
|
|
12
|
+
readonly role?: EmbedTextRole;
|
|
13
|
+
readonly signal?: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
export type EmbedHandle = {
|
|
16
|
+
/** Vector size for this bound model; packs MUST reject a mismatch. */
|
|
17
|
+
readonly dim: number;
|
|
18
|
+
readonly embedTexts: (texts: readonly string[], options?: EmbedTextsOptions) => Promise<readonly Float32Array[]>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Duck-type guard for {@link EmbedHandle} on `defineNode` `execute` inputs
|
|
22
|
+
* (`Record<string, unknown>`). Requires a positive finite `dim` (emit only
|
|
23
|
+
* once dim is set) and an `embedTexts` function.
|
|
24
|
+
*/
|
|
25
|
+
export declare const isEmbedHandle: (value: unknown) => value is EmbedHandle;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canvas capability for batch embeddings. Parallel to {@link ToolHandle}
|
|
3
|
+
* but **not** agent inventory: `embedTexts` returns float vectors, never
|
|
4
|
+
* LLM-facing strings.
|
|
5
|
+
*
|
|
6
|
+
* Wire: provider `embed` out → consumer `embed` in, both
|
|
7
|
+
* {@link EMBED_HANDLE_WIRE_TYPE}. Live closure (not JSON-serializable).
|
|
8
|
+
*/
|
|
9
|
+
export const EMBED_HANDLE_WIRE_TYPE = 'embed-handle';
|
|
10
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
11
|
+
/**
|
|
12
|
+
* Duck-type guard for {@link EmbedHandle} on `defineNode` `execute` inputs
|
|
13
|
+
* (`Record<string, unknown>`). Requires a positive finite `dim` (emit only
|
|
14
|
+
* once dim is set) and an `embedTexts` function.
|
|
15
|
+
*/
|
|
16
|
+
export const isEmbedHandle = (value) => {
|
|
17
|
+
if (!isRecord(value)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const dim = value['dim'];
|
|
21
|
+
return (typeof dim === 'number' &&
|
|
22
|
+
Number.isFinite(dim) &&
|
|
23
|
+
dim > 0 &&
|
|
24
|
+
typeof value['embedTexts'] === 'function');
|
|
25
|
+
};
|
|
@@ -30,5 +30,7 @@ export type DefineNodeConfig<UI extends readonly UISchemaConstItem[] = readonly
|
|
|
30
30
|
/**
|
|
31
31
|
* Simple authoring factory: sync/Promise `execute` without RxJS.
|
|
32
32
|
* Adapter over {@link defineReactiveNode} (one reactive runtime path).
|
|
33
|
+
* Stamps `{ pending: true }` on outputs before each `execute` via
|
|
34
|
+
* {@link withLoading} — authors do not call it.
|
|
33
35
|
*/
|
|
34
36
|
export declare const defineNode: <const UI extends readonly UISchemaConstItem[]>(config: DefineNodeConfig<UI>) => ReactiveNodeDefinition;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { from, mergeMap, of, throwError } from 'rxjs';
|
|
2
|
+
import { withLoading, } from '../define-reactive-node/io-helpers.js';
|
|
2
3
|
import { defineReactiveNode, } from '../define-reactive-node/define-reactive-node.js';
|
|
3
4
|
const inputParamsFromMeta = (portId, meta) => {
|
|
4
5
|
const params = {
|
|
@@ -21,6 +22,8 @@ const inputParamsFromMeta = (portId, meta) => {
|
|
|
21
22
|
/**
|
|
22
23
|
* Simple authoring factory: sync/Promise `execute` without RxJS.
|
|
23
24
|
* Adapter over {@link defineReactiveNode} (one reactive runtime path).
|
|
25
|
+
* Stamps `{ pending: true }` on outputs before each `execute` via
|
|
26
|
+
* {@link withLoading} — authors do not call it.
|
|
24
27
|
*/
|
|
25
28
|
export const defineNode = (config) => {
|
|
26
29
|
const inputEntries = Object.entries(config.inputs ?? {});
|
|
@@ -56,7 +59,7 @@ export const defineNode = (config) => {
|
|
|
56
59
|
}
|
|
57
60
|
return { ec, inputs };
|
|
58
61
|
});
|
|
59
|
-
const result$ = combined$.pipeValue(mergeMap(({ ec, inputs }) => from(Promise.resolve(config.execute(ec, inputs)).then((outputs) => outputs, (error) => {
|
|
62
|
+
const result$ = combined$.pipe(withLoading()).pipeValue(mergeMap(({ ec, inputs }) => from(Promise.resolve(config.execute(ec, inputs)).then((outputs) => outputs, (error) => {
|
|
60
63
|
throw error instanceof Error
|
|
61
64
|
? error
|
|
62
65
|
: new Error(String(error));
|
|
@@ -4,11 +4,13 @@ import type { DefinedReactiveNodeConfig, ExecutionContext, ReactiveNodeInstance
|
|
|
4
4
|
import type { UISchemaConstItem } from './ui-schema-inference.js';
|
|
5
5
|
export type { CtxError } from './ctx-error.js';
|
|
6
6
|
export type { HitlButtonControl, HitlControl, HitlFileControl, HitlFileValue, HitlInputConfig, HitlPayloadTemplate, HitlTextareaControl, HitlTextValue, HitlUploadedFile, } from './hitl-config.js';
|
|
7
|
-
export { configureOutput, DEFAULT_MULTILINE_MIN_HEIGHT_PX, InlineConfig, InlineSelectOption, InlineTextMultilineConfig, InputConfig, InputParams, InputPortMeta, makeInput, OutputConfig, OutputParams, OutputPortMeta, resolveMultilineInlineLayout, ResolvedMultilineInlineLayout, } from './io-helpers.js';
|
|
7
|
+
export { withLoading, configureOutput, DEFAULT_MULTILINE_MIN_HEIGHT_PX, InlineConfig, InlineSelectOption, InlineTextMultilineConfig, InputConfig, InputParams, InputPortMeta, makeInput, OutputConfig, OutputParams, OutputPortMeta, resolveMultilineInlineLayout, ResolvedMultilineInlineLayout, } from './io-helpers.js';
|
|
8
8
|
export type { FeedPortMeta, FeedRole } from './io-helpers.js';
|
|
9
9
|
export type { InputPortMode, MetaFromStatefulObservable, PortMeta, WireType, } from './port-meta.js';
|
|
10
10
|
export type { ToolHandle, ToolHandler, ToolHandlerContext, } from '../define-tool-registrations/tool-handle.js';
|
|
11
11
|
export { TOOL_HANDLE_WIRE_TYPE } from '../define-tool-registrations/tool-handle.js';
|
|
12
|
+
export type { EmbedHandle, EmbedTextRole, EmbedTextsOptions, } from '../define-embed/embed-handle.js';
|
|
13
|
+
export { EMBED_HANDLE_WIRE_TYPE, isEmbedHandle, } from '../define-embed/embed-handle.js';
|
|
12
14
|
export type { DefinedReactiveNodeConfig, ExecutionContext, ReactiveNodeInstance, } from './types.js';
|
|
13
15
|
export declare const contextSymbol: unique symbol;
|
|
14
16
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { combineStatefulObservables, statefulConnection, } from '@rx-evo/stateful-observable';
|
|
2
2
|
import { configureOutput, makeInput } from './io-helpers.js';
|
|
3
|
-
export { configureOutput, DEFAULT_MULTILINE_MIN_HEIGHT_PX, makeInput, resolveMultilineInlineLayout, } from './io-helpers.js';
|
|
3
|
+
export { withLoading, configureOutput, DEFAULT_MULTILINE_MIN_HEIGHT_PX, makeInput, resolveMultilineInlineLayout, } from './io-helpers.js';
|
|
4
4
|
export { TOOL_HANDLE_WIRE_TYPE } from '../define-tool-registrations/tool-handle.js';
|
|
5
|
+
export { EMBED_HANDLE_WIRE_TYPE, isEmbedHandle, } from '../define-embed/embed-handle.js';
|
|
5
6
|
export const contextSymbol = Symbol('node context');
|
|
6
7
|
const bindHelpers = {
|
|
7
8
|
makeInput,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FeedPortMeta, MetaFromStatefulObservable, PortMeta } from './port-meta.js';
|
|
2
2
|
export type { FeedPortMeta, FeedRole } from './port-meta.js';
|
|
3
3
|
import { StatefulObservable } from '@rx-evo/stateful-observable';
|
|
4
|
+
import { type OperatorFunction } from 'rxjs';
|
|
4
5
|
import type { HitlInputConfig } from './hitl-config.js';
|
|
5
6
|
/** One choice for the `select` / `multiselect` / `radio` inline kinds. */
|
|
6
7
|
export type InlineSelectOption = {
|
|
@@ -63,6 +64,10 @@ export type InputParams<T> = {
|
|
|
63
64
|
readonly defaultValue?: T;
|
|
64
65
|
/** On-node editor kind — unset means no on-node control at all. */
|
|
65
66
|
readonly inline?: InlineConfig;
|
|
67
|
+
/**
|
|
68
|
+
* Hidden wire port — no canvas handle. With editable `inline`, still a
|
|
69
|
+
* visible on-node / inspector field (Chat Input `message`).
|
|
70
|
+
*/
|
|
66
71
|
readonly hidden?: boolean;
|
|
67
72
|
readonly dynamic?: boolean;
|
|
68
73
|
readonly hitl?: HitlInputConfig;
|
|
@@ -127,3 +132,13 @@ export declare function configureOutput<T>(portId: string, stream: StatefulObser
|
|
|
127
132
|
export type OutputConfig = ReturnType<typeof configureOutput>;
|
|
128
133
|
/** Serializable descriptor for one output port — `OutputConfig['meta']`. */
|
|
129
134
|
export type OutputPortMeta = MetaFromStatefulObservable<OutputConfig>;
|
|
135
|
+
/**
|
|
136
|
+
* Stamp `{ pending: true }` on each new success, then re-emit that success.
|
|
137
|
+
* Use on the raw SO stream **before** async `pipeValue` work:
|
|
138
|
+
*
|
|
139
|
+
* `combineInputs(...).pipe(withLoading()).pipeValue(concatMap(delay))`
|
|
140
|
+
*
|
|
141
|
+
* `pipeValue` + `delay` / `from(Promise)` does **not** create loading.
|
|
142
|
+
* Do not wrap token/chunk streams (re-pends every emit through demux).
|
|
143
|
+
*/
|
|
144
|
+
export declare const withLoading: <T>() => OperatorFunction<T, T>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { statefulConnection, } from '@rx-evo/stateful-observable';
|
|
1
|
+
import { isSuccess, statefulConnection, } from '@rx-evo/stateful-observable';
|
|
2
|
+
import { concat, concatMap, of } from 'rxjs';
|
|
2
3
|
/** Default floor for canvas/inspector multiline textareas (px). */
|
|
3
4
|
export const DEFAULT_MULTILINE_MIN_HEIGHT_PX = 100;
|
|
4
5
|
/** Resolve multiline layout; `null` when `config` is not text-multiline. */
|
|
@@ -86,3 +87,18 @@ export function configureOutput(portId, stream, meta) {
|
|
|
86
87
|
},
|
|
87
88
|
});
|
|
88
89
|
}
|
|
90
|
+
const LOADING = {
|
|
91
|
+
state: Symbol.for('@rx-evo/stateful-observable/loading'),
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Stamp `{ pending: true }` on each new success, then re-emit that success.
|
|
95
|
+
* Use on the raw SO stream **before** async `pipeValue` work:
|
|
96
|
+
*
|
|
97
|
+
* `combineInputs(...).pipe(withLoading()).pipeValue(concatMap(delay))`
|
|
98
|
+
*
|
|
99
|
+
* `pipeValue` + `delay` / `from(Promise)` does **not** create loading.
|
|
100
|
+
* Do not wrap token/chunk streams (re-pends every emit through demux).
|
|
101
|
+
*/
|
|
102
|
+
export const withLoading = () => concatMap((response) => isSuccess(response)
|
|
103
|
+
? concat(of(LOADING), of(response))
|
|
104
|
+
: of(response));
|
|
@@ -12,13 +12,26 @@ export type WireType = string & {
|
|
|
12
12
|
__brand: 'RuntimeWireType';
|
|
13
13
|
};
|
|
14
14
|
export type InputPortMode = 'single' | 'merge' | 'combine' | 'zip' | 'bypass';
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Sidebar work-log role stamped on a port's `feed.role`.
|
|
17
|
+
*
|
|
18
|
+
* - `none` — omit from the work log
|
|
19
|
+
* - `reasoning` — technical growing stream; caption Reasoning (LLM thinking)
|
|
20
|
+
* - `progress` — same layout as reasoning; caption PROGRESS (ingest/crawl/job logs)
|
|
21
|
+
* - `draft` — markdown draft bubble
|
|
22
|
+
* - `tool` / `shell` — tool/shell rows (growing when streaming)
|
|
23
|
+
* - `result` — conversation bubble; one row per emit (not for multi-emit logs)
|
|
24
|
+
* - `recovery` — amber recovery banner
|
|
25
|
+
*/
|
|
26
|
+
export type FeedRole = 'none' | 'reasoning' | 'progress' | 'draft' | 'tool' | 'shell' | 'result' | 'recovery';
|
|
16
27
|
/**
|
|
17
28
|
* Feed projection metadata shared by input and output port contracts.
|
|
18
29
|
*
|
|
19
|
-
* `streaming: true` keeps the visit open
|
|
20
|
-
*
|
|
21
|
-
*
|
|
30
|
+
* `streaming: true` keeps the visit open and appends chunks on growing
|
|
31
|
+
* roles (`reasoning`, `progress`, `draft`, `tool`, `shell`). Omit
|
|
32
|
+
* `streaming` so the frame closes the visit.
|
|
33
|
+
*
|
|
34
|
+
* Progress logs use `{ role: 'progress', streaming: true }`.
|
|
22
35
|
*/
|
|
23
36
|
export type FeedPortMeta = {
|
|
24
37
|
readonly role?: FeedRole;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { concatMap, delay, of } from 'rxjs';
|
|
2
|
-
import { defineReactiveNode } from '../../define-reactive-node.js';
|
|
2
|
+
import { defineReactiveNode, withLoading } from '../../define-reactive-node.js';
|
|
3
3
|
/** Pass-through with async delay on `value` input. */
|
|
4
4
|
export const delaySampleNode = defineReactiveNode({
|
|
5
5
|
type: 'sample-delay',
|
|
@@ -12,7 +12,9 @@ export const delaySampleNode = defineReactiveNode({
|
|
|
12
12
|
const output$ = combineInputs([value, ctx], ([inputValue, ec]) => {
|
|
13
13
|
const delayMs = Math.max(0, Number(ec.params?.delayMs ?? 50));
|
|
14
14
|
return { inputValue, delayMs };
|
|
15
|
-
})
|
|
15
|
+
})
|
|
16
|
+
.pipe(withLoading())
|
|
17
|
+
.pipeValue(concatMap(({ inputValue, delayMs }) => of(inputValue).pipe(delay(delayMs))));
|
|
16
18
|
return {
|
|
17
19
|
inputs: [value],
|
|
18
20
|
outputs: [
|
|
@@ -52,9 +52,10 @@ export type DefinedReactiveNodeConfig<UI extends readonly UISchemaConstItem[] =
|
|
|
52
52
|
*/
|
|
53
53
|
readonly paletteSecondary?: boolean;
|
|
54
54
|
/**
|
|
55
|
-
* Optional markdown for the palette detail popover
|
|
56
|
-
*
|
|
57
|
-
*
|
|
55
|
+
* Optional markdown for the palette detail popover and inspector.
|
|
56
|
+
* Omitted → no description block. Copied onto the definition /
|
|
57
|
+
* `PaletteNodeDefinition` when set. Write user-facing use cases, not
|
|
58
|
+
* author internals.
|
|
58
59
|
*/
|
|
59
60
|
readonly description?: string;
|
|
60
61
|
/**
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ReactiveNodeDefinition, ReactiveNodeInstance } from '../node-factory/define-reactive-node/define-reactive-node.js';
|
|
2
|
+
import { type UISchemaConstItem } from '../node-factory/define-reactive-node/ui-schema-inference.js';
|
|
3
|
+
type HarnessUi = readonly UISchemaConstItem[];
|
|
4
|
+
type HarnessInstance = ReactiveNodeInstance<HarnessUi, object>;
|
|
5
|
+
export type NodeHarnessOptions = {
|
|
6
|
+
readonly projectDir?: string;
|
|
7
|
+
readonly runId?: string;
|
|
8
|
+
readonly nodeId?: string;
|
|
9
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
10
|
+
};
|
|
11
|
+
export type CollectedPort<T> = {
|
|
12
|
+
readonly values: readonly T[];
|
|
13
|
+
readonly stop: () => void;
|
|
14
|
+
};
|
|
15
|
+
export type NodeHarness = {
|
|
16
|
+
readonly instance: HarnessInstance;
|
|
17
|
+
readonly send: (portId: string, value: unknown) => void;
|
|
18
|
+
readonly next: <T>(portId: string) => Promise<T>;
|
|
19
|
+
readonly collect: <T>(portId: string) => CollectedPort<T>;
|
|
20
|
+
readonly dispose: () => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Drive a {@link ReactiveNodeDefinition} in unit tests: seed context,
|
|
24
|
+
* `send` on input ports, `next` / `collect` on outputs.
|
|
25
|
+
*
|
|
26
|
+
* Subscribe with `next` or `collect` **before** `send` when the graph can
|
|
27
|
+
* emit synchronously.
|
|
28
|
+
*/
|
|
29
|
+
export declare const createNodeHarness: (definition: Pick<ReactiveNodeDefinition, "getInstance" | "uiSchema">, options?: NodeHarnessOptions) => NodeHarness;
|
|
30
|
+
export {};
|