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
@@ -0,0 +1,68 @@
1
+ import { of, firstValueFrom } from 'rxjs';
2
+ import { defaultParamsFromUiSchema, } from '../node-factory/define-reactive-node/ui-schema-inference.js';
3
+ const missingPort = (kind, portId) => new Error(`Unknown ${kind} port "${portId}"`);
4
+ /**
5
+ * Drive a {@link ReactiveNodeDefinition} in unit tests: seed context,
6
+ * `send` on input ports, `next` / `collect` on outputs.
7
+ *
8
+ * Subscribe with `next` or `collect` **before** `send` when the graph can
9
+ * emit synchronously.
10
+ */
11
+ export const createNodeHarness = (definition, options) => {
12
+ const instance = definition.getInstance();
13
+ const collectors = [];
14
+ const uiSchema = definition.uiSchema;
15
+ const params = {
16
+ ...defaultParamsFromUiSchema(uiSchema),
17
+ ...options?.params,
18
+ };
19
+ instance.ctxConnection.connect(of({
20
+ projectDir: options?.projectDir ?? '/tmp',
21
+ runId: options?.runId ?? 'test',
22
+ nodeId: options?.nodeId ?? 'node-1',
23
+ params,
24
+ uiSchema,
25
+ }));
26
+ const send = (portId, value) => {
27
+ const input = instance.inputs[portId];
28
+ if (input === undefined) {
29
+ throw missingPort('input', portId);
30
+ }
31
+ input.connect(of(value));
32
+ };
33
+ const next = (portId) => {
34
+ const output = instance.outputs[portId];
35
+ if (output === undefined) {
36
+ return Promise.reject(missingPort('output', portId));
37
+ }
38
+ return firstValueFrom(output.value$);
39
+ };
40
+ const collect = (portId) => {
41
+ const output = instance.outputs[portId];
42
+ if (output === undefined) {
43
+ throw missingPort('output', portId);
44
+ }
45
+ const values = [];
46
+ const subscription = output.value$.subscribe({
47
+ next: (value) => {
48
+ values.push(value);
49
+ },
50
+ });
51
+ collectors.push(subscription);
52
+ return {
53
+ get values() {
54
+ return values;
55
+ },
56
+ stop: () => {
57
+ subscription.unsubscribe();
58
+ },
59
+ };
60
+ };
61
+ const dispose = () => {
62
+ for (const subscription of collectors) {
63
+ subscription.unsubscribe();
64
+ }
65
+ collectors.length = 0;
66
+ };
67
+ return { instance, send, next, collect, dispose };
68
+ };
@@ -13,13 +13,13 @@
13
13
  "types": "./dist/node-factory/define-llm-node/define-llm-node.d.ts",
14
14
  "import": "./dist/node-factory/define-llm-node/define-llm-node.js"
15
15
  },
16
- "./mcp": {
17
- "types": "./dist/node-factory/define-mcp/mcp-handle.d.ts",
18
- "import": "./dist/node-factory/define-mcp/mcp-handle.js"
19
- },
20
16
  "./create-typed-ui-schema": {
21
17
  "types": "./dist/node-factory/define-reactive-node/ui-schema-inference.d.ts",
22
18
  "import": "./dist/node-factory/define-reactive-node/ui-schema-inference.js"
19
+ },
20
+ "./testing": {
21
+ "types": "./dist/testing/create-node-harness.d.ts",
22
+ "import": "./dist/testing/create-node-harness.js"
23
23
  }
24
24
  },
