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.
Files changed (159) hide show
  1. package/README.md +96 -70
  2. package/docs/public/README.md +2 -2
  3. package/docs/public/configuration.md +4 -0
  4. package/docs/public/extending.md +18 -2
  5. package/docs/public/getting-started.md +7 -0
  6. package/docs/public/how-it-works.md +18 -11
  7. package/docs/public/product.md +35 -25
  8. package/package.json +1 -1
  9. package/ui-dist/chunk-QI4IYILJ.js +4 -0
  10. package/ui-dist/{chunk-7UMVYN3A.js → chunk-UG3GFV4Y.js} +1 -1
  11. package/ui-dist/index.html +2 -2
  12. package/ui-dist/main-ODIDCCNX.js +315 -0
  13. package/ui-dist/styles-2JMBAQC3.css +1 -0
  14. package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +2 -2
  15. package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +39 -7
  16. package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.d.ts +11 -0
  17. package/vendor/common-nodes/dist/ai/features/llm-session/run-session-machine.js +2 -2
  18. package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +10 -0
  19. package/vendor/common-nodes/dist/ai/nodes/critique/node.js +5 -1
  20. package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +7 -1
  21. package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +5 -1
  22. package/vendor/common-nodes/dist/ai/nodes/review/node.js +7 -1
  23. package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +62 -22
  24. package/vendor/common-nodes/dist/catalog.js +10 -0
  25. package/vendor/common-nodes/dist/crawl/crawl/node.js +11 -3
  26. package/vendor/common-nodes/dist/crawl/crawl-tools/node.js +5 -1
  27. package/vendor/common-nodes/dist/crawl/extract-links/node.js +7 -1
  28. package/vendor/common-nodes/dist/crawl/fetch-url/node.js +13 -4
  29. package/vendor/common-nodes/dist/crawl/save-page/node.js +11 -3
  30. package/vendor/common-nodes/dist/embeddings/create-embedding.d.ts +39 -0
  31. package/vendor/common-nodes/dist/embeddings/create-embedding.js +154 -0
  32. package/vendor/common-nodes/dist/embeddings/embed-provider/node.d.ts +53 -0
  33. package/vendor/common-nodes/dist/embeddings/embed-provider/node.js +126 -0
  34. package/vendor/common-nodes/dist/embeddings/embed-similarity/node.d.ts +20 -0
  35. package/vendor/common-nodes/dist/embeddings/embed-similarity/node.js +81 -0
  36. package/vendor/common-nodes/dist/embeddings/embed-text/node.d.ts +53 -0
  37. package/vendor/common-nodes/dist/embeddings/embed-text/node.js +91 -0
  38. package/vendor/common-nodes/dist/embeddings/from-embedding.d.ts +8 -0
  39. package/vendor/common-nodes/dist/embeddings/from-embedding.js +24 -0
  40. package/vendor/common-nodes/dist/embeddings/resolve-embedding-provider-model.d.ts +11 -0
  41. package/vendor/common-nodes/dist/embeddings/resolve-embedding-provider-model.js +8 -0
  42. package/vendor/common-nodes/dist/flow/checkpoint/node.js +7 -3
  43. package/vendor/common-nodes/dist/flow/delay/node.d.ts +2 -6
  44. package/vendor/common-nodes/dist/flow/delay/node.js +13 -9
  45. package/vendor/common-nodes/dist/flow/loop/node.js +7 -1
  46. package/vendor/common-nodes/dist/flow/merge/node.js +7 -1
  47. package/vendor/common-nodes/dist/flow/repeat/node.js +5 -1
  48. package/vendor/common-nodes/dist/flow/router/node.js +7 -1
  49. package/vendor/common-nodes/dist/hitl/chat-input/node.d.ts +2 -2
  50. package/vendor/common-nodes/dist/hitl/chat-input/node.js +8 -4
  51. package/vendor/common-nodes/dist/hitl/review-gate/node.js +11 -5
  52. package/vendor/common-nodes/dist/langflower-tools/node.js +5 -1
  53. package/vendor/common-nodes/dist/logic/assert/node.js +7 -1
  54. package/vendor/common-nodes/dist/logic/compare/node.js +7 -1
  55. package/vendor/common-nodes/dist/logic/gate/node.js +7 -1
  56. package/vendor/common-nodes/dist/logic/if/node.js +7 -1
  57. package/vendor/common-nodes/dist/logic/switch/node.js +7 -1
  58. package/vendor/common-nodes/dist/mcp/mcp-http/node.js +55 -53
  59. package/vendor/common-nodes/dist/mcp/mcp-stdio/node.js +52 -50
  60. package/vendor/common-nodes/dist/memory/memory-tools/node.js +5 -1
  61. package/vendor/common-nodes/dist/output/finish/node.js +13 -1
  62. package/vendor/common-nodes/dist/output/preview/node.js +9 -2
  63. package/vendor/common-nodes/dist/primitives/boolean/node.js +7 -1
  64. package/vendor/common-nodes/dist/primitives/number/node.js +7 -1
  65. package/vendor/common-nodes/dist/primitives/string/node.js +7 -1
  66. package/vendor/common-nodes/dist/primitives/string-multiline/node.d.ts +17 -0
  67. package/vendor/common-nodes/dist/primitives/string-multiline/node.js +31 -0
  68. package/vendor/common-nodes/dist/text/append-file/node.js +11 -3
  69. package/vendor/common-nodes/dist/text/concat/node.js +5 -1
  70. package/vendor/common-nodes/dist/text/read-file/node.js +9 -3
  71. package/vendor/common-nodes/dist/text/split-paced/node.d.ts +21 -0
  72. package/vendor/common-nodes/dist/text/split-paced/node.js +93 -0
  73. package/vendor/common-nodes/dist/text/write-file/node.js +11 -3
  74. package/vendor/common-nodes/dist/tools/tool-collection/node.js +5 -2
  75. package/vendor/common-nodes/package.json +4 -0
  76. package/vendor/node-sdk/dist/node-factory/define-embed/embed-handle.d.ts +25 -0
  77. package/vendor/node-sdk/dist/node-factory/define-embed/embed-handle.js +25 -0
  78. package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.d.ts +1 -0
  79. package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.js +1 -0
  80. package/vendor/node-sdk/dist/node-factory/define-node/define-node.d.ts +2 -0
  81. package/vendor/node-sdk/dist/node-factory/define-node/define-node.js +4 -1
  82. package/vendor/node-sdk/dist/node-factory/define-reactive-node/define-reactive-node.d.ts +3 -1
  83. package/vendor/node-sdk/dist/node-factory/define-reactive-node/define-reactive-node.js +2 -1
  84. package/vendor/node-sdk/dist/node-factory/define-reactive-node/io-helpers.d.ts +15 -0
  85. package/vendor/node-sdk/dist/node-factory/define-reactive-node/io-helpers.js +17 -1
  86. package/vendor/node-sdk/dist/node-factory/define-reactive-node/port-meta.d.ts +17 -4
  87. package/vendor/node-sdk/dist/node-factory/define-reactive-node/test/samples/delay-node.js +4 -2
  88. package/vendor/node-sdk/dist/node-factory/define-reactive-node/types.d.ts +4 -3
  89. package/vendor/node-sdk/dist/testing/create-node-harness.d.ts +30 -0
  90. package/vendor/node-sdk/dist/testing/create-node-harness.js +68 -0
  91. package/vendor/node-sdk/package.json +4 -4
  92. package/vendor/runtime/dist/types.d.ts +1 -1
  93. package/vendor/server/dist/bootstrap/project-bootstrap.service.d.ts +1 -1
  94. package/vendor/server/dist/bootstrap/seed-skeleton.d.ts +2 -2
  95. package/vendor/server/dist/bootstrap/seed-skeleton.js +17 -2
  96. package/vendor/server/dist/bridge/bind-embed-context.d.ts +7 -0
  97. package/vendor/server/dist/bridge/bind-embed-context.js +16 -0
  98. package/vendor/server/dist/bridge/build-execution-context.d.ts +2 -2
  99. package/vendor/server/dist/bridge/build-execution-context.js +6 -2
  100. package/vendor/server/dist/bridge/emit-bootstrap.js +4 -1
  101. package/vendor/server/dist/bridge/settings-draft-controller.js +7 -3
  102. package/vendor/server/dist/bridge/wire-config-handlers.js +2 -0
  103. package/vendor/server/dist/bridge/wire-editor-handlers.js +14 -0
  104. package/vendor/server/dist/config/langflower-config.service.d.ts +2 -0
  105. package/vendor/server/dist/config/langflower-config.service.js +30 -0
  106. package/vendor/server/dist/session/build-session-bootstrap.js +1 -0
  107. package/vendor/server/dist/session/langflower-session.d.ts +3 -1
  108. package/vendor/server/dist/session/langflower-session.js +3 -1
  109. package/vendor/server/dist/workflow/apply-editor-mutation.js +6 -0
  110. package/vendor/server/dist/workflow/workflow-persisted-inputs.js +8 -6
  111. package/vendor/server/skeleton/instructions.md +15 -3
  112. package/vendor/server/skeleton/nodes/hello-embed/README.md +185 -0
  113. package/vendor/server/skeleton/nodes/hello-embed/hello-embed.nodes.test.ts +181 -0
  114. package/vendor/server/skeleton/nodes/hello-embed/ingest.ts +126 -0
  115. package/vendor/server/skeleton/nodes/hello-embed/lib/chunk-markdown.test.ts +40 -0
  116. package/vendor/server/skeleton/nodes/hello-embed/lib/chunk-markdown.ts +123 -0
  117. package/vendor/server/skeleton/nodes/hello-embed/lib/ingest-search.test.ts +180 -0
  118. package/vendor/server/skeleton/nodes/hello-embed/lib/ingest.ts +128 -0
  119. package/vendor/server/skeleton/nodes/hello-embed/lib/paths.test.ts +20 -0
  120. package/vendor/server/skeleton/nodes/hello-embed/lib/paths.ts +70 -0
  121. package/vendor/server/skeleton/nodes/hello-embed/lib/schema.ts +21 -0
  122. package/vendor/server/skeleton/nodes/hello-embed/lib/search.ts +254 -0
  123. package/vendor/server/skeleton/nodes/hello-embed/lib/text-embedder.ts +14 -0
  124. package/vendor/server/skeleton/nodes/hello-embed/lib/vectors.ts +126 -0
  125. package/vendor/server/skeleton/nodes/hello-embed/lib/walk-markdown.test.ts +62 -0
  126. package/vendor/server/skeleton/nodes/hello-embed/lib/walk-markdown.ts +67 -0
  127. package/vendor/server/skeleton/nodes/hello-embed/package.json +11 -0
  128. package/vendor/server/skeleton/nodes/hello-embed/search-handle.ts +119 -0
  129. package/vendor/server/skeleton/nodes/hello-embed/search.ts +98 -0
  130. package/vendor/server/skeleton/nodes/hello-embed/tsconfig.json +14 -0
  131. package/vendor/server/skeleton/nodes/my-nodes/README.md +13 -3
  132. package/vendor/server/skeleton/schemas/langflower-config.schema.json +4 -0
  133. package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +99 -26
  134. package/vendor/server/skeleton/skills/langflower-helper/architecture.md +11 -6
  135. package/vendor/server/skeleton/skills/langflower-helper/layout.md +24 -18
  136. package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +8 -1
  137. package/vendor/server/skeleton/workflows/kb-ingest.json +88 -0
  138. package/vendor/server/skeleton/workflows/kb-manual-search.json +105 -0
  139. package/vendor/server/skeleton/workflows/kb-rag.json +192 -0
  140. package/vendor/server/skeleton/workflows/kb-tool.json +111 -0
  141. package/vendor/shared/dist/langflower-bus-config.d.ts +22 -6
  142. package/vendor/shared/dist/langflower-bus-config.js +23 -7
  143. package/vendor/shared/dist/langflower-config/merge-langflower-config-layers.test.js +8 -0
  144. package/vendor/shared/dist/langflower-config/settings-draft.d.ts +5 -0
  145. package/vendor/shared/dist/langflower-config/settings-draft.js +15 -7
  146. package/vendor/shared/dist/langflower.d.ts +1 -1
  147. package/vendor/shared/dist/langflower.js +1 -1
  148. package/vendor/shared/dist/types/langflower-bootstrap.d.ts +6 -0
  149. package/vendor/shared/dist/types/langflower-config.d.ts +17 -0
  150. package/vendor/shared/dist/types/langflower-palette.d.ts +2 -1
  151. package/vendor/tools/dist/harness-types.d.ts +1 -1
  152. package/vendor/tools/dist/mcp/build-mcp-handle.d.ts +3 -21
  153. package/vendor/tools/dist/mcp/build-mcp-handle.js +2 -11
  154. package/vendor/tools/dist/mcp/create-system-mcp-handles.d.ts +9 -6
  155. package/vendor/tools/dist/mcp/create-system-mcp-handles.js +21 -17
  156. package/vendor/tools/package.json +1 -0
  157. package/ui-dist/chunk-MDGXX73Z.js +0 -4
  158. package/ui-dist/main-Z7CPFXFA.js +0 -313
  159. 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: 'Emits a **boolean literal** from the inline `value` field.',
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: 'Emits a **number literal** from the inline `value` field.',
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: 'Emits a **string literal** from the inline `value` field.',
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: 'Appends `content` to a project-relative file. When the file is non-empty, inserts `delimiter` before the new content.',
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
- })).pipeValue(mergeMap(({ path: filePath, delimiter: fileDelimiter, content: fileContent, ec, }) => {
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: 'Joins wired string `value` slots with a `separator` into one string. Emits only when every wired `value` slot has a new event (`multi: zip`). In `separator`, `\\n` is replaced with a line break.',
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: 'Reads a project-relative file into `content`. Absolute paths are rejected. Emit on `update` to re-read.',
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
- })).pipeValue(mergeMap(({ path: filePath, ec }) => {
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: 'Writes `content` to a project-relative `path` (creates parents). Absolute paths are rejected.',
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
- })).pipeValue(mergeMap(({ path: filePath, content: fileContent, ec }) => {
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: 'Merges many `tools` wires into one `ToolHandle[]`. ' +
23
- 'Duplicate toolId last-wins. Optional — agents still accept multi combine.',
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
+ };
@@ -12,3 +12,4 @@ export type McpHandle = {
12
12
  /** Tools listed+bound at connect (`tools/list`; system MCP may filter). */
13
13
  readonly tools: readonly ToolHandle[];
14
14
  };
15
+ //# sourceMappingURL=mcp-handle.d.ts.map
@@ -1 +1,2 @@
1
1
  export {};
2
+ //# sourceMappingURL=mcp-handle.js.map
@@ -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
- export type FeedRole = 'none' | 'reasoning' | 'draft' | 'tool' | 'shell' | 'result' | 'recovery';
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 (chunks / interleaved streams).
20
- * Omit it so the frame closes the visit; same-node frames while last still
21
- * append to that visit.
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
- }).pipeValue(concatMap(({ inputValue, delayMs }) => of(inputValue).pipe(delay(delayMs))));
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 footer
56
- * (`renderMarkdown`). Omitted → no description block. Copied onto the
57
- * definition / `PaletteNodeDefinition` when set.
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 {};