@nextsparkjs/plugin-langchain 0.1.0-beta.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/.env.example +41 -0
- package/api/observability/metrics/route.ts +110 -0
- package/api/observability/traces/[traceId]/route.ts +398 -0
- package/api/observability/traces/route.ts +205 -0
- package/api/sessions/route.ts +332 -0
- package/components/observability/CollapsibleJson.tsx +71 -0
- package/components/observability/CompactTimeline.tsx +75 -0
- package/components/observability/ConversationFlow.tsx +271 -0
- package/components/observability/DisabledMessage.tsx +21 -0
- package/components/observability/FiltersPanel.tsx +82 -0
- package/components/observability/ObservabilityDashboard.tsx +230 -0
- package/components/observability/SpansList.tsx +210 -0
- package/components/observability/TraceDetail.tsx +335 -0
- package/components/observability/TraceStatusBadge.tsx +39 -0
- package/components/observability/TracesTable.tsx +97 -0
- package/components/observability/index.ts +7 -0
- package/docs/01-getting-started/01-overview.md +196 -0
- package/docs/01-getting-started/02-installation.md +368 -0
- package/docs/01-getting-started/03-configuration.md +794 -0
- package/docs/02-core-concepts/01-architecture.md +566 -0
- package/docs/02-core-concepts/02-agents.md +597 -0
- package/docs/02-core-concepts/03-tools.md +689 -0
- package/docs/03-orchestration/01-graph-orchestrator.md +809 -0
- package/docs/03-orchestration/02-legacy-react.md +650 -0
- package/docs/04-advanced/01-observability.md +645 -0
- package/docs/04-advanced/02-token-tracking.md +469 -0
- package/docs/04-advanced/03-streaming.md +476 -0
- package/docs/04-advanced/04-guardrails.md +597 -0
- package/docs/05-reference/01-api-reference.md +1403 -0
- package/docs/05-reference/02-customization.md +646 -0
- package/docs/05-reference/03-examples.md +881 -0
- package/docs/index.md +85 -0
- package/hooks/observability/useMetrics.ts +31 -0
- package/hooks/observability/useTraceDetail.ts +48 -0
- package/hooks/observability/useTraces.ts +59 -0
- package/lib/agent-factory.ts +354 -0
- package/lib/agent-helpers.ts +201 -0
- package/lib/db-memory-store.ts +417 -0
- package/lib/graph/index.ts +58 -0
- package/lib/graph/nodes/combiner.ts +399 -0
- package/lib/graph/nodes/router.ts +440 -0
- package/lib/graph/orchestrator-graph.ts +386 -0
- package/lib/graph/prompts/combiner.md +131 -0
- package/lib/graph/prompts/router.md +193 -0
- package/lib/graph/types.ts +365 -0
- package/lib/guardrails.ts +230 -0
- package/lib/index.ts +44 -0
- package/lib/logger.ts +70 -0
- package/lib/memory-store.ts +168 -0
- package/lib/message-serializer.ts +110 -0
- package/lib/prompt-renderer.ts +94 -0
- package/lib/providers.ts +226 -0
- package/lib/streaming.ts +232 -0
- package/lib/token-tracker.ts +298 -0
- package/lib/tools-builder.ts +192 -0
- package/lib/tracer-callbacks.ts +342 -0
- package/lib/tracer.ts +350 -0
- package/migrations/001_langchain_memory.sql +83 -0
- package/migrations/002_token_usage.sql +127 -0
- package/migrations/003_observability.sql +257 -0
- package/package.json +28 -0
- package/plugin.config.ts +170 -0
- package/presets/lib/langchain.config.ts.preset +142 -0
- package/presets/templates/sector7/ai-observability/[traceId]/page.tsx +91 -0
- package/presets/templates/sector7/ai-observability/page.tsx +54 -0
- package/types/langchain.types.ts +274 -0
- package/types/observability.types.ts +270 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LangGraph Orchestrator Graph (GENERIC - Theme-Agnostic)
|
|
3
|
+
*
|
|
4
|
+
* Explicit state machine for multi-agent orchestration.
|
|
5
|
+
* Replaces inefficient ReAct loops with deterministic graph flow.
|
|
6
|
+
*
|
|
7
|
+
* GENERIC ARCHITECTURE:
|
|
8
|
+
* - Graph structure is generated dynamically from OrchestratorConfig
|
|
9
|
+
* - No hardcoded knowledge of specific entities (task, customer, page)
|
|
10
|
+
* - Supports N tools registered by theme
|
|
11
|
+
*
|
|
12
|
+
* Flow:
|
|
13
|
+
* START → Router (1 LLM) → Handlers (0 LLM, sequential) → Combiner (1 LLM if needed) → END
|
|
14
|
+
*
|
|
15
|
+
* Benefits:
|
|
16
|
+
* - 1-2 LLM calls instead of 50+
|
|
17
|
+
* - No recursion limit issues
|
|
18
|
+
* - Deterministic execution
|
|
19
|
+
* - Theme-agnostic - works with any registered tools
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { StateGraph, END, START } from '@langchain/langgraph'
|
|
23
|
+
import type { BaseMessage } from '@langchain/core/messages'
|
|
24
|
+
import type { AgentContext, SessionConfig } from '../../types/langchain.types'
|
|
25
|
+
import { createAgentLogger } from '../logger'
|
|
26
|
+
import type {
|
|
27
|
+
OrchestratorState,
|
|
28
|
+
Intent,
|
|
29
|
+
HandlerResults,
|
|
30
|
+
IntentType,
|
|
31
|
+
RouterRoute,
|
|
32
|
+
GraphConfig,
|
|
33
|
+
OrchestratorConfig,
|
|
34
|
+
ModelConfig,
|
|
35
|
+
} from './types'
|
|
36
|
+
import { DEFAULT_GRAPH_CONFIG } from './types'
|
|
37
|
+
import { createRouterNode } from './nodes/router'
|
|
38
|
+
import { combinerNode } from './nodes/combiner'
|
|
39
|
+
|
|
40
|
+
// ============================================
|
|
41
|
+
// STATE CHANNELS DEFINITION
|
|
42
|
+
// ============================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Channel definitions for state management
|
|
46
|
+
*/
|
|
47
|
+
const orchestratorChannels = {
|
|
48
|
+
// Input channels
|
|
49
|
+
input: {
|
|
50
|
+
value: (x: string, y?: string) => y ?? x,
|
|
51
|
+
default: () => '',
|
|
52
|
+
},
|
|
53
|
+
sessionId: {
|
|
54
|
+
value: (x: string, y?: string) => y ?? x,
|
|
55
|
+
default: () => '',
|
|
56
|
+
},
|
|
57
|
+
context: {
|
|
58
|
+
value: (x: AgentContext, y?: AgentContext) => y ?? x,
|
|
59
|
+
default: () => ({ userId: '', teamId: '' } as AgentContext),
|
|
60
|
+
},
|
|
61
|
+
conversationHistory: {
|
|
62
|
+
value: (x: BaseMessage[], y?: BaseMessage[]) => y ?? x,
|
|
63
|
+
default: () => [] as BaseMessage[],
|
|
64
|
+
},
|
|
65
|
+
sessionConfig: {
|
|
66
|
+
value: (x: SessionConfig | undefined, y?: SessionConfig | undefined) => y ?? x,
|
|
67
|
+
default: () => undefined as SessionConfig | undefined,
|
|
68
|
+
},
|
|
69
|
+
modelConfig: {
|
|
70
|
+
value: (x: ModelConfig | undefined, y?: ModelConfig | undefined) => y ?? x,
|
|
71
|
+
default: () => undefined as ModelConfig | undefined,
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Router output channels
|
|
75
|
+
intents: {
|
|
76
|
+
value: (_: Intent[], y?: Intent[]) => y ?? [],
|
|
77
|
+
default: () => [] as Intent[],
|
|
78
|
+
},
|
|
79
|
+
needsClarification: {
|
|
80
|
+
value: (_: boolean, y?: boolean) => y ?? false,
|
|
81
|
+
default: () => false,
|
|
82
|
+
},
|
|
83
|
+
clarificationQuestion: {
|
|
84
|
+
value: (x: string | undefined, y?: string | undefined) => y ?? x,
|
|
85
|
+
default: () => undefined as string | undefined,
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
// Handler output channels - merge results
|
|
89
|
+
handlerResults: {
|
|
90
|
+
value: (current: HandlerResults, update?: HandlerResults) =>
|
|
91
|
+
update ? { ...current, ...update } : current,
|
|
92
|
+
default: () => ({} as HandlerResults),
|
|
93
|
+
},
|
|
94
|
+
completedHandlers: {
|
|
95
|
+
value: (current: IntentType[], update?: IntentType[]) =>
|
|
96
|
+
update ? [...new Set([...current, ...update])] : current,
|
|
97
|
+
default: () => [] as IntentType[],
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// Final output channels
|
|
101
|
+
finalResponse: {
|
|
102
|
+
value: (_: string | null, y?: string | null) => y ?? null,
|
|
103
|
+
default: () => null as string | null,
|
|
104
|
+
},
|
|
105
|
+
error: {
|
|
106
|
+
value: (_: string | null, y?: string | null) => y ?? null,
|
|
107
|
+
default: () => null as string | null,
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Tracing channels
|
|
111
|
+
traceId: {
|
|
112
|
+
value: (x: string | undefined, y?: string | undefined) => y ?? x,
|
|
113
|
+
default: () => undefined as string | undefined,
|
|
114
|
+
},
|
|
115
|
+
parentTraceId: {
|
|
116
|
+
value: (x: string | undefined, y?: string | undefined) => y ?? x,
|
|
117
|
+
default: () => undefined as string | undefined,
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
// Logging channels
|
|
121
|
+
loggerTimestamp: {
|
|
122
|
+
value: (x: number | undefined, y?: number | undefined) => y ?? x,
|
|
123
|
+
default: () => undefined as number | undefined,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================
|
|
128
|
+
// GENERIC ROUTING FUNCTIONS
|
|
129
|
+
// ============================================
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create generic routing function from router to first handler
|
|
133
|
+
*/
|
|
134
|
+
function createRouteByIntents(config: OrchestratorConfig) {
|
|
135
|
+
const toolNames = config.tools.map(t => t.name)
|
|
136
|
+
const systemIntents = config.systemIntents || ['greeting', 'clarification']
|
|
137
|
+
|
|
138
|
+
return function routeByIntents(state: OrchestratorState): RouterRoute {
|
|
139
|
+
// Handle errors
|
|
140
|
+
if (state.error) {
|
|
141
|
+
return 'error_handler'
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Handle clarification
|
|
145
|
+
if (state.needsClarification) {
|
|
146
|
+
return 'clarification'
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const intentTypes = state.intents.map((i) => i.type)
|
|
150
|
+
|
|
151
|
+
// Check for greeting-only
|
|
152
|
+
if (intentTypes.length === 1 && intentTypes[0] === 'greeting') {
|
|
153
|
+
return 'greeting'
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Find first tool handler that's needed
|
|
157
|
+
const neededTools = toolNames.filter(t => intentTypes.includes(t))
|
|
158
|
+
if (neededTools.length === 0) {
|
|
159
|
+
return 'clarification'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Route to first tool handler
|
|
163
|
+
return `${neededTools[0]}_handler`
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Create generic routing function for after a handler completes
|
|
169
|
+
*/
|
|
170
|
+
function createRouteAfterHandler(config: OrchestratorConfig, currentToolIndex: number) {
|
|
171
|
+
const toolNames = config.tools.map(t => t.name)
|
|
172
|
+
|
|
173
|
+
return function routeAfterHandler(state: OrchestratorState): string {
|
|
174
|
+
const intentTypes = state.intents.map((i) => i.type)
|
|
175
|
+
const completed = state.completedHandlers
|
|
176
|
+
|
|
177
|
+
// Find next needed handler that hasn't been completed
|
|
178
|
+
for (let i = currentToolIndex + 1; i < toolNames.length; i++) {
|
|
179
|
+
const toolName = toolNames[i]
|
|
180
|
+
if (intentTypes.includes(toolName) && !completed.includes(toolName)) {
|
|
181
|
+
return `${toolName}_handler`
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// All handlers done, go to combiner
|
|
186
|
+
return 'combiner'
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ============================================
|
|
191
|
+
// SIMPLE NODE WRAPPERS
|
|
192
|
+
// ============================================
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Simple pass-through for greeting
|
|
196
|
+
*/
|
|
197
|
+
async function greetingNodeWrapper(state: OrchestratorState): Promise<Partial<OrchestratorState>> {
|
|
198
|
+
return { completedHandlers: ['greeting' as IntentType] }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Clarification node - sets response asking for more info
|
|
203
|
+
*/
|
|
204
|
+
async function clarificationNodeWrapper(state: OrchestratorState): Promise<Partial<OrchestratorState>> {
|
|
205
|
+
const question = state.clarificationQuestion || 'Could you please clarify what you would like me to do?'
|
|
206
|
+
return {
|
|
207
|
+
finalResponse: question,
|
|
208
|
+
completedHandlers: ['clarification' as IntentType],
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Error handling node
|
|
214
|
+
*/
|
|
215
|
+
async function errorNodeWrapper(state: OrchestratorState): Promise<Partial<OrchestratorState>> {
|
|
216
|
+
return {
|
|
217
|
+
finalResponse: state.error || 'An unexpected error occurred. Please try again.',
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ============================================
|
|
222
|
+
// GRAPH BUILDER (GENERIC)
|
|
223
|
+
// ============================================
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Create the orchestrator graph dynamically from configuration
|
|
227
|
+
*
|
|
228
|
+
* @param config - Orchestrator configuration with tools
|
|
229
|
+
* @param _graphConfig - Optional graph configuration (reserved for future use)
|
|
230
|
+
* @returns Compiled LangGraph
|
|
231
|
+
*/
|
|
232
|
+
export function createOrchestratorGraph(
|
|
233
|
+
config: OrchestratorConfig,
|
|
234
|
+
_graphConfig: Partial<GraphConfig> = {}
|
|
235
|
+
) {
|
|
236
|
+
// Create the graph with channels
|
|
237
|
+
// Using 'any' to bypass strict typing of StateGraph which expects literal node names
|
|
238
|
+
const graph: any = new StateGraph<OrchestratorState>({
|
|
239
|
+
channels: orchestratorChannels as any,
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
// Add router node (uses config for dynamic prompt/schema)
|
|
243
|
+
const routerNode = createRouterNode(config)
|
|
244
|
+
graph.addNode('router', routerNode)
|
|
245
|
+
|
|
246
|
+
// Add handler nodes dynamically from config
|
|
247
|
+
for (const tool of config.tools) {
|
|
248
|
+
graph.addNode(`${tool.name}_handler`, tool.handler)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Add system nodes
|
|
252
|
+
graph.addNode('combiner', combinerNode)
|
|
253
|
+
graph.addNode('greeting', greetingNodeWrapper)
|
|
254
|
+
graph.addNode('clarification', clarificationNodeWrapper)
|
|
255
|
+
graph.addNode('error_handler', errorNodeWrapper)
|
|
256
|
+
|
|
257
|
+
// Edge from START to router
|
|
258
|
+
graph.addEdge(START, 'router')
|
|
259
|
+
|
|
260
|
+
// Build routing map for router node
|
|
261
|
+
const routingMap: Record<string, string> = {
|
|
262
|
+
greeting: 'greeting',
|
|
263
|
+
clarification: 'clarification',
|
|
264
|
+
error_handler: 'error_handler',
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Add each tool handler as a possible route
|
|
268
|
+
for (const tool of config.tools) {
|
|
269
|
+
routingMap[`${tool.name}_handler`] = `${tool.name}_handler`
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Conditional edges from router
|
|
273
|
+
const routeByIntents = createRouteByIntents(config)
|
|
274
|
+
graph.addConditionalEdges('router', routeByIntents, routingMap)
|
|
275
|
+
|
|
276
|
+
// Add conditional edges from each handler to next handler or combiner
|
|
277
|
+
for (let i = 0; i < config.tools.length; i++) {
|
|
278
|
+
const tool = config.tools[i]
|
|
279
|
+
const routeAfterHandler = createRouteAfterHandler(config, i)
|
|
280
|
+
|
|
281
|
+
// Build routing map for this handler
|
|
282
|
+
const handlerRoutingMap: Record<string, string> = { combiner: 'combiner' }
|
|
283
|
+
|
|
284
|
+
// Add all handlers that could come after this one
|
|
285
|
+
for (let j = i + 1; j < config.tools.length; j++) {
|
|
286
|
+
const nextTool = config.tools[j]
|
|
287
|
+
handlerRoutingMap[`${nextTool.name}_handler`] = `${nextTool.name}_handler`
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
graph.addConditionalEdges(
|
|
291
|
+
`${tool.name}_handler`,
|
|
292
|
+
routeAfterHandler,
|
|
293
|
+
handlerRoutingMap
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Greeting goes to combiner for friendly response
|
|
298
|
+
graph.addEdge('greeting', 'combiner')
|
|
299
|
+
|
|
300
|
+
// Terminal nodes
|
|
301
|
+
graph.addEdge('combiner', END)
|
|
302
|
+
graph.addEdge('clarification', END)
|
|
303
|
+
graph.addEdge('error_handler', END)
|
|
304
|
+
|
|
305
|
+
// Compile and return
|
|
306
|
+
return graph.compile()
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ============================================
|
|
310
|
+
// INVOKE HELPER (UPDATED FOR GENERIC CONFIG)
|
|
311
|
+
// ============================================
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Invoke the orchestrator graph with user input
|
|
315
|
+
*
|
|
316
|
+
* @param input - User message
|
|
317
|
+
* @param sessionId - Session identifier
|
|
318
|
+
* @param context - Multi-tenant context
|
|
319
|
+
* @param conversationHistory - Recent conversation history
|
|
320
|
+
* @param config - Orchestrator configuration with tools (NEW)
|
|
321
|
+
* @param options - Additional options
|
|
322
|
+
* @returns Final state with response
|
|
323
|
+
*/
|
|
324
|
+
export async function invokeOrchestrator(
|
|
325
|
+
input: string,
|
|
326
|
+
sessionId: string,
|
|
327
|
+
context: AgentContext,
|
|
328
|
+
conversationHistory: BaseMessage[] = [],
|
|
329
|
+
config: OrchestratorConfig,
|
|
330
|
+
options: {
|
|
331
|
+
sessionConfig?: SessionConfig
|
|
332
|
+
traceId?: string
|
|
333
|
+
parentTraceId?: string
|
|
334
|
+
graphConfig?: Partial<GraphConfig>
|
|
335
|
+
modelConfig?: ModelConfig
|
|
336
|
+
} = {}
|
|
337
|
+
): Promise<OrchestratorState> {
|
|
338
|
+
const graph = createOrchestratorGraph(config, options.graphConfig)
|
|
339
|
+
const loggerTimestamp = Date.now()
|
|
340
|
+
const logger = createAgentLogger({
|
|
341
|
+
agentName: 'graph-orchestrator',
|
|
342
|
+
userName: (context.userName as string) || 'system',
|
|
343
|
+
timestamp: loggerTimestamp,
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
await logger.info('GRAPH_ORCHESTRATOR_START', {
|
|
347
|
+
input,
|
|
348
|
+
sessionId,
|
|
349
|
+
userId: context.userId,
|
|
350
|
+
teamId: context.teamId,
|
|
351
|
+
historyLength: conversationHistory.length,
|
|
352
|
+
toolsRegistered: config.tools.map(t => t.name),
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
const initialState: OrchestratorState = {
|
|
356
|
+
input,
|
|
357
|
+
sessionId,
|
|
358
|
+
context,
|
|
359
|
+
conversationHistory,
|
|
360
|
+
sessionConfig: options.sessionConfig,
|
|
361
|
+
modelConfig: options.modelConfig,
|
|
362
|
+
intents: [],
|
|
363
|
+
needsClarification: false,
|
|
364
|
+
handlerResults: {},
|
|
365
|
+
completedHandlers: [],
|
|
366
|
+
finalResponse: null,
|
|
367
|
+
error: null,
|
|
368
|
+
traceId: options.traceId,
|
|
369
|
+
parentTraceId: options.parentTraceId,
|
|
370
|
+
loggerTimestamp,
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const result = await graph.invoke(initialState)
|
|
374
|
+
|
|
375
|
+
await logger.info('GRAPH_ORCHESTRATOR_END', {
|
|
376
|
+
intents: (result as OrchestratorState).intents,
|
|
377
|
+
completedHandlers: (result as OrchestratorState).completedHandlers,
|
|
378
|
+
hasResponse: !!(result as OrchestratorState).finalResponse,
|
|
379
|
+
error: (result as OrchestratorState).error,
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
return result as OrchestratorState
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Export types
|
|
386
|
+
export type { GraphConfig, OrchestratorConfig }
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Response Combiner
|
|
2
|
+
|
|
3
|
+
You are a response synthesizer that converts JSON operation results into natural language responses for users.
|
|
4
|
+
|
|
5
|
+
## Your Task
|
|
6
|
+
|
|
7
|
+
Given the original user request and the results from various operations (tasks, customers, pages), generate a clear, natural response that:
|
|
8
|
+
1. Summarizes ALL results
|
|
9
|
+
2. Uses the same language as the user
|
|
10
|
+
3. Is concise but complete
|
|
11
|
+
4. Includes relevant data (names, counts, IDs)
|
|
12
|
+
|
|
13
|
+
## Input Format
|
|
14
|
+
|
|
15
|
+
You receive JSON with:
|
|
16
|
+
- `originalRequest`: The user's original message
|
|
17
|
+
- `results`: Object containing handler results (task, customer, page)
|
|
18
|
+
|
|
19
|
+
Each result contains:
|
|
20
|
+
- `success`: Whether the operation succeeded
|
|
21
|
+
- `operation`: What was done (list, create, update, search, etc.)
|
|
22
|
+
- `data`: The actual data (array or single object)
|
|
23
|
+
- `count`: Number of items (for list/search)
|
|
24
|
+
- `message`: Technical description
|
|
25
|
+
- `error`: Error message if failed
|
|
26
|
+
|
|
27
|
+
## Output Format
|
|
28
|
+
|
|
29
|
+
Return ONLY the response text. No JSON, no markdown code blocks, just natural text.
|
|
30
|
+
|
|
31
|
+
## Examples
|
|
32
|
+
|
|
33
|
+
### Single Result - Tasks List
|
|
34
|
+
Input:
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"originalRequest": "Show me my tasks",
|
|
38
|
+
"results": {
|
|
39
|
+
"task": {
|
|
40
|
+
"success": true,
|
|
41
|
+
"operation": "list",
|
|
42
|
+
"data": [
|
|
43
|
+
{"title": "Buy milk", "priority": "high", "status": "todo"},
|
|
44
|
+
{"title": "Call mom", "priority": "medium", "status": "in_progress"}
|
|
45
|
+
],
|
|
46
|
+
"count": 2
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Output:
|
|
53
|
+
```
|
|
54
|
+
You have 2 tasks:
|
|
55
|
+
- Buy milk (high priority, pending)
|
|
56
|
+
- Call mom (medium priority, in progress)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Multi Result - Tasks and Customer Search
|
|
60
|
+
Input:
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"originalRequest": "Muéstrame mis tareas y el número de cuenta de StartupXYZ",
|
|
64
|
+
"results": {
|
|
65
|
+
"task": {
|
|
66
|
+
"success": true,
|
|
67
|
+
"operation": "list",
|
|
68
|
+
"data": [{"title": "Review proposal", "priority": "high"}],
|
|
69
|
+
"count": 1
|
|
70
|
+
},
|
|
71
|
+
"customer": {
|
|
72
|
+
"success": true,
|
|
73
|
+
"operation": "search",
|
|
74
|
+
"data": [{"name": "StartupXYZ", "account": 12345}],
|
|
75
|
+
"count": 1
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Output:
|
|
82
|
+
```
|
|
83
|
+
Tienes 1 tarea:
|
|
84
|
+
- Review proposal (alta prioridad)
|
|
85
|
+
|
|
86
|
+
El número de cuenta de StartupXYZ es 12345.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Error Handling
|
|
90
|
+
Input:
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"originalRequest": "Find customer ACME",
|
|
94
|
+
"results": {
|
|
95
|
+
"customer": {
|
|
96
|
+
"success": false,
|
|
97
|
+
"operation": "search",
|
|
98
|
+
"data": null,
|
|
99
|
+
"error": "No customers found matching 'ACME'"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Output:
|
|
106
|
+
```
|
|
107
|
+
I couldn't find any customers matching "ACME". Would you like me to search with different criteria?
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Greeting Response
|
|
111
|
+
Input:
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"originalRequest": "Hola!",
|
|
115
|
+
"results": {}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Output:
|
|
120
|
+
```
|
|
121
|
+
¡Hola! ¿En qué puedo ayudarte hoy? Puedo gestionar tus tareas, buscar clientes o consultar páginas.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Rules
|
|
125
|
+
|
|
126
|
+
1. **Match the user's language** - If they wrote in Spanish, respond in Spanish
|
|
127
|
+
2. **Be concise** - Don't repeat unnecessary information
|
|
128
|
+
3. **Format lists nicely** - Use bullet points for multiple items
|
|
129
|
+
4. **Include key data** - Account numbers, task titles, counts, etc.
|
|
130
|
+
5. **Handle errors gracefully** - Explain what went wrong and offer alternatives
|
|
131
|
+
6. **Don't expose technical details** - No JSON, no error codes, no internal messages
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Intent Router
|
|
2
|
+
|
|
3
|
+
You are an intent classifier for a multi-agent system. Your job is to analyze user messages and extract ALL intents in structured JSON format.
|
|
4
|
+
|
|
5
|
+
## Your Task
|
|
6
|
+
|
|
7
|
+
Parse the user's message and identify:
|
|
8
|
+
1. What type(s) of operation they want
|
|
9
|
+
2. What action to perform
|
|
10
|
+
3. What parameters are needed
|
|
11
|
+
4. Which part of their message maps to each intent
|
|
12
|
+
|
|
13
|
+
## Intent Types
|
|
14
|
+
|
|
15
|
+
| Type | Description | Example Actions |
|
|
16
|
+
|------|-------------|-----------------|
|
|
17
|
+
| `task` | Task/todo management | list, create, update, delete, search, get |
|
|
18
|
+
| `customer` | Customer/contact management | list, create, update, delete, search, get |
|
|
19
|
+
| `page` | Page/content management | list, create, update, delete, search, get |
|
|
20
|
+
| `greeting` | Greeting or small talk | - |
|
|
21
|
+
| `clarification` | Unclear request | - |
|
|
22
|
+
|
|
23
|
+
## Output Format
|
|
24
|
+
|
|
25
|
+
Always respond with valid JSON:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"intents": [
|
|
30
|
+
{
|
|
31
|
+
"type": "task|customer|page|greeting|clarification",
|
|
32
|
+
"action": "list|create|update|delete|search|get|unknown",
|
|
33
|
+
"parameters": { ... },
|
|
34
|
+
"originalText": "part of the user message"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"needsClarification": false,
|
|
38
|
+
"clarificationQuestion": null
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
### Single Intent - List Tasks
|
|
45
|
+
**Input:** "Show me my tasks"
|
|
46
|
+
**Output:**
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"intents": [
|
|
50
|
+
{
|
|
51
|
+
"type": "task",
|
|
52
|
+
"action": "list",
|
|
53
|
+
"parameters": {},
|
|
54
|
+
"originalText": "Show me my tasks"
|
|
55
|
+
}
|
|
56
|
+
],
|
|
57
|
+
"needsClarification": false
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Single Intent - Create Task with Parameters
|
|
62
|
+
**Input:** "Create a task called 'Buy milk' with high priority"
|
|
63
|
+
**Output:**
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"intents": [
|
|
67
|
+
{
|
|
68
|
+
"type": "task",
|
|
69
|
+
"action": "create",
|
|
70
|
+
"parameters": {
|
|
71
|
+
"title": "Buy milk",
|
|
72
|
+
"priority": "high"
|
|
73
|
+
},
|
|
74
|
+
"originalText": "Create a task called 'Buy milk' with high priority"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"needsClarification": false
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Multi-Intent - Tasks and Customer
|
|
82
|
+
**Input:** "Show me my tasks and find the account number for StartupXYZ"
|
|
83
|
+
**Output:**
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"intents": [
|
|
87
|
+
{
|
|
88
|
+
"type": "task",
|
|
89
|
+
"action": "list",
|
|
90
|
+
"parameters": {},
|
|
91
|
+
"originalText": "Show me my tasks"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "customer",
|
|
95
|
+
"action": "search",
|
|
96
|
+
"parameters": {
|
|
97
|
+
"query": "StartupXYZ",
|
|
98
|
+
"fields": ["accountNumber", "name"]
|
|
99
|
+
},
|
|
100
|
+
"originalText": "find the account number for StartupXYZ"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"needsClarification": false
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Multi-Intent - Tasks with Filter and Customer
|
|
108
|
+
**Input:** "Muéstrame mis tareas de prioridad baja y el número de cuenta de StartupXYZ"
|
|
109
|
+
**Output:**
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"intents": [
|
|
113
|
+
{
|
|
114
|
+
"type": "task",
|
|
115
|
+
"action": "list",
|
|
116
|
+
"parameters": {
|
|
117
|
+
"priority": "low"
|
|
118
|
+
},
|
|
119
|
+
"originalText": "Muéstrame mis tareas de prioridad baja"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"type": "customer",
|
|
123
|
+
"action": "search",
|
|
124
|
+
"parameters": {
|
|
125
|
+
"query": "StartupXYZ",
|
|
126
|
+
"fields": ["accountNumber"]
|
|
127
|
+
},
|
|
128
|
+
"originalText": "el número de cuenta de StartupXYZ"
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"needsClarification": false
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Greeting
|
|
136
|
+
**Input:** "Hello!"
|
|
137
|
+
**Output:**
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"intents": [
|
|
141
|
+
{
|
|
142
|
+
"type": "greeting",
|
|
143
|
+
"action": "unknown",
|
|
144
|
+
"parameters": {},
|
|
145
|
+
"originalText": "Hello!"
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"needsClarification": false
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Needs Clarification
|
|
153
|
+
**Input:** "Do that thing"
|
|
154
|
+
**Output:**
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"intents": [],
|
|
158
|
+
"needsClarification": true,
|
|
159
|
+
"clarificationQuestion": "I'm not sure what you'd like me to do. Could you please be more specific? I can help with tasks, customers, or pages."
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Rules
|
|
164
|
+
|
|
165
|
+
1. **Always return valid JSON** - No markdown, no explanation, just JSON
|
|
166
|
+
2. **Extract ALL intents** - If user asks for multiple things, include them all
|
|
167
|
+
3. **Preserve user's language** - If they write in Spanish, clarification should be in Spanish
|
|
168
|
+
4. **Be specific with parameters** - Extract as much detail as possible
|
|
169
|
+
5. **Use clarification wisely** - Only if truly unclear, not for minor ambiguity
|
|
170
|
+
6. **Map text accurately** - originalText should match the relevant portion
|
|
171
|
+
|
|
172
|
+
## Parameter Extraction Guide
|
|
173
|
+
|
|
174
|
+
### Task Parameters
|
|
175
|
+
- `title`: Task name/title
|
|
176
|
+
- `description`: Task description
|
|
177
|
+
- `priority`: "low", "medium", "high", "urgent"
|
|
178
|
+
- `status`: "pending", "in_progress", "completed"
|
|
179
|
+
- `dueDate`: ISO date or relative ("tomorrow", "next week")
|
|
180
|
+
|
|
181
|
+
### Customer Parameters
|
|
182
|
+
- `query`: Search term
|
|
183
|
+
- `name`: Customer name
|
|
184
|
+
- `email`: Email address
|
|
185
|
+
- `phone`: Phone number
|
|
186
|
+
- `accountNumber`: Account number
|
|
187
|
+
- `fields`: Specific fields to retrieve
|
|
188
|
+
|
|
189
|
+
### Page Parameters
|
|
190
|
+
- `title`: Page title
|
|
191
|
+
- `slug`: URL slug
|
|
192
|
+
- `status`: "draft", "published"
|
|
193
|
+
- `content`: Page content
|