25
25
  "dependencies": {
@@ -14,7 +14,7 @@ export type RunId = string & {
14
14
  readonly __brand: 'RunId';
15
15
  };
16
16
  type RuntimeInputPortMode = 'single' | 'merge' | 'combine' | 'zip' | 'bypass';
17
- export type RuntimeFeedRole = 'none' | 'reasoning' | 'draft' | 'tool' | 'shell' | 'result' | 'recovery';
17
+ export type RuntimeFeedRole = 'none' | 'reasoning' | 'progress' | 'draft' | 'tool' | 'shell' | 'result' | 'recovery';
18
18
  export type RuntimeFeedPortMeta = {
19
19
  readonly role?: RuntimeFeedRole;
20
20
  /** When true, feed visit stays open for chunks / interleaved streams. */
@@ -1,7 +1,7 @@
1
1
  import { type SeedSkeletonMode, type SeedSkeletonResult } from './seed-skeleton.js';
2
2
  export type BootstrapProjectOptions = {
3
3
  readonly mode?: SeedSkeletonMode;
4
- /** When false, skip seeding skeleton `my-nodes`. Default true. */
4
+ /** When false, skip seeding skeleton custom-node packs. Default true. */
5
5
  readonly seedCustomNodes?: boolean;
6
6
  };
7
7
  /**
@@ -4,11 +4,11 @@ export type SeedSkeletonResult = {
4
4
  };
5
5
  export type SeedSkeletonOptions = {
6
6
  readonly mode: SeedSkeletonMode;
7
- /** When false, leave `nodes/` empty (no skeleton `my-nodes`). Default true. */
7
+ /** When false, leave `nodes/` empty (no skeleton packs). Default true. */
8
8
  readonly seedCustomNodes?: boolean;
9
9
  };
10
10
  /**
11
- * Copy skeleton workflows, skills, my-nodes, and instructions into
11
+ * Copy skeleton workflows, skills, custom-node packs, and instructions into
12
12
  * `.langflower/`. Create mode skips existing paths; force overwrites
13
13
  * skeleton-owned files (extra project-only files stay).
14
14
  */
@@ -23,8 +23,21 @@ const listSkeletonSkillIds = async (skeletonRoot) => {
23
23
  return [];
24
24
  }
25
25
  };
26
+ const listSkeletonPackIds = async (skeletonRoot) => {
27
+ const nodesDir = path.join(skeletonRoot, 'nodes');
28
+ try {
29
+ const entries = await fs.readdir(nodesDir, { withFileTypes: true });
30
+ return entries
31
+ .filter((entry) => entry.isDirectory())
32
+ .map((entry) => entry.name)
33
+ .sort();
34
+ }
35
+ catch {
36
+ return [];
37
+ }
38
+ };
26
39
  /**
27
- * Copy skeleton workflows, skills, my-nodes, and instructions into
40
+ * Copy skeleton workflows, skills, custom-node packs, and instructions into
28
41
  * `.langflower/`. Create mode skips existing paths; force overwrites
29
42
  * skeleton-owned files (extra project-only files stay).
30
43
  */
@@ -34,7 +47,9 @@ export const seedSkeletonContent = async (skeletonRoot, langflowerDir, options)
34
47
  const copyFile = mode === 'force' ? copyFileForce : copyFileIfMissing;
35
48
  const copyDir = mode === 'force' ? copyDirForce : copyDirIfMissing;
36
49
  if (seedCustomNodes) {
37
- await copyDir(path.join(skeletonRoot, 'nodes', 'my-nodes'), path.join(langflowerDir, 'nodes', 'my-nodes'));
50
+ for (const packId of await listSkeletonPackIds(skeletonRoot)) {
51
+ await copyDir(path.join(skeletonRoot, 'nodes', packId), path.join(langflowerDir, 'nodes', packId));
52
+ }
38
53
  }
