JUNOHUB-test/src/lib/ai/orchestrator/intentRouter.ts
Saad Mohsin 80b53535c1 yes
2026-04-12 04:15:11 +05:00

94 lines
3.5 KiB
TypeScript

import { groqChatCompletion } from "@/lib/ai/groqClient"
import { logger } from "@/lib/logger"
import type { L1Module } from "@/lib/ai/orchestrator/types"
/** All routable modules except CATEGORY_METADETAILS (store toggle only; sizing routes via PRODUCT_FIT). */
const MODULES: L1Module[] = [
"FAQ",
"STORE_LOCAL",
"FEEDBACK",
"HUMAN_ESCALATION",
"PRODUCT_FIT",
"ORDER_STATUS",
"ORDER_SUMMARY",
"SHIPPING_TRACKING",
"ORDER_CANCEL",
"REFUND_STATUS",
"INVENTORY_STOCK",
"RETURN_EXCHANGE",
"ORDER_CHANGE",
"COMPLAINT",
"NOT_RECEIVED_MARKED_DELIVERED",
"SHIPMENT_STUCK_OR_DELAYED",
"PAYMENT_PROBLEM",
"WARRANTY",
"WRONG_ITEM",
"UNKNOWN",
]
const ROUTER_PROMPT = `You route ecommerce customer messages to exactly one module.
Modules:
- FAQ: general policies, how shipping works, refund policy wording (not live refund/payout status for an order).
- STORE_LOCAL: hours, pickup, local/in-store, physical location.
- FEEDBACK: thanks, praise, short satisfaction (not a complaint).
- HUMAN_ESCALATION: wants a real person, speak to someone, agent.
- PRODUCT_FIT: sizing, fit, compatibility, materials for a product.
- ORDER_STATUS: where is my order, processing time, not shipped yet.
- ORDER_SUMMARY: receipt, what did I order, line items recap.
- SHIPPING_TRACKING: tracking number, carrier, delivered, ETA.
- ORDER_CANCEL: cancel my order, can I still cancel.
- REFUND_STATUS: has my refund been issued, refund timeline for MY order (not generic policy).
- INVENTORY_STOCK: is item in stock, availability.
- RETURN_EXCHANGE: start a return or exchange.
- ORDER_CHANGE: change address/items/qty.
- COMPLAINT: strong dissatisfaction, angry escalation.
- NOT_RECEIVED_MARKED_DELIVERED: tracking says delivered but customer did not receive.
- SHIPMENT_STUCK_OR_DELAYED: package not moving / very late.
- PAYMENT_PROBLEM: double charge, wrong amount, promo failed, payment declined.
- WARRANTY: warranty claim, defect after return window.
- WRONG_ITEM: wrong item, damaged item, missing item from order.
- UNKNOWN: does not fit above.
Return JSON only: {"module":"<ONE_FROM_LIST>","missing_slots":["order_ref"|"product_query"|"reason"|"issue"]}
Use missing_slots: order_ref if order # would help; product_query if product name/SKU needed.`
export type RoutedIntent = {
module: L1Module
missing_slots: string[]
}
function parseModule(s: string | undefined): L1Module {
const t = (s || "").trim().toUpperCase()
const found = MODULES.find((m) => m === t)
return found ?? "UNKNOWN"
}
export async function routeCustomerIntent(userMessage: string): Promise<RoutedIntent> {
const { content, error } = await groqChatCompletion(
[
{ role: "system", content: ROUTER_PROMPT },
{ role: "user", content: userMessage.slice(0, 4000) },
],
{ jsonObject: true, temperature: 0.1, maxTokens: 320 },
)
if (!content) {
logger.warn("intentRouter: groq failed", { error })
return { module: "FAQ", missing_slots: [] }
}
try {
const j = JSON.parse(content) as { module?: string; missing_slots?: string[] }
const mod = parseModule(j.module)
const slots = Array.isArray(j.missing_slots)
? j.missing_slots.filter((x) => typeof x === "string")
: []
return { module: mod, missing_slots: slots }
} catch {
logger.warn("intentRouter: invalid JSON", { preview: content.slice(0, 120) })
return { module: "FAQ", missing_slots: [] }
}
}