langflower 0.0.9 → 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 +2 -2
- 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-loop/llm-loop-reducer.js +0 -12
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.d.ts +2 -7
- package/vendor/common-nodes/dist/ai/features/llm-loop/llm-loop-types.js +1 -1
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.d.ts +0 -3
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-agent-loop.js +22 -36
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.d.ts +1 -7
- package/vendor/common-nodes/dist/ai/features/llm-loop/run-llm-loop.js +12 -35
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.d.ts +5 -12
- package/vendor/common-nodes/dist/ai/features/llm-session/llm-session-shell.js +48 -21
- 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/path-choice/run-reactive-path-choice-loop.d.ts +0 -3
- package/vendor/common-nodes/dist/ai/features/path-choice/run-reactive-path-choice-loop.js +2 -15
- package/vendor/common-nodes/dist/ai/features/run-host-services.d.ts +10 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/sub-agent-protocol.js +1 -0
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.d.ts +1 -1
- package/vendor/common-nodes/dist/ai/features/ui-schema/llm-recovery-ui-schema.js +1 -1
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.d.ts +1 -0
- package/vendor/common-nodes/dist/ai/features/wait-for-subagent-result.js +1 -0
- package/vendor/common-nodes/dist/ai/nodes/critique/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/critique/node.js +10 -19
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/fake-llm/node.js +10 -11
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/openai-llm/node.js +7 -10
- package/vendor/common-nodes/dist/ai/nodes/review/node.d.ts +3 -3
- package/vendor/common-nodes/dist/ai/nodes/review/node.js +12 -19
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.d.ts +5 -5
- package/vendor/common-nodes/dist/ai/nodes/sub-agent/node.js +251 -96
- package/vendor/common-nodes/dist/catalog.js +12 -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.d.ts +4 -4
- package/vendor/common-nodes/dist/mcp/mcp-http/node.js +61 -60
- package/vendor/common-nodes/dist/mcp/mcp-stdio/node.d.ts +4 -4
- package/vendor/common-nodes/dist/mcp/mcp-stdio/node.js +58 -57
- 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/collect-agent-tool-handles.d.ts +4 -4
- package/vendor/common-nodes/dist/tools/collect-agent-tool-handles.js +3 -11
- package/vendor/common-nodes/dist/tools/inventory-tool-round.d.ts +0 -10
- package/vendor/common-nodes/dist/tools/inventory-tool-round.js +0 -84
- package/vendor/common-nodes/dist/tools/tool-collection/node.d.ts +20 -0
- package/vendor/common-nodes/dist/tools/tool-collection/node.js +45 -0
- package/vendor/common-nodes/package.json +5 -5
- package/vendor/compiler/package.json +1 -1
- 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-llm-node/default-llm-ports.d.ts +3 -7
- package/vendor/node-sdk/dist/node-factory/define-llm-node/default-llm-ports.js +4 -44
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.d.ts +3 -5
- package/vendor/node-sdk/dist/node-factory/define-llm-node/define-llm-node.js +3 -5
- package/vendor/node-sdk/dist/node-factory/define-llm-node/llm-inventory-wire.d.ts +1 -0
- package/vendor/node-sdk/dist/node-factory/define-llm-node/llm-inventory-wire.js +1 -0
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.d.ts +4 -4
- package/vendor/node-sdk/dist/node-factory/define-mcp/mcp-handle.js +2 -1
- 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 +6 -7
- package/vendor/node-sdk/dist/node-factory/define-reactive-node/ui-schema-inference.d.ts +1 -1
- 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 +5 -5
- package/vendor/runtime/dist/port-feed-override.d.ts +13 -0
- package/vendor/runtime/dist/port-feed-override.js +21 -0
- package/vendor/runtime/dist/port-signal-from-response.d.ts +15 -0
- package/vendor/runtime/dist/port-signal-from-response.js +48 -0
- package/vendor/runtime/dist/runtime-runner.d.ts +8 -3
- package/vendor/runtime/dist/runtime-runner.js +102 -81
- package/vendor/runtime/dist/runtime.d.ts +3 -2
- package/vendor/runtime/dist/runtime.js +1 -1
- package/vendor/runtime/dist/testing/workflows/workflow-events.d.ts +2 -0
- package/vendor/runtime/dist/testing/workflows/workflow-events.js +7 -5
- package/vendor/runtime/dist/types.d.ts +26 -9
- package/vendor/runtime/dist/types.js +3 -0
- package/vendor/runtime/package.json +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 +8 -4
- package/vendor/server/dist/bridge/emit-bootstrap.js +4 -1
- package/vendor/server/dist/bridge/get-live-wired-tools.d.ts +3 -2
- package/vendor/server/dist/bridge/get-live-wired-tools.js +8 -5
- 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/bridge/wire-runner-handlers.js +2 -2
- package/vendor/server/dist/bridge/wire-workflow-handlers.js +23 -9
- package/vendor/server/dist/checkpoint/run-checkpoint-session.js +3 -3
- 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/session/reset-session-execution-feed.d.ts +6 -0
- package/vendor/server/dist/session/reset-session-execution-feed.js +8 -0
- 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/nodes/my-nodes/package.json +1 -1
- package/vendor/server/skeleton/schemas/langflower-config.schema.json +4 -0
- package/vendor/server/skeleton/skills/langflower-helper/SKILL.md +125 -38
- package/vendor/server/skeleton/skills/langflower-helper/architecture.md +17 -6
- package/vendor/server/skeleton/skills/langflower-helper/layout.md +24 -17
- package/vendor/server/skeleton/skills/langflower-node-writer/SKILL.md +8 -1
- package/vendor/server/skeleton/skills/langflower-workflow-writer/SKILL.md +12 -8
- package/vendor/server/skeleton/workflows/kb-create.json +15 -43
- 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-navigate.json +8 -22
- package/vendor/server/skeleton/workflows/kb-rag.json +192 -0
- package/vendor/server/skeleton/workflows/kb-tool.json +111 -0
- package/vendor/server/skeleton/workflows/simple-coder.json +18 -46
- package/vendor/server/skeleton/workflows/starter.json +8 -22
- package/vendor/shared/dist/execution/derive-run-settle-outcome.js +2 -2
- package/vendor/shared/dist/execution/derive-run-settle-outcome.test.js +11 -8
- 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-ws-waits.d.ts +3 -1
- package/vendor/shared/dist/langflower-ws-waits.js +2 -2
- 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-2JJGCNTD.js +0 -313
- package/ui-dist/styles-EOXODOI7.css +0 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { defineLlmNode, isSteerControlPayload, toLlmRecoveryPortValue, } from '@langflower/node-sdk/llm';
|
|
2
|
-
import {
|
|
2
|
+
import { TOOL_HANDLE_WIRE_TYPE, } from '@langflower/node-sdk';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { catchError, concat, concatMap, filter, firstValueFrom, map, mergeMap, Observable, of, Subject, take, tap, TimeoutError, } from 'rxjs';
|
|
5
|
+
import { statefulObservable } from '@rx-evo/stateful-observable';
|
|
3
6
|
import { llmPanelUiSchema } from '../../features/ui-schema/llm-panel-ui-schema.js';
|
|
4
7
|
import { llmCompactionUiSchema } from '../../features/ui-schema/llm-compaction-ui-schema.js';
|
|
5
8
|
import { llmRecoveryUiSchema } from '../../features/ui-schema/llm-recovery-ui-schema.js';
|
|
6
9
|
import { AGENT_MAX_ITERATIONS_CAP, DEFAULT_AGENT_MAX_ITERATIONS, normalizeMaxIterations, } from '../../features/prompt/normalize-max-iterations.js';
|
|
7
|
-
import { appendToolInventory,
|
|
10
|
+
import { appendToolInventory, demuxByKind, } from '../../features/llm-session/llm-session-shell.js';
|
|
8
11
|
import { runAgentLoop, } from '../../features/llm-loop/run-agent-loop.js';
|
|
9
|
-
import { waitForSubagentResult } from '../../features/wait-for-subagent-result.js';
|
|
10
|
-
import { flattenSubAgentRegistrations, isSubAgentSpawnPayload, SUBAGENT_REGISTRATION_WIRE_TYPE, SUBAGENT_RESULT_WIRE_TYPE, SUBAGENT_SPAWN_WIRE_TYPE, } from '../../features/sub-agent-protocol.js';
|
|
11
12
|
import { collectAgentToolHandles } from '../../../tools/collect-agent-tool-handles.js';
|
|
12
13
|
import { parseLlmRolePreset, resolveEffectiveSkillId, } from '../../features/llm-role-preset.js';
|
|
13
14
|
import { resolveChatProviderModel } from '../../features/prompt/resolve-chat-provider-model.js';
|
|
@@ -24,23 +25,59 @@ const normalizeSkillIds = (raw) => {
|
|
|
24
25
|
.map((entry) => String(entry ?? '').trim())
|
|
25
26
|
.filter((entry) => entry.length > 0);
|
|
26
27
|
};
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const formatSpecialistHandleName = (displayName) => `${displayName}(subagent)`;
|
|
29
|
+
const slugToolId = (name, nodeId) => {
|
|
30
|
+
const slug = (raw) => raw
|
|
31
|
+
.replace(/[^a-zA-Z0-9_-]+/g, '_')
|
|
32
|
+
.replace(/^_+|_+$/g, '')
|
|
33
|
+
.slice(0, 64);
|
|
34
|
+
const fromName = slug(name.trim());
|
|
35
|
+
if (fromName.length > 0) {
|
|
36
|
+
return fromName;
|
|
30
37
|
}
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
const fromNode = slug(nodeId);
|
|
39
|
+
return fromNode.length > 0 ? fromNode : 'subagent';
|
|
40
|
+
};
|
|
41
|
+
const formatSpawnUserContent = (task, skillId) => {
|
|
42
|
+
if (skillId.length === 0) {
|
|
43
|
+
return task;
|
|
44
|
+
}
|
|
45
|
+
return `[skill:${skillId}]\n${task}`;
|
|
46
|
+
};
|
|
47
|
+
const buildSpecialistDescription = (name, description, skillIds) => {
|
|
48
|
+
const parts = [
|
|
49
|
+
`${name} is a canvas Sub-Agent — a separate specialist agent, not a regular tool (not files, shell, or MCP).`,
|
|
50
|
+
'Call this tool to delegate a task; the specialist runs its own model and tools and returns a text result.',
|
|
51
|
+
'Do not try to do this specialist work yourself when this tool is the right owner.',
|
|
52
|
+
];
|
|
53
|
+
if (description.trim().length > 0) {
|
|
54
|
+
parts.push(description.trim());
|
|
33
55
|
}
|
|
34
|
-
if (
|
|
35
|
-
|
|
56
|
+
if (skillIds.length > 0) {
|
|
57
|
+
parts.push(`Skills: ${skillIds.join(', ')}.`);
|
|
36
58
|
}
|
|
37
|
-
|
|
59
|
+
parts.push('Required argument: task — the assignment for this Sub-Agent.');
|
|
60
|
+
return parts.join(' ');
|
|
38
61
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
const buildSpecialistInputSchema = (skillIds) => {
|
|
63
|
+
const properties = {
|
|
64
|
+
task: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'Task to delegate to this Sub-Agent specialist',
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
if (skillIds.length > 0) {
|
|
70
|
+
properties.skillId = {
|
|
71
|
+
type: 'string',
|
|
72
|
+
enum: [...skillIds],
|
|
73
|
+
description: 'Optional skill announced by this Sub-Agent Inspector selector',
|
|
74
|
+
};
|
|
42
75
|
}
|
|
43
|
-
return
|
|
76
|
+
return {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties,
|
|
79
|
+
required: ['task'],
|
|
80
|
+
};
|
|
44
81
|
};
|
|
45
82
|
const resolveTurnFactory = (context) => {
|
|
46
83
|
if (context.scriptedTurns !== undefined) {
|
|
@@ -48,33 +85,78 @@ const resolveTurnFactory = (context) => {
|
|
|
48
85
|
}
|
|
49
86
|
return context.factory;
|
|
50
87
|
};
|
|
51
|
-
const
|
|
88
|
+
const chatConfigError = (providerId, model, scripted) => {
|
|
52
89
|
if (scripted) {
|
|
53
|
-
return;
|
|
90
|
+
return undefined;
|
|
54
91
|
}
|
|
55
92
|
if (providerId.trim().length === 0) {
|
|
56
|
-
|
|
93
|
+
return 'Error: Provider is required for Sub-Agent chat';
|
|
57
94
|
}
|
|
58
95
|
if (model.trim().length === 0) {
|
|
59
|
-
|
|
96
|
+
return 'Error: Model is required for Sub-Agent chat';
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
};
|
|
100
|
+
const EMPTY_INVOKE_RESULT = 'Error: Sub-Agent returned no content';
|
|
101
|
+
const finalizeInvokeText = (responseText, lastDraft, lastReasoning) => {
|
|
102
|
+
if (responseText.trim().length > 0) {
|
|
103
|
+
return responseText;
|
|
104
|
+
}
|
|
105
|
+
if (lastDraft.trim().length > 0) {
|
|
106
|
+
return lastDraft;
|
|
107
|
+
}
|
|
108
|
+
if (lastReasoning.trim().length > 0) {
|
|
109
|
+
return lastReasoning;
|
|
110
|
+
}
|
|
111
|
+
return EMPTY_INVOKE_RESULT;
|
|
112
|
+
};
|
|
113
|
+
const seedInvokeHistory = (history, context) => {
|
|
114
|
+
const system = {
|
|
115
|
+
role: 'system',
|
|
116
|
+
content: appendToolInventory(context.effectiveSystemPrompt, context.tools),
|
|
117
|
+
};
|
|
118
|
+
if (history.length === 0) {
|
|
119
|
+
return [system];
|
|
120
|
+
}
|
|
121
|
+
if (history[0]?.role === 'system') {
|
|
122
|
+
return [system, ...history.slice(1)];
|
|
60
123
|
}
|
|
124
|
+
return [system, ...history];
|
|
61
125
|
};
|
|
62
|
-
const
|
|
126
|
+
const reduceInvokeHistory = (history, chunk) => {
|
|
127
|
+
if (chunk.kind === 'historySync') {
|
|
128
|
+
return [...chunk.messages];
|
|
129
|
+
}
|
|
130
|
+
if (chunk.kind === 'invokeDone' && chunk.text.trim().length > 0) {
|
|
131
|
+
return [...history, { role: 'assistant', content: chunk.text }];
|
|
132
|
+
}
|
|
133
|
+
return history;
|
|
134
|
+
};
|
|
135
|
+
const runSubAgentTurn = (context, turn, history) => {
|
|
136
|
+
const fail = (text) => of({ kind: 'response', text }, {
|
|
137
|
+
kind: 'invokeDone',
|
|
138
|
+
requestId: turn.requestId,
|
|
139
|
+
text,
|
|
140
|
+
});
|
|
63
141
|
const scripted = context.scriptedTurns !== undefined;
|
|
64
142
|
const factory = resolveTurnFactory(context);
|
|
65
143
|
if (factory === undefined) {
|
|
66
|
-
return
|
|
67
|
-
subscriber.error(new Error('Sub-Agent chat is only available during server workflow runs'));
|
|
68
|
-
});
|
|
144
|
+
return fail('Error: Sub-Agent chat is only available during server workflow runs');
|
|
69
145
|
}
|
|
70
|
-
|
|
146
|
+
const configError = chatConfigError(context.providerId, context.model, scripted);
|
|
147
|
+
if (configError !== undefined) {
|
|
148
|
+
return fail(configError);
|
|
149
|
+
}
|
|
150
|
+
const userContent = formatSpawnUserContent(turn.task, turn.skillId);
|
|
151
|
+
let lastDraft = '';
|
|
152
|
+
let lastReasoning = '';
|
|
71
153
|
return concat(of({
|
|
72
154
|
kind: 'historySync',
|
|
73
155
|
messages: [
|
|
74
156
|
...history,
|
|
75
157
|
{
|
|
76
158
|
role: 'user',
|
|
77
|
-
content:
|
|
159
|
+
content: userContent,
|
|
78
160
|
},
|
|
79
161
|
],
|
|
80
162
|
}), runAgentLoop({
|
|
@@ -85,50 +167,106 @@ const runSubAgentTurn = (context, spawn, history, subagentResult$) => {
|
|
|
85
167
|
model: scripted
|
|
86
168
|
? context.model.trim() || 'scripted'
|
|
87
169
|
: context.model,
|
|
88
|
-
messages: [
|
|
89
|
-
...history,
|
|
90
|
-
{ role: 'user', content: formatSpawnUserContent(spawn) },
|
|
91
|
-
],
|
|
170
|
+
messages: [...history, { role: 'user', content: userContent }],
|
|
92
171
|
tools: context.tools,
|
|
93
172
|
toolCtx: context.toolCtx,
|
|
94
173
|
maxIterations: context.maxIterations,
|
|
95
174
|
compaction: context.compaction,
|
|
96
175
|
recovery: context.recovery,
|
|
97
|
-
subagentRegistrations: context.subagentRegistrations,
|
|
98
176
|
...(context.requestPermission !== undefined
|
|
99
177
|
? { requestPermission: context.requestPermission }
|
|
100
178
|
: {}),
|
|
101
179
|
...(context.steerControl$ !== undefined
|
|
102
180
|
? { steerControl$: context.steerControl$ }
|
|
103
181
|
: {}),
|
|
104
|
-
...(context.subagentRegistrations.length > 0
|
|
105
|
-
? {
|
|
106
|
-
waitForSubagentResult: (callId, signal) => waitForSubagentResult(subagentResult$, callId, signal, context.recovery.subagentTimeoutMs),
|
|
107
|
-
}
|
|
108
|
-
: {}),
|
|
109
182
|
}).pipe(mergeMap((chunk) => {
|
|
183
|
+
if (chunk.kind === 'reasoning' && chunk.text.length > 0) {
|
|
184
|
+
lastReasoning += chunk.text;
|
|
185
|
+
}
|
|
186
|
+
if (chunk.kind === 'draftResponse' && chunk.text.length > 0) {
|
|
187
|
+
lastDraft += chunk.text;
|
|
188
|
+
}
|
|
110
189
|
if (chunk.kind !== 'response') {
|
|
111
190
|
return of(chunk);
|
|
112
191
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
192
|
+
const text = finalizeInvokeText(chunk.text, lastDraft, lastReasoning);
|
|
193
|
+
return of({ kind: 'response', text }, {
|
|
194
|
+
kind: 'invokeDone',
|
|
195
|
+
requestId: turn.requestId,
|
|
196
|
+
text,
|
|
119
197
|
});
|
|
198
|
+
}), catchError((error) => {
|
|
199
|
+
const text = error instanceof Error
|
|
200
|
+
? `Error: ${error.message}`
|
|
201
|
+
: 'Error: Sub-Agent turn failed.';
|
|
202
|
+
return fail(text);
|
|
120
203
|
})));
|
|
121
204
|
};
|
|
205
|
+
/** Wall-clock until the inner completes — not `timeout({ first })`, which
|
|
206
|
+
* is satisfied by `historySync` and would never abort a hung generator. */
|
|
207
|
+
const withInvokeDeadline = (source, timeoutMs) => new Observable((subscriber) => {
|
|
208
|
+
const handle = setTimeout(() => {
|
|
209
|
+
subscriber.error(new TimeoutError());
|
|
210
|
+
}, timeoutMs);
|
|
211
|
+
const inner = source.subscribe({
|
|
212
|
+
next: (value) => {
|
|
213
|
+
subscriber.next(value);
|
|
214
|
+
},
|
|
215
|
+
error: (err) => {
|
|
216
|
+
clearTimeout(handle);
|
|
217
|
+
subscriber.error(err);
|
|
218
|
+
},
|
|
219
|
+
complete: () => {
|
|
220
|
+
clearTimeout(handle);
|
|
221
|
+
subscriber.complete();
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
return () => {
|
|
225
|
+
clearTimeout(handle);
|
|
226
|
+
inner.unsubscribe();
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
const parseInvokeTurn = (raw, skillIds) => {
|
|
230
|
+
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
231
|
+
return {
|
|
232
|
+
ok: false,
|
|
233
|
+
text: 'Error: Sub-Agent invoke payload is invalid.',
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const record = raw;
|
|
237
|
+
const requestId = typeof record.requestId === 'string' && record.requestId.length > 0
|
|
238
|
+
? record.requestId
|
|
239
|
+
: randomUUID();
|
|
240
|
+
const task = typeof record.task === 'string' ? record.task.trim() : '';
|
|
241
|
+
const skillId = typeof record.skillId === 'string' ? record.skillId.trim() : '';
|
|
242
|
+
if (task.length === 0) {
|
|
243
|
+
return {
|
|
244
|
+
ok: false,
|
|
245
|
+
text: 'Error: Sub-Agent requires a non-empty task.',
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (skillId.length > 0 && !skillIds.includes(skillId)) {
|
|
249
|
+
return {
|
|
250
|
+
ok: false,
|
|
251
|
+
text: `Error: Skill «${skillId}» is not announced by this Sub-Agent.`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return { ok: true, turn: { requestId, task, skillId } };
|
|
255
|
+
};
|
|
122
256
|
/**
|
|
123
|
-
* Sub-Agent: ordinary OpenAI-compatible agent
|
|
124
|
-
*
|
|
257
|
+
* Sub-Agent: ordinary OpenAI-compatible agent that announces one ToolHandle
|
|
258
|
+
* on `subagent-registration`. Parent invoke runs this node's in-node loop.
|
|
125
259
|
* @see docs/ADR.md ADR-021
|
|
126
260
|
*/
|
|
127
261
|
export const subAgentNode = defineLlmNode({
|
|
128
262
|
type: 'common-sub-agent',
|
|
129
263
|
displayName: 'Sub-Agent',
|
|
130
264
|
category: 'AI',
|
|
131
|
-
description:
|
|
265
|
+
description: `
|
|
266
|
+
A specialist agent on the canvas. Wire it into a parent agent's **tools** so the parent can delegate a task.
|
|
267
|
+
|
|
268
|
+
The specialist streams in its own work-log card. Set name, role, and skills on this node.
|
|
269
|
+
`.trim(),
|
|
132
270
|
uiSchema: [
|
|
133
271
|
{
|
|
134
272
|
field: 'name',
|
|
@@ -153,47 +291,31 @@ export const subAgentNode = defineLlmNode({
|
|
|
153
291
|
...llmRecoveryUiSchema,
|
|
154
292
|
],
|
|
155
293
|
bind(ctx, { makeInput, configureOutput, combineInputs }, inventory) {
|
|
156
|
-
const { tools,
|
|
294
|
+
const { tools, steerControl } = inventory;
|
|
157
295
|
const systemPrompt = makeInput('systemPrompt', {
|
|
158
296
|
name: 'systemPrompt',
|
|
159
297
|
wireType: 'string',
|
|
160
298
|
inline: 'text-multiline',
|
|
161
299
|
defaultValue: '',
|
|
162
300
|
});
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const registration$ = combineInputs([ctx], ([ec]) => {
|
|
169
|
-
const skillIds = normalizeSkillIds(ec.params.skillIds);
|
|
170
|
-
const name = String(ec.params.name ?? 'Sub-Agent').trim();
|
|
171
|
-
const description = String(ec.params.description ?? '');
|
|
172
|
-
return {
|
|
173
|
-
targetNodeId: ec.nodeId,
|
|
174
|
-
name: name.length > 0 ? name : 'Sub-Agent',
|
|
175
|
-
description,
|
|
176
|
-
skills: skillIds.map((skillId) => ({
|
|
177
|
-
skillId,
|
|
178
|
-
description: skillId,
|
|
179
|
-
})),
|
|
180
|
-
};
|
|
181
|
-
});
|
|
182
|
-
const context$ = combineInputs([systemPrompt, tools, subagentRegistration, mcp, ctx], ([systemPromptValue, toolList, subagentList, mcpList, ec]) => {
|
|
301
|
+
const invoke$ = new Subject();
|
|
302
|
+
let latestContext;
|
|
303
|
+
let history = [];
|
|
304
|
+
let invokeTail = Promise.resolve();
|
|
305
|
+
const context$ = combineInputs([systemPrompt, tools, ctx], ([systemPromptValue, toolList, ec]) => {
|
|
183
306
|
const rolePreset = parseLlmRolePreset(ec.params.rolePreset);
|
|
184
307
|
const skillId = resolveEffectiveSkillId(rolePreset, ec.params.skillId);
|
|
185
308
|
const hostServices = getRunHostServices(ec);
|
|
186
309
|
const skillMarkdown = hostServices?.skillMarkdown ?? '';
|
|
187
310
|
const agentsMarkdown = hostServices?.agentsMarkdown ?? '';
|
|
311
|
+
const name = String(ec.params.name ?? 'Sub-Agent').trim();
|
|
312
|
+
const inspectorDescription = String(ec.params.description ?? '');
|
|
188
313
|
return {
|
|
189
314
|
prompt: '',
|
|
190
315
|
tools: collectAgentToolHandles({
|
|
191
316
|
toolHandles: ec.toolHandles,
|
|
192
317
|
toolsPort: toolList,
|
|
193
|
-
mcpHandles: ec.mcpHandles,
|
|
194
|
-
mcpPort: mcpList,
|
|
195
318
|
}),
|
|
196
|
-
subagentRegistrations: flattenSubAgentRegistrations(subagentList),
|
|
197
319
|
...resolveChatProviderModel(ec.params, hostServices),
|
|
198
320
|
skillId,
|
|
199
321
|
rolePreset,
|
|
@@ -234,26 +356,67 @@ export const subAgentNode = defineLlmNode({
|
|
|
234
356
|
factory: hostServices?.createChatCompletionStream,
|
|
235
357
|
scriptedTurns: parseScriptedTurns(ec.params['scriptedToolTurns']),
|
|
236
358
|
nodeId: ec.nodeId,
|
|
359
|
+
displayName: name.length > 0 ? name : 'Sub-Agent',
|
|
360
|
+
inspectorDescription,
|
|
237
361
|
skillIds: normalizeSkillIds(ec.params.skillIds),
|
|
238
362
|
};
|
|
239
363
|
});
|
|
240
|
-
|
|
241
|
-
|
|
364
|
+
// Loader input is invoke start, not the chunk Subject: wrapping
|
|
365
|
+
// chunks$ would re-pending on every token (demux fans loading).
|
|
366
|
+
const cycle$ = statefulObservable({
|
|
367
|
+
input: invoke$,
|
|
368
|
+
mapOperator: concatMap,
|
|
369
|
+
loader: ({ context, turn }) => {
|
|
370
|
+
history = seedInvokeHistory(history, context);
|
|
371
|
+
const timeoutMs = context.recovery.subagentTimeoutMs;
|
|
372
|
+
const failTimedOut = (text) => of({ kind: 'response', text }, {
|
|
373
|
+
kind: 'invokeDone',
|
|
374
|
+
requestId: turn.requestId,
|
|
375
|
+
text,
|
|
376
|
+
});
|
|
377
|
+
const inner$ = runSubAgentTurn(context, turn, history).pipe(tap((chunk) => {
|
|
378
|
+
history = reduceInvokeHistory(history, chunk);
|
|
379
|
+
}));
|
|
380
|
+
if (timeoutMs <= 0) {
|
|
381
|
+
return inner$;
|
|
382
|
+
}
|
|
383
|
+
return withInvokeDeadline(inner$, timeoutMs).pipe(catchError((error) => failTimedOut(error instanceof TimeoutError
|
|
384
|
+
? `Error: Sub-Agent timed out after ${timeoutMs}ms.`
|
|
385
|
+
: error instanceof Error
|
|
386
|
+
? `Error: ${error.message}`
|
|
387
|
+
: 'Error: Sub-Agent turn failed.')));
|
|
388
|
+
},
|
|
389
|
+
});
|
|
390
|
+
const enqueueInvoke = (announced, args, _toolCtx) => {
|
|
391
|
+
const context = latestContext ?? announced;
|
|
392
|
+
const parsed = parseInvokeTurn({ ...args, requestId: randomUUID() }, announced.skillIds);
|
|
393
|
+
if (!parsed.ok) {
|
|
394
|
+
return Promise.resolve(parsed.text);
|
|
395
|
+
}
|
|
396
|
+
const run = () => {
|
|
397
|
+
const result = firstValueFrom(cycle$.value$.pipe(filter((chunk) => chunk.kind === 'invokeDone' &&
|
|
398
|
+
chunk.requestId === parsed.turn.requestId), map((chunk) => chunk.text), take(1)), { defaultValue: EMPTY_INVOKE_RESULT });
|
|
399
|
+
invoke$.next({ context, turn: parsed.turn });
|
|
400
|
+
return result;
|
|
401
|
+
};
|
|
402
|
+
const next = invokeTail.then(run, run);
|
|
403
|
+
invokeTail = next.then(() => undefined, () => undefined);
|
|
404
|
+
return next;
|
|
405
|
+
};
|
|
406
|
+
const specialistTools$ = context$.pipeValue(tap((context) => {
|
|
407
|
+
latestContext = context;
|
|
408
|
+
}), map((context) => {
|
|
409
|
+
const handleName = formatSpecialistHandleName(context.displayName);
|
|
410
|
+
return [
|
|
242
411
|
{
|
|
243
|
-
|
|
244
|
-
|
|
412
|
+
toolId: slugToolId(handleName, context.nodeId),
|
|
413
|
+
name: handleName,
|
|
414
|
+
description: buildSpecialistDescription(handleName, context.inspectorDescription, context.skillIds),
|
|
415
|
+
inputSchema: buildSpecialistInputSchema(context.skillIds),
|
|
416
|
+
invoke: (args, toolCtx) => enqueueInvoke(context, args, toolCtx),
|
|
245
417
|
},
|
|
246
|
-
]
|
|
247
|
-
|
|
248
|
-
appendUserFeedbackToHistory: false,
|
|
249
|
-
session: undefined,
|
|
250
|
-
}), (context, turnPayload, history, _session) => {
|
|
251
|
-
const spawn = acceptSpawn(turnPayload, context.nodeId, context.skillIds);
|
|
252
|
-
if (spawn === null) {
|
|
253
|
-
return EMPTY;
|
|
254
|
-
}
|
|
255
|
-
return runSubAgentTurn(context, spawn, history, subagentResult.value$);
|
|
256
|
-
}, { primeTurn0: false });
|
|
418
|
+
];
|
|
419
|
+
}));
|
|
257
420
|
const reasoning$ = cycle$.pipeValue(demuxByKind('reasoning', (chunk) => chunk
|
|
258
421
|
.text));
|
|
259
422
|
const draftResponse$ = cycle$.pipeValue(demuxByKind('draftResponse', (chunk) => chunk
|
|
@@ -265,14 +428,12 @@ export const subAgentNode = defineLlmNode({
|
|
|
265
428
|
}));
|
|
266
429
|
const response$ = cycle$.pipeValue(demuxByKind('response', (chunk) => chunk
|
|
267
430
|
.text));
|
|
268
|
-
const result$ = cycle$.pipeValue(demuxByKind('subagentResult', (chunk) => chunk.payload));
|
|
269
|
-
const nestedSubagent$ = cycle$.pipeValue(demuxByKind('subagentSpawn', (chunk) => chunk
|
|
270
|
-
.payload));
|
|
271
431
|
return {
|
|
272
|
-
inputs: [systemPrompt
|
|
432
|
+
inputs: [systemPrompt],
|
|
273
433
|
outputs: [
|
|
274
|
-
configureOutput('registration',
|
|
275
|
-
|
|
434
|
+
configureOutput('subagent-registration', specialistTools$, {
|
|
435
|
+
name: 'subagent-registration',
|
|
436
|
+
wireType: TOOL_HANDLE_WIRE_TYPE,
|
|
276
437
|
}),
|
|
277
438
|
configureOutput('reasoning', reasoning$, {
|
|
278
439
|
wireType: 'string',
|
|
@@ -286,16 +447,10 @@ export const subAgentNode = defineLlmNode({
|
|
|
286
447
|
wireType: 'string',
|
|
287
448
|
feed: { role: 'result' },
|
|
288
449
|
}),
|
|
289
|
-
configureOutput('result', result$, {
|
|
290
|
-
wireType: SUBAGENT_RESULT_WIRE_TYPE,
|
|
291
|
-
// Protocol payload for parent wiring — not work-log content.
|
|
292
|
-
feed: { role: 'none' },
|
|
293
|
-
}),
|
|
294
450
|
],
|
|
295
451
|
inventoryOutputs: {
|
|
296
452
|
toolLog$,
|
|
297
453
|
recovery$,
|
|
298
|
-
subagent$: nestedSubagent$,
|
|
299
454
|
},
|
|
300
455
|
};
|
|
301
456
|
},
|
|
@@ -23,9 +23,11 @@ import { previewNode } from './output/preview/node.js';
|
|
|
23
23
|
import { booleanNode } from './primitives/boolean/node.js';
|
|
24
24
|
import { numberNode } from './primitives/number/node.js';
|
|
25
25
|
import { stringNode } from './primitives/string/node.js';
|
|
26
|
+
import { stringMultilineNode } from './primitives/string-multiline/node.js';
|
|
26
27
|
import { appendFileNode } from './text/append-file/node.js';
|
|
27
28
|
import { concatNode } from './text/concat/node.js';
|
|
28
29
|
import { readFileNode } from './text/read-file/node.js';
|
|
30
|
+
import { splitPacedNode } from './text/split-paced/node.js';
|
|
29
31
|
import { writeFileNode } from './text/write-file/node.js';
|
|
30
32
|
import { crawlNode } from './crawl/crawl/node.js';
|
|
31
33
|
import { crawlToolsNode } from './crawl/crawl-tools/node.js';
|
|
@@ -34,10 +36,15 @@ import { fetchUrlNode } from './crawl/fetch-url/node.js';
|
|
|
34
36
|
import { savePageNode } from './crawl/save-page/node.js';
|
|
35
37
|
import { memoryToolsNode } from './memory/memory-tools/node.js';
|
|
36
38
|
import { langflowerToolsNode } from './langflower-tools/node.js';
|
|
39
|
+
import { toolCollectionNode } from './tools/tool-collection/node.js';
|
|
40
|
+
import { embedTextNode } from './embeddings/embed-text/node.js';
|
|
41
|
+
import { embedSimilarityNode } from './embeddings/embed-similarity/node.js';
|
|
42
|
+
import { embedProviderNode } from './embeddings/embed-provider/node.js';
|
|
37
43
|
const COMMON_REACTIVE_NODES = [
|
|
38
44
|
booleanNode,
|
|
39
45
|
numberNode,
|
|
40
46
|
stringNode,
|
|
47
|
+
stringMultilineNode,
|
|
41
48
|
mergeNode,
|
|
42
49
|
delayNode,
|
|
43
50
|
checkpointNode,
|
|
@@ -47,6 +54,7 @@ const COMMON_REACTIVE_NODES = [
|
|
|
47
54
|
previewNode,
|
|
48
55
|
finishNode,
|
|
49
56
|
concatNode,
|
|
57
|
+
splitPacedNode,
|
|
50
58
|
readFileNode,
|
|
51
59
|
writeFileNode,
|
|
52
60
|
appendFileNode,
|
|
@@ -66,11 +74,15 @@ const COMMON_REACTIVE_NODES = [
|
|
|
66
74
|
switchNode,
|
|
67
75
|
memoryToolsNode,
|
|
68
76
|
langflowerToolsNode,
|
|
77
|
+
toolCollectionNode,
|
|
69
78
|
fetchUrlNode,
|
|
70
79
|
extractLinksNode,
|
|
71
80
|
savePageNode,
|
|
72
81
|
crawlNode,
|
|
73
82
|
crawlToolsNode,
|
|
83
|
+
embedTextNode,
|
|
84
|
+
embedSimilarityNode,
|
|
85
|
+
embedProviderNode,
|
|
74
86
|
];
|
|
75
87
|
const COMMON_REACTIVE_NODE_CATALOG = Object.fromEntries(COMMON_REACTIVE_NODES.map((node) => [node.type, node]));
|
|
76
88
|
export function getCommonReactiveNodeCatalog() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createCrawlContext } from '@langflower/tools/create-crawl-context';
|
|
3
3
|
import { createWebFetch } from '@langflower/tools/create-web-fetch';
|
|
4
4
|
import { runBfsCrawl } from '@langflower/tools/run-bfs-crawl';
|
|
@@ -24,7 +24,13 @@ export const crawlNode = defineReactiveNode({
|
|
|
24
24
|
displayName: 'Crawl',
|
|
25
25
|
category: 'Crawl',
|
|
26
26
|
paletteSecondary: true,
|
|
27
|
-
description:
|
|
27
|
+
description: `
|
|
28
|
+
Start from a URL and follow links up to a depth and page cap. Pages are saved as they are visited.
|
|
29
|
+
|
|
30
|
+
Typical uses:
|
|
31
|
+
- Research a site without clicking by hand
|
|
32
|
+
- Collect a small corpus for an LLM
|
|
33
|
+
`.trim(),
|
|
28
34
|
uiSchema: [
|
|
29
35
|
{
|
|
30
36
|
field: 'maxDepth',
|
|
@@ -67,7 +73,9 @@ export const crawlNode = defineReactiveNode({
|
|
|
67
73
|
const pages$ = combineInputs([startUrl, ctx], ([rawUrl, ec]) => ({
|
|
68
74
|
startUrl: String(rawUrl ?? '').trim(),
|
|
69
75
|
ec,
|
|
70
|
-
}))
|
|
76
|
+
}))
|
|
77
|
+
.pipe(withLoading())
|
|
78
|
+
.pipeValue(mergeMap(({ startUrl: seed, ec }) => {
|
|
71
79
|
if (seed.length === 0) {
|
|
72
80
|
return throwError(() => new Error('Crawl requires a non-empty startUrl.'));
|
|
73
81
|
}
|
|
@@ -8,6 +8,10 @@ export const crawlToolsNode = defineToolRegistrations({
|
|
|
8
8
|
type: 'common-crawl-tools',
|
|
9
9
|
displayName: 'Crawl Tools',
|
|
10
10
|
category: 'Tools',
|
|
11
|
-
description:
|
|
11
|
+
description: `
|
|
12
|
+
Give an agent fetch, extract-links, save-page, and crawl tools.
|
|
13
|
+
|
|
14
|
+
Wire **tools** into an LLM so it can research the web from chat.
|
|
15
|
+
`.trim(),
|
|
12
16
|
tools: CRAWL_TOOL_CONFIGS,
|
|
13
17
|
});
|
|
@@ -11,7 +11,13 @@ export const extractLinksNode = defineReactiveNode({
|
|
|
11
11
|
displayName: 'Extract Links',
|
|
12
12
|
category: 'Crawl',
|
|
13
13
|
paletteSecondary: true,
|
|
14
|
-
description:
|
|
14
|
+
description: `
|
|
15
|
+
Pull absolute URLs from HTML.
|
|
16
|
+
|
|
17
|
+
Typical uses:
|
|
18
|
+
- List outbound links after Fetch URL
|
|
19
|
+
- Feed those URLs into Crawl or another fetch
|
|
20
|
+
`.trim(),
|
|
15
21
|
uiSchema: [],
|
|
16
22
|
bind(_ctx, { makeInput, configureOutput, combineInputs }) {
|
|
17
23
|
const html = makeInput('html', {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createWebFetch } from '@langflower/tools/create-web-fetch';
|
|
3
3
|
import { from, map, mergeMap, throwError } from 'rxjs';
|
|
4
4
|
import { htmlToText } from '@langflower/tools/html';
|
|
@@ -18,7 +18,13 @@ export const fetchUrlNode = defineReactiveNode({
|
|
|
18
18
|
displayName: 'Fetch URL',
|
|
19
19
|
category: 'Crawl',
|
|
20
20
|
paletteSecondary: true,
|
|
21
|
-
description:
|
|
21
|
+
description: `
|
|
22
|
+
Download a page and get HTML plus readable text.
|
|
23
|
+
|
|
24
|
+
Typical uses:
|
|
25
|
+
- Pull an article before Extract Links or an LLM
|
|
26
|
+
- Seed a Crawl
|
|
27
|
+
`.trim(),
|
|
22
28
|
uiSchema: [
|
|
23
29
|
{
|
|
24
30
|
field: 'timeoutMs',
|
|
@@ -43,7 +49,9 @@ export const fetchUrlNode = defineReactiveNode({
|
|
|
43
49
|
const fetched$ = combineInputs([url, ctx], ([rawUrl, ec]) => ({
|
|
44
50
|
rawUrl: String(rawUrl ?? '').trim(),
|
|
45
51
|
ec,
|
|
46
|
-
}))
|
|
52
|
+
}))
|
|
53
|
+
.pipe(withLoading())
|
|
54
|
+
.pipeValue(mergeMap(({ rawUrl, ec }) => {
|
|
47
55
|
if (rawUrl.length === 0) {
|
|
48
56
|
return throwError(() => new Error('Fetch URL requires a non-empty url.'));
|
|
49
57
|
}
|
|
@@ -59,7 +67,8 @@ export const fetchUrlNode = defineReactiveNode({
|
|
|
59
67
|
maxBytes,
|
|
60
68
|
})).pipe(map((result) => {
|
|
61
69
|
if (!result.ok && result.body.length === 0) {
|
|
62
|
-
throw new Error(result.error ??
|
|
70
|
+
throw new Error(result.error ??
|
|
71
|
+
`Fetch failed for ${rawUrl}`);
|
|
63
72
|
}
|
|
64
73
|
return {
|
|
65
74
|
html: result.body,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineReactiveNode } from '@langflower/node-sdk';
|
|
1
|
+
import { defineReactiveNode, withLoading } from '@langflower/node-sdk';
|
|
2
2
|
import { createCrawlContext } from '@langflower/tools/create-crawl-context';
|
|
3
3
|
import { from, mergeMap, throwError } from 'rxjs';
|
|
4
4
|
import { extractHtmlTitle } from '@langflower/tools/html';
|
|
@@ -12,7 +12,13 @@ export const savePageNode = defineReactiveNode({
|
|
|
12
12
|
displayName: 'Save Page',
|
|
13
13
|
category: 'Crawl',
|
|
14
14
|
paletteSecondary: true,
|
|
15
|
-
description:
|
|
15
|
+
description: `
|
|
16
|
+
Store a fetched page in this run's crawl folder and emit where it was saved.
|
|
17
|
+
|
|
18
|
+
Typical uses:
|
|
19
|
+
- Keep HTML/text for later review
|
|
20
|
+
- Persist a page before the next crawl hop
|
|
21
|
+
`.trim(),
|
|
16
22
|
uiSchema: [],
|
|
17
23
|
bind(ctx, { makeInput, configureOutput, combineInputs }) {
|
|
18
24
|
const url = makeInput('url', {
|
|
@@ -38,7 +44,9 @@ export const savePageNode = defineReactiveNode({
|
|
|
38
44
|
html: String(rawHtml ?? ''),
|
|
39
45
|
text: String(rawText ?? ''),
|
|
40
46
|
ec,
|
|
41
|
-
}))
|
|
47
|
+
}))
|
|
48
|
+
.pipe(withLoading())
|
|
49
|
+
.pipeValue(mergeMap(({ url: pageUrl, html: pageHtml, text: pageText, ec }) => {
|
|
42
50
|
if (pageUrl.length === 0) {
|
|
43
51
|
return throwError(() => new Error('Save Page requires a non-empty url.'));
|
|
44
52
|
}
|