mirror of
https://github.com/mosh-hamedani/helpdesk.git
synced 2026-05-21 11:58:19 +02:00
74 lines
1.8 KiB
TypeScript
74 lines
1.8 KiB
TypeScript
import type { PgBoss } from "pg-boss";
|
|
import { generateText } from "ai";
|
|
import { openai } from "@ai-sdk/openai";
|
|
import {
|
|
ticketCategories,
|
|
type TicketCategory,
|
|
} from "core/constants/ticket-category.ts";
|
|
import Sentry from "./sentry";
|
|
import prisma from "../db";
|
|
|
|
const QUEUE_NAME = "classify-ticket";
|
|
|
|
interface ClassifyJobData {
|
|
ticketId: number;
|
|
subject: string;
|
|
body: string;
|
|
}
|
|
|
|
export async function registerClassifyWorker(boss: PgBoss): Promise<void> {
|
|
await boss.createQueue(QUEUE_NAME, {
|
|
retryLimit: 3,
|
|
retryDelay: 30,
|
|
retryBackoff: true,
|
|
});
|
|
|
|
await boss.work<ClassifyJobData>(QUEUE_NAME, async (jobs) => {
|
|
const { ticketId, subject, body } = jobs[0]!.data;
|
|
|
|
try {
|
|
const { text } = await generateText({
|
|
model: openai("gpt-5-nano"),
|
|
system:
|
|
"You are a support ticket classifier. " +
|
|
"Classify the ticket into exactly one of these categories: " +
|
|
`${ticketCategories.join(", ")}. ` +
|
|
"Return only the category value with no extra text.",
|
|
prompt: `Subject: ${subject}\n\nBody: ${body}`,
|
|
});
|
|
|
|
const category = text.trim() as TicketCategory;
|
|
|
|
if (!ticketCategories.includes(category)) {
|
|
console.warn(
|
|
`Invalid category "${text.trim()}" returned for ticket ${ticketId}`
|
|
);
|
|
return;
|
|
}
|
|
|
|
await prisma.ticket.update({
|
|
where: { id: ticketId },
|
|
data: { category },
|
|
});
|
|
} catch (error) {
|
|
Sentry.captureException(error, {
|
|
tags: { queue: QUEUE_NAME, ticketId },
|
|
});
|
|
throw error;
|
|
}
|
|
});
|
|
}
|
|
|
|
export async function sendClassifyJob(ticket: {
|
|
id: number;
|
|
subject: string;
|
|
body: string;
|
|
}): Promise<void> {
|
|
const { boss } = await import("./queue");
|
|
await boss.send(QUEUE_NAME, {
|
|
ticketId: ticket.id,
|
|
subject: ticket.subject,
|
|
body: ticket.body,
|
|
});
|
|
}
|