@loopstack/hitl-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 +165 -0
- package/dist/hitl-examples.module.d.ts +3 -0
- package/dist/hitl-examples.module.d.ts.map +1 -0
- package/dist/hitl-examples.module.js +52 -0
- package/dist/hitl-examples.module.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/workflows/agent-ask-clarification/agent-ask-clarification-example.workflow.d.ts +9 -0
- package/dist/workflows/agent-ask-clarification/agent-ask-clarification-example.workflow.d.ts.map +1 -0
- package/dist/workflows/agent-ask-clarification/agent-ask-clarification-example.workflow.js +69 -0
- package/dist/workflows/agent-ask-clarification/agent-ask-clarification-example.workflow.js.map +1 -0
- package/dist/workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow.d.ts +9 -0
- package/dist/workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow.d.ts.map +1 -0
- package/dist/workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow.js +67 -0
- package/dist/workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow.js.map +1 -0
- package/dist/workflows/ask-user-confirm/ask-user-confirm-example.workflow.d.ts +11 -0
- package/dist/workflows/ask-user-confirm/ask-user-confirm-example.workflow.d.ts.map +1 -0
- package/dist/workflows/ask-user-confirm/ask-user-confirm-example.workflow.js +61 -0
- package/dist/workflows/ask-user-confirm/ask-user-confirm-example.workflow.js.map +1 -0
- package/dist/workflows/ask-user-options/ask-user-options-example.workflow.d.ts +11 -0
- package/dist/workflows/ask-user-options/ask-user-options-example.workflow.d.ts.map +1 -0
- package/dist/workflows/ask-user-options/ask-user-options-example.workflow.js +68 -0
- package/dist/workflows/ask-user-options/ask-user-options-example.workflow.js.map +1 -0
- package/dist/workflows/ask-user-text/ask-user-text-example.workflow.d.ts +11 -0
- package/dist/workflows/ask-user-text/ask-user-text-example.workflow.d.ts.map +1 -0
- package/dist/workflows/ask-user-text/ask-user-text-example.workflow.js +60 -0
- package/dist/workflows/ask-user-text/ask-user-text-example.workflow.js.map +1 -0
- package/dist/workflows/confirm-content/confirm-content-example.workflow.d.ts +16 -0
- package/dist/workflows/confirm-content/confirm-content-example.workflow.d.ts.map +1 -0
- package/dist/workflows/confirm-content/confirm-content-example.workflow.js +68 -0
- package/dist/workflows/confirm-content/confirm-content-example.workflow.js.map +1 -0
- package/dist/workflows/inline-form/feedback-form-document.d.ts +10 -0
- package/dist/workflows/inline-form/feedback-form-document.d.ts.map +1 -0
- package/dist/workflows/inline-form/feedback-form-document.js +27 -0
- package/dist/workflows/inline-form/feedback-form-document.js.map +1 -0
- package/dist/workflows/inline-form/feedback-form-document.yaml +13 -0
- package/dist/workflows/inline-form/inline-form-example.workflow.d.ts +11 -0
- package/dist/workflows/inline-form/inline-form-example.workflow.d.ts.map +1 -0
- package/dist/workflows/inline-form/inline-form-example.workflow.js +54 -0
- package/dist/workflows/inline-form/inline-form-example.workflow.js.map +1 -0
- package/dist/workflows/meeting-notes/documents/meeting-notes-document.d.ts +8 -0
- package/dist/workflows/meeting-notes/documents/meeting-notes-document.d.ts.map +1 -0
- package/dist/workflows/meeting-notes/documents/meeting-notes-document.js +25 -0
- package/dist/workflows/meeting-notes/documents/meeting-notes-document.js.map +1 -0
- package/dist/workflows/meeting-notes/documents/meeting-notes-document.yaml +10 -0
- package/dist/workflows/meeting-notes/documents/optimized-notes-document.d.ts +16 -0
- package/dist/workflows/meeting-notes/documents/optimized-notes-document.d.ts.map +1 -0
- package/dist/workflows/meeting-notes/documents/optimized-notes-document.js +33 -0
- package/dist/workflows/meeting-notes/documents/optimized-notes-document.js.map +1 -0
- package/dist/workflows/meeting-notes/documents/optimized-notes-document.yaml +33 -0
- package/dist/workflows/meeting-notes/meeting-notes-example.workflow.d.ts +24 -0
- package/dist/workflows/meeting-notes/meeting-notes-example.workflow.d.ts.map +1 -0
- package/dist/workflows/meeting-notes/meeting-notes-example.workflow.js +86 -0
- package/dist/workflows/meeting-notes/meeting-notes-example.workflow.js.map +1 -0
- package/dist/workflows/meeting-notes/templates/extract-notes.md +5 -0
- package/dist/workflows/prompt-input-chat/prompt-input-chat-example.workflow.d.ts +8 -0
- package/dist/workflows/prompt-input-chat/prompt-input-chat-example.workflow.d.ts.map +1 -0
- package/dist/workflows/prompt-input-chat/prompt-input-chat-example.workflow.js +56 -0
- package/dist/workflows/prompt-input-chat/prompt-input-chat-example.workflow.js.map +1 -0
- package/dist/workflows/prompt-input-chat/prompt-input-chat-example.workflow.yaml +6 -0
- package/package.json +55 -0
- package/src/hitl-examples.module.ts +40 -0
- package/src/index.ts +16 -0
- package/src/workflows/agent-ask-clarification/agent-ask-clarification-example.workflow.ts +48 -0
- package/src/workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow.ts +47 -0
- package/src/workflows/ask-user-confirm/ask-user-confirm-example.workflow.ts +40 -0
- package/src/workflows/ask-user-options/ask-user-options-example.workflow.ts +48 -0
- package/src/workflows/ask-user-text/ask-user-text-example.workflow.ts +39 -0
- package/src/workflows/confirm-content/confirm-content-example.workflow.ts +49 -0
- package/src/workflows/inline-form/feedback-form-document.ts +16 -0
- package/src/workflows/inline-form/feedback-form-document.yaml +13 -0
- package/src/workflows/inline-form/inline-form-example.workflow.ts +37 -0
- package/src/workflows/meeting-notes/documents/meeting-notes-document.ts +14 -0
- package/src/workflows/meeting-notes/documents/meeting-notes-document.yaml +10 -0
- package/src/workflows/meeting-notes/documents/optimized-notes-document.ts +22 -0
- package/src/workflows/meeting-notes/documents/optimized-notes-document.yaml +33 -0
- package/src/workflows/meeting-notes/meeting-notes-example.workflow.ts +75 -0
- package/src/workflows/meeting-notes/templates/extract-notes.md +5 -0
- package/src/workflows/prompt-input-chat/prompt-input-chat-example.workflow.ts +30 -0
- package/src/workflows/prompt-input-chat/prompt-input-chat-example.workflow.yaml +6 -0
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.PromptInputChatExampleWorkflow = void 0;
|
|
13
|
+
const zod_1 = require("zod");
|
|
14
|
+
const common_1 = require("@loopstack/common");
|
|
15
|
+
let PromptInputChatExampleWorkflow = class PromptInputChatExampleWorkflow extends common_1.BaseWorkflow {
|
|
16
|
+
async greet(_state) {
|
|
17
|
+
await this.documentStore.save(common_1.MessageDocument, {
|
|
18
|
+
role: 'assistant',
|
|
19
|
+
text: 'Hi! Send me a message and I will echo it back.',
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async userMessage(state, input) {
|
|
23
|
+
const payload = input.data;
|
|
24
|
+
await this.documentStore.save(common_1.MessageDocument, { role: 'user', text: payload });
|
|
25
|
+
await this.documentStore.save(common_1.MessageDocument, { role: 'assistant', text: `You said: ${payload}` });
|
|
26
|
+
}
|
|
27
|
+
loop(_state) { }
|
|
28
|
+
};
|
|
29
|
+
exports.PromptInputChatExampleWorkflow = PromptInputChatExampleWorkflow;
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, common_1.Transition)({ to: 'waiting_for_user' }),
|
|
32
|
+
__metadata("design:type", Function),
|
|
33
|
+
__metadata("design:paramtypes", [Object]),
|
|
34
|
+
__metadata("design:returntype", Promise)
|
|
35
|
+
], PromptInputChatExampleWorkflow.prototype, "greet", null);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, common_1.Transition)({ from: 'waiting_for_user', to: 'reply_sent', wait: true, schema: zod_1.z.string() }),
|
|
38
|
+
__metadata("design:type", Function),
|
|
39
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
40
|
+
__metadata("design:returntype", Promise)
|
|
41
|
+
], PromptInputChatExampleWorkflow.prototype, "userMessage", null);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, common_1.Transition)({ from: 'reply_sent', to: 'waiting_for_user' }),
|
|
44
|
+
__metadata("design:type", Function),
|
|
45
|
+
__metadata("design:paramtypes", [Object]),
|
|
46
|
+
__metadata("design:returntype", void 0)
|
|
47
|
+
], PromptInputChatExampleWorkflow.prototype, "loop", null);
|
|
48
|
+
exports.PromptInputChatExampleWorkflow = PromptInputChatExampleWorkflow = __decorate([
|
|
49
|
+
(0, common_1.Workflow)({
|
|
50
|
+
title: 'HITL - Prompt Input Chat Example',
|
|
51
|
+
description: 'Predefined-workflow HITL via the prompt-input widget. The workflow loops: it waits for a user ' +
|
|
52
|
+
'message, echoes back a reply, and re-enters the waiting state. No LLM and no sub-workflow involved.',
|
|
53
|
+
widget: './prompt-input-chat-example.workflow.yaml',
|
|
54
|
+
})
|
|
55
|
+
], PromptInputChatExampleWorkflow);
|
|
56
|
+
//# sourceMappingURL=prompt-input-chat-example.workflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-input-chat-example.workflow.js","sourceRoot":"","sources":["../../../src/workflows/prompt-input-chat/prompt-input-chat-example.workflow.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6BAAwB;AACxB,8CAAwF;AAUjF,IAAM,8BAA8B,GAApC,MAAM,8BAA+B,SAAQ,qBAAY;IAExD,AAAN,KAAK,CAAC,KAAK,CAAC,MAA+B;QACzC,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,wBAAe,EAAE;YAC7C,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,gDAAgD;SACvD,CAAC,CAAC;IACL,CAAC;IAGK,AAAN,KAAK,CAAC,WAAW,CAAC,KAA8B,EAAE,KAA8B;QAC9E,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC;QAC3B,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,wBAAe,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAChF,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,wBAAe,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,aAAa,OAAO,EAAE,EAAE,CAAC,CAAC;IACtG,CAAC;IAGD,IAAI,CAAC,MAA+B,IAAG,CAAC;CACzC,CAAA;AAlBY,wEAA8B;AAEnC;IADL,IAAA,mBAAU,EAAC,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC;;;;2DAMtC;AAGK;IADL,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,EAAE,CAAC;;;;iEAK1F;AAGD;IADC,IAAA,mBAAU,EAAC,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,kBAAkB,EAAE,CAAC;;;;0DACnB;yCAjB7B,8BAA8B;IAP1C,IAAA,iBAAQ,EAAC;QACR,KAAK,EAAE,kCAAkC;QACzC,WAAW,EACT,gGAAgG;YAChG,qGAAqG;QACvG,MAAM,EAAE,2CAA2C;KACpD,CAAC;GACW,8BAA8B,CAkB1C"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loopstack/hitl-examples",
|
|
3
|
+
"displayName": "Loopstack HITL Examples",
|
|
4
|
+
"description": "Workflow examples for Human-in-the-Loop patterns in Loopstack — custom Document with widget, AskUserWorkflow / ConfirmUserWorkflow sub-workflow shortcuts, LLM agent loops with ask_clarification / ask_for_approval, and a meeting-notes review flow.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"loopstack",
|
|
7
|
+
"example",
|
|
8
|
+
"hitl",
|
|
9
|
+
"human-in-the-loop",
|
|
10
|
+
"ask-user",
|
|
11
|
+
"confirm",
|
|
12
|
+
"agent",
|
|
13
|
+
"form",
|
|
14
|
+
"prompt-input",
|
|
15
|
+
"workflow"
|
|
16
|
+
],
|
|
17
|
+
"version": "0.1.1",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": {
|
|
20
|
+
"name": "Jakob Klippel",
|
|
21
|
+
"url": "https://www.linkedin.com/in/jakob-klippel/"
|
|
22
|
+
},
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"types": "dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./dist/index.js",
|
|
27
|
+
"./src/*": "./src/*"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "nest build",
|
|
31
|
+
"compile": "tsc --noEmit",
|
|
32
|
+
"format": "prettier --write .",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"test": "vitest run --passWithNoTests",
|
|
35
|
+
"watch": "nest build --watch"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@loopstack/agent": "*",
|
|
39
|
+
"@loopstack/claude-module": "*",
|
|
40
|
+
"@loopstack/common": "*",
|
|
41
|
+
"@loopstack/hitl": "*",
|
|
42
|
+
"@loopstack/llm-provider-module": "*",
|
|
43
|
+
"@nestjs/common": "^11.1.19",
|
|
44
|
+
"zod": "^4.3.6"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist",
|
|
48
|
+
"src"
|
|
49
|
+
],
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"vitest": "^4.1.6",
|
|
52
|
+
"@swc/core": "^1.15.33",
|
|
53
|
+
"unplugin-swc": "^1.5.9"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { AgentModule } from '@loopstack/agent';
|
|
3
|
+
import { ClaudeModule } from '@loopstack/claude-module';
|
|
4
|
+
import { StudioApp } from '@loopstack/common';
|
|
5
|
+
import { HitlModule } from '@loopstack/hitl';
|
|
6
|
+
import { AgentAskClarificationExampleWorkflow } from './workflows/agent-ask-clarification/agent-ask-clarification-example.workflow';
|
|
7
|
+
import { AgentAskForApprovalExampleWorkflow } from './workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow';
|
|
8
|
+
import { AskUserConfirmExampleWorkflow } from './workflows/ask-user-confirm/ask-user-confirm-example.workflow';
|
|
9
|
+
import { AskUserOptionsExampleWorkflow } from './workflows/ask-user-options/ask-user-options-example.workflow';
|
|
10
|
+
import { AskUserTextExampleWorkflow } from './workflows/ask-user-text/ask-user-text-example.workflow';
|
|
11
|
+
import { ConfirmContentExampleWorkflow } from './workflows/confirm-content/confirm-content-example.workflow';
|
|
12
|
+
import { FeedbackFormDocument } from './workflows/inline-form/feedback-form-document';
|
|
13
|
+
import { InlineFormExampleWorkflow } from './workflows/inline-form/inline-form-example.workflow';
|
|
14
|
+
import { MeetingNotesDocument } from './workflows/meeting-notes/documents/meeting-notes-document';
|
|
15
|
+
import { OptimizedNotesDocument } from './workflows/meeting-notes/documents/optimized-notes-document';
|
|
16
|
+
import { MeetingNotesExampleWorkflow } from './workflows/meeting-notes/meeting-notes-example.workflow';
|
|
17
|
+
import { PromptInputChatExampleWorkflow } from './workflows/prompt-input-chat/prompt-input-chat-example.workflow';
|
|
18
|
+
|
|
19
|
+
const WORKFLOWS = [
|
|
20
|
+
InlineFormExampleWorkflow,
|
|
21
|
+
PromptInputChatExampleWorkflow,
|
|
22
|
+
AskUserTextExampleWorkflow,
|
|
23
|
+
AskUserOptionsExampleWorkflow,
|
|
24
|
+
AskUserConfirmExampleWorkflow,
|
|
25
|
+
ConfirmContentExampleWorkflow,
|
|
26
|
+
AgentAskClarificationExampleWorkflow,
|
|
27
|
+
AgentAskForApprovalExampleWorkflow,
|
|
28
|
+
MeetingNotesExampleWorkflow,
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
@StudioApp({
|
|
32
|
+
title: 'HITL Examples',
|
|
33
|
+
workflows: WORKFLOWS,
|
|
34
|
+
})
|
|
35
|
+
@Module({
|
|
36
|
+
imports: [HitlModule, AgentModule, ClaudeModule],
|
|
37
|
+
providers: [FeedbackFormDocument, MeetingNotesDocument, OptimizedNotesDocument, ...WORKFLOWS],
|
|
38
|
+
exports: WORKFLOWS,
|
|
39
|
+
})
|
|
40
|
+
export class HitlExamplesModule {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './hitl-examples.module';
|
|
2
|
+
|
|
3
|
+
export * from './workflows/inline-form/inline-form-example.workflow';
|
|
4
|
+
export * from './workflows/inline-form/feedback-form-document';
|
|
5
|
+
|
|
6
|
+
export * from './workflows/prompt-input-chat/prompt-input-chat-example.workflow';
|
|
7
|
+
|
|
8
|
+
export * from './workflows/ask-user-text/ask-user-text-example.workflow';
|
|
9
|
+
export * from './workflows/ask-user-options/ask-user-options-example.workflow';
|
|
10
|
+
export * from './workflows/ask-user-confirm/ask-user-confirm-example.workflow';
|
|
11
|
+
export * from './workflows/confirm-content/confirm-content-example.workflow';
|
|
12
|
+
|
|
13
|
+
export * from './workflows/agent-ask-clarification/agent-ask-clarification-example.workflow';
|
|
14
|
+
export * from './workflows/agent-ask-for-approval/agent-ask-for-approval-example.workflow';
|
|
15
|
+
|
|
16
|
+
export * from './workflows/meeting-notes/meeting-notes-example.workflow';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type AgentResult, AgentResultSchema, AgentWorkflow } from '@loopstack/agent';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
const SYSTEM_PROMPT = `You are a trip-planning assistant.
|
|
5
|
+
|
|
6
|
+
CRITICAL RULES — your response MUST follow these:
|
|
7
|
+
- Before recommending a destination, you MUST know BOTH the user's budget AND their preferred climate.
|
|
8
|
+
- If EITHER is missing from the conversation, your response MUST be exactly ONE tool call to "ask_clarification".
|
|
9
|
+
- Do NOT guess. Do NOT recommend a destination based on partial info.
|
|
10
|
+
- Only after you have received BOTH pieces of information, recommend a single destination as a short paragraph.`;
|
|
11
|
+
|
|
12
|
+
@Workflow({
|
|
13
|
+
title: 'HITL - Agent Ask Clarification Example',
|
|
14
|
+
description:
|
|
15
|
+
'Agent HITL: an LLM agent runs with the ask_clarification tool. When it lacks information, ' +
|
|
16
|
+
'it pauses the loop and prompts the user; the answer flows back into the agent context.',
|
|
17
|
+
})
|
|
18
|
+
export class AgentAskClarificationExampleWorkflow extends BaseWorkflow {
|
|
19
|
+
constructor(private readonly agentWorkflow: AgentWorkflow) {
|
|
20
|
+
super();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Transition({ to: 'running' })
|
|
24
|
+
async start(_state: Record<string, unknown>) {
|
|
25
|
+
await this.agentWorkflow.run(
|
|
26
|
+
{
|
|
27
|
+
system: SYSTEM_PROMPT,
|
|
28
|
+
tools: ['ask_clarification'],
|
|
29
|
+
userMessage: 'Where should I go on holiday next month?',
|
|
30
|
+
},
|
|
31
|
+
{ callback: { transition: 'agentComplete' }, show: 'inline', label: 'Trip planner working...' },
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Transition({
|
|
36
|
+
from: 'running',
|
|
37
|
+
to: 'end',
|
|
38
|
+
wait: true,
|
|
39
|
+
schema: AgentResultSchema,
|
|
40
|
+
})
|
|
41
|
+
async agentComplete(_state: Record<string, unknown>, input: TransitionInput<AgentResult>) {
|
|
42
|
+
await this.documentStore.save(MessageDocument, {
|
|
43
|
+
role: 'assistant',
|
|
44
|
+
text: input.data.response,
|
|
45
|
+
});
|
|
46
|
+
this.setResult(input.data);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type AgentResult, AgentResultSchema, AgentWorkflow } from '@loopstack/agent';
|
|
2
|
+
import { BaseWorkflow, MarkdownDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
const SYSTEM_PROMPT = `You are a release-notes drafting assistant.
|
|
5
|
+
|
|
6
|
+
Your task: produce a concise release-notes markdown draft, get explicit user approval via the "ask_for_approval" tool, then output the approved markdown as your final response.
|
|
7
|
+
|
|
8
|
+
CRITICAL RULES — your response MUST follow these:
|
|
9
|
+
- First turn: respond with exactly ONE tool call to "ask_for_approval". The "concept" argument IS the markdown draft. Do NOT write the draft as plain text in this turn.
|
|
10
|
+
- After approval: your final text response MUST be the approved markdown verbatim, with no preamble, commentary, or trailing text.
|
|
11
|
+
- After denial: respond with a brief one-line note that the draft was rejected.`;
|
|
12
|
+
|
|
13
|
+
@Workflow({
|
|
14
|
+
title: 'HITL - Agent Ask For Approval Example',
|
|
15
|
+
description:
|
|
16
|
+
'Agent HITL: an LLM agent drafts content, then calls the ask_for_approval tool to show the markdown ' +
|
|
17
|
+
'to the user and pause until they confirm or deny.',
|
|
18
|
+
})
|
|
19
|
+
export class AgentAskForApprovalExampleWorkflow extends BaseWorkflow {
|
|
20
|
+
constructor(private readonly agentWorkflow: AgentWorkflow) {
|
|
21
|
+
super();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Transition({ to: 'running' })
|
|
25
|
+
async start(_state: Record<string, unknown>) {
|
|
26
|
+
await this.agentWorkflow.run(
|
|
27
|
+
{
|
|
28
|
+
system: SYSTEM_PROMPT,
|
|
29
|
+
tools: ['ask_for_approval'],
|
|
30
|
+
userMessage:
|
|
31
|
+
'Draft release notes for v1.2.3: added webhook signature verification; fixed a date-parsing bug in the importer.',
|
|
32
|
+
},
|
|
33
|
+
{ callback: { transition: 'agentComplete' }, show: 'inline', label: 'Drafting release notes...' },
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Transition({
|
|
38
|
+
from: 'running',
|
|
39
|
+
to: 'end',
|
|
40
|
+
wait: true,
|
|
41
|
+
schema: AgentResultSchema,
|
|
42
|
+
})
|
|
43
|
+
async agentComplete(_state: Record<string, unknown>, input: TransitionInput<AgentResult>) {
|
|
44
|
+
await this.documentStore.save(MarkdownDocument, { markdown: input.data.response });
|
|
45
|
+
this.setResult(input.data);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
import { AskUserWorkflow } from '@loopstack/hitl';
|
|
4
|
+
|
|
5
|
+
const AnswerSchema = z.object({ answer: z.string() });
|
|
6
|
+
|
|
7
|
+
@Workflow({
|
|
8
|
+
title: 'HITL - Ask User Confirm Example',
|
|
9
|
+
description:
|
|
10
|
+
'Shortcut HITL: delegate a yes/no decision to AskUserWorkflow with mode "confirm". ' +
|
|
11
|
+
'The answer comes back as the literal string "yes" or "no".',
|
|
12
|
+
})
|
|
13
|
+
export class AskUserConfirmExampleWorkflow extends BaseWorkflow {
|
|
14
|
+
constructor(private readonly askUserWorkflow: AskUserWorkflow) {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Transition({ to: 'waiting_for_yes_no' })
|
|
19
|
+
async askToSend(_state: Record<string, unknown>) {
|
|
20
|
+
await this.askUserWorkflow.run(
|
|
21
|
+
{ question: 'Send the email now?', mode: 'confirm' },
|
|
22
|
+
{ callback: { transition: 'decisionReceived' }, show: 'inline', label: 'Waiting for yes/no...' },
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Transition({
|
|
27
|
+
from: 'waiting_for_yes_no',
|
|
28
|
+
to: 'end',
|
|
29
|
+
wait: true,
|
|
30
|
+
schema: AnswerSchema,
|
|
31
|
+
})
|
|
32
|
+
async decisionReceived(state: Record<string, unknown>, input: TransitionInput<{ answer: string }>) {
|
|
33
|
+
const accepted = input.data.answer === 'yes';
|
|
34
|
+
await this.documentStore.save(MessageDocument, {
|
|
35
|
+
role: 'assistant',
|
|
36
|
+
text: accepted ? 'Sending the email now.' : 'Skipping — email was not sent.',
|
|
37
|
+
});
|
|
38
|
+
this.setResult({ sent: accepted } as unknown as Record<string, unknown>);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
import { AskUserWorkflow } from '@loopstack/hitl';
|
|
4
|
+
|
|
5
|
+
const AnswerSchema = z.object({ answer: z.string() });
|
|
6
|
+
|
|
7
|
+
const ENV_OPTIONS = ['staging', 'production'];
|
|
8
|
+
|
|
9
|
+
@Workflow({
|
|
10
|
+
title: 'HITL - Ask User Options Example',
|
|
11
|
+
description:
|
|
12
|
+
'Shortcut HITL: delegate a generic "pick from a list" ask to AskUserWorkflow with mode "options". ' +
|
|
13
|
+
'allowCustomAnswer adds a free-text field next to the choices.',
|
|
14
|
+
})
|
|
15
|
+
export class AskUserOptionsExampleWorkflow extends BaseWorkflow {
|
|
16
|
+
constructor(private readonly askUserWorkflow: AskUserWorkflow) {
|
|
17
|
+
super();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Transition({ to: 'waiting_for_choice' })
|
|
21
|
+
async askEnvironment(_state: Record<string, unknown>) {
|
|
22
|
+
await this.askUserWorkflow.run(
|
|
23
|
+
{
|
|
24
|
+
question: 'Which environment should we deploy to?',
|
|
25
|
+
mode: 'options',
|
|
26
|
+
options: ENV_OPTIONS,
|
|
27
|
+
allowCustomAnswer: true,
|
|
28
|
+
},
|
|
29
|
+
{ callback: { transition: 'choiceReceived' }, show: 'inline', label: 'Waiting for choice...' },
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Transition({
|
|
34
|
+
from: 'waiting_for_choice',
|
|
35
|
+
to: 'end',
|
|
36
|
+
wait: true,
|
|
37
|
+
schema: AnswerSchema,
|
|
38
|
+
})
|
|
39
|
+
async choiceReceived(state: Record<string, unknown>, input: TransitionInput<{ answer: string }>) {
|
|
40
|
+
const choice = input.data.answer;
|
|
41
|
+
const isCustom = !ENV_OPTIONS.includes(choice);
|
|
42
|
+
await this.documentStore.save(MessageDocument, {
|
|
43
|
+
role: 'assistant',
|
|
44
|
+
text: isCustom ? `Custom environment selected: ${choice}` : `Deploying to ${choice}.`,
|
|
45
|
+
});
|
|
46
|
+
this.setResult({ environment: choice, custom: isCustom } as unknown as Record<string, unknown>);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
import { AskUserWorkflow } from '@loopstack/hitl';
|
|
4
|
+
|
|
5
|
+
const AnswerSchema = z.object({ answer: z.string() });
|
|
6
|
+
|
|
7
|
+
@Workflow({
|
|
8
|
+
title: 'HITL - Ask User Text Example',
|
|
9
|
+
description:
|
|
10
|
+
'Shortcut HITL: delegate a generic free-text ask to AskUserWorkflow. The parent only owns the ' +
|
|
11
|
+
'callback transition. Use the inline-form pattern instead when you need a structured form.',
|
|
12
|
+
})
|
|
13
|
+
export class AskUserTextExampleWorkflow extends BaseWorkflow {
|
|
14
|
+
constructor(private readonly askUserWorkflow: AskUserWorkflow) {
|
|
15
|
+
super();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Transition({ to: 'waiting_for_answer' })
|
|
19
|
+
async askName(_state: Record<string, unknown>) {
|
|
20
|
+
await this.askUserWorkflow.run(
|
|
21
|
+
{ question: 'What is your name?' },
|
|
22
|
+
{ callback: { transition: 'answerReceived' }, show: 'inline', label: 'Waiting for answer...' },
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Transition({
|
|
27
|
+
from: 'waiting_for_answer',
|
|
28
|
+
to: 'end',
|
|
29
|
+
wait: true,
|
|
30
|
+
schema: AnswerSchema,
|
|
31
|
+
})
|
|
32
|
+
async answerReceived(state: Record<string, unknown>, input: TransitionInput<{ answer: string }>) {
|
|
33
|
+
await this.documentStore.save(MessageDocument, {
|
|
34
|
+
role: 'assistant',
|
|
35
|
+
text: `Hello, ${input.data.answer}!`,
|
|
36
|
+
});
|
|
37
|
+
this.setResult({ name: input.data.answer } as unknown as Record<string, unknown>);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
|
|
3
|
+
import { ConfirmUserWorkflow } from '@loopstack/hitl';
|
|
4
|
+
|
|
5
|
+
const ConfirmSchema = z.object({ confirmed: z.boolean(), markdown: z.string() });
|
|
6
|
+
type ConfirmData = z.infer<typeof ConfirmSchema>;
|
|
7
|
+
|
|
8
|
+
const DEPLOY_SUMMARY = `## Ready to deploy?
|
|
9
|
+
|
|
10
|
+
We're about to deploy **v1.2.3** to production.
|
|
11
|
+
|
|
12
|
+
- 3 commits since last release
|
|
13
|
+
- Smoke tests passing
|
|
14
|
+
|
|
15
|
+
Proceed?`;
|
|
16
|
+
|
|
17
|
+
@Workflow({
|
|
18
|
+
title: 'HITL - Confirm Content Example',
|
|
19
|
+
description:
|
|
20
|
+
'Shortcut HITL: hand a pre-rendered markdown blob to ConfirmUserWorkflow for a user-facing review/confirm. ' +
|
|
21
|
+
'Useful for showing a release plan, a summary, or a code-diff for explicit approval.',
|
|
22
|
+
})
|
|
23
|
+
export class ConfirmContentExampleWorkflow extends BaseWorkflow {
|
|
24
|
+
constructor(private readonly confirmUserWorkflow: ConfirmUserWorkflow) {
|
|
25
|
+
super();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Transition({ to: 'waiting_for_confirmation' })
|
|
29
|
+
async showSummary(_state: Record<string, unknown>) {
|
|
30
|
+
await this.confirmUserWorkflow.run(
|
|
31
|
+
{ markdown: DEPLOY_SUMMARY },
|
|
32
|
+
{ callback: { transition: 'decisionReceived' }, show: 'inline', label: 'Waiting for confirmation...' },
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Transition({
|
|
37
|
+
from: 'waiting_for_confirmation',
|
|
38
|
+
to: 'end',
|
|
39
|
+
wait: true,
|
|
40
|
+
schema: ConfirmSchema,
|
|
41
|
+
})
|
|
42
|
+
async decisionReceived(state: Record<string, unknown>, input: TransitionInput<ConfirmData>) {
|
|
43
|
+
await this.documentStore.save(MessageDocument, {
|
|
44
|
+
role: 'assistant',
|
|
45
|
+
text: input.data.confirmed ? 'User confirmed — proceeding with deploy.' : 'User denied — aborting deploy.',
|
|
46
|
+
});
|
|
47
|
+
this.setResult({ confirmed: input.data.confirmed } as unknown as Record<string, unknown>);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Document } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
export const FeedbackFormDocumentSchema = z.object({
|
|
5
|
+
rating: z.number().min(1).max(5),
|
|
6
|
+
comment: z.string(),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
@Document({
|
|
10
|
+
schema: FeedbackFormDocumentSchema,
|
|
11
|
+
widget: './feedback-form-document.yaml',
|
|
12
|
+
})
|
|
13
|
+
export class FeedbackFormDocument {
|
|
14
|
+
rating: number;
|
|
15
|
+
comment: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { BaseWorkflow, MessageDocument, Transition, Workflow } from '@loopstack/common';
|
|
3
|
+
import type { TransitionInput } from '@loopstack/common';
|
|
4
|
+
import { FeedbackFormDocument, FeedbackFormDocumentSchema } from './feedback-form-document.js';
|
|
5
|
+
|
|
6
|
+
type FeedbackPayload = z.infer<typeof FeedbackFormDocumentSchema>;
|
|
7
|
+
|
|
8
|
+
@Workflow({
|
|
9
|
+
title: 'HITL - Inline Form Example',
|
|
10
|
+
description:
|
|
11
|
+
'Predefined-workflow HITL: the workflow owns a custom Document with a form widget. ' +
|
|
12
|
+
'The form button triggers a wait:true transition on the same workflow with the form payload as its schema.',
|
|
13
|
+
})
|
|
14
|
+
export class InlineFormExampleWorkflow extends BaseWorkflow {
|
|
15
|
+
@Transition({ to: 'waiting_for_feedback' })
|
|
16
|
+
async showForm(_state: Record<string, unknown>) {
|
|
17
|
+
await this.documentStore.save(FeedbackFormDocument, { rating: 3, comment: '' }, { key: 'feedback' });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Transition({
|
|
21
|
+
from: 'waiting_for_feedback',
|
|
22
|
+
to: 'end',
|
|
23
|
+
wait: true,
|
|
24
|
+
schema: FeedbackFormDocumentSchema,
|
|
25
|
+
})
|
|
26
|
+
async submitFeedback(state: Record<string, unknown>, input: TransitionInput<FeedbackPayload>) {
|
|
27
|
+
const payload = input.data;
|
|
28
|
+
await this.documentStore.save(FeedbackFormDocument, payload, { key: 'feedback' });
|
|
29
|
+
|
|
30
|
+
await this.documentStore.save(MessageDocument, {
|
|
31
|
+
role: 'assistant',
|
|
32
|
+
text: `Thanks for your feedback! Rating: ${payload.rating}/5. Comment: ${payload.comment}`,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.setResult({ feedback: payload } as unknown as Record<string, unknown>);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Document } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
export const MeetingNotesDocumentSchema = z.object({
|
|
5
|
+
text: z.string(),
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
@Document({
|
|
9
|
+
schema: MeetingNotesDocumentSchema,
|
|
10
|
+
widget: './meeting-notes-document.yaml',
|
|
11
|
+
})
|
|
12
|
+
export class MeetingNotesDocument {
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Document } from '@loopstack/common';
|
|
3
|
+
|
|
4
|
+
export const OptimizedMeetingNotesDocumentSchema = z.object({
|
|
5
|
+
date: z.string(),
|
|
6
|
+
summary: z.string(),
|
|
7
|
+
participants: z.array(z.string()),
|
|
8
|
+
decisions: z.array(z.string()),
|
|
9
|
+
actionItems: z.array(z.string()),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
@Document({
|
|
13
|
+
schema: OptimizedMeetingNotesDocumentSchema,
|
|
14
|
+
widget: './optimized-notes-document.yaml',
|
|
15
|
+
})
|
|
16
|
+
export class OptimizedNotesDocument {
|
|
17
|
+
date: string;
|
|
18
|
+
summary: string;
|
|
19
|
+
participants: string[];
|
|
20
|
+
decisions: string[];
|
|
21
|
+
actionItems: string[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
widget: form
|
|
2
|
+
options:
|
|
3
|
+
order:
|
|
4
|
+
- date
|
|
5
|
+
- summary
|
|
6
|
+
- participants
|
|
7
|
+
- decisions
|
|
8
|
+
- actionItems
|
|
9
|
+
properties:
|
|
10
|
+
date:
|
|
11
|
+
title: Date
|
|
12
|
+
summary:
|
|
13
|
+
title: Summary
|
|
14
|
+
widget: textarea
|
|
15
|
+
participants:
|
|
16
|
+
title: Participants
|
|
17
|
+
collapsed: true
|
|
18
|
+
items:
|
|
19
|
+
title: Participant
|
|
20
|
+
decisions:
|
|
21
|
+
title: Decisions
|
|
22
|
+
collapsed: true
|
|
23
|
+
items:
|
|
24
|
+
title: Decision
|
|
25
|
+
actionItems:
|
|
26
|
+
title: Action Items
|
|
27
|
+
collapsed: true
|
|
28
|
+
items:
|
|
29
|
+
title: Action Item
|
|
30
|
+
actions:
|
|
31
|
+
- type: button
|
|
32
|
+
transition: confirm
|
|
33
|
+
label: 'Confirm'
|