@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.
Files changed (67) hide show
  1. package/.env.example +41 -0
  2. package/api/observability/metrics/route.ts +110 -0
  3. package/api/observability/traces/[traceId]/route.ts +398 -0
  4. package/api/observability/traces/route.ts +205 -0
  5. package/api/sessions/route.ts +332 -0
  6. package/components/observability/CollapsibleJson.tsx +71 -0
  7. package/components/observability/CompactTimeline.tsx +75 -0
  8. package/components/observability/ConversationFlow.tsx +271 -0
  9. package/components/observability/DisabledMessage.tsx +21 -0
  10. package/components/observability/FiltersPanel.tsx +82 -0
  11. package/components/observability/ObservabilityDashboard.tsx +230 -0
  12. package/components/observability/SpansList.tsx +210 -0
  13. package/components/observability/TraceDetail.tsx +335 -0
  14. package/components/observability/TraceStatusBadge.tsx +39 -0
  15. package/components/observability/TracesTable.tsx +97 -0
  16. package/components/observability/index.ts +7 -0
  17. package/docs/01-getting-started/01-overview.md +196 -0
  18. package/docs/01-getting-started/02-installation.md +368 -0
  19. package/docs/01-getting-started/03-configuration.md +794 -0
  20. package/docs/02-core-concepts/01-architecture.md +566 -0
  21. package/docs/02-core-concepts/02-agents.md +597 -0
  22. package/docs/02-core-concepts/03-tools.md +689 -0
  23. package/docs/03-orchestration/01-graph-orchestrator.md +809 -0
  24. package/docs/03-orchestration/02-legacy-react.md +650 -0
  25. package/docs/04-advanced/01-observability.md +645 -0
  26. package/docs/04-advanced/02-token-tracking.md +469 -0
  27. package/docs/04-advanced/03-streaming.md +476 -0
  28. package/docs/04-advanced/04-guardrails.md +597 -0
  29. package/docs/05-reference/01-api-reference.md +1403 -0
  30. package/docs/05-reference/02-customization.md +646 -0
  31. package/docs/05-reference/03-examples.md +881 -0
  32. package/docs/index.md +85 -0
  33. package/hooks/observability/useMetrics.ts +31 -0
  34. package/hooks/observability/useTraceDetail.ts +48 -0
  35. package/hooks/observability/useTraces.ts +59 -0
  36. package/lib/agent-factory.ts +354 -0
  37. package/lib/agent-helpers.ts +201 -0
  38. package/lib/db-memory-store.ts +417 -0
  39. package/lib/graph/index.ts +58 -0
  40. package/lib/graph/nodes/combiner.ts +399 -0
  41. package/lib/graph/nodes/router.ts +440 -0
  42. package/lib/graph/orchestrator-graph.ts +386 -0
  43. package/lib/graph/prompts/combiner.md +131 -0
  44. package/lib/graph/prompts/router.md +193 -0
  45. package/lib/graph/types.ts +365 -0
  46. package/lib/guardrails.ts +230 -0
  47. package/lib/index.ts +44 -0
  48. package/lib/logger.ts +70 -0
  49. package/lib/memory-store.ts +168 -0
  50. package/lib/message-serializer.ts +110 -0
  51. package/lib/prompt-renderer.ts +94 -0
  52. package/lib/providers.ts +226 -0
  53. package/lib/streaming.ts +232 -0
  54. package/lib/token-tracker.ts +298 -0
  55. package/lib/tools-builder.ts +192 -0
  56. package/lib/tracer-callbacks.ts +342 -0
  57. package/lib/tracer.ts +350 -0
  58. package/migrations/001_langchain_memory.sql +83 -0
  59. package/migrations/002_token_usage.sql +127 -0
  60. package/migrations/003_observability.sql +257 -0
  61. package/package.json +28 -0
  62. package/plugin.config.ts +170 -0
  63. package/presets/lib/langchain.config.ts.preset +142 -0
  64. package/presets/templates/sector7/ai-observability/[traceId]/page.tsx +91 -0
  65. package/presets/templates/sector7/ai-observability/page.tsx +54 -0
  66. package/types/langchain.types.ts +274 -0
  67. 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