1import { Inngest } from 'inngest';
2import OpenAI from 'openai';
3
4const inngest = new Inngest({ id: 'support-bot' });
5const openai = new OpenAI();
6
7export const triageWorkflow = inngest.createFunction(
8 { id: 'triage-ticket', retries: 3 },
9 { event: 'ticket.created' },
10 async ({ event, step }) => {
11
12 // 1. Step 1: extract structured fields (LLM, durable)
13 const fields = await step.run('extract', async () => {
14 const r = await openai.chat.completions.create({
15 model: 'gpt-4o-mini',
16 response_format: { type: 'json_object' },
17 messages: [{ role: 'user', content: `Extract from: ${event.data.body}` }],
18 });
19 return JSON.parse(r.choices[0].message.content!);
20 });
21
22 // 2. Step 2: classify intent
23 const intent = await step.run('classify', async () =>
24 classifyWith(fields)
25 );
26
27 // 3. Step 3: branch on intent (deterministic routing)
28 if (intent.category === 'billing') {
29 const customer = await step.run('fetch-customer', () => crm.getCustomer(fields.email));
30 await step.run('notify-billing', () => slack.send('#billing', { fields, customer }));
31 } else if (intent.confidence < 0.8) {
32 // 4. Human-in-the-loop on low confidence
33 await step.waitForEvent('human-decision', { event: 'ticket.reviewed', match: 'data.id', timeout: '4h' });
34 }
35
36 // 5. Emit final
37 await step.sendEvent('done', { name: 'ticket.processed', data: { id: event.data.id, intent } });
38 }
39);