@loopstack/advanced-workflows-examples 0.1.1
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 +176 -0
- package/dist/advanced-workflows-examples.module.d.ts +3 -0
- package/dist/advanced-workflows-examples.module.d.ts.map +1 -0
- package/dist/advanced-workflows-examples.module.js +95 -0
- package/dist/advanced-workflows-examples.module.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.d.ts +5 -0
- package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.d.ts.map +1 -0
- package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.js +29 -0
- package/dist/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.js.map +1 -0
- package/dist/workflows/agent-error-handling/agent-error-handling.ui.yaml +6 -0
- package/dist/workflows/agent-error-handling/agent-error-handling.workflow.d.ts +34 -0
- package/dist/workflows/agent-error-handling/agent-error-handling.workflow.d.ts.map +1 -0
- package/dist/workflows/agent-error-handling/agent-error-handling.workflow.js +158 -0
- package/dist/workflows/agent-error-handling/agent-error-handling.workflow.js.map +1 -0
- package/dist/workflows/agent-error-handling/templates/system.md +12 -0
- package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.d.ts +12 -0
- package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.d.ts.map +1 -0
- package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.js +40 -0
- package/dist/workflows/agent-error-handling/tools/failing-sub-workflow.tool.js.map +1 -0
- package/dist/workflows/agent-error-handling/tools/runtime-error.tool.d.ts +11 -0
- package/dist/workflows/agent-error-handling/tools/runtime-error.tool.d.ts.map +1 -0
- package/dist/workflows/agent-error-handling/tools/runtime-error.tool.js +33 -0
- package/dist/workflows/agent-error-handling/tools/runtime-error.tool.js.map +1 -0
- package/dist/workflows/agent-error-handling/tools/strict-schema.tool.d.ts +10 -0
- package/dist/workflows/agent-error-handling/tools/strict-schema.tool.d.ts.map +1 -0
- package/dist/workflows/agent-error-handling/tools/strict-schema.tool.js +31 -0
- package/dist/workflows/agent-error-handling/tools/strict-schema.tool.js.map +1 -0
- package/dist/workflows/batch-processing/batch-processing-example.workflow.d.ts +27 -0
- package/dist/workflows/batch-processing/batch-processing-example.workflow.d.ts.map +1 -0
- package/dist/workflows/batch-processing/batch-processing-example.workflow.js +93 -0
- package/dist/workflows/batch-processing/batch-processing-example.workflow.js.map +1 -0
- package/dist/workflows/custom-tool/custom-tool-example.ui.yaml +6 -0
- package/dist/workflows/custom-tool/custom-tool-example.workflow.d.ts +23 -0
- package/dist/workflows/custom-tool/custom-tool-example.workflow.d.ts.map +1 -0
- package/dist/workflows/custom-tool/custom-tool-example.workflow.js +95 -0
- package/dist/workflows/custom-tool/custom-tool-example.workflow.js.map +1 -0
- package/dist/workflows/custom-tool/index.d.ts +2 -0
- package/dist/workflows/custom-tool/index.d.ts.map +1 -0
- package/dist/workflows/custom-tool/index.js +18 -0
- package/dist/workflows/custom-tool/index.js.map +1 -0
- package/dist/workflows/custom-tool/services/math.service.d.ts +4 -0
- package/dist/workflows/custom-tool/services/math.service.d.ts.map +1 -0
- package/dist/workflows/custom-tool/services/math.service.js +20 -0
- package/dist/workflows/custom-tool/services/math.service.js.map +1 -0
- package/dist/workflows/custom-tool/tools/counter.tool.d.ts +7 -0
- package/dist/workflows/custom-tool/tools/counter.tool.d.ts.map +1 -0
- package/dist/workflows/custom-tool/tools/counter.tool.js +25 -0
- package/dist/workflows/custom-tool/tools/counter.tool.js.map +1 -0
- package/dist/workflows/custom-tool/tools/index.d.ts +3 -0
- package/dist/workflows/custom-tool/tools/index.d.ts.map +1 -0
- package/dist/workflows/custom-tool/tools/index.js +19 -0
- package/dist/workflows/custom-tool/tools/index.js.map +1 -0
- package/dist/workflows/custom-tool/tools/math-sum.tool.d.ts +16 -0
- package/dist/workflows/custom-tool/tools/math-sum.tool.d.ts.map +1 -0
- package/dist/workflows/custom-tool/tools/math-sum.tool.js +42 -0
- package/dist/workflows/custom-tool/tools/math-sum.tool.js.map +1 -0
- package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.d.ts +24 -0
- package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.d.ts.map +1 -0
- package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.js +115 -0
- package/dist/workflows/dynamic-routing/dynamic-routing-example.workflow.js.map +1 -0
- package/dist/workflows/error-retry/error-retry-example.ui.yaml +25 -0
- package/dist/workflows/error-retry/error-retry-example.workflow.d.ts +43 -0
- package/dist/workflows/error-retry/error-retry-example.workflow.d.ts.map +1 -0
- package/dist/workflows/error-retry/error-retry-example.workflow.js +343 -0
- package/dist/workflows/error-retry/error-retry-example.workflow.js.map +1 -0
- package/dist/workflows/error-retry/failing-child.workflow.d.ts +5 -0
- package/dist/workflows/error-retry/failing-child.workflow.d.ts.map +1 -0
- package/dist/workflows/error-retry/failing-child.workflow.js +36 -0
- package/dist/workflows/error-retry/failing-child.workflow.js.map +1 -0
- package/dist/workflows/error-retry/tools/slow.tool.d.ts +10 -0
- package/dist/workflows/error-retry/tools/slow.tool.d.ts.map +1 -0
- package/dist/workflows/error-retry/tools/slow.tool.js +27 -0
- package/dist/workflows/error-retry/tools/slow.tool.js.map +1 -0
- package/dist/workflows/error-retry/tools/step1.tool.d.ts +6 -0
- package/dist/workflows/error-retry/tools/step1.tool.d.ts.map +1 -0
- package/dist/workflows/error-retry/tools/step1.tool.js +26 -0
- package/dist/workflows/error-retry/tools/step1.tool.js.map +1 -0
- package/dist/workflows/error-retry/tools/step2.tool.d.ts +10 -0
- package/dist/workflows/error-retry/tools/step2.tool.d.ts.map +1 -0
- package/dist/workflows/error-retry/tools/step2.tool.js +29 -0
- package/dist/workflows/error-retry/tools/step2.tool.js.map +1 -0
- package/dist/workflows/fan-out/fan-out-example.workflow.d.ts +12 -0
- package/dist/workflows/fan-out/fan-out-example.workflow.d.ts.map +1 -0
- package/dist/workflows/fan-out/fan-out-example.workflow.js +68 -0
- package/dist/workflows/fan-out/fan-out-example.workflow.js.map +1 -0
- package/dist/workflows/module-config/consumers/default-greeting.module.d.ts +3 -0
- package/dist/workflows/module-config/consumers/default-greeting.module.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/default-greeting.module.js +21 -0
- package/dist/workflows/module-config/consumers/default-greeting.module.js.map +1 -0
- package/dist/workflows/module-config/consumers/default-greeting.workflow.d.ts +9 -0
- package/dist/workflows/module-config/consumers/default-greeting.workflow.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/default-greeting.workflow.js +47 -0
- package/dist/workflows/module-config/consumers/default-greeting.workflow.js.map +1 -0
- package/dist/workflows/module-config/consumers/french-greeting.module.d.ts +3 -0
- package/dist/workflows/module-config/consumers/french-greeting.module.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/french-greeting.module.js +23 -0
- package/dist/workflows/module-config/consumers/french-greeting.module.js.map +1 -0
- package/dist/workflows/module-config/consumers/french-greeting.workflow.d.ts +9 -0
- package/dist/workflows/module-config/consumers/french-greeting.workflow.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/french-greeting.workflow.js +47 -0
- package/dist/workflows/module-config/consumers/french-greeting.workflow.js.map +1 -0
- package/dist/workflows/module-config/consumers/german-greeting.module.d.ts +3 -0
- package/dist/workflows/module-config/consumers/german-greeting.module.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/german-greeting.module.js +23 -0
- package/dist/workflows/module-config/consumers/german-greeting.module.js.map +1 -0
- package/dist/workflows/module-config/consumers/german-greeting.workflow.d.ts +9 -0
- package/dist/workflows/module-config/consumers/german-greeting.workflow.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/german-greeting.workflow.js +47 -0
- package/dist/workflows/module-config/consumers/german-greeting.workflow.js.map +1 -0
- package/dist/workflows/module-config/consumers/nested-greeting.module.d.ts +3 -0
- package/dist/workflows/module-config/consumers/nested-greeting.module.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/nested-greeting.module.js +23 -0
- package/dist/workflows/module-config/consumers/nested-greeting.module.js.map +1 -0
- package/dist/workflows/module-config/consumers/nested-greeting.workflow.d.ts +9 -0
- package/dist/workflows/module-config/consumers/nested-greeting.workflow.d.ts.map +1 -0
- package/dist/workflows/module-config/consumers/nested-greeting.workflow.js +47 -0
- package/dist/workflows/module-config/consumers/nested-greeting.workflow.js.map +1 -0
- package/dist/workflows/module-config/greeter/greeter-agent.module.d.ts +8 -0
- package/dist/workflows/module-config/greeter/greeter-agent.module.d.ts.map +1 -0
- package/dist/workflows/module-config/greeter/greeter-agent.module.js +36 -0
- package/dist/workflows/module-config/greeter/greeter-agent.module.js.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.constants.d.ts +6 -0
- package/dist/workflows/module-config/greeter/greeter.constants.d.ts.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.constants.js +5 -0
- package/dist/workflows/module-config/greeter/greeter.constants.js.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.module.d.ts +7 -0
- package/dist/workflows/module-config/greeter/greeter.module.d.ts.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.module.js +47 -0
- package/dist/workflows/module-config/greeter/greeter.module.js.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.tool.d.ts +17 -0
- package/dist/workflows/module-config/greeter/greeter.tool.d.ts.map +1 -0
- package/dist/workflows/module-config/greeter/greeter.tool.js +45 -0
- package/dist/workflows/module-config/greeter/greeter.tool.js.map +1 -0
- package/dist/workflows/module-config/greeter/index.d.ts +4 -0
- package/dist/workflows/module-config/greeter/index.d.ts.map +1 -0
- package/dist/workflows/module-config/greeter/index.js +20 -0
- package/dist/workflows/module-config/greeter/index.js.map +1 -0
- package/dist/workflows/sequence/sequence-example.workflow.d.ts +12 -0
- package/dist/workflows/sequence/sequence-example.workflow.d.ts.map +1 -0
- package/dist/workflows/sequence/sequence-example.workflow.js +67 -0
- package/dist/workflows/sequence/sequence-example.workflow.js.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.d.ts +10 -0
- package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.d.ts.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.js +77 -0
- package/dist/workflows/sub-workflow/sub-workflow-error-handling.workflow.js.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.d.ts +5 -0
- package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.d.ts.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.js +36 -0
- package/dist/workflows/sub-workflow/sub-workflow-failing-sub.workflow.js.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.d.ts +15 -0
- package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.d.ts.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.js +84 -0
- package/dist/workflows/sub-workflow/sub-workflow-parent.workflow.js.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.d.ts +17 -0
- package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.d.ts.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.js +93 -0
- package/dist/workflows/sub-workflow/sub-workflow-show-modes.workflow.js.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.d.ts +5 -0
- package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.d.ts.map +1 -0
- package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.js +36 -0
- package/dist/workflows/sub-workflow/sub-workflow-sub.workflow.js.map +1 -0
- package/dist/workflows/ui-documents/ui-documents-example.workflow.d.ts +6 -0
- package/dist/workflows/ui-documents/ui-documents-example.workflow.d.ts.map +1 -0
- package/dist/workflows/ui-documents/ui-documents-example.workflow.js +51 -0
- package/dist/workflows/ui-documents/ui-documents-example.workflow.js.map +1 -0
- package/dist/workflows/workflow-state/tool-results-example.workflow.d.ts +10 -0
- package/dist/workflows/workflow-state/tool-results-example.workflow.d.ts.map +1 -0
- package/dist/workflows/workflow-state/tool-results-example.workflow.js +48 -0
- package/dist/workflows/workflow-state/tool-results-example.workflow.js.map +1 -0
- package/dist/workflows/workflow-state/workflow-state-example.workflow.d.ts +11 -0
- package/dist/workflows/workflow-state/workflow-state-example.workflow.d.ts.map +1 -0
- package/dist/workflows/workflow-state/workflow-state-example.workflow.js +51 -0
- package/dist/workflows/workflow-state/workflow-state-example.workflow.js.map +1 -0
- package/package.json +56 -0
- package/src/advanced-workflows-examples.module.ts +83 -0
- package/src/index.ts +17 -0
- package/src/workflows/agent-error-handling/agent-error-handling-failing-sub.workflow.ts +13 -0
- package/src/workflows/agent-error-handling/agent-error-handling.ui.yaml +6 -0
- package/src/workflows/agent-error-handling/agent-error-handling.workflow.ts +130 -0
- package/src/workflows/agent-error-handling/templates/system.md +12 -0
- package/src/workflows/agent-error-handling/tools/failing-sub-workflow.tool.ts +32 -0
- package/src/workflows/agent-error-handling/tools/runtime-error.tool.ts +29 -0
- package/src/workflows/agent-error-handling/tools/strict-schema.tool.ts +21 -0
- package/src/workflows/batch-processing/batch-processing-example.workflow.ts +79 -0
- package/src/workflows/custom-tool/custom-tool-example.ui.yaml +6 -0
- package/src/workflows/custom-tool/custom-tool-example.workflow.ts +87 -0
- package/src/workflows/custom-tool/index.ts +1 -0
- package/src/workflows/custom-tool/services/math.service.ts +8 -0
- package/src/workflows/custom-tool/tools/counter.tool.ts +16 -0
- package/src/workflows/custom-tool/tools/index.ts +2 -0
- package/src/workflows/custom-tool/tools/math-sum.tool.ts +30 -0
- package/src/workflows/dynamic-routing/dynamic-routing-example.workflow.ts +86 -0
- package/src/workflows/error-retry/error-retry-example.ui.yaml +25 -0
- package/src/workflows/error-retry/error-retry-example.workflow.ts +286 -0
- package/src/workflows/error-retry/failing-child.workflow.ts +20 -0
- package/src/workflows/error-retry/tools/slow.tool.ts +17 -0
- package/src/workflows/error-retry/tools/step1.tool.ts +16 -0
- package/src/workflows/error-retry/tools/step2.tool.ts +20 -0
- package/src/workflows/fan-out/fan-out-example.workflow.ts +58 -0
- package/src/workflows/module-config/consumers/default-greeting.module.ts +12 -0
- package/src/workflows/module-config/consumers/default-greeting.workflow.ts +24 -0
- package/src/workflows/module-config/consumers/french-greeting.module.ts +14 -0
- package/src/workflows/module-config/consumers/french-greeting.workflow.ts +25 -0
- package/src/workflows/module-config/consumers/german-greeting.module.ts +14 -0
- package/src/workflows/module-config/consumers/german-greeting.workflow.ts +24 -0
- package/src/workflows/module-config/consumers/nested-greeting.module.ts +15 -0
- package/src/workflows/module-config/consumers/nested-greeting.workflow.ts +25 -0
- package/src/workflows/module-config/greeter/greeter-agent.module.ts +33 -0
- package/src/workflows/module-config/greeter/greeter.constants.ts +6 -0
- package/src/workflows/module-config/greeter/greeter.module.ts +48 -0
- package/src/workflows/module-config/greeter/greeter.tool.ts +38 -0
- package/src/workflows/module-config/greeter/index.ts +3 -0
- package/src/workflows/sequence/sequence-example.workflow.ts +57 -0
- package/src/workflows/sub-workflow/sub-workflow-error-handling.workflow.ts +63 -0
- package/src/workflows/sub-workflow/sub-workflow-failing-sub.workflow.ts +21 -0
- package/src/workflows/sub-workflow/sub-workflow-parent.workflow.ts +58 -0
- package/src/workflows/sub-workflow/sub-workflow-show-modes.workflow.ts +73 -0
- package/src/workflows/sub-workflow/sub-workflow-sub.workflow.ts +17 -0
- package/src/workflows/ui-documents/ui-documents-example.workflow.ts +43 -0
- package/src/workflows/workflow-state/tool-results-example.workflow.ts +28 -0
- package/src/workflows/workflow-state/workflow-state-example.workflow.ts +33 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
import type { RunContext, TransitionInput } from '@loopstack/common';
|
|
3
|
+
import { ErrorRetryFailingChildWorkflow } from './failing-child.workflow';
|
|
4
|
+
import { SlowTool } from './tools/slow.tool';
|
|
5
|
+
import { Step1Tool } from './tools/step1.tool';
|
|
6
|
+
import { Step2Tool } from './tools/step2.tool';
|
|
7
|
+
|
|
8
|
+
interface ErrorRetryState {
|
|
9
|
+
autoRetryAttempts: number;
|
|
10
|
+
manualRetryAttempts: number;
|
|
11
|
+
timeoutAttempts: number;
|
|
12
|
+
retryTargetAttempts: number;
|
|
13
|
+
retryTargetPreps: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Demonstrates all retry/error modes in sequence:
|
|
18
|
+
*
|
|
19
|
+
* Title: 'Advanced - Error Retry Example'
|
|
20
|
+
*
|
|
21
|
+
* Step 1: Auto-retry — fails twice, auto-retries handle it, succeeds on 3rd.
|
|
22
|
+
* Step 2: Manual retry — fails once, user clicks Retry, succeeds.
|
|
23
|
+
* Step 3: Custom error place — always fails, transitions to error_custom for recovery.
|
|
24
|
+
* Step 4: Timeout — slow operation times out, user clicks Retry, fast attempt succeeds.
|
|
25
|
+
* Step 5: Hybrid (auto-retry + custom place) — always fails, auto-retries once, then error_hybrid.
|
|
26
|
+
* Step 6: retryTarget — auto-retries route through a recovery place that "prepares" then loops back.
|
|
27
|
+
* Step 7: Sub-workflow errorPlace — child always fails; parent routes the failure callback to a recovery place.
|
|
28
|
+
*/
|
|
29
|
+
@Workflow({
|
|
30
|
+
title: 'Advanced - Error Retry Example',
|
|
31
|
+
description:
|
|
32
|
+
'Demonstrates seven retry/error modes: auto-retry with exponential backoff, manual retry via Retry button, ' +
|
|
33
|
+
'custom error place with recovery transition, timeout with manual retry, hybrid (auto-retry + custom error place), ' +
|
|
34
|
+
'retryTarget (auto-retry via a different place), and sub-workflow failure callback routed via errorPlace.',
|
|
35
|
+
widget: './error-retry-example.ui.yaml',
|
|
36
|
+
})
|
|
37
|
+
export class ErrorRetryWorkflow extends BaseWorkflow {
|
|
38
|
+
constructor(
|
|
39
|
+
private readonly step1Tool: Step1Tool,
|
|
40
|
+
private readonly step2Tool: Step2Tool,
|
|
41
|
+
private readonly slowTool: SlowTool,
|
|
42
|
+
private readonly failingChild: ErrorRetryFailingChildWorkflow,
|
|
43
|
+
) {
|
|
44
|
+
super();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@Transition({ to: 'step1_done' })
|
|
48
|
+
async setup(_state: ErrorRetryState) {
|
|
49
|
+
await this.documentStore.save(MessageDocument, {
|
|
50
|
+
role: 'assistant',
|
|
51
|
+
text: '# Error Retry Example\n\nThis workflow tests seven retry/error modes in sequence.',
|
|
52
|
+
});
|
|
53
|
+
await this.documentStore.save(MessageDocument, {
|
|
54
|
+
role: 'assistant',
|
|
55
|
+
text:
|
|
56
|
+
'## Step 1: Auto-retry\n\n' +
|
|
57
|
+
'The next step will fail twice. The framework auto-retries with exponential backoff ' +
|
|
58
|
+
'(1s, then 2s). On the 3rd attempt it succeeds.\n\n' +
|
|
59
|
+
'**No action required — just watch.**',
|
|
60
|
+
});
|
|
61
|
+
this.assignState({
|
|
62
|
+
autoRetryAttempts: 0,
|
|
63
|
+
manualRetryAttempts: 0,
|
|
64
|
+
timeoutAttempts: 0,
|
|
65
|
+
retryTargetAttempts: 0,
|
|
66
|
+
retryTargetPreps: 0,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// -- Step 1: Auto-retry --
|
|
71
|
+
@Transition({ from: 'step1_done', to: 'step2_done', retryAttempts: 2 })
|
|
72
|
+
async autoRetryStep(state: ErrorRetryState, ctx: RunContext) {
|
|
73
|
+
const attempt = ctx.execution!.retryCount + 1;
|
|
74
|
+
await this.step2Tool.call({ shouldFail: attempt <= 2 });
|
|
75
|
+
await this.documentStore.save(MessageDocument, {
|
|
76
|
+
role: 'assistant',
|
|
77
|
+
text: `Auto-retry succeeded on attempt ${attempt}.`,
|
|
78
|
+
});
|
|
79
|
+
this.assignState({ autoRetryAttempts: attempt });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// -- Bridge: instructions for step 2 --
|
|
83
|
+
@Transition({ from: 'step2_done', to: 'step2_ready' })
|
|
84
|
+
async step2Instructions(_state: ErrorRetryState) {
|
|
85
|
+
await this.documentStore.save(MessageDocument, {
|
|
86
|
+
role: 'assistant',
|
|
87
|
+
text:
|
|
88
|
+
'## Step 2: Manual retry\n\n' +
|
|
89
|
+
'The next step will fail once. The workflow stays at the current place and shows an error.\n\n' +
|
|
90
|
+
'**Click the Retry button next to the error message to re-run the failed step.**',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// -- Step 2: Manual retry --
|
|
95
|
+
@Transition({ from: 'step2_ready', to: 'step3_done' })
|
|
96
|
+
async manualRetryStep(_state: ErrorRetryState, ctx: RunContext) {
|
|
97
|
+
const attempt = ctx.execution!.retryCount + 1;
|
|
98
|
+
await this.step2Tool.call({ shouldFail: attempt <= 1 });
|
|
99
|
+
await this.documentStore.save(MessageDocument, {
|
|
100
|
+
role: 'assistant',
|
|
101
|
+
text: `Manual retry succeeded on attempt ${attempt}.`,
|
|
102
|
+
});
|
|
103
|
+
this.assignState({ manualRetryAttempts: attempt });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// -- Bridge: instructions for step 3 --
|
|
107
|
+
@Transition({ from: 'step3_done', to: 'step3_ready' })
|
|
108
|
+
async step3Instructions(_state: ErrorRetryState) {
|
|
109
|
+
await this.documentStore.save(MessageDocument, {
|
|
110
|
+
role: 'assistant',
|
|
111
|
+
text:
|
|
112
|
+
'## Step 3: Custom error place\n\n' +
|
|
113
|
+
'The next step always fails and transitions to a custom error place (`error_custom`).\n\n' +
|
|
114
|
+
'**Click the "Recover" button that appears below to trigger the recovery transition.**',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// -- Step 3: Custom error place --
|
|
119
|
+
@Transition({ from: 'step3_ready', to: 'step4_done', errorPlace: 'error_custom' })
|
|
120
|
+
async customErrorStep(_state: ErrorRetryState) {
|
|
121
|
+
await this.step2Tool.call({ shouldFail: true });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@Transition({ from: 'error_custom', to: 'step4_done', wait: true })
|
|
125
|
+
async handleCustomError(_state: ErrorRetryState) {
|
|
126
|
+
await this.documentStore.save(MessageDocument, {
|
|
127
|
+
role: 'assistant',
|
|
128
|
+
text: 'Recovered via custom error handler!',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// -- Bridge: instructions for step 4 --
|
|
133
|
+
@Transition({ from: 'step4_done', to: 'step4_ready' })
|
|
134
|
+
async step4Instructions(_state: ErrorRetryState) {
|
|
135
|
+
await this.documentStore.save(MessageDocument, {
|
|
136
|
+
role: 'assistant',
|
|
137
|
+
text:
|
|
138
|
+
'## Step 4: Timeout\n\n' +
|
|
139
|
+
'The next step has a 2-second timeout but takes 5 seconds. It will be killed by the timeout.\n\n' +
|
|
140
|
+
'**Click the Retry button — the second attempt will be fast and succeed.**',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -- Step 4: Timeout --
|
|
145
|
+
@Transition({ from: 'step4_ready', to: 'step5_done', timeout: 2000 })
|
|
146
|
+
async timeoutStep(_state: ErrorRetryState, ctx: RunContext) {
|
|
147
|
+
const attempt = ctx.execution!.retryCount + 1;
|
|
148
|
+
// First attempt: 5s (exceeds 2s timeout). Second attempt: instant.
|
|
149
|
+
await this.slowTool.call({ delayMs: attempt <= 1 ? 5000 : 0 });
|
|
150
|
+
await this.documentStore.save(MessageDocument, {
|
|
151
|
+
role: 'assistant',
|
|
152
|
+
text: `Timeout step succeeded on attempt ${attempt}.`,
|
|
153
|
+
});
|
|
154
|
+
this.assignState({ timeoutAttempts: attempt });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// -- Bridge: instructions for step 5 --
|
|
158
|
+
@Transition({ from: 'step5_done', to: 'step5_ready' })
|
|
159
|
+
async step5Instructions(_state: ErrorRetryState) {
|
|
160
|
+
await this.documentStore.save(MessageDocument, {
|
|
161
|
+
role: 'assistant',
|
|
162
|
+
text:
|
|
163
|
+
'## Step 5: Hybrid (auto-retry + custom error place)\n\n' +
|
|
164
|
+
'The next step always fails. It auto-retries once, then transitions to `error_hybrid`.\n\n' +
|
|
165
|
+
'**Click the "Recover" button that appears below.**',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// -- Step 5: Hybrid --
|
|
170
|
+
@Transition({ from: 'step5_ready', to: 'step6_done', retryAttempts: 1, errorPlace: 'error_hybrid' })
|
|
171
|
+
async hybridStep(_state: ErrorRetryState) {
|
|
172
|
+
await this.step2Tool.call({ shouldFail: true });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@Transition({ from: 'error_hybrid', to: 'step6_done', wait: true })
|
|
176
|
+
async handleHybridError(_state: ErrorRetryState) {
|
|
177
|
+
await this.documentStore.save(MessageDocument, {
|
|
178
|
+
role: 'assistant',
|
|
179
|
+
text: 'Recovered via hybrid error handler!',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// -- Bridge: instructions for step 6 --
|
|
184
|
+
@Transition({ from: 'step6_done', to: 'step6_ready' })
|
|
185
|
+
async step6Instructions(_state: ErrorRetryState) {
|
|
186
|
+
await this.documentStore.save(MessageDocument, {
|
|
187
|
+
role: 'assistant',
|
|
188
|
+
text:
|
|
189
|
+
'## Step 6: retryTarget (auto-retry via a different place)\n\n' +
|
|
190
|
+
'The next step fails twice. Each failure auto-retries — but instead of re-running the same ' +
|
|
191
|
+
'transition, the framework routes through `retry_prep`, which does some preparation work ' +
|
|
192
|
+
'(e.g. cache invalidation, token refresh) and then advances back to retry the operation. ' +
|
|
193
|
+
'On the 3rd attempt the operation succeeds.\n\n' +
|
|
194
|
+
'**No action required — just watch.**',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// -- Step 6: retryTarget --
|
|
199
|
+
// On failure: auto-retry up to 2x via 'retry_prep' (each retry re-enters there, not here).
|
|
200
|
+
// After 2 retries are exhausted, route to errorPlace.
|
|
201
|
+
@Transition({
|
|
202
|
+
from: 'step6_ready',
|
|
203
|
+
to: 'step7_done',
|
|
204
|
+
retryAttempts: 2,
|
|
205
|
+
retryTarget: 'retry_prep',
|
|
206
|
+
errorPlace: 'error_retry_target',
|
|
207
|
+
})
|
|
208
|
+
async retryTargetStep(state: ErrorRetryState, ctx: RunContext) {
|
|
209
|
+
const attempt = ctx.execution!.retryCount + 1;
|
|
210
|
+
await this.step2Tool.call({ shouldFail: attempt <= 2 });
|
|
211
|
+
await this.documentStore.save(MessageDocument, {
|
|
212
|
+
role: 'assistant',
|
|
213
|
+
text: `retryTarget step succeeded on attempt ${attempt} (after ${state.retryTargetPreps} prep run(s)).`,
|
|
214
|
+
});
|
|
215
|
+
this.assignState({ retryTargetAttempts: attempt });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// The retry-target place: does prep work, then loops back to re-attempt the originator.
|
|
219
|
+
@Transition({ from: 'retry_prep', to: 'step6_ready' })
|
|
220
|
+
async retryPrep(state: ErrorRetryState) {
|
|
221
|
+
const preps = (state.retryTargetPreps ?? 0) + 1;
|
|
222
|
+
await this.documentStore.save(MessageDocument, {
|
|
223
|
+
role: 'assistant',
|
|
224
|
+
text: `Running prep #${preps} before retrying the failed step.`,
|
|
225
|
+
});
|
|
226
|
+
this.assignState({ retryTargetPreps: preps });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Reached only if retryTargetStep keeps failing past its budget.
|
|
230
|
+
@Transition({ from: 'error_retry_target', to: 'step7_done', wait: true })
|
|
231
|
+
async handleRetryTargetError(_state: ErrorRetryState) {
|
|
232
|
+
await this.documentStore.save(MessageDocument, {
|
|
233
|
+
role: 'assistant',
|
|
234
|
+
text: 'retryTarget budget exhausted — recovered via error handler.',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// -- Bridge: instructions for step 7 --
|
|
239
|
+
@Transition({ from: 'step7_done', to: 'step7_ready' })
|
|
240
|
+
async step7Instructions(_state: ErrorRetryState) {
|
|
241
|
+
await this.documentStore.save(MessageDocument, {
|
|
242
|
+
role: 'assistant',
|
|
243
|
+
text: [
|
|
244
|
+
'## Step 7: Sub-workflow failure routed via errorPlace',
|
|
245
|
+
'',
|
|
246
|
+
`The next step spawns a child workflow that always throws. The parent's wait transition ` +
|
|
247
|
+
'declares `errorPlace: "sub_failed"`, so the failure callback is routed to a recovery ' +
|
|
248
|
+
'transition instead of leaking null data into the happy-path body.',
|
|
249
|
+
'',
|
|
250
|
+
'**Click the "Recover" button that appears below.**',
|
|
251
|
+
].join('\n'),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// -- Step 7: Sub-workflow + errorPlace --
|
|
256
|
+
@Transition({ from: 'step7_ready', to: 'awaiting_child' })
|
|
257
|
+
async spawnFailingChild(_state: ErrorRetryState) {
|
|
258
|
+
await this.failingChild.run({}, { callback: { transition: 'childCompleted' } });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Happy path: this body only runs if the child succeeds (it won't, in this example).
|
|
262
|
+
@Transition({ from: 'awaiting_child', to: 'done', wait: true, errorPlace: 'sub_failed' })
|
|
263
|
+
async childCompleted(_state: ErrorRetryState, _input: TransitionInput) {
|
|
264
|
+
await this.documentStore.save(MessageDocument, {
|
|
265
|
+
role: 'assistant',
|
|
266
|
+
text: 'Child workflow completed successfully (unexpected for this demo).',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Failure recovery transition for the failing child.
|
|
271
|
+
@Transition({ from: 'sub_failed', to: 'done', wait: true })
|
|
272
|
+
async handleSubFailed(_state: ErrorRetryState) {
|
|
273
|
+
await this.documentStore.save(MessageDocument, {
|
|
274
|
+
role: 'assistant',
|
|
275
|
+
text: 'Recovered from sub-workflow failure via errorPlace routing.',
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@Transition({ from: 'done', to: 'end' })
|
|
280
|
+
async showResult(_state: ErrorRetryState) {
|
|
281
|
+
await this.documentStore.save(MessageDocument, {
|
|
282
|
+
role: 'assistant',
|
|
283
|
+
text: 'All seven retry modes completed successfully!',
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Always-failing child used by the error-retry example to exercise the parent-side
|
|
5
|
+
* `errorPlace` routing for sub-workflow callback failures.
|
|
6
|
+
*/
|
|
7
|
+
@Workflow({
|
|
8
|
+
name: 'error_retry_failing_child',
|
|
9
|
+
title: 'Advanced - Error Retry Failing Child',
|
|
10
|
+
})
|
|
11
|
+
export class ErrorRetryFailingChildWorkflow extends BaseWorkflow {
|
|
12
|
+
@Transition({ to: 'end' })
|
|
13
|
+
async fail(_state: Record<string, unknown>) {
|
|
14
|
+
await this.documentStore.save(MessageDocument, {
|
|
15
|
+
role: 'assistant',
|
|
16
|
+
text: 'Failing child: about to throw.',
|
|
17
|
+
});
|
|
18
|
+
throw new Error('Demo failure: this child is wired to always throw.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
|
|
2
|
+
|
|
3
|
+
export type SlowToolResult = string;
|
|
4
|
+
|
|
5
|
+
@Tool({
|
|
6
|
+
name: 'slow',
|
|
7
|
+
description: 'A tool that takes a configurable amount of time to complete.',
|
|
8
|
+
})
|
|
9
|
+
export class SlowTool extends BaseTool<{ delayMs: number }, object, SlowToolResult> {
|
|
10
|
+
protected async handle(args: { delayMs: number }): Promise<ToolEnvelope<SlowToolResult>> {
|
|
11
|
+
await new Promise((resolve) => setTimeout(resolve, args.delayMs));
|
|
12
|
+
return {
|
|
13
|
+
type: 'text',
|
|
14
|
+
data: 'Slow tool completed.',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
|
|
2
|
+
|
|
3
|
+
export type Step1ToolResult = string;
|
|
4
|
+
|
|
5
|
+
@Tool({
|
|
6
|
+
name: 'step1',
|
|
7
|
+
description: 'A tool that always succeeds.',
|
|
8
|
+
})
|
|
9
|
+
export class Step1Tool extends BaseTool<object, object, Step1ToolResult> {
|
|
10
|
+
protected async handle(): Promise<ToolEnvelope<Step1ToolResult>> {
|
|
11
|
+
return {
|
|
12
|
+
type: 'text',
|
|
13
|
+
data: 'Step completed successfully.',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseTool, Tool, ToolEnvelope } from '@loopstack/common';
|
|
2
|
+
|
|
3
|
+
export type Step2ToolResult = string;
|
|
4
|
+
|
|
5
|
+
@Tool({
|
|
6
|
+
name: 'step2',
|
|
7
|
+
description: 'A tool that fails when shouldFail is true.',
|
|
8
|
+
})
|
|
9
|
+
export class Step2Tool extends BaseTool<{ shouldFail: boolean }, object, Step2ToolResult> {
|
|
10
|
+
protected async handle(args: { shouldFail: boolean }): Promise<ToolEnvelope<Step2ToolResult>> {
|
|
11
|
+
if (args.shouldFail) {
|
|
12
|
+
throw new Error('Simulated external service error');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
type: 'text',
|
|
17
|
+
data: 'Step completed successfully.',
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
import { FanOutResultSchema, FanOutWorkflow } from '@loopstack/core';
|
|
4
|
+
|
|
5
|
+
type FanOutResultData = z.infer<typeof FanOutResultSchema>;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Demonstrates `FanOutWorkflow` — launches three sub-workflows in parallel and
|
|
9
|
+
* receives a single aggregated callback once all of them complete.
|
|
10
|
+
*/
|
|
11
|
+
@Workflow({
|
|
12
|
+
title: 'Advanced - Fan-Out Example',
|
|
13
|
+
description:
|
|
14
|
+
'Launches multiple sub-workflows in parallel via FanOutWorkflow and receives a single aggregated callback.',
|
|
15
|
+
})
|
|
16
|
+
export class RunSubWorkflowExampleFanOutWorkflow extends BaseWorkflow {
|
|
17
|
+
constructor(private readonly fanOut: FanOutWorkflow) {
|
|
18
|
+
super();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Transition({ to: 'awaiting' })
|
|
22
|
+
async launch(_state: Record<string, unknown>) {
|
|
23
|
+
await this.fanOut.run(
|
|
24
|
+
{
|
|
25
|
+
items: {
|
|
26
|
+
first: { workflow: 'run_sub_workflow_example_sub' },
|
|
27
|
+
second: { workflow: 'run_sub_workflow_example_sub' },
|
|
28
|
+
third: { workflow: 'run_sub_workflow_example_sub' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{ callback: { transition: 'onAllDone' }, label: 'Fan-out children', show: 'link' },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Transition({
|
|
36
|
+
from: 'awaiting',
|
|
37
|
+
to: 'end',
|
|
38
|
+
wait: true,
|
|
39
|
+
schema: FanOutResultSchema,
|
|
40
|
+
})
|
|
41
|
+
async onAllDone(state: Record<string, unknown>, input: TransitionInput<FanOutResultData>) {
|
|
42
|
+
const results = input.data.results as Record<string, { data?: unknown }>;
|
|
43
|
+
|
|
44
|
+
for (const key of Object.keys(results)) {
|
|
45
|
+
const child = results[key];
|
|
46
|
+
const data = (child.data ?? {}) as { message?: string };
|
|
47
|
+
await this.documentStore.save(MessageDocument, {
|
|
48
|
+
role: 'assistant',
|
|
49
|
+
text: `Fan-out ${key}: ${data.message ?? '(no message)'}`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.setResult({ hasErrors: input.data.hasErrors, errorCount: input.data.errorCount } as unknown as Record<
|
|
54
|
+
string,
|
|
55
|
+
unknown
|
|
56
|
+
>);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { DefaultGreetingWorkflow } from './default-greeting.workflow.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scenario 1: No forFeature import — GreeterTool resolves from the global
|
|
6
|
+
* GreeterRootModule (provided by forRoot in the app module).
|
|
7
|
+
*/
|
|
8
|
+
@Module({
|
|
9
|
+
providers: [DefaultGreetingWorkflow],
|
|
10
|
+
exports: [DefaultGreetingWorkflow],
|
|
11
|
+
})
|
|
12
|
+
export class DefaultGreetingModule {}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
import { GreeterTool } from '../greeter/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scenario 1: No forFeature — uses forRoot global defaults.
|
|
6
|
+
*/
|
|
7
|
+
@Workflow({ name: 'default_greeting', title: 'Advanced - Module Config (Default)' })
|
|
8
|
+
export class DefaultGreetingWorkflow extends BaseWorkflow {
|
|
9
|
+
constructor(private readonly greeter: GreeterTool) {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@Transition({ to: 'done' })
|
|
14
|
+
async greet(_state: Record<string, unknown>) {
|
|
15
|
+
const result = await this.greeter.call({ name: 'World' });
|
|
16
|
+
await this.documentStore.save(MessageDocument, {
|
|
17
|
+
role: 'assistant',
|
|
18
|
+
text: `[Default] ${result.data.message}`,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Transition({ from: 'done', to: 'end' })
|
|
23
|
+
finish(_state: Record<string, unknown>) {}
|
|
24
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { GreeterModule } from '../greeter/index.js';
|
|
3
|
+
import { FrenchGreetingWorkflow } from './french-greeting.workflow.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scenario 3: forFeature with French config.
|
|
7
|
+
* Runs alongside GermanGreetingModule — proves each module gets its own config.
|
|
8
|
+
*/
|
|
9
|
+
@Module({
|
|
10
|
+
imports: [GreeterModule.forFeature({ language: 'fr', greeting: 'Bonjour' })],
|
|
11
|
+
providers: [FrenchGreetingWorkflow],
|
|
12
|
+
exports: [FrenchGreetingWorkflow],
|
|
13
|
+
})
|
|
14
|
+
export class FrenchGreetingModule {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
import { GreeterTool } from '../greeter/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scenario 3: forFeature override with French config.
|
|
6
|
+
* Runs alongside GermanGreetingWorkflow to prove per-module isolation.
|
|
7
|
+
*/
|
|
8
|
+
@Workflow({ name: 'french_greeting', title: 'Advanced - Module Config (French)' })
|
|
9
|
+
export class FrenchGreetingWorkflow extends BaseWorkflow {
|
|
10
|
+
constructor(private readonly greeter: GreeterTool) {
|
|
11
|
+
super();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Transition({ to: 'done' })
|
|
15
|
+
async greet(_state: Record<string, unknown>) {
|
|
16
|
+
const result = await this.greeter.call({ name: 'Monde' });
|
|
17
|
+
await this.documentStore.save(MessageDocument, {
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
text: `[French] ${result.data.message}`,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Transition({ from: 'done', to: 'end' })
|
|
24
|
+
finish(_state: Record<string, unknown>) {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { GreeterModule } from '../greeter/index.js';
|
|
3
|
+
import { GermanGreetingWorkflow } from './german-greeting.workflow.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scenario 2: forFeature with German config.
|
|
7
|
+
* The module-scoped GreeterTool overrides the global default for this module's workflows.
|
|
8
|
+
*/
|
|
9
|
+
@Module({
|
|
10
|
+
imports: [GreeterModule.forFeature({ language: 'de', greeting: 'Hallo' })],
|
|
11
|
+
providers: [GermanGreetingWorkflow],
|
|
12
|
+
exports: [GermanGreetingWorkflow],
|
|
13
|
+
})
|
|
14
|
+
export class GermanGreetingModule {}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
import { GreeterTool } from '../greeter/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scenario 2: forFeature override with German config.
|
|
6
|
+
*/
|
|
7
|
+
@Workflow({ name: 'german_greeting', title: 'Advanced - Module Config (German)' })
|
|
8
|
+
export class GermanGreetingWorkflow extends BaseWorkflow {
|
|
9
|
+
constructor(private readonly greeter: GreeterTool) {
|
|
10
|
+
super();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@Transition({ to: 'done' })
|
|
14
|
+
async greet(_state: Record<string, unknown>) {
|
|
15
|
+
const result = await this.greeter.call({ name: 'Welt' });
|
|
16
|
+
await this.documentStore.save(MessageDocument, {
|
|
17
|
+
role: 'assistant',
|
|
18
|
+
text: `[German] ${result.data.message}`,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Transition({ from: 'done', to: 'end' })
|
|
23
|
+
finish(_state: Record<string, unknown>) {}
|
|
24
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { GreeterAgentModule } from '../greeter/greeter-agent.module.js';
|
|
3
|
+
import { NestedGreetingWorkflow } from './nested-greeting.workflow.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scenario 4: Config passed through a wrapper module.
|
|
7
|
+
* GreeterAgentModule.forFeature passes the config to GreeterModule.forFeature internally.
|
|
8
|
+
* This is analogous to AgentModule.forFeature → LlmProviderModule.forFeature.
|
|
9
|
+
*/
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [GreeterAgentModule.forFeature({ greeter: { language: 'es', greeting: 'Hola' } })],
|
|
12
|
+
providers: [NestedGreetingWorkflow],
|
|
13
|
+
exports: [NestedGreetingWorkflow],
|
|
14
|
+
})
|
|
15
|
+
export class NestedGreetingModule {}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
2
|
+
import { GreeterTool } from '../greeter/index.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scenario 4: Config passed through a wrapper module (GreeterAgentModule.forFeature).
|
|
6
|
+
* Demonstrates the nested forFeature pattern like AgentModule.forFeature → LlmProviderModule.forFeature.
|
|
7
|
+
*/
|
|
8
|
+
@Workflow({ name: 'nested_greeting', title: 'Advanced - Module Config (Nested)' })
|
|
9
|
+
export class NestedGreetingWorkflow extends BaseWorkflow {
|
|
10
|
+
constructor(private readonly greeter: GreeterTool) {
|
|
11
|
+
super();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@Transition({ to: 'done' })
|
|
15
|
+
async greet(_state: Record<string, unknown>) {
|
|
16
|
+
const result = await this.greeter.call({ name: 'Mundo' });
|
|
17
|
+
await this.documentStore.save(MessageDocument, {
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
text: `[Nested/Spanish] ${result.data.message}`,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Transition({ from: 'done', to: 'end' })
|
|
24
|
+
finish(_state: Record<string, unknown>) {}
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { DynamicModule, Module } from '@nestjs/common';
|
|
2
|
+
import type { GreeterConfig } from './greeter.constants.js';
|
|
3
|
+
import { GreeterModule } from './greeter.module.js';
|
|
4
|
+
import { GreeterTool } from './greeter.tool.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Example "wrapper" module — analogous to AgentModule.
|
|
8
|
+
*
|
|
9
|
+
* Demonstrates how a module that depends on a configurable module (GreeterModule)
|
|
10
|
+
* can pass config through via its own forFeature() method.
|
|
11
|
+
*
|
|
12
|
+
* The RootModule class prevents NestJS deduplication between the bare import
|
|
13
|
+
* (static decorator) and the forFeature() dynamic import.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
@Module({})
|
|
17
|
+
class GreeterAgentRootModule {}
|
|
18
|
+
|
|
19
|
+
@Module({
|
|
20
|
+
imports: [GreeterModule],
|
|
21
|
+
providers: [GreeterTool],
|
|
22
|
+
exports: [GreeterTool],
|
|
23
|
+
})
|
|
24
|
+
export class GreeterAgentModule {
|
|
25
|
+
static forFeature(config: { greeter: GreeterConfig }): DynamicModule {
|
|
26
|
+
return {
|
|
27
|
+
module: GreeterAgentRootModule,
|
|
28
|
+
imports: [GreeterModule.forFeature(config.greeter)],
|
|
29
|
+
providers: [GreeterTool],
|
|
30
|
+
exports: [GreeterTool],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DynamicModule, Global, Module } from '@nestjs/common';
|
|
2
|
+
import { GREETER_CONFIG } from './greeter.constants.js';
|
|
3
|
+
import type { GreeterConfig } from './greeter.constants.js';
|
|
4
|
+
import { GreeterTool } from './greeter.tool.js';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CONFIG: GreeterConfig = {};
|
|
7
|
+
const TOOLS = [GreeterTool];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Internal global root module — provides the default GreeterTool globally.
|
|
11
|
+
* Separate class so NestJS doesn't deduplicate it with forFeature() imports.
|
|
12
|
+
*/
|
|
13
|
+
@Global()
|
|
14
|
+
@Module({
|
|
15
|
+
providers: [{ provide: GREETER_CONFIG, useValue: DEFAULT_CONFIG }, ...TOOLS],
|
|
16
|
+
exports: [GREETER_CONFIG, ...TOOLS],
|
|
17
|
+
})
|
|
18
|
+
class GreeterRootModule {}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Configurable Greeter Module — demonstrates the forRoot/forFeature pattern.
|
|
22
|
+
*
|
|
23
|
+
* - `forRoot(config)` — sets the global default config (call once in root AppModule)
|
|
24
|
+
* - `forFeature(config)` — overrides config for a specific module's tools
|
|
25
|
+
*
|
|
26
|
+
* If neither is called, the global root module provides empty defaults.
|
|
27
|
+
* forFeature automatically ensures the global root module is available.
|
|
28
|
+
*/
|
|
29
|
+
@Module({})
|
|
30
|
+
export class GreeterModule {
|
|
31
|
+
static forRoot(config: GreeterConfig): DynamicModule {
|
|
32
|
+
return {
|
|
33
|
+
module: GreeterRootModule,
|
|
34
|
+
global: true,
|
|
35
|
+
providers: [{ provide: GREETER_CONFIG, useValue: config }, ...TOOLS],
|
|
36
|
+
exports: [GREETER_CONFIG, ...TOOLS],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
static forFeature(config: GreeterConfig): DynamicModule {
|
|
41
|
+
return {
|
|
42
|
+
module: GreeterModule,
|
|
43
|
+
imports: [GreeterRootModule],
|
|
44
|
+
providers: [{ provide: GREETER_CONFIG, useValue: config }, ...TOOLS],
|
|
45
|
+
exports: [...TOOLS],
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|