@loopstack/accessing-tool-results-example-workflow 0.16.0 → 0.18.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 +167 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tool-results-example.module.d.ts +2 -0
- package/dist/tool-results-example.module.js +26 -0
- package/dist/tool-results-example.module.js.map +1 -0
- package/dist/workflow-tool-results.workflow.js +11 -10
- package/dist/workflow-tool-results.workflow.js.map +1 -1
- package/dist/workflow-tool-results.workflow.yaml +5 -7
- package/package.json +24 -37
- package/src/__tests__/workflow-tool-results.workflow.spec.ts +118 -0
- package/src/index.ts +2 -0
- package/src/tool-results-example.module.ts +28 -0
- package/src/workflow-tool-results.workflow.ts +29 -0
- package/src/workflow-tool-results.workflow.yaml +40 -0
- package/LICENSE +0 -19
package/README.md
CHANGED
|
@@ -1 +1,167 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @loopstack/accessing-tool-results-example-workflow
|
|
2
|
+
|
|
3
|
+
> A module for the [Loopstack AI](https://loopstack.ai) automation framework.
|
|
4
|
+
|
|
5
|
+
This module provides an example workflow demonstrating different methods for accessing tool results within and across workflow transitions.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The Tool Results Example Workflow shows how to retrieve and use data returned by tools in subsequent workflow steps. Understanding these patterns is essential for building workflows that pass data between operations.
|
|
10
|
+
|
|
11
|
+
By using this workflow as a reference, you'll learn how to:
|
|
12
|
+
|
|
13
|
+
- Access tool results using call IDs
|
|
14
|
+
- Access tool results using call indices
|
|
15
|
+
- Retrieve data from previous transitions
|
|
16
|
+
- Create custom helper functions for data extraction
|
|
17
|
+
|
|
18
|
+
This example is useful for developers learning to build data-driven workflows that need to pass information between steps.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### Prerequisites
|
|
23
|
+
|
|
24
|
+
Create a new Loopstack project if you haven't already:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx create-loopstack-app my-project
|
|
28
|
+
cd my-project
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Start Environment
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd my-project
|
|
35
|
+
docker compose up -d
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Add the Module
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
loopstack add @loopstack/accessing-tool-results-example-workflow
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
This copies the source files into your `src` directory.
|
|
45
|
+
|
|
46
|
+
> Using the `loopstack add` command is a great way to explore the code to learn new concepts or add own customizations.
|
|
47
|
+
|
|
48
|
+
## Setup
|
|
49
|
+
|
|
50
|
+
### 1. Import the Module
|
|
51
|
+
|
|
52
|
+
Add `AccessingToolResultsExampleModule` to your `default.module.ts` (included in the skeleton app) or to your own module:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Module } from '@nestjs/common';
|
|
56
|
+
import { LoopCoreModule } from '@loopstack/core';
|
|
57
|
+
import { CoreUiModule } from '@loopstack/core-ui-module';
|
|
58
|
+
import { AccessingToolResultsExampleModule } from './accessing-tool-results-example-workflow';
|
|
59
|
+
import { DefaultWorkspace } from './default.workspace';
|
|
60
|
+
|
|
61
|
+
@Module({
|
|
62
|
+
imports: [LoopCoreModule, CoreUiModule, AccessingToolResultsExampleModule],
|
|
63
|
+
providers: [DefaultWorkspace],
|
|
64
|
+
})
|
|
65
|
+
export class DefaultModule {}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Register in Your Workspace
|
|
69
|
+
|
|
70
|
+
Add the workflow to your workspace class using the `@Workflow()` decorator:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { Injectable } from '@nestjs/common';
|
|
74
|
+
import { BlockConfig, Workflow } from '@loopstack/common';
|
|
75
|
+
import { WorkspaceBase } from '@loopstack/core';
|
|
76
|
+
import { WorkflowToolResultsWorkflow } from './accessing-tool-results-example-workflow';
|
|
77
|
+
|
|
78
|
+
@Injectable()
|
|
79
|
+
@BlockConfig({
|
|
80
|
+
config: {
|
|
81
|
+
title: 'My Workspace',
|
|
82
|
+
description: 'A workspace with the tool results example workflow',
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
export class MyWorkspace extends WorkspaceBase {
|
|
86
|
+
@Workflow() workflowToolResults: WorkflowToolResultsWorkflow;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## How It Works
|
|
91
|
+
|
|
92
|
+
### Accessing Tool Results
|
|
93
|
+
|
|
94
|
+
#### 1. Using Call IDs
|
|
95
|
+
|
|
96
|
+
Assign a unique `id` to a tool call, then reference it via `metadata.tools.<transition_id>.<call_id>.data`:
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
- id: say_hello
|
|
100
|
+
tool: createValue
|
|
101
|
+
args:
|
|
102
|
+
input: 'Hello World.'
|
|
103
|
+
|
|
104
|
+
- tool: createChatMessage
|
|
105
|
+
args:
|
|
106
|
+
content: '{{ metadata.tools.create_some_data.say_hello.data }}'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### 2. Using Call Indices
|
|
110
|
+
|
|
111
|
+
Access tool results by their position (zero-indexed) within the transition:
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
- tool: createChatMessage
|
|
115
|
+
args:
|
|
116
|
+
content: '{{ metadata.tools.create_some_data.0.data }}'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### 3. Across Transitions
|
|
120
|
+
|
|
121
|
+
Tool results persist and can be accessed from subsequent transitions using the same patterns:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
# In a later transition
|
|
125
|
+
- tool: createChatMessage
|
|
126
|
+
args:
|
|
127
|
+
content: '{{ metadata.tools.create_some_data.say_hello.data }}'
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### 4. Using Helper Functions
|
|
131
|
+
|
|
132
|
+
Define custom helper functions in your workflow class for complex data extraction:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
@Helper()
|
|
136
|
+
extractMessage(metadata: WorkflowMetadataInterface): string {
|
|
137
|
+
return metadata.tools.create_some_data.say_hello.data;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Then use them in your YAML configuration:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
- tool: createChatMessage
|
|
145
|
+
args:
|
|
146
|
+
content: '{{ extractMessage metadata }}'
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Dependencies
|
|
150
|
+
|
|
151
|
+
This workflow uses the following Loopstack modules:
|
|
152
|
+
|
|
153
|
+
- `@loopstack/core` - Core framework functionality
|
|
154
|
+
- `@loopstack/core-ui-module` - Provides `CreateChatMessage` tool
|
|
155
|
+
- `@loopstack/create-value-tool` - Provides `CreateValue` tool
|
|
156
|
+
|
|
157
|
+
## About
|
|
158
|
+
|
|
159
|
+
Author: [Jakob Klippel](https://www.linkedin.com/in/jakob-klippel/)
|
|
160
|
+
|
|
161
|
+
License: Apache-2.0
|
|
162
|
+
|
|
163
|
+
### Additional Resources
|
|
164
|
+
|
|
165
|
+
- [Loopstack Documentation](https://loopstack.ai/docs)
|
|
166
|
+
- [Getting Started with Loopstack](https://loopstack.ai/docs/getting-started)
|
|
167
|
+
- Find more Loopstack examples in the [Loopstack Registry](https://loopstack.ai/registry)
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./workflow-tool-results.workflow"), exports);
|
|
18
|
+
__exportStar(require("./tool-results-example.module"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mEAAiD"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mEAAiD;AACjD,gEAA8C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ToolResultsExampleModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const core_1 = require("@loopstack/core");
|
|
12
|
+
const core_ui_module_1 = require("@loopstack/core-ui-module");
|
|
13
|
+
const create_chat_message_tool_1 = require("@loopstack/create-chat-message-tool");
|
|
14
|
+
const create_value_tool_1 = require("@loopstack/create-value-tool");
|
|
15
|
+
const workflow_tool_results_workflow_1 = require("./workflow-tool-results.workflow");
|
|
16
|
+
let ToolResultsExampleModule = class ToolResultsExampleModule {
|
|
17
|
+
};
|
|
18
|
+
exports.ToolResultsExampleModule = ToolResultsExampleModule;
|
|
19
|
+
exports.ToolResultsExampleModule = ToolResultsExampleModule = __decorate([
|
|
20
|
+
(0, common_1.Module)({
|
|
21
|
+
imports: [core_1.LoopCoreModule, core_ui_module_1.CoreUiModule, create_value_tool_1.CreateValueToolModule, create_chat_message_tool_1.CreateChatMessageToolModule],
|
|
22
|
+
providers: [workflow_tool_results_workflow_1.WorkflowToolResultsWorkflow],
|
|
23
|
+
exports: [workflow_tool_results_workflow_1.WorkflowToolResultsWorkflow],
|
|
24
|
+
})
|
|
25
|
+
], ToolResultsExampleModule);
|
|
26
|
+
//# sourceMappingURL=tool-results-example.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-results-example.module.js","sourceRoot":"","sources":["../src/tool-results-example.module.ts"],"names":[],"mappings":";;;;;;;;;AAeA,2CAAwC;AACxC,0CAAiD;AACjD,8DAAyD;AACzD,kFAAkF;AAClF,oEAAqE;AACrE,qFAA+E;AAOxE,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;CAAG,CAAA;AAA3B,4DAAwB;mCAAxB,wBAAwB;IALpC,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,qBAAc,EAAE,6BAAY,EAAE,yCAAqB,EAAE,sDAA2B,CAAC;QAC3F,SAAS,EAAE,CAAC,4DAA2B,CAAC;QACxC,OAAO,EAAE,CAAC,4DAA2B,CAAC;KACvC,CAAC;GACW,wBAAwB,CAAG"}
|
|
@@ -10,36 +10,37 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.WorkflowToolResultsWorkflow = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const common_2 = require("@loopstack/common");
|
|
13
15
|
const core_1 = require("@loopstack/core");
|
|
14
|
-
const
|
|
15
|
-
const common_2 = require("@nestjs/common");
|
|
16
|
-
const core_ui_module_1 = require("@loopstack/core-ui-module");
|
|
16
|
+
const create_chat_message_tool_1 = require("@loopstack/create-chat-message-tool");
|
|
17
17
|
const create_value_tool_1 = require("@loopstack/create-value-tool");
|
|
18
18
|
let WorkflowToolResultsWorkflow = class WorkflowToolResultsWorkflow extends core_1.WorkflowBase {
|
|
19
19
|
createValue;
|
|
20
20
|
createChatMessage;
|
|
21
21
|
extractMessage(metadata) {
|
|
22
|
-
|
|
22
|
+
const tools = metadata.tools;
|
|
23
|
+
return tools.create_some_data.say_hello.data;
|
|
23
24
|
}
|
|
24
25
|
};
|
|
25
26
|
exports.WorkflowToolResultsWorkflow = WorkflowToolResultsWorkflow;
|
|
26
27
|
__decorate([
|
|
27
|
-
(0,
|
|
28
|
+
(0, common_2.Tool)(),
|
|
28
29
|
__metadata("design:type", create_value_tool_1.CreateValue)
|
|
29
30
|
], WorkflowToolResultsWorkflow.prototype, "createValue", void 0);
|
|
30
31
|
__decorate([
|
|
31
|
-
(0,
|
|
32
|
-
__metadata("design:type",
|
|
32
|
+
(0, common_2.Tool)(),
|
|
33
|
+
__metadata("design:type", create_chat_message_tool_1.CreateChatMessage)
|
|
33
34
|
], WorkflowToolResultsWorkflow.prototype, "createChatMessage", void 0);
|
|
34
35
|
__decorate([
|
|
35
|
-
(0,
|
|
36
|
+
(0, common_2.Helper)(),
|
|
36
37
|
__metadata("design:type", Function),
|
|
37
38
|
__metadata("design:paramtypes", [Object]),
|
|
38
39
|
__metadata("design:returntype", String)
|
|
39
40
|
], WorkflowToolResultsWorkflow.prototype, "extractMessage", null);
|
|
40
41
|
exports.WorkflowToolResultsWorkflow = WorkflowToolResultsWorkflow = __decorate([
|
|
41
|
-
(0,
|
|
42
|
-
(0,
|
|
42
|
+
(0, common_1.Injectable)(),
|
|
43
|
+
(0, common_2.BlockConfig)({
|
|
43
44
|
configFile: __dirname + '/workflow-tool-results.workflow.yaml',
|
|
44
45
|
})
|
|
45
46
|
], WorkflowToolResultsWorkflow);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-tool-results.workflow.js","sourceRoot":"","sources":["../src/workflow-tool-results.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"workflow-tool-results.workflow.js","sourceRoot":"","sources":["../src/workflow-tool-results.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA4C;AAC5C,8CAA8D;AAC9D,0CAA+C;AAE/C,kFAAwE;AACxE,oEAA2D;AAcpD,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,mBAAY;IAC3C,WAAW,CAAc;IACzB,iBAAiB,CAAoB;IAGrD,cAAc,CAAC,QAAmC;QAChD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAyC,CAAC;QACjE,OAAO,KAAK,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,CAAC;IAC/C,CAAC;CACF,CAAA;AATY,kEAA2B;AACtB;IAAf,IAAA,aAAI,GAAE;8BAAsB,+BAAW;gEAAC;AACzB;IAAf,IAAA,aAAI,GAAE;8BAA4B,4CAAiB;sEAAC;AAGrD;IADC,IAAA,eAAM,GAAE;;;;iEAIR;sCARU,2BAA2B;IAJvC,IAAA,mBAAU,GAAE;IACZ,IAAA,oBAAW,EAAC;QACX,UAAU,EAAE,SAAS,GAAG,sCAAsC;KAC/D,CAAC;GACW,2BAA2B,CASvC"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
title:
|
|
1
|
+
title: 'Workflow Tool Result'
|
|
2
2
|
|
|
3
3
|
transitions:
|
|
4
4
|
- id: create_some_data
|
|
5
5
|
from: start
|
|
6
6
|
to: data_created
|
|
7
7
|
call:
|
|
8
|
-
|
|
9
8
|
# Create test data and return it in the tool result
|
|
10
9
|
- id: say_hello
|
|
11
10
|
tool: createValue
|
|
@@ -16,27 +15,26 @@ transitions:
|
|
|
16
15
|
- tool: createChatMessage
|
|
17
16
|
args:
|
|
18
17
|
role: 'assistant'
|
|
19
|
-
content:
|
|
18
|
+
content: 'Data from specific call id: {{ metadata.tools.create_some_data.say_hello.data }}'
|
|
20
19
|
|
|
21
20
|
# Access the data using the call index
|
|
22
21
|
- tool: createChatMessage
|
|
23
22
|
args:
|
|
24
23
|
role: 'assistant'
|
|
25
|
-
content:
|
|
24
|
+
content: 'Data from first tool call: {{ metadata.tools.create_some_data.0.data }}'
|
|
26
25
|
|
|
27
26
|
- id: access_data
|
|
28
27
|
from: data_created
|
|
29
28
|
to: end
|
|
30
29
|
call:
|
|
31
|
-
|
|
32
30
|
# Access the data from a previous transition using transition and call id
|
|
33
31
|
- tool: createChatMessage
|
|
34
32
|
args:
|
|
35
33
|
role: 'assistant'
|
|
36
|
-
content:
|
|
34
|
+
content: 'Data from previous transition: {{ metadata.tools.create_some_data.say_hello.data }}'
|
|
37
35
|
|
|
38
36
|
# Access the data using a custom helper function of the workflow
|
|
39
37
|
- tool: createChatMessage
|
|
40
38
|
args:
|
|
41
39
|
role: 'assistant'
|
|
42
|
-
content:
|
|
40
|
+
content: 'Data access using custom helper: {{ extractMessage metadata }}'
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@loopstack/accessing-tool-results-example-workflow",
|
|
3
3
|
"displayName": "Loopstack Tool Results Example",
|
|
4
4
|
"description": "A simple workflow showing different methods of how to access tool results in a subsequent workflow step.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.18.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Jakob Klippel",
|
|
8
8
|
"url": "https://www.linkedin.com/in/jakob-klippel/"
|
|
@@ -18,46 +18,31 @@
|
|
|
18
18
|
"license": "Apache-2.0",
|
|
19
19
|
"main": "dist/index.js",
|
|
20
20
|
"types": "dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": "./dist/index.js",
|
|
23
|
+
"./src/*": "./src/*"
|
|
24
|
+
},
|
|
21
25
|
"scripts": {
|
|
22
26
|
"build": "nest build",
|
|
23
|
-
"watch": "nest build --watch",
|
|
24
27
|
"compile": "tsc --noEmit",
|
|
25
|
-
"format": "prettier --write
|
|
26
|
-
"lint": "eslint
|
|
27
|
-
"test": "jest",
|
|
28
|
-
"
|
|
29
|
-
"test:cov": "jest --coverage",
|
|
30
|
-
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
|
31
|
-
"test:e2e": "jest --config ./test/jest-e2e.json"
|
|
28
|
+
"format": "prettier --write .",
|
|
29
|
+
"lint": "eslint .",
|
|
30
|
+
"test": "jest --passWithNoTests",
|
|
31
|
+
"watch": "nest build --watch"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@loopstack/common": "^0.
|
|
35
|
-
"@loopstack/core": "^0.
|
|
36
|
-
"@loopstack/core-ui-module": "^0.
|
|
37
|
-
"@loopstack/create-
|
|
38
|
-
"@
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@eslint/eslintrc": "^3.3.0",
|
|
43
|
-
"@eslint/js": "^9.22.0",
|
|
44
|
-
"@nestjs/cli": "^11.0.5",
|
|
45
|
-
"@nestjs/testing": "^11.0.12",
|
|
46
|
-
"@swc/cli": "^0.6.0",
|
|
47
|
-
"@swc/core": "^1.11.11",
|
|
48
|
-
"@types/jest": "^29.5.14",
|
|
49
|
-
"@types/node": "^22.13.10",
|
|
50
|
-
"eslint": "^9.22.0",
|
|
51
|
-
"eslint-config-prettier": "^10.1.1",
|
|
52
|
-
"eslint-plugin-prettier": "^5.2.3",
|
|
53
|
-
"globals": "^16.0.0",
|
|
54
|
-
"jest": "^29.7.0",
|
|
55
|
-
"prettier": "^3.5.3",
|
|
56
|
-
"ts-jest": "^29.2.6",
|
|
57
|
-
"tsconfig-paths": "^4.2.0",
|
|
58
|
-
"typescript": "^5.8.3",
|
|
59
|
-
"typescript-eslint": "^8.38.0"
|
|
34
|
+
"@loopstack/common": "^0.18.0",
|
|
35
|
+
"@loopstack/core": "^0.18.0",
|
|
36
|
+
"@loopstack/core-ui-module": "^0.18.0",
|
|
37
|
+
"@loopstack/create-chat-message-tool": "^0.18.0",
|
|
38
|
+
"@loopstack/create-value-tool": "^0.18.0",
|
|
39
|
+
"@nestjs/common": "^11.1.12",
|
|
40
|
+
"zod": "^4.3.5"
|
|
60
41
|
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"src"
|
|
45
|
+
],
|
|
61
46
|
"jest": {
|
|
62
47
|
"moduleFileExtensions": [
|
|
63
48
|
"js",
|
|
@@ -74,6 +59,8 @@
|
|
|
74
59
|
],
|
|
75
60
|
"coverageDirectory": "../coverage",
|
|
76
61
|
"testEnvironment": "node",
|
|
77
|
-
"maxWorkers": 1
|
|
62
|
+
"maxWorkers": 1,
|
|
63
|
+
"testTimeout": 10000,
|
|
64
|
+
"forceExit": true
|
|
78
65
|
}
|
|
79
|
-
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { TestingModule } from '@nestjs/testing';
|
|
2
|
+
import { BlockExecutionContextDto, LoopCoreModule, WorkflowProcessorService } from '@loopstack/core';
|
|
3
|
+
import { CoreUiModule } from '@loopstack/core-ui-module';
|
|
4
|
+
import { CreateChatMessage, CreateChatMessageToolModule } from '@loopstack/create-chat-message-tool';
|
|
5
|
+
import { CreateValue, CreateValueToolModule } from '@loopstack/create-value-tool';
|
|
6
|
+
import { ToolMock, createWorkflowTest } from '@loopstack/testing';
|
|
7
|
+
import { WorkflowToolResultsWorkflow } from '../workflow-tool-results.workflow';
|
|
8
|
+
|
|
9
|
+
describe('WorkflowToolResultsWorkflow', () => {
|
|
10
|
+
let module: TestingModule;
|
|
11
|
+
let workflow: WorkflowToolResultsWorkflow;
|
|
12
|
+
let processor: WorkflowProcessorService;
|
|
13
|
+
|
|
14
|
+
let mockCreateValue: ToolMock;
|
|
15
|
+
let mockCreateChatMessage: ToolMock;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
module = await createWorkflowTest()
|
|
19
|
+
.forWorkflow(WorkflowToolResultsWorkflow)
|
|
20
|
+
.withImports(LoopCoreModule, CoreUiModule, CreateValueToolModule, CreateChatMessageToolModule)
|
|
21
|
+
.withToolOverride(CreateValue)
|
|
22
|
+
.withToolOverride(CreateChatMessage)
|
|
23
|
+
.compile();
|
|
24
|
+
|
|
25
|
+
workflow = module.get(WorkflowToolResultsWorkflow);
|
|
26
|
+
processor = module.get(WorkflowProcessorService);
|
|
27
|
+
|
|
28
|
+
mockCreateValue = module.get(CreateValue);
|
|
29
|
+
mockCreateChatMessage = module.get(CreateChatMessage);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
await module.close();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should be defined', () => {
|
|
37
|
+
expect(workflow).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should have extractMessage helper', () => {
|
|
41
|
+
expect(workflow.helpers).toContain('extractMessage');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('tool result access', () => {
|
|
45
|
+
it('should access tool results by call id and index in same transition', async () => {
|
|
46
|
+
const context = new BlockExecutionContextDto({});
|
|
47
|
+
mockCreateValue.execute.mockResolvedValue({ data: 'Hello World.' });
|
|
48
|
+
mockCreateChatMessage.execute.mockResolvedValue({ data: undefined });
|
|
49
|
+
|
|
50
|
+
await processor.process(workflow, {}, context);
|
|
51
|
+
|
|
52
|
+
// Verify CreateValue was called
|
|
53
|
+
expect(mockCreateValue.execute).toHaveBeenCalledWith(
|
|
54
|
+
{ input: 'Hello World.' },
|
|
55
|
+
expect.anything(),
|
|
56
|
+
expect.anything(),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// Verify CreateChatMessage received resolved template values
|
|
60
|
+
expect(mockCreateChatMessage.execute).toHaveBeenCalledWith(
|
|
61
|
+
{
|
|
62
|
+
role: 'assistant',
|
|
63
|
+
content: 'Data from specific call id: Hello World.',
|
|
64
|
+
},
|
|
65
|
+
expect.anything(),
|
|
66
|
+
expect.anything(),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(mockCreateChatMessage.execute).toHaveBeenCalledWith(
|
|
70
|
+
{
|
|
71
|
+
role: 'assistant',
|
|
72
|
+
content: 'Data from first tool call: Hello World.',
|
|
73
|
+
},
|
|
74
|
+
expect.anything(),
|
|
75
|
+
expect.anything(),
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should access tool results from previous transition', async () => {
|
|
80
|
+
const context = new BlockExecutionContextDto({});
|
|
81
|
+
mockCreateValue.execute.mockResolvedValue({ data: 'Hello World.' });
|
|
82
|
+
mockCreateChatMessage.execute.mockResolvedValue({ data: undefined });
|
|
83
|
+
|
|
84
|
+
const result = await processor.process(workflow, {}, context);
|
|
85
|
+
|
|
86
|
+
// Should complete both transitions
|
|
87
|
+
const history = result.state.caretaker.getHistory();
|
|
88
|
+
expect(history.some((h) => h.metadata.transition?.transition === 'create_some_data')).toBe(true);
|
|
89
|
+
expect(history.some((h) => h.metadata.transition?.transition === 'access_data')).toBe(true);
|
|
90
|
+
|
|
91
|
+
expect(mockCreateChatMessage.execute).toHaveBeenCalledWith(
|
|
92
|
+
{
|
|
93
|
+
role: 'assistant',
|
|
94
|
+
content: 'Data from previous transition: Hello World.',
|
|
95
|
+
},
|
|
96
|
+
expect.anything(),
|
|
97
|
+
expect.anything(),
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should access tool results using custom helper', async () => {
|
|
102
|
+
const context = new BlockExecutionContextDto({});
|
|
103
|
+
mockCreateValue.execute.mockResolvedValue({ data: 'Hello World.' });
|
|
104
|
+
mockCreateChatMessage.execute.mockResolvedValue({ data: undefined });
|
|
105
|
+
|
|
106
|
+
await processor.process(workflow, {}, context);
|
|
107
|
+
|
|
108
|
+
expect(mockCreateChatMessage.execute).toHaveBeenCalledWith(
|
|
109
|
+
{
|
|
110
|
+
role: 'assistant',
|
|
111
|
+
content: 'Data access using custom helper: Hello World.',
|
|
112
|
+
},
|
|
113
|
+
expect.anything(),
|
|
114
|
+
expect.anything(),
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Jakob Klippel.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
import { Module } from '@nestjs/common';
|
|
17
|
+
import { LoopCoreModule } from '@loopstack/core';
|
|
18
|
+
import { CoreUiModule } from '@loopstack/core-ui-module';
|
|
19
|
+
import { CreateChatMessageToolModule } from '@loopstack/create-chat-message-tool';
|
|
20
|
+
import { CreateValueToolModule } from '@loopstack/create-value-tool';
|
|
21
|
+
import { WorkflowToolResultsWorkflow } from './workflow-tool-results.workflow';
|
|
22
|
+
|
|
23
|
+
@Module({
|
|
24
|
+
imports: [LoopCoreModule, CoreUiModule, CreateValueToolModule, CreateChatMessageToolModule],
|
|
25
|
+
providers: [WorkflowToolResultsWorkflow],
|
|
26
|
+
exports: [WorkflowToolResultsWorkflow],
|
|
27
|
+
})
|
|
28
|
+
export class ToolResultsExampleModule {}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { BlockConfig, Helper, Tool } from '@loopstack/common';
|
|
3
|
+
import { WorkflowBase } from '@loopstack/core';
|
|
4
|
+
import { WorkflowMetadataInterface } from '@loopstack/core/dist/workflow-processor/interfaces/workflow-metadata.interface';
|
|
5
|
+
import { CreateChatMessage } from '@loopstack/create-chat-message-tool';
|
|
6
|
+
import { CreateValue } from '@loopstack/create-value-tool';
|
|
7
|
+
|
|
8
|
+
interface WorkflowToolsMetadata {
|
|
9
|
+
create_some_data: {
|
|
10
|
+
say_hello: {
|
|
11
|
+
data: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Injectable()
|
|
17
|
+
@BlockConfig({
|
|
18
|
+
configFile: __dirname + '/workflow-tool-results.workflow.yaml',
|
|
19
|
+
})
|
|
20
|
+
export class WorkflowToolResultsWorkflow extends WorkflowBase {
|
|
21
|
+
@Tool() private createValue: CreateValue;
|
|
22
|
+
@Tool() private createChatMessage: CreateChatMessage;
|
|
23
|
+
|
|
24
|
+
@Helper()
|
|
25
|
+
extractMessage(metadata: WorkflowMetadataInterface): string {
|
|
26
|
+
const tools = metadata.tools as unknown as WorkflowToolsMetadata;
|
|
27
|
+
return tools.create_some_data.say_hello.data;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
title: 'Workflow Tool Result'
|
|
2
|
+
|
|
3
|
+
transitions:
|
|
4
|
+
- id: create_some_data
|
|
5
|
+
from: start
|
|
6
|
+
to: data_created
|
|
7
|
+
call:
|
|
8
|
+
# Create test data and return it in the tool result
|
|
9
|
+
- id: say_hello
|
|
10
|
+
tool: createValue
|
|
11
|
+
args:
|
|
12
|
+
input: 'Hello World.'
|
|
13
|
+
|
|
14
|
+
# Access the data using the call id as identifier
|
|
15
|
+
- tool: createChatMessage
|
|
16
|
+
args:
|
|
17
|
+
role: 'assistant'
|
|
18
|
+
content: 'Data from specific call id: {{ metadata.tools.create_some_data.say_hello.data }}'
|
|
19
|
+
|
|
20
|
+
# Access the data using the call index
|
|
21
|
+
- tool: createChatMessage
|
|
22
|
+
args:
|
|
23
|
+
role: 'assistant'
|
|
24
|
+
content: 'Data from first tool call: {{ metadata.tools.create_some_data.0.data }}'
|
|
25
|
+
|
|
26
|
+
- id: access_data
|
|
27
|
+
from: data_created
|
|
28
|
+
to: end
|
|
29
|
+
call:
|
|
30
|
+
# Access the data from a previous transition using transition and call id
|
|
31
|
+
- tool: createChatMessage
|
|
32
|
+
args:
|
|
33
|
+
role: 'assistant'
|
|
34
|
+
content: 'Data from previous transition: {{ metadata.tools.create_some_data.say_hello.data }}'
|
|
35
|
+
|
|
36
|
+
# Access the data using a custom helper function of the workflow
|
|
37
|
+
- tool: createChatMessage
|
|
38
|
+
args:
|
|
39
|
+
role: 'assistant'
|
|
40
|
+
content: 'Data access using custom helper: {{ extractMessage metadata }}'
|
package/LICENSE
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
Apache License
|
|
2
|
-
Version 2.0, January 2004
|
|
3
|
-
http://www.apache.org/licenses/
|
|
4
|
-
|
|
5
|
-
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
-
|
|
7
|
-
Copyright 2025 Loopstack GmbH
|
|
8
|
-
|
|
9
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
|
-
you may not use this skeleton application except in compliance with the License.
|
|
11
|
-
You may obtain a copy of the License at
|
|
12
|
-
|
|
13
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
14
|
-
|
|
15
|
-
Unless required by applicable law or agreed to in writing, software
|
|
16
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
17
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
18
|
-
See the License for the specific language governing permissions and
|
|
19
|
-
limitations under the License.
|