mirror of
https://github.com/mosh-hamedani/helpdesk.git
synced 2026-05-21 11:58:19 +02:00
Add the ability to classify tickets
This commit is contained in:
parent
199a58a3a4
commit
d77c810427
37
server/src/lib/classify-ticket.ts
Normal file
37
server/src/lib/classify-ticket.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { generateText } from "ai";
|
||||||
|
import { openai } from "@ai-sdk/openai";
|
||||||
|
import type { Ticket } from "../generated/prisma/client";
|
||||||
|
import { ticketCategories, type TicketCategory } from "core/constants/ticket-category.ts";
|
||||||
|
import prisma from "../db";
|
||||||
|
|
||||||
|
export function classifyTicket(ticket: Ticket): void {
|
||||||
|
doClassify(ticket).catch((error) =>
|
||||||
|
console.error(`Failed to classify ticket ${ticket.id}:`, error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doClassify(ticket: Ticket): Promise<void> {
|
||||||
|
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: ${ticket.subject}\n\nBody: ${ticket.body}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const category = text.trim() as TicketCategory;
|
||||||
|
|
||||||
|
if (!ticketCategories.includes(category)) {
|
||||||
|
console.warn(
|
||||||
|
`Invalid category "${text.trim()}" returned for ticket ${ticket.id}`
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.ticket.update({
|
||||||
|
where: { id: ticket.id },
|
||||||
|
data: { category },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { inboundEmailSchema } from "core/schemas/tickets.ts";
|
||||||
import { requireWebhookSecret } from "../middleware/require-webhook-secret";
|
import { requireWebhookSecret } from "../middleware/require-webhook-secret";
|
||||||
import { validate } from "../lib/validate";
|
import { validate } from "../lib/validate";
|
||||||
import prisma from "../db";
|
import prisma from "../db";
|
||||||
|
import { classifyTicket } from "../lib/classify-ticket";
|
||||||
|
|
||||||
function stripSubjectPrefixes(subject: string): string {
|
function stripSubjectPrefixes(subject: string): string {
|
||||||
return subject.replace(/^(Re:\s*|Fwd:\s*)+/i, "").trim();
|
return subject.replace(/^(Re:\s*|Fwd:\s*)+/i, "").trim();
|
||||||
|
|
@ -50,6 +51,8 @@ router.post("/inbound-email", requireWebhookSecret, async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({ ticket });
|
res.status(201).json({ ticket });
|
||||||
|
|
||||||
|
classifyTicket(ticket);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue