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
|
@@ -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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
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)
|
|
158
|
-
// trailing draft.patch cannot race past commit. Otherwise persist
|
|
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 =
|
|
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 =
|
|
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 /
|
|
4
|
-
*
|
|
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
|
-
/**
|
|
22
|
-
const hasVisibleUiInputField = (config) => config.
|
|
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
|
|
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
|
-
|
|
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
|