@loopstack/meeting-notes-example-workflow 0.21.0 → 0.21.2
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 +75 -111
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> A module for the [Loopstack AI](https://loopstack.ai) automation framework.
|
|
4
4
|
|
|
5
|
-
This module provides an example workflow demonstrating how to build human-in-the-loop AI workflows with
|
|
5
|
+
This module provides an example workflow demonstrating how to build human-in-the-loop AI workflows with interactive documents and review steps.
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
@@ -10,13 +10,13 @@ The Meeting Notes Example Workflow shows how to create workflows that pause for
|
|
|
10
10
|
|
|
11
11
|
By using this workflow as a reference, you'll learn how to:
|
|
12
12
|
|
|
13
|
-
- Use
|
|
13
|
+
- Use `wait: true` transitions to pause workflows for user input
|
|
14
14
|
- Create interactive documents with action buttons
|
|
15
|
-
- Handle transition payloads from user interactions
|
|
16
|
-
- Transform unstructured text into structured data with AI
|
|
15
|
+
- Handle transition payloads from user interactions
|
|
16
|
+
- Transform unstructured text into structured data with AI using `ClaudeGenerateDocument`
|
|
17
17
|
- Build review-and-confirm patterns for AI outputs
|
|
18
|
-
-
|
|
19
|
-
- Use
|
|
18
|
+
- Define workflow input schemas via the `@Workflow` decorator
|
|
19
|
+
- Use the document repository to save and update documents
|
|
20
20
|
|
|
21
21
|
This example is essential for developers building workflows that require human oversight or approval steps.
|
|
22
22
|
|
|
@@ -30,54 +30,48 @@ See [SETUP.md](./SETUP.md) for installation and setup instructions.
|
|
|
30
30
|
|
|
31
31
|
1. **Start** - User provides unstructured meeting notes via the input form
|
|
32
32
|
2. **Wait for Input** - User can edit the notes, then clicks "Optimize Notes"
|
|
33
|
-
3. **AI Processing** -
|
|
33
|
+
3. **AI Processing** - Claude extracts structured information into a formatted document
|
|
34
34
|
4. **Review** - User reviews and can edit the structured output
|
|
35
35
|
5. **Confirm** - User clicks "Confirm" to finalize
|
|
36
36
|
|
|
37
37
|
### Key Concepts
|
|
38
38
|
|
|
39
|
-
#### 1. Workflow Input
|
|
39
|
+
#### 1. Workflow Input Schema
|
|
40
40
|
|
|
41
|
-
Define input parameters
|
|
41
|
+
Define input parameters directly in the `@Workflow` decorator with a Zod schema:
|
|
42
42
|
|
|
43
43
|
```typescript
|
|
44
|
-
@
|
|
44
|
+
@Workflow({
|
|
45
|
+
uiConfig: __dirname + '/meeting-notes.ui.yaml',
|
|
45
46
|
schema: z.object({
|
|
46
47
|
inputText: z
|
|
47
48
|
.string()
|
|
48
49
|
.default(
|
|
49
|
-
'- meeting 1.1.2025\n- budget: need 2 cut costs sarah said\n
|
|
50
|
+
'- meeting 1.1.2025\n- budget: need 2 cut costs sarah said\n- hire new person?? --> marketing\n- vendor pricing - follow up needed by anna',
|
|
50
51
|
),
|
|
51
52
|
}),
|
|
52
53
|
})
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
};
|
|
54
|
+
export class MeetingNotesWorkflow extends BaseWorkflow<{ inputText: string }> {
|
|
55
|
+
@InjectTool() claudeGenerateDocument: ClaudeGenerateDocument;
|
|
56
56
|
|
|
57
|
-
@State({
|
|
58
|
-
schema: z.object({
|
|
59
|
-
meetingNotes: MeetingNotesDocumentSchema.optional(),
|
|
60
|
-
optimizedNotes: OptimizedMeetingNotesDocumentSchema.optional(),
|
|
61
|
-
}),
|
|
62
|
-
})
|
|
63
|
-
state: {
|
|
64
57
|
meetingNotes?: z.infer<typeof MeetingNotesDocumentSchema>;
|
|
65
58
|
optimizedNotes?: z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
|
|
66
|
-
}
|
|
59
|
+
}
|
|
67
60
|
```
|
|
68
61
|
|
|
69
|
-
#### 2.
|
|
62
|
+
#### 2. Waiting Transitions
|
|
70
63
|
|
|
71
|
-
Use `
|
|
64
|
+
Use `wait: true` with a `schema` to pause the workflow and wait for user interaction. The schema validates the payload submitted by the user:
|
|
72
65
|
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
```typescript
|
|
67
|
+
@Transition({ from: 'waiting_for_response', to: 'response_received', wait: true, schema: MeetingNotesDocumentSchema })
|
|
68
|
+
async userResponse(payload: z.infer<typeof MeetingNotesDocumentSchema>) {
|
|
69
|
+
const result = await this.repository.save(MeetingNotesDocument, payload, { id: 'input' });
|
|
70
|
+
this.meetingNotes = result.content as z.infer<typeof MeetingNotesDocumentSchema>;
|
|
71
|
+
}
|
|
78
72
|
```
|
|
79
73
|
|
|
80
|
-
The workflow pauses at `waiting_for_response` until the user
|
|
74
|
+
The workflow pauses at `waiting_for_response` until the user submits data via the document button.
|
|
81
75
|
|
|
82
76
|
#### 3. Document Actions with Buttons
|
|
83
77
|
|
|
@@ -96,38 +90,15 @@ ui:
|
|
|
96
90
|
widget: textarea
|
|
97
91
|
actions:
|
|
98
92
|
- type: button
|
|
99
|
-
transition:
|
|
93
|
+
transition: userResponse
|
|
100
94
|
label: 'Optimize Notes'
|
|
101
95
|
```
|
|
102
96
|
|
|
103
|
-
When clicked, the button triggers the `
|
|
97
|
+
When clicked, the button triggers the `userResponse` transition with the current document content as the payload.
|
|
104
98
|
|
|
105
|
-
#### 4.
|
|
99
|
+
#### 4. Custom Document Schemas
|
|
106
100
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
```yaml
|
|
110
|
-
- id: user_response
|
|
111
|
-
from: waiting_for_response
|
|
112
|
-
to: response_received
|
|
113
|
-
trigger: manual
|
|
114
|
-
call:
|
|
115
|
-
- id: create_response
|
|
116
|
-
tool: createDocument
|
|
117
|
-
args:
|
|
118
|
-
id: input
|
|
119
|
-
document: meetingNotesDocument
|
|
120
|
-
update:
|
|
121
|
-
content: ${{ runtime.transition.payload }}
|
|
122
|
-
assign:
|
|
123
|
-
meetingNotes: ${{ result.data.content }}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
The `runtime.transition.payload` contains the document content when the user clicked the button. The result is saved to workflow state via `assign`.
|
|
127
|
-
|
|
128
|
-
#### 5. Custom Document Schemas
|
|
129
|
-
|
|
130
|
-
Define document content schemas using `@Input` on the `content` property:
|
|
101
|
+
Define document content schemas using the `@Document` decorator with a Zod schema:
|
|
131
102
|
|
|
132
103
|
```typescript
|
|
133
104
|
export const MeetingNotesDocumentSchema = z.object({
|
|
@@ -135,19 +106,15 @@ export const MeetingNotesDocumentSchema = z.object({
|
|
|
135
106
|
});
|
|
136
107
|
|
|
137
108
|
@Document({
|
|
109
|
+
schema: MeetingNotesDocumentSchema,
|
|
138
110
|
uiConfig: __dirname + '/meeting-notes-document.yaml',
|
|
139
111
|
})
|
|
140
|
-
export class MeetingNotesDocument
|
|
141
|
-
|
|
142
|
-
schema: MeetingNotesDocumentSchema,
|
|
143
|
-
})
|
|
144
|
-
content: {
|
|
145
|
-
text: string;
|
|
146
|
-
};
|
|
112
|
+
export class MeetingNotesDocument {
|
|
113
|
+
text: string;
|
|
147
114
|
}
|
|
148
115
|
```
|
|
149
116
|
|
|
150
|
-
####
|
|
117
|
+
#### 5. Structured Output Documents
|
|
151
118
|
|
|
152
119
|
Define complex document schemas for structured AI output:
|
|
153
120
|
|
|
@@ -159,6 +126,18 @@ export const OptimizedMeetingNotesDocumentSchema = z.object({
|
|
|
159
126
|
decisions: z.array(z.string()),
|
|
160
127
|
actionItems: z.array(z.string()),
|
|
161
128
|
});
|
|
129
|
+
|
|
130
|
+
@Document({
|
|
131
|
+
schema: OptimizedMeetingNotesDocumentSchema,
|
|
132
|
+
uiConfig: __dirname + '/optimized-notes-document.yaml',
|
|
133
|
+
})
|
|
134
|
+
export class OptimizedNotesDocument {
|
|
135
|
+
date: string;
|
|
136
|
+
summary: string;
|
|
137
|
+
participants: string[];
|
|
138
|
+
decisions: string[];
|
|
139
|
+
actionItems: string[];
|
|
140
|
+
}
|
|
162
141
|
```
|
|
163
142
|
|
|
164
143
|
Configure the document UI with ordering, collapsible arrays, and confirm button:
|
|
@@ -170,67 +149,52 @@ ui:
|
|
|
170
149
|
widgets:
|
|
171
150
|
- widget: form
|
|
172
151
|
options:
|
|
173
|
-
order:
|
|
174
|
-
- date
|
|
175
|
-
- summary
|
|
176
|
-
- participants
|
|
177
|
-
- decisions
|
|
178
|
-
- actionItems
|
|
152
|
+
order: [date, summary, participants, decisions, actionItems]
|
|
179
153
|
properties:
|
|
180
|
-
date:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
participants:
|
|
186
|
-
title: Participants
|
|
187
|
-
collapsed: true
|
|
188
|
-
items:
|
|
189
|
-
title: Participant
|
|
190
|
-
actionItems:
|
|
191
|
-
title: Action Items
|
|
192
|
-
collapsed: true
|
|
193
|
-
items:
|
|
194
|
-
title: Action Item
|
|
154
|
+
date: { title: Date }
|
|
155
|
+
summary: { title: Summary, widget: textarea }
|
|
156
|
+
participants: { title: Participants, collapsed: true, items: { title: Participant } }
|
|
157
|
+
decisions: { title: Decisions, collapsed: true, items: { title: Decision } }
|
|
158
|
+
actionItems: { title: Action Items, collapsed: true, items: { title: Action Item } }
|
|
195
159
|
actions:
|
|
196
160
|
- type: button
|
|
197
161
|
transition: confirm
|
|
198
162
|
label: 'Confirm'
|
|
199
163
|
```
|
|
200
164
|
|
|
201
|
-
####
|
|
165
|
+
#### 6. AI Document Generation
|
|
202
166
|
|
|
203
|
-
Use `
|
|
167
|
+
Use `ClaudeGenerateDocument` to populate a structured document. Reference workflow properties for dynamic content:
|
|
204
168
|
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
169
|
+
```typescript
|
|
170
|
+
@Transition({ from: 'response_received', to: 'notes_optimized' })
|
|
171
|
+
async optimizeNotes() {
|
|
172
|
+
await this.claudeGenerateDocument.call({
|
|
173
|
+
claude: { model: 'claude-sonnet-4-6' },
|
|
174
|
+
response: { id: 'final', document: OptimizedNotesDocument },
|
|
175
|
+
prompt: `Extract all information from the provided meeting notes into the structured document.\n\n<Meeting Notes>\n${this.meetingNotes?.text}\n</Meeting Notes>`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### 7. Final Confirmation with Wait
|
|
181
|
+
|
|
182
|
+
Use `@Final` with `wait: true` to create a review step before the workflow ends:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
@Final({ from: 'notes_optimized', wait: true, schema: OptimizedMeetingNotesDocumentSchema })
|
|
186
|
+
async confirm(payload: z.infer<typeof OptimizedMeetingNotesDocumentSchema>) {
|
|
187
|
+
const result = await this.repository.save(OptimizedNotesDocument, payload, { id: 'final' });
|
|
188
|
+
this.optimizedNotes = result.content as z.infer<typeof OptimizedMeetingNotesDocumentSchema>;
|
|
189
|
+
}
|
|
225
190
|
```
|
|
226
191
|
|
|
227
192
|
## Dependencies
|
|
228
193
|
|
|
229
194
|
This workflow uses the following Loopstack modules:
|
|
230
195
|
|
|
231
|
-
- `@loopstack/
|
|
232
|
-
- `@loopstack/
|
|
233
|
-
- `@loopstack/ai-module` - Provides `AiGenerateDocument` tool
|
|
196
|
+
- `@loopstack/common` - Core framework decorators (`BaseWorkflow`, `@Workflow`, `@Initial`, `@Transition`, `@Final`, `@InjectTool`, `Document`)
|
|
197
|
+
- `@loopstack/claude-module` - Provides `ClaudeGenerateDocument` tool for AI-powered document generation
|
|
234
198
|
|
|
235
199
|
## About
|
|
236
200
|
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"summary",
|
|
10
10
|
"workflow"
|
|
11
11
|
],
|
|
12
|
-
"version": "0.21.
|
|
12
|
+
"version": "0.21.2",
|
|
13
13
|
"license": "Apache-2.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Jakob Klippel",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"watch": "nest build --watch"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@loopstack/claude-module": "^0.22.
|
|
34
|
-
"@loopstack/common": "^0.25.
|
|
35
|
-
"@loopstack/core": "^0.25.
|
|
36
|
-
"@nestjs/common": "^11.1.
|
|
33
|
+
"@loopstack/claude-module": "^0.22.1",
|
|
34
|
+
"@loopstack/common": "^0.25.1",
|
|
35
|
+
"@loopstack/core": "^0.25.1",
|
|
36
|
+
"@nestjs/common": "^11.1.19",
|
|
37
37
|
"zod": "^4.3.6"
|
|
38
38
|
},
|
|
39
39
|
"files": [
|
|
@@ -45,7 +45,12 @@
|
|
|
45
45
|
"rootDir": "src",
|
|
46
46
|
"testRegex": ".*\\.spec\\.ts$",
|
|
47
47
|
"transform": {
|
|
48
|
-
"^.+\\.ts$":
|
|
48
|
+
"^.+\\.ts$": [
|
|
49
|
+
"ts-jest",
|
|
50
|
+
{
|
|
51
|
+
"tsconfig": "tsconfig.spec.json"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
49
54
|
},
|
|
50
55
|
"testTimeout": 10000,
|
|
51
56
|
"forceExit": true,
|