@loopman/langchain-sdk 1.0.9 → 1.8.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 +72 -5
- package/dist/agents/loopman-agent.d.ts.map +1 -1
- package/dist/agents/loopman-agent.js +51 -92
- package/dist/agents/loopman-agent.js.map +1 -1
- package/dist/client/loopman-api.d.ts +19 -64
- package/dist/client/loopman-api.d.ts.map +1 -1
- package/dist/client/loopman-api.js +85 -248
- package/dist/client/loopman-api.js.map +1 -1
- package/dist/helpers/prompt-orchestrator.js +17 -17
- package/dist/helpers/prompt-orchestrator.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/langgraph/helpers.d.ts +171 -0
- package/dist/langgraph/helpers.d.ts.map +1 -0
- package/dist/langgraph/helpers.js +216 -0
- package/dist/langgraph/helpers.js.map +1 -0
- package/dist/langgraph/index.d.ts +16 -0
- package/dist/langgraph/index.d.ts.map +1 -0
- package/dist/langgraph/index.js +17 -0
- package/dist/langgraph/index.js.map +1 -0
- package/dist/langgraph/loopman-conditional-edge.d.ts +58 -0
- package/dist/langgraph/loopman-conditional-edge.d.ts.map +1 -0
- package/dist/langgraph/loopman-conditional-edge.js +77 -0
- package/dist/langgraph/loopman-conditional-edge.js.map +1 -0
- package/dist/langgraph/loopman-context-node.d.ts +74 -0
- package/dist/langgraph/loopman-context-node.d.ts.map +1 -0
- package/dist/langgraph/loopman-context-node.js +131 -0
- package/dist/langgraph/loopman-context-node.js.map +1 -0
- package/dist/langgraph/loopman-validation-node.d.ts +89 -0
- package/dist/langgraph/loopman-validation-node.d.ts.map +1 -0
- package/dist/langgraph/loopman-validation-node.js +297 -0
- package/dist/langgraph/loopman-validation-node.js.map +1 -0
- package/dist/langgraph/types.d.ts +75 -0
- package/dist/langgraph/types.d.ts.map +1 -0
- package/dist/langgraph/types.js +74 -0
- package/dist/langgraph/types.js.map +1 -0
- package/dist/loopman-agent-wrapper.d.ts +18 -0
- package/dist/loopman-agent-wrapper.d.ts.map +1 -1
- package/dist/loopman-agent-wrapper.js +31 -21
- package/dist/loopman-agent-wrapper.js.map +1 -1
- package/dist/loopman-middleware.d.ts +0 -5
- package/dist/loopman-middleware.d.ts.map +1 -1
- package/dist/loopman-middleware.js +22 -23
- package/dist/loopman-middleware.js.map +1 -1
- package/dist/mcp/tool-registry.d.ts +7 -1
- package/dist/mcp/tool-registry.d.ts.map +1 -1
- package/dist/mcp/tool-registry.js +23 -16
- package/dist/mcp/tool-registry.js.map +1 -1
- package/dist/services/logger.service.d.ts.map +1 -1
- package/dist/services/logger.service.js +4 -12
- package/dist/services/logger.service.js.map +1 -1
- package/dist/services/loopman.service.d.ts +15 -10
- package/dist/services/loopman.service.d.ts.map +1 -1
- package/dist/services/loopman.service.js +49 -37
- package/dist/services/loopman.service.js.map +1 -1
- package/dist/services/polling.service.d.ts +7 -3
- package/dist/services/polling.service.d.ts.map +1 -1
- package/dist/services/polling.service.js +61 -47
- package/dist/services/polling.service.js.map +1 -1
- package/dist/types.d.ts +9 -35
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -4
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopman Context Enrichment Node for LangGraph
|
|
3
|
+
*
|
|
4
|
+
* This node fetches validation guidelines and historical decision context
|
|
5
|
+
* from Loopman and adds them to the graph state.
|
|
6
|
+
*/
|
|
7
|
+
import { LoopmanService } from "../services/loopman.service";
|
|
8
|
+
/**
|
|
9
|
+
* Factory function to create a Loopman context enrichment node
|
|
10
|
+
*
|
|
11
|
+
* This node fetches validation guidelines and historical decision context
|
|
12
|
+
* from Loopman and adds them to the graph state. This enriched context
|
|
13
|
+
* can then be used by agents to make better-informed decisions.
|
|
14
|
+
*
|
|
15
|
+
* **Works with any state**: This node only adds `guidelines` and `decisionContext`
|
|
16
|
+
* to your state. It doesn't require `LoopmanGraphState` - you can use your own
|
|
17
|
+
* custom state with any fields you want!
|
|
18
|
+
*
|
|
19
|
+
* @example With LoopmanGraphState
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createLoopmanContextNode, LoopmanGraphState } from "loopman-langchain-sdk";
|
|
22
|
+
*
|
|
23
|
+
* const contextNode = createLoopmanContextNode({
|
|
24
|
+
* apiKey: process.env.LOOPMAN_API_KEY!,
|
|
25
|
+
* workflowId: "my-workflow",
|
|
26
|
+
* category: "email", // Optional category filter
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const workflow = new StateGraph(LoopmanGraphState)
|
|
30
|
+
* .addNode("load_context", contextNode)
|
|
31
|
+
* .addNode("agent", agentNode)
|
|
32
|
+
* .addEdge(START, "load_context")
|
|
33
|
+
* .addEdge("load_context", "agent");
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @example With custom state
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const MyCustomState = Annotation.Root({
|
|
39
|
+
* messages: Annotation<BaseMessage[]>({ ... }),
|
|
40
|
+
* myCustomField: Annotation<string>({ ... }),
|
|
41
|
+
* guidelines: Annotation<LoopmanGuideline[]>({ ... }), // Add Loopman fields
|
|
42
|
+
* decisionContext: Annotation<LoopmanDecision[]>({ ... }), // to your state
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* const contextNode = createLoopmanContextNode({
|
|
46
|
+
* apiKey: process.env.LOOPMAN_API_KEY!,
|
|
47
|
+
* workflowId: "my-workflow",
|
|
48
|
+
* });
|
|
49
|
+
*
|
|
50
|
+
* const workflow = new StateGraph(MyCustomState)
|
|
51
|
+
* .addNode("load_context", contextNode) // Works with custom state!
|
|
52
|
+
* .addNode("agent", agentNode);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function createLoopmanContextNode(config) {
|
|
56
|
+
const loopmanService = new LoopmanService({
|
|
57
|
+
apiKey: config.apiKey,
|
|
58
|
+
workflowId: config.workflowId,
|
|
59
|
+
executionId: config.executionId || `exec-${Date.now()}`,
|
|
60
|
+
debug: config.debug,
|
|
61
|
+
});
|
|
62
|
+
const includeGuidelines = config.includeGuidelines ?? true;
|
|
63
|
+
const includeDecisionHistory = config.includeDecisionHistory ?? true;
|
|
64
|
+
const debug = config.debug ?? false;
|
|
65
|
+
const logger = loopmanService.logger;
|
|
66
|
+
return async (state) => {
|
|
67
|
+
logger.debug("[LoopmanContextNode] Loading context...");
|
|
68
|
+
const updates = {};
|
|
69
|
+
// Fetch guidelines if requested
|
|
70
|
+
if (includeGuidelines) {
|
|
71
|
+
try {
|
|
72
|
+
logger.debug(`[LoopmanContextNode] Fetching guidelines (category: ${config.category || "all"})`);
|
|
73
|
+
const response = await loopmanService.getGuidelines(config.category || undefined);
|
|
74
|
+
// Convert API response to graph state format (lowercase severity)
|
|
75
|
+
updates.guidelines = response.guidelines.map((g) => ({
|
|
76
|
+
ruleId: g.ruleId,
|
|
77
|
+
description: g.description,
|
|
78
|
+
category: g.category,
|
|
79
|
+
severity: g.severity.toLowerCase(),
|
|
80
|
+
}));
|
|
81
|
+
logger.debug(`[LoopmanContextNode] Loaded ${response.guidelines.length} guideline(s)`, {
|
|
82
|
+
severities: response.guidelines.length > 0
|
|
83
|
+
? response.guidelines.map((g) => g.severity)
|
|
84
|
+
: [],
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (debug) {
|
|
89
|
+
logger.error(`[LoopmanContextNode] Failed to fetch guidelines: ${error.message}`);
|
|
90
|
+
}
|
|
91
|
+
// Continue execution with empty guidelines
|
|
92
|
+
updates.guidelines = [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Fetch decision context if requested
|
|
96
|
+
// Note: Decision context requires a taskId to fetch decisions from that task
|
|
97
|
+
// If you need decision history, pass the taskId in the state
|
|
98
|
+
if (includeDecisionHistory) {
|
|
99
|
+
try {
|
|
100
|
+
logger.debug(`[LoopmanContextNode] Decision history requested (executionId: ${config.executionId || "auto-generated"})`);
|
|
101
|
+
// Check if state contains a taskId to fetch decisions from
|
|
102
|
+
const taskId = state.taskId;
|
|
103
|
+
if (taskId) {
|
|
104
|
+
logger.debug(`[LoopmanContextNode] Fetching task ${taskId} to get decisions`);
|
|
105
|
+
const task = await loopmanService.getTask(taskId);
|
|
106
|
+
const response = loopmanService.getDecisionContextFromTask(task);
|
|
107
|
+
updates.decisionContext = response.decisions;
|
|
108
|
+
logger.debug(`[LoopmanContextNode] Loaded ${response.decisions.length} decision(s) from task ${taskId}`, {
|
|
109
|
+
latest: response.decisions.length > 0
|
|
110
|
+
? response.decisions[response.decisions.length - 1]
|
|
111
|
+
: null,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
logger.debug("[LoopmanContextNode] No taskId in state - skipping decision history");
|
|
116
|
+
updates.decisionContext = [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (debug) {
|
|
121
|
+
logger.error(`[LoopmanContextNode] Failed to fetch decision context: ${error.message}`);
|
|
122
|
+
}
|
|
123
|
+
// Continue execution with empty decision context
|
|
124
|
+
updates.decisionContext = [];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
logger.debug("[LoopmanContextNode] Context loaded successfully");
|
|
128
|
+
return updates;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=loopman-context-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopman-context-node.js","sourceRoot":"","sources":["../../src/langgraph/loopman-context-node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAuB7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAgC;IACvE,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC;QACxC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,QAAQ,IAAI,CAAC,GAAG,EAAE,EAAE;QACvD,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CAAC;IAEH,MAAM,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,IAAI,IAAI,CAAC;IAC3D,MAAM,sBAAsB,GAAG,MAAM,CAAC,sBAAsB,IAAI,IAAI,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC;IACpC,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC;IAErC,OAAO,KAAK,EAAE,KAAU,EAAgB,EAAE;QACxC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAExD,MAAM,OAAO,GAAQ,EAAE,CAAC;QAExB,gCAAgC;QAChC,IAAI,iBAAiB,EAAE,CAAC;YACtB,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,CACV,uDACE,MAAM,CAAC,QAAQ,IAAI,KACrB,GAAG,CACJ,CAAC;gBAEF,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,aAAa,CACjD,MAAM,CAAC,QAAQ,IAAI,SAAS,CAC7B,CAAC;gBAEF,kEAAkE;gBAClE,OAAO,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;oBACnD,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,WAAW,EAAE,CAAC,CAAC,WAAW;oBAC1B,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAoC;iBACrE,CAAC,CAAC,CAAC;gBAEJ,MAAM,CAAC,KAAK,CACV,+BAA+B,QAAQ,CAAC,UAAU,CAAC,MAAM,eAAe,EACxE;oBACE,UAAU,EACR,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;wBAC5B,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;wBAC5C,CAAC,CAAC,EAAE;iBACT,CACF,CAAC;YACJ,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,KAAK,CACV,oDAAoD,KAAK,CAAC,OAAO,EAAE,CACpE,CAAC;gBACJ,CAAC;gBACD,2CAA2C;gBAC3C,OAAO,CAAC,UAAU,GAAG,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,6EAA6E;QAC7E,6DAA6D;QAC7D,IAAI,sBAAsB,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,CACV,iEACE,MAAM,CAAC,WAAW,IAAI,gBACxB,GAAG,CACJ,CAAC;gBAEF,2DAA2D;gBAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;gBAE5B,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,CAAC,KAAK,CACV,sCAAsC,MAAM,mBAAmB,CAChE,CAAC;oBAEF,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;oBAClD,MAAM,QAAQ,GAAG,cAAc,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC;oBAEjE,OAAO,CAAC,eAAe,GAAG,QAAQ,CAAC,SAA8B,CAAC;oBAElE,MAAM,CAAC,KAAK,CACV,+BAA+B,QAAQ,CAAC,SAAS,CAAC,MAAM,0BAA0B,MAAM,EAAE,EAC1F;wBACE,MAAM,EACJ,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;4BAC3B,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;4BACnD,CAAC,CAAC,IAAI;qBACX,CACF,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,KAAK,CACV,qEAAqE,CACtE,CAAC;oBACF,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC;gBAC/B,CAAC;YACH,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,KAAK,CACV,0DAA0D,KAAK,CAAC,OAAO,EAAE,CAC1E,CAAC;gBACJ,CAAC;gBACD,iDAAiD;gBACjD,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC;YAC/B,CAAC;QACH,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;QAEjE,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopman Validation Node for LangGraph
|
|
3
|
+
*
|
|
4
|
+
* This node handles human-in-the-loop validation by:
|
|
5
|
+
* 1. Extracting tool calls from agent messages
|
|
6
|
+
* 2. Creating validation tasks in Loopman
|
|
7
|
+
* 3. Polling for human decisions
|
|
8
|
+
* 4. Enriching state with guidelines and decision context
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Configuration for Loopman validation node
|
|
12
|
+
*/
|
|
13
|
+
export interface LoopmanValidationNodeConfig {
|
|
14
|
+
/** Loopman API key for authentication */
|
|
15
|
+
apiKey: string;
|
|
16
|
+
/** Workflow identifier */
|
|
17
|
+
workflowId: string;
|
|
18
|
+
/** Execution identifier (auto-generated if not provided) */
|
|
19
|
+
executionId?: string;
|
|
20
|
+
/** Category to filter guidelines (optional) */
|
|
21
|
+
category?: string;
|
|
22
|
+
/** Polling timeout in milliseconds (default: 5 minutes) */
|
|
23
|
+
timeout?: number;
|
|
24
|
+
/** Polling interval in milliseconds (default: 5 seconds) */
|
|
25
|
+
pollingInterval?: number;
|
|
26
|
+
/** Enable debug logging */
|
|
27
|
+
debug?: boolean;
|
|
28
|
+
/** Custom message to display when creating task */
|
|
29
|
+
taskMessage?: string;
|
|
30
|
+
/** Custom function to extract tool call from message */
|
|
31
|
+
extractToolCall?: (messages: any[]) => {
|
|
32
|
+
name: string;
|
|
33
|
+
args: any;
|
|
34
|
+
id: string;
|
|
35
|
+
} | null;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Loopman validation node for LangGraph
|
|
39
|
+
*
|
|
40
|
+
* This node:
|
|
41
|
+
* 1. Extracts the tool call from the last AI message
|
|
42
|
+
* 2. Creates a Loopman task for human validation
|
|
43
|
+
* 3. Polls until human decision is received
|
|
44
|
+
* 4. Automatically enriches state with guidelines and decisionContext
|
|
45
|
+
*
|
|
46
|
+
* **Works with any state**: This node reads `messages` and `requiresValidation`
|
|
47
|
+
* from the state, and updates it with `loopmanTaskId`, `loopmanTaskStatus`,
|
|
48
|
+
* `guidelines`, and `decisionContext`. You can use your own custom state schema!
|
|
49
|
+
*
|
|
50
|
+
* **Context Enrichment**: After validation, this node automatically fetches
|
|
51
|
+
* and adds `guidelines` and `decisionContext` to the state, making them
|
|
52
|
+
* available for the agent on retry (NEEDS_CHANGES) or subsequent executions.
|
|
53
|
+
*
|
|
54
|
+
* @example With LoopmanGraphState
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { createLoopmanValidationNode, LoopmanGraphState } from "loopman-langchain-sdk";
|
|
57
|
+
*
|
|
58
|
+
* const loopmanNode = createLoopmanValidationNode({
|
|
59
|
+
* apiKey: process.env.LOOPMAN_API_KEY!,
|
|
60
|
+
* workflowId: "my-workflow",
|
|
61
|
+
* executionId: `exec-${Date.now()}`,
|
|
62
|
+
* timeout: 5 * 60 * 1000,
|
|
63
|
+
* pollingInterval: 5000,
|
|
64
|
+
* debug: true,
|
|
65
|
+
* taskMessage: "Please review this action"
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* workflow.addNode("loopman_validation", loopmanNode);
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example With custom state
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const MyCustomState = Annotation.Root({
|
|
74
|
+
* messages: Annotation<BaseMessage[]>({ ... }),
|
|
75
|
+
* requiresValidation: Annotation<boolean>({ ... }),
|
|
76
|
+
* loopmanTaskId: Annotation<string | null>({ ... }),
|
|
77
|
+
* loopmanTaskStatus: Annotation<string | null>({ ... }),
|
|
78
|
+
* guidelines: Annotation<LoopmanGuideline[]>({ ... }),
|
|
79
|
+
* decisionContext: Annotation<LoopmanDecision[]>({ ... }),
|
|
80
|
+
* // ... your custom fields
|
|
81
|
+
* });
|
|
82
|
+
*
|
|
83
|
+
* const loopmanNode = createLoopmanValidationNode({ ... });
|
|
84
|
+
* const workflow = new StateGraph(MyCustomState)
|
|
85
|
+
* .addNode("validation", loopmanNode); // Works!
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function createLoopmanValidationNode(config: LoopmanValidationNodeConfig): (state: any) => Promise<any>;
|
|
89
|
+
//# sourceMappingURL=loopman-validation-node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopman-validation-node.d.ts","sourceRoot":"","sources":["../../src/langgraph/loopman-validation-node.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK;QACrC,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,MAAM,CAAC;KACZ,GAAG,IAAI,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,IAYrB,OAAO,GAAG,kBAkLzB"}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopman Validation Node for LangGraph
|
|
3
|
+
*
|
|
4
|
+
* This node handles human-in-the-loop validation by:
|
|
5
|
+
* 1. Extracting tool calls from agent messages
|
|
6
|
+
* 2. Creating validation tasks in Loopman
|
|
7
|
+
* 3. Polling for human decisions
|
|
8
|
+
* 4. Enriching state with guidelines and decision context
|
|
9
|
+
*/
|
|
10
|
+
import { ToolMessage } from "@langchain/core/messages";
|
|
11
|
+
import { LoopmanService } from "../services/loopman.service";
|
|
12
|
+
import { createLoopmanContextNode } from "./loopman-context-node";
|
|
13
|
+
/**
|
|
14
|
+
* Creates a Loopman validation node for LangGraph
|
|
15
|
+
*
|
|
16
|
+
* This node:
|
|
17
|
+
* 1. Extracts the tool call from the last AI message
|
|
18
|
+
* 2. Creates a Loopman task for human validation
|
|
19
|
+
* 3. Polls until human decision is received
|
|
20
|
+
* 4. Automatically enriches state with guidelines and decisionContext
|
|
21
|
+
*
|
|
22
|
+
* **Works with any state**: This node reads `messages` and `requiresValidation`
|
|
23
|
+
* from the state, and updates it with `loopmanTaskId`, `loopmanTaskStatus`,
|
|
24
|
+
* `guidelines`, and `decisionContext`. You can use your own custom state schema!
|
|
25
|
+
*
|
|
26
|
+
* **Context Enrichment**: After validation, this node automatically fetches
|
|
27
|
+
* and adds `guidelines` and `decisionContext` to the state, making them
|
|
28
|
+
* available for the agent on retry (NEEDS_CHANGES) or subsequent executions.
|
|
29
|
+
*
|
|
30
|
+
* @example With LoopmanGraphState
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { createLoopmanValidationNode, LoopmanGraphState } from "loopman-langchain-sdk";
|
|
33
|
+
*
|
|
34
|
+
* const loopmanNode = createLoopmanValidationNode({
|
|
35
|
+
* apiKey: process.env.LOOPMAN_API_KEY!,
|
|
36
|
+
* workflowId: "my-workflow",
|
|
37
|
+
* executionId: `exec-${Date.now()}`,
|
|
38
|
+
* timeout: 5 * 60 * 1000,
|
|
39
|
+
* pollingInterval: 5000,
|
|
40
|
+
* debug: true,
|
|
41
|
+
* taskMessage: "Please review this action"
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* workflow.addNode("loopman_validation", loopmanNode);
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example With custom state
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const MyCustomState = Annotation.Root({
|
|
50
|
+
* messages: Annotation<BaseMessage[]>({ ... }),
|
|
51
|
+
* requiresValidation: Annotation<boolean>({ ... }),
|
|
52
|
+
* loopmanTaskId: Annotation<string | null>({ ... }),
|
|
53
|
+
* loopmanTaskStatus: Annotation<string | null>({ ... }),
|
|
54
|
+
* guidelines: Annotation<LoopmanGuideline[]>({ ... }),
|
|
55
|
+
* decisionContext: Annotation<LoopmanDecision[]>({ ... }),
|
|
56
|
+
* // ... your custom fields
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* const loopmanNode = createLoopmanValidationNode({ ... });
|
|
60
|
+
* const workflow = new StateGraph(MyCustomState)
|
|
61
|
+
* .addNode("validation", loopmanNode); // Works!
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function createLoopmanValidationNode(config) {
|
|
65
|
+
// Create LoopmanService instance with the provided configuration
|
|
66
|
+
const loopmanService = new LoopmanService({
|
|
67
|
+
apiKey: config.apiKey,
|
|
68
|
+
workflowId: config.workflowId,
|
|
69
|
+
executionId: config.executionId || `exec-${Date.now()}`,
|
|
70
|
+
timeout: config.timeout,
|
|
71
|
+
pollingInterval: config.pollingInterval,
|
|
72
|
+
debug: config.debug,
|
|
73
|
+
});
|
|
74
|
+
return async (state) => {
|
|
75
|
+
const { messages, requiresValidation, loopmanTaskId } = state;
|
|
76
|
+
const { debug } = config;
|
|
77
|
+
const logger = loopmanService.logger;
|
|
78
|
+
// Skip if validation not required
|
|
79
|
+
if (!requiresValidation) {
|
|
80
|
+
logger.debug("[LoopmanNode] Validation not required, skipping");
|
|
81
|
+
return {};
|
|
82
|
+
}
|
|
83
|
+
// Extract tool call from last message
|
|
84
|
+
const lastMessage = messages[messages.length - 1];
|
|
85
|
+
const toolCall = config.extractToolCall
|
|
86
|
+
? config.extractToolCall(messages)
|
|
87
|
+
: extractDefaultToolCall(lastMessage);
|
|
88
|
+
if (!toolCall) {
|
|
89
|
+
logger.warn("[LoopmanNode] No tool call found, skipping");
|
|
90
|
+
return { requiresValidation: false };
|
|
91
|
+
}
|
|
92
|
+
// Step 1: Check if we're retrying an existing task (NEEDS_CHANGES scenario)
|
|
93
|
+
const isRetry = !!loopmanTaskId;
|
|
94
|
+
if (isRetry) {
|
|
95
|
+
logger.debug("[LoopmanNode] Updating existing validation task", {
|
|
96
|
+
taskId: loopmanTaskId,
|
|
97
|
+
tool: toolCall.name,
|
|
98
|
+
args: toolCall.args,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
logger.debug("[LoopmanNode] Creating validation task", {
|
|
103
|
+
tool: toolCall.name,
|
|
104
|
+
args: toolCall.args,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// Step 2: Create or update task in Loopman
|
|
108
|
+
const taskMessage = config.taskMessage || `Validation required for tool: ${toolCall.name}`;
|
|
109
|
+
try {
|
|
110
|
+
const task = await loopmanService.createTaskForValidation(toolCall.name, toolCall.args, taskMessage, toolCall.id, undefined, // businessContext
|
|
111
|
+
undefined, // parentTaskId
|
|
112
|
+
undefined, // proposedDecision
|
|
113
|
+
undefined, // decisionReasoning
|
|
114
|
+
isRetry ? loopmanTaskId : undefined // existingTaskId for retry
|
|
115
|
+
);
|
|
116
|
+
logger.debug("[LoopmanNode] Task created, polling for human decision", {
|
|
117
|
+
taskId: task.id,
|
|
118
|
+
});
|
|
119
|
+
// Step 2: Poll for human decision
|
|
120
|
+
const pollingResult = await loopmanService.polling.waitForDecision(loopmanService.getPollingOptions(task.id));
|
|
121
|
+
logger.debug("[LoopmanNode] Polling result", {
|
|
122
|
+
status: pollingResult.status,
|
|
123
|
+
});
|
|
124
|
+
// Step 3: Handle timeout/abort
|
|
125
|
+
if (pollingResult.status === "TIMEOUT") {
|
|
126
|
+
return {
|
|
127
|
+
loopmanTaskId: task.id,
|
|
128
|
+
loopmanTaskStatus: "TIMEOUT",
|
|
129
|
+
requiresValidation: false,
|
|
130
|
+
messages: [
|
|
131
|
+
new ToolMessage({
|
|
132
|
+
content: "Human validation timeout - task was not reviewed in time",
|
|
133
|
+
tool_call_id: toolCall.id,
|
|
134
|
+
}),
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (pollingResult.status === "ABORTED") {
|
|
139
|
+
return {
|
|
140
|
+
loopmanTaskId: task.id,
|
|
141
|
+
loopmanTaskStatus: "ABORTED",
|
|
142
|
+
requiresValidation: false,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Step 4: Get final task with decision
|
|
146
|
+
const finalTask = await loopmanService.getTask(task.id);
|
|
147
|
+
const latestDecision = finalTask.decisions?.[0];
|
|
148
|
+
const feedback = latestDecision?.comment || null;
|
|
149
|
+
if (debug) {
|
|
150
|
+
logger.debug("[LoopmanNode] Human decision received", {
|
|
151
|
+
status: finalTask.status,
|
|
152
|
+
feedback,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Step 5: Fetch enriched context only if NEEDS_CHANGES
|
|
156
|
+
// This optimizes API calls by loading guidelines and decisions only when needed
|
|
157
|
+
let contextUpdates = {};
|
|
158
|
+
if (finalTask.status === "NEEDS_CHANGES") {
|
|
159
|
+
logger.debug("[LoopmanNode] Status is NEEDS_CHANGES - loading context for retry");
|
|
160
|
+
try {
|
|
161
|
+
// Use the context node to load guidelines and decisions
|
|
162
|
+
const contextNode = createLoopmanContextNode({
|
|
163
|
+
apiKey: config.apiKey,
|
|
164
|
+
workflowId: config.workflowId,
|
|
165
|
+
executionId: config.executionId,
|
|
166
|
+
category: config.category,
|
|
167
|
+
includeGuidelines: true,
|
|
168
|
+
includeDecisionHistory: true,
|
|
169
|
+
debug,
|
|
170
|
+
});
|
|
171
|
+
// Add taskId to state so context node can fetch decisions
|
|
172
|
+
const stateWithTaskId = { ...state, taskId: finalTask.id };
|
|
173
|
+
contextUpdates = await contextNode(stateWithTaskId);
|
|
174
|
+
if (debug) {
|
|
175
|
+
const guidelinesCount = contextUpdates.guidelines?.length || 0;
|
|
176
|
+
const decisionsCount = contextUpdates.decisionContext?.length || 0;
|
|
177
|
+
logger.debug("[LoopmanNode] Context loaded", {
|
|
178
|
+
guidelinesCount,
|
|
179
|
+
decisionsCount,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
logger.warn(`[LoopmanNode] Failed to load context: ${error.message} (continuing without)`);
|
|
185
|
+
// Continue without context if loading fails
|
|
186
|
+
contextUpdates = {};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else if (debug) {
|
|
190
|
+
logger.debug(`[LoopmanNode] Status is ${finalTask.status} - skipping context loading`);
|
|
191
|
+
}
|
|
192
|
+
// Step 6: Return state update with enriched context (only if NEEDS_CHANGES)
|
|
193
|
+
return {
|
|
194
|
+
loopmanTaskId: task.id,
|
|
195
|
+
loopmanTaskStatus: finalTask.status,
|
|
196
|
+
requiresValidation: false, // Reset flag
|
|
197
|
+
...contextUpdates, // Add guidelines and decisionContext if loaded
|
|
198
|
+
messages: buildDecisionMessages(finalTask.status, feedback, toolCall.id),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
if (debug) {
|
|
203
|
+
logger.error("[LoopmanNode] Error during validation", error.message);
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
loopmanTaskStatus: "ERROR",
|
|
207
|
+
requiresValidation: false,
|
|
208
|
+
messages: [
|
|
209
|
+
new ToolMessage({
|
|
210
|
+
content: `Error during validation: ${error.message}`,
|
|
211
|
+
tool_call_id: toolCall.id,
|
|
212
|
+
}),
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Helper Functions
|
|
220
|
+
// ============================================================================
|
|
221
|
+
/**
|
|
222
|
+
* Extracts tool call from a message
|
|
223
|
+
*/
|
|
224
|
+
function extractDefaultToolCall(message) {
|
|
225
|
+
if (!message)
|
|
226
|
+
return null;
|
|
227
|
+
// Handle AIMessage with tool_calls
|
|
228
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
229
|
+
const tc = message.tool_calls[0];
|
|
230
|
+
return {
|
|
231
|
+
name: tc.name,
|
|
232
|
+
args: tc.args,
|
|
233
|
+
id: tc.id || `call_${Date.now()}`,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
// Handle raw message with toolCalls
|
|
237
|
+
if (message.toolCalls && message.toolCalls.length > 0) {
|
|
238
|
+
const tc = message.toolCalls[0];
|
|
239
|
+
return {
|
|
240
|
+
name: tc.name,
|
|
241
|
+
args: tc.args,
|
|
242
|
+
id: tc.id || `call_${Date.now()}`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Builds messages to add to state based on decision
|
|
249
|
+
*/
|
|
250
|
+
function buildDecisionMessages(status, feedback, toolCallId) {
|
|
251
|
+
switch (status) {
|
|
252
|
+
case "APPROVED":
|
|
253
|
+
return [
|
|
254
|
+
new ToolMessage({
|
|
255
|
+
content: feedback
|
|
256
|
+
? `Approved by human reviewer. Feedback: "${feedback}"`
|
|
257
|
+
: "Approved by human reviewer. Proceed with execution.",
|
|
258
|
+
tool_call_id: toolCallId,
|
|
259
|
+
}),
|
|
260
|
+
];
|
|
261
|
+
case "REJECTED":
|
|
262
|
+
return [
|
|
263
|
+
new ToolMessage({
|
|
264
|
+
content: feedback
|
|
265
|
+
? `Rejected by human reviewer. Reason: "${feedback}"`
|
|
266
|
+
: "Rejected by human reviewer. Action cannot be executed.",
|
|
267
|
+
tool_call_id: toolCallId,
|
|
268
|
+
}),
|
|
269
|
+
];
|
|
270
|
+
case "NEEDS_CHANGES":
|
|
271
|
+
return [
|
|
272
|
+
new ToolMessage({
|
|
273
|
+
content: feedback
|
|
274
|
+
? `Changes requested by human reviewer: "${feedback}"`
|
|
275
|
+
: "Changes requested by human reviewer. Please revise your approach.",
|
|
276
|
+
tool_call_id: toolCallId,
|
|
277
|
+
}),
|
|
278
|
+
];
|
|
279
|
+
case "TIMEOUT":
|
|
280
|
+
return [
|
|
281
|
+
new ToolMessage({
|
|
282
|
+
content: "Human validation timeout - task was not reviewed in time",
|
|
283
|
+
tool_call_id: toolCallId,
|
|
284
|
+
}),
|
|
285
|
+
];
|
|
286
|
+
case "PENDING":
|
|
287
|
+
case "ERROR":
|
|
288
|
+
default:
|
|
289
|
+
return [
|
|
290
|
+
new ToolMessage({
|
|
291
|
+
content: `Validation status: ${status}`,
|
|
292
|
+
tool_call_id: toolCallId,
|
|
293
|
+
}),
|
|
294
|
+
];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=loopman-validation-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopman-validation-node.js","sourceRoot":"","sources":["../../src/langgraph/loopman-validation-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common types and state definition for LangGraph integration
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Guideline from Loopman API
|
|
6
|
+
*/
|
|
7
|
+
export interface LoopmanGuideline {
|
|
8
|
+
ruleId: string;
|
|
9
|
+
description: string;
|
|
10
|
+
category?: string | null;
|
|
11
|
+
severity: "critical" | "major" | "minor";
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Decision from Loopman decision context
|
|
15
|
+
*/
|
|
16
|
+
export interface LoopmanDecision {
|
|
17
|
+
date: string;
|
|
18
|
+
userType: "USER" | "SYSTEM";
|
|
19
|
+
status: "PENDING" | "APPROVED" | "REJECTED" | "NEEDS_CHANGES";
|
|
20
|
+
comment?: string;
|
|
21
|
+
firstName?: string;
|
|
22
|
+
lastName?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* State definition for LangGraph with Loopman integration
|
|
26
|
+
*
|
|
27
|
+
* This state includes all fields needed for Loopman's Human-in-the-Loop
|
|
28
|
+
* validation and context enrichment features. You can also use your own
|
|
29
|
+
* custom state by including only the fields you need.
|
|
30
|
+
*
|
|
31
|
+
* @example Basic usage
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const workflow = new StateGraph(LoopmanGraphState)
|
|
34
|
+
* .addNode("agent", agentNode)
|
|
35
|
+
* .addNode("validation", validationNode);
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example Custom state
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const MyState = Annotation.Root({
|
|
41
|
+
* messages: Annotation<BaseMessage[]>({ ... }),
|
|
42
|
+
* myCustomField: Annotation<string>({ ... }),
|
|
43
|
+
* // Add only Loopman fields you need:
|
|
44
|
+
* guidelines: Annotation<LoopmanGuideline[]>({ ... }),
|
|
45
|
+
* requiresValidation: Annotation<boolean>({ ... }),
|
|
46
|
+
* });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare const LoopmanGraphState: import("@langchain/langgraph").AnnotationRoot<{
|
|
50
|
+
/**
|
|
51
|
+
* Messages in the conversation
|
|
52
|
+
*/
|
|
53
|
+
messages: import("@langchain/langgraph").BinaryOperatorAggregate<any[], any[]>;
|
|
54
|
+
/**
|
|
55
|
+
* ID of the Loopman task awaiting validation
|
|
56
|
+
*/
|
|
57
|
+
loopmanTaskId: import("@langchain/langgraph").BinaryOperatorAggregate<string | null, string | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Status of the Loopman task (APPROVED, REJECTED, NEEDS_CHANGES, etc.)
|
|
60
|
+
*/
|
|
61
|
+
loopmanTaskStatus: import("@langchain/langgraph").BinaryOperatorAggregate<string | null, string | null>;
|
|
62
|
+
/**
|
|
63
|
+
* Flag indicating if the current action requires validation
|
|
64
|
+
*/
|
|
65
|
+
requiresValidation: import("@langchain/langgraph").BinaryOperatorAggregate<boolean, boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Validation guidelines for the workflow
|
|
68
|
+
*/
|
|
69
|
+
guidelines: import("@langchain/langgraph").BinaryOperatorAggregate<LoopmanGuideline[], LoopmanGuideline[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Historical decision context from previous human reviews
|
|
72
|
+
*/
|
|
73
|
+
decisionContext: import("@langchain/langgraph").BinaryOperatorAggregate<LoopmanDecision[], LoopmanDecision[]>;
|
|
74
|
+
}>;
|
|
75
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/langgraph/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,eAAe,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,iBAAiB;IAC5B;;OAEG;;IAMH;;OAEG;;IAMH;;OAEG;;IAMH;;OAEG;;IAMH;;OAEG;;IAMH;;OAEG;;EAKH,CAAC"}
|