39
54
  else {
40
55
  await fs.mkdir(path.join(langflowerDir, 'nodes'), { recursive: true });
@@ -0,0 +1,7 @@
1
+ import { type CreateEmbedding } from '@langflower/common-nodes/embeddings/create-embedding';
2
+ import type { LangflowerConfigService } from '../config/langflower-config.service.js';
3
+ /**
4
+ * Thin credential bind: secrets stay server-side; embeddings HTTP lives in
5
+ * common-nodes.
6
+ */
7
+ export declare const bindCreateEmbedding: (langflowerConfigService: LangflowerConfigService) => CreateEmbedding;
@@ -0,0 +1,16 @@
1
+ import { createEmbedding, } from '@langflower/common-nodes/embeddings/create-embedding';
2
+ import { resolveProviderCredentials } from '../config/resolve-provider-credentials.js';
3
+ /**
4
+ * Thin credential bind: secrets stay server-side; embeddings HTTP lives in
5
+ * common-nodes.
6
+ */
7
+ export const bindCreateEmbedding = (langflowerConfigService) => createEmbedding({
8
+ resolveProvider: async (providerId) => {
9
+ const config = await langflowerConfigService.read();
10
+ const resolved = resolveProviderCredentials(config, providerId);
11
+ if (!resolved.ok) {
12
+ throw new Error(resolved.message);
13
+ }
14
+ return resolved.credentials;
15
+ },
16
+ });
@@ -1,9 +1,9 @@
1
1
  import type { LangflowerBusRequest } from '@langflower/common-nodes/ai/run-host-services';
2
2
  import { type ExecutionContext, type ToolHandle } from '@langflower/node-sdk';
3
3
  import type { LlmExecutionCaps } from '@langflower/node-sdk/llm';
4
- import type { McpHandle } from '@langflower/node-sdk/mcp';
5
4
  import type { RuntimeSeedPortValue } from '@langflower/runtime';
6
5
  import { type LangflowerConfig, type RunnerPermissionAskPayload } from '@langflower/shared/langflower.js';
6
+ import { type SystemMcpServerTools } from '@langflower/tools/create-system-mcp-handles';
7
7
  import type { ServerContext } from '../server-context.js';
8
8
  import type { LangflowerSession } from '../session/langflower-session.js';
9
9
  type WorkflowGraphNode = {
@@ -20,7 +20,7 @@ type BuildHarnessHooks = {
20
20
  readonly requestLangflowerBus?: LangflowerBusRequest;
21
21
  readonly getLiveWiredTools?: (agentNodeId: string) => readonly ToolHandle[];
22
22
  };
23
- export declare const buildExecutionContext: (context: ExecutionContextDeps, runId: string, node: WorkflowGraphNode, hooks?: BuildHarnessHooks, preloadedConfig?: LangflowerConfig, runMcpHandles?: readonly McpHandle[]) => Promise<ExecutionContext<never, LlmExecutionCaps>>;
23
+ export declare const buildExecutionContext: (context: ExecutionContextDeps, runId: string, node: WorkflowGraphNode, hooks?: BuildHarnessHooks, preloadedConfig?: LangflowerConfig, runMcpHandles?: readonly SystemMcpServerTools[]) => Promise<ExecutionContext<never, LlmExecutionCaps>>;
24
24
  /**
25
25
  * Context seeds whose `value` is an Observable (e.g. `throwError` → CtxError)
26
26
  * must be `connect`ed directly. Runner `applySeeds` wraps with `of(value)`,
@@ -2,12 +2,13 @@ import { parseLlmRolePreset, resolveEffectiveSkillId, resolveEffectiveToolPermis
2
2
  import { attachRunHostServices } from '@langflower/common-nodes/ai/run-host-services';
3
3
  import { contextSymbol, } from '@langflower/node-sdk';
4
4
  import { parseDefaultChatModel, } from '@langflower/shared/langflower.js';
5
- import { collectEnabledMcpIdsFromNodes, createSystemMcpHandles, filterMcpFailuresForNode, filterMcpHandlesByIds, parseEnabledMcpIds, } from '@langflower/tools/create-system-mcp-handles';
5
+ import { collectEnabledMcpIdsFromNodes, createSystemMcpHandles, filterMcpFailuresForNode, filterSystemMcpToolsByServerIds, parseEnabledMcpIds, } from '@langflower/tools/create-system-mcp-handles';
6
6
  import { createProjectHarness, } from '@langflower/tools/create-project-harness';
7
7
  import { createWebFetch } from '@langflower/tools/create-web-fetch';
8
8
  import { mergeProjectAndNodePermissions, } from '@langflower/tools/permission';
9
9
  import { isObservable, throwError } from 'rxjs';
10
10
  import { bindCreateChatCompletionStream } from './bind-llm-context.js';
11
+ import { bindCreateEmbedding } from './bind-embed-context.js';
11
12
  import { wrapBuiltinToolHandles } from './wrap-builtin-tool-handles.js';
12
13
  import { readAgentsMarkdown } from '../skills/read-agents-markdown.js';
13
14
  import { readSkillMarkdown } from '../skills/read-skill-markdown.js';
@@ -74,7 +75,7 @@ export const buildExecutionContext = async (context, runId, node, hooks, preload
74
75
  };
75
76
  const enabledToolIds = toolPermissionsToEnabledIds(toolPermissions);
76
77
  const builtinHandles = wrapBuiltinToolHandles(harness, enabledToolIds, permission);
77
- const mcpTools = filterMcpHandlesByIds(runMcpHandles ?? [], parseEnabledMcpIds(node.params.enabledMcpIds)).flatMap((handle) => handle.tools);
78
+ const mcpTools = filterSystemMcpToolsByServerIds(runMcpHandles ?? [], parseEnabledMcpIds(node.params.enabledMcpIds)).flatMap((entry) => entry.tools);
78
79
  const toolHandles = [...builtinHandles, ...mcpTools];
79
80
  const base = {
80
81
  projectDir: context.projectDir,
@@ -88,13 +89,16 @@ export const buildExecutionContext = async (context, runId, node, hooks, preload
88
89
  ...(toolHandles.length > 0 ? { toolHandles } : {}),
89
90
  };
90
91
  const defaultChat = parseDefaultChatModel(config.model);
92
+ const defaultEmbedding = parseDefaultChatModel(config.embedding);
91
93
  return attachRunHostServices(base, {
92
94
  ...(node.type !== 'common-fake-llm'
93
95
  ? {
94
96
  createChatCompletionStream: bindCreateChatCompletionStream(context.langflowerConfigService),
95
97
  }
96
98
  : {}),
99
+ createEmbedding: bindCreateEmbedding(context.langflowerConfigService),
97
100
  ...(defaultChat !== null ? { defaultChat } : {}),
101
+ ...(defaultEmbedding !== null ? { defaultEmbedding } : {}),
98
102
  ...(skillMarkdown.length > 0 ? { skillMarkdown } : {}),
99
103
  ...(agentsMarkdown.length > 0 ? { agentsMarkdown } : {}),
100
104
  ...(toolHarness.authorize !== undefined
@@ -26,7 +26,10 @@ export const emitBootstrap = async (client, context, session, checkpoints, draft
26
26
  if (langflowerConfig.dividerPositions !== undefined) {
27
27
  session.dividerPositions = langflowerConfig.dividerPositions;
28
28
  }
29
- // Thin bootstrap — version + config + dividers + selected node only
29
+ if (langflowerConfig.paletteVisible !== undefined) {
30
+ session.paletteVisible = langflowerConfig.paletteVisible;
31
+ }
32
+ // Thin bootstrap — version + config + dividers + palette chrome + selected node only
30
33
  const bootstrap = await buildSessionBootstrap(session, context.langflowerConfigService, context.resolveDefinition, context.projectDir);
31
34
  clientEmit(client, 'session.state.snapshot', bootstrap);
32
35
  // Domain snapshots
@@ -154,12 +154,13 @@ export const createSettingsDraftController = (bridge, context, session) => {
154
154
  const commitSave = async (scope, fallback) => {
155
155
  await ensureSeeded(scope);
156
156
  const state = getState(scope);
157
- // Prefer an explicit Save payload (provider/model/serverLogs) so a
158
- // trailing draft.patch cannot race past commit. Otherwise persist the
159
- // session draft (`{ scope }` only).
157
+ // Prefer an explicit Save payload (provider/model/embedding/serverLogs)
158
+ // so a trailing draft.patch cannot race past commit. Otherwise persist
159
+ // the session draft (`{ scope }` only).
160
160
  const payload = fallback !== undefined &&
161
161
  (fallback.provider !== undefined ||
162
162
  fallback.model !== undefined ||
163
+ fallback.embedding !== undefined ||
163
164
  'serverLogs' in fallback)
164
165
  ? fallback
165
166
  : state !== undefined
@@ -172,6 +173,9 @@ export const createSettingsDraftController = (bridge, context, session) => {
172
173
  const layers = await context.langflowerConfigService.writeSettings({
173
174
  scope: payload.scope,
174
175
  ...(payload.model !== undefined ? { model: payload.model } : {}),
176
+ ...(payload.embedding !== undefined
177
+ ? { embedding: payload.embedding }
178
+ : {}),
175
179
  ...(payload.provider !== undefined
176
180
  ? { provider: payload.provider }
177
181
  : {}),
@@ -9,6 +9,8 @@ const isSettingsDraft = (value) => {
9
9
  const draft = value;
10
10
  return (typeof draft['defaultProviderId'] === 'string' &&
11
11
  typeof draft['defaultModelId'] === 'string' &&
12
+ typeof draft['defaultEmbeddingProviderId'] === 'string' &&
13
+ typeof draft['defaultEmbeddingModelId'] === 'string' &&
12
14
  Array.isArray(draft['providers']) &&
13
15
  (draft['serverLogs'] === 'off' ||
14
16
  draft['serverLogs'] === 'default' ||
@@ -182,6 +182,20 @@ export const wireEditorHandlers = (bridge, context, session, draftController) =>
182
182
  bridgeEmit(bridge, 'editor.dividers.snapshot', clamped);
183
183
  void context.langflowerConfigService.setDividerPositions(clamped);
184
184
  }));
185
+ subscription.add(bridge['editor.paletteVisible.requested'].subscribe((raw) => {
186
+ if (!isInboundEvent(raw)) {
187
+ return;
188
+ }
189
+ if (findClientById(bridge, raw.clientId) === undefined) {
190
+ return;
191
+ }
192
+ if (typeof raw.payload !== 'boolean') {
193
+ return;
194
+ }
195
+ session.paletteVisible = raw.payload;
196
+ bridgeEmit(bridge, 'editor.paletteVisible.snapshot', raw.payload);
197
+ void context.langflowerConfigService.setPaletteVisible(raw.payload);
198
+ }));
185
199
  subscription.add(bridge['editor.settings.requested'].subscribe((raw) => {
186
200
  if (!isInboundEvent(raw)) {
187
201
  return;
@@ -6,6 +6,7 @@ export type LangflowerConfigLayers = {
6
6
  export type LangflowerConfigSettingsWrite = {
7
7
  readonly scope: LangflowerConfigScope;
8
8
  readonly model?: string;
9
+ readonly embedding?: string;
9
10
  readonly provider?: Readonly<Record<string, LangflowerProviderConfig>>;
10
11
  readonly providerApiKeys?: Readonly<Record<string, string>>;
11
12
  /** `null` clears the scope key (Settings Default). */
@@ -32,4 +33,5 @@ export declare class LangflowerConfigService {
32
33
  writeSettings(input: LangflowerConfigSettingsWrite): Promise<LangflowerConfigLayers>;
33
34
  setCurrentWorkflowId(workflowId: string | undefined): Promise<LangflowerConfig>;
34
35
  setDividerPositions(positions: DividerPositions): Promise<LangflowerConfig>;
36
+ setPaletteVisible(visible: boolean): Promise<LangflowerConfig>;
35
37
  }
@@ -147,6 +147,9 @@ function parseLangflowerConfig(raw) {
147
147
  ? { currentWorkflowId: raw.currentWorkflowId }
148
148
  : {}),
149
149
  ...(typeof raw.model === 'string' ? { model: raw.model } : {}),
150
+ ...(typeof raw.embedding === 'string'
151
+ ? { embedding: raw.embedding }
152
+ : {}),
150
153
  ...(typeof raw.serverLogs === 'boolean'
151
154
  ? { serverLogs: raw.serverLogs }
152
155
  : {}),
@@ -156,6 +159,9 @@ function parseLangflowerConfig(raw) {
156
159
  ...(harness !== undefined ? { harness } : {}),
157
160
  ...(mcp !== undefined ? { mcp } : {}),
158
161
  ...parseDividerPositions(raw.dividerPositions),
162
+ ...(typeof raw.paletteVisible === 'boolean'
163
+ ? { paletteVisible: raw.paletteVisible }
164
+ : {}),
159
165
  };
160
166
  }
161
167
  function parseDividerPositions(raw) {
@@ -205,6 +211,14 @@ function mergeLangflowerConfig(existing, patch) {
205
211
  merged.model = patch.model;
206
212
  }
207
213
  }
214
+ if (patch.embedding !== undefined) {
215
+ if (patch.embedding.trim().length === 0) {
216
+ delete merged.embedding;
217
+ }
218
+ else {
219
+ merged.embedding = patch.embedding;
220
+ }
221
+ }
208
222
  if ('serverLogs' in patch) {
209
223
  if (patch.serverLogs === undefined) {
210
224
  delete merged.serverLogs;
@@ -219,6 +233,9 @@ function mergeLangflowerConfig(existing, patch) {
219
233
  if (patch.dividerPositions !== undefined) {
220
234
  merged.dividerPositions = patch.dividerPositions;
221
235
  }
236
+ if (patch.paletteVisible !== undefined) {
237
+ merged.paletteVisible = patch.paletteVisible;
238
+ }
222
239
  return merged;
223
240
  }
224
241
  function serializeLangflowerConfig(merged) {
@@ -302,6 +319,9 @@ export class LangflowerConfigService {
302
319
  ? { currentWorkflowId: config.currentWorkflowId }
303
320
  : {}),
304
321
  ...(config.model !== undefined ? { model: config.model } : {}),
322
+ ...(config.embedding !== undefined
323
+ ? { embedding: config.embedding }
324
+ : {}),
305
325
  ...(config.provider !== undefined
306
326
  ? { provider: config.provider }
307
327
  : {}),
@@ -330,6 +350,9 @@ export class LangflowerConfigService {
330
350
  ]));
331
351
  const patch = {
332
352
  ...(input.model !== undefined ? { model: input.model } : {}),
353
+ ...(input.embedding !== undefined
354
+ ? { embedding: input.embedding }
355
+ : {}),
333
356
  ...(provider !== undefined ? { provider } : {}),
334
357
  ...('serverLogs' in input
335
358
  ? {
@@ -357,4 +380,11 @@ export class LangflowerConfigService {
357
380
  await this.writeRawAt(this.projectConfigPath(), merged);
358
381
  return parseLangflowerConfig(merged);
359
382
  }
383
+ async setPaletteVisible(visible) {
384
+ const merged = mergeLangflowerConfig(await this.readRawAt(this.projectConfigPath()), {
385
+ paletteVisible: visible,
386
+ });
387
+ await this.writeRawAt(this.projectConfigPath(), merged);
388
+ return parseLangflowerConfig(merged);
389
+ }
360
390
  }
@@ -16,6 +16,7 @@ export async function buildSessionBootstrap(session, langflowerConfigService, re
16
16
  version: LangflowerSession.snapshotVersion,
17
17
  langflowerConfig,
18
18
  dividerPositions: session.dividerPositions,
19
+ paletteVisible: session.paletteVisible,
19
20
  selectedNode: buildSelectedNodePayload(session, resolveDefinition).node,
20
21
  settings: session.settings,
21
22
  };
@@ -14,6 +14,8 @@ export declare class LangflowerSession {
14
14
  runnerStatus: RuntimeRunnerStatus;
15
15
  runId: RunId | undefined;
16
16
  dividerPositions: DividerPositions;
17
+ /** Left node palette shown. Hydrated from project jsonc; default visible. */
18
+ paletteVisible: boolean;
17
19
  /** In-memory canvas selection, shared across tabs. `null` = nothing selected. */
18
20
  selectedNodeId: string | null;
19
21
  /** Settings aside chrome — session memory only (not persisted to disk). */
@@ -32,5 +34,5 @@ export declare class LangflowerSession {
32
34
  markPristine(): void;
33
35
  buildExecutionFeed(): ExecutionFeedSnapshotPayload | null;
34
36
  static readonly sessionReadyVersion = 1;
35
- static readonly snapshotVersion = 6;
37
+ static readonly snapshotVersion = 7;
36
38
  }
@@ -4,7 +4,7 @@ import { Subscription } from 'rxjs';
4
4
  import { PendingPermissionAsks } from '../harness/pending-permission-asks.js';
5
5
  import { emptySettingsDraftStore, } from './settings-draft-session.js';
6
6
  const SESSION_READY_VERSION = 1;
7
- const SNAPSHOT_VERSION = 6;
7
+ const SNAPSHOT_VERSION = 7;
8
8
  export class LangflowerSession {
9
9
  runtime = new RuntimeFacade({ log: true });
10
10
  /** Feed permission.ask pause/resume for the internal tool loop. */
@@ -20,6 +20,8 @@ export class LangflowerSession {
20
20
  rightWidth: 360,
21
21
  composerHeight: 168,
22
22
  };
23
+ /** Left node palette shown. Hydrated from project jsonc; default visible. */
24
+ paletteVisible = true;
23
25
  /** In-memory canvas selection, shared across tabs. `null` = nothing selected. */
24
26
  selectedNodeId = null;
25
27
  /** Settings aside chrome — session memory only (not persisted to disk). */
@@ -149,6 +149,12 @@ const materializeRuntimeNode = (projectDir, node, resolveDefinition) => {
149
149
  if (input === undefined) {
150
150
  continue;
151
151
  }
152
+ // HITL ports stay inactive until `pushIntoInput` (composer Start /
153
+ // reply). Connecting a persisted Chat Input message here would
154
+ // double-emit when cold-start does startNode + pushIntoInput.
155
+ if (config.hitl !== undefined) {
156
+ continue;
157
+ }
152
158
  if (Object.hasOwn(node.inputs, config.portId)) {
153
159
  input.connect(of(node.inputs[config.portId]));
154
160
  continue;
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Persisted `node.inputs` hold visible UI overrides only (ADR-028).
3
- * Wire-only / hidden / preview ports and values equal to the current
4
- * definition `defaultValue` must not be written to disk.
3
+ * Wire-only / preview ports and values equal to the current definition
4
+ * `defaultValue` must not be written to disk. Hidden ports with an
5
+ * editable `inline` still persist (Chat Input `message`).
5
6
  */
6
7
  const valuesEqual = (left, right) => JSON.stringify(left) === JSON.stringify(right);
7
8
  const isPreviewInline = (inline) => {
@@ -18,10 +19,8 @@ const isPreviewInline = (inline) => {
18
19
  }
19
20
  return false;
20
21
  };
21
- /** Visible editable field on the canvas / inspector (not wire-only, not preview). */
22
- const hasVisibleUiInputField = (config) => config.hidden !== true &&
23
- config.inline !== undefined &&
24
- !isPreviewInline(config.inline);
22
+ /** Editable inline field (canvas / inspector), including hidden+inline. */
23
+ const hasVisibleUiInputField = (config) => config.inline !== undefined && !isPreviewInline(config.inline);
25
24
  /**
26
25
  * Keep only persistable overrides: visible UI field and value !== current
27
26
  * definition defaultValue.
@@ -36,6 +35,9 @@ export const prunePersistedInputs = (inputs, definition) => {
36
35
  if (!hasVisibleUiInputField(config)) {
37
36
  continue;
38
37
  }
38
+ if (typeof value === 'string' && value.trim() === '') {
39
+ continue;
40
+ }
39
41
  if (config.defaultValue !== undefined &&
40
42
  valuesEqual(value, config.defaultValue)) {
41
43
  continue;
@@ -1,7 +1,8 @@
1
1
  # Custom Nodes
2
2
 
3
- Author packs under `.langflower/nodes/<pack>/`. Default seed pack:
4
- **`my-nodes/`**.
3
+ Author packs under `.langflower/nodes/<pack>/`. Default seed packs:
4
+ **`my-nodes/`** (git-diff / review-gate) and **`hello-embed/`** (markdown
5
+ → local sqlite embeddings; copy as a starting point).
5
6
 
6
7
  Import **`defineNode`** (default), `defineToolRegistrations` (LLM tool packs),
7
8
  or `defineReactiveNode` from `@langflower/node-sdk` — not from a root
@@ -10,8 +11,19 @@ or `defineReactiveNode` from `@langflower/node-sdk` — not from a root
10
11
  Each `*.ts` file may `export default` a definition or an array. No required
11
12
  `index.ts`. Run `npm install` inside the pack when you add author dependencies;
12
13
  Langflower does **not** auto-install on start, bootstrap, or palette reload.
14
+ Startup **does** compile packs under `.langflower/nodes/`.
13
15
 
14
- See [`nodes/my-nodes/README.md`](nodes/my-nodes/README.md) for the full
16
+ If pack files `import` each other with a **`.ts` suffix**, set
17
+ `"allowImportingTsExtensions": true` next to `"noEmit": true` in the pack
18
+ `tsconfig.json` (hello-embed seed). Without it the pack does not compile.
19
+ `my-nodes` uses extensionless imports and does not need the flag.
20
+
21
+ - `hello-embed/` — sample embedding KB (`ingest` / `search` /
22
+ `search-handle`). See [`nodes/hello-embed/README.md`](nodes/hello-embed/README.md).
23
+ Workflows: `kb-ingest`, `kb-manual-search`, `kb-tool`, `kb-rag`.
24
+ Settings default embedding model required.
25
+
26
+ See [`nodes/my-nodes/README.md`](nodes/my-nodes/README.md) for the author
15
27
  contract.
16
28
 
17
29
  ## Seed demo