feat: Refactor sentiment handling and enhance processing logic for session data

This commit is contained in:
Max Kowalski
2025-06-26 21:14:24 +02:00
parent 653d70022b
commit 8774a1f155
11 changed files with 195 additions and 280 deletions

View File

@ -9,13 +9,13 @@ async function createAdminUser() {
const existingUser = await prisma.user.findUnique({
where: { email: 'admin@example.com' }
});
if (existingUser) {
console.log('✅ User already exists:', existingUser.email);
console.log('Password hash:', existingUser.password);
return;
}
// First, ensure we have a company
let company = await prisma.company.findFirst();
if (!company) {
@ -27,7 +27,7 @@ async function createAdminUser() {
});
console.log('✅ Created demo company:', company.name);
}
// Create user
const hashedPassword = await bcrypt.hash('admin123', 10);
const user = await prisma.user.create({
@ -38,7 +38,7 @@ async function createAdminUser() {
companyId: company.id,
}
});
console.log('✅ User created successfully:', user.email);
console.log('Password hash:', user.password);
console.log('Role:', user.role);

View File

@ -8,14 +8,14 @@ const OPENAI_API_URL = "https://api.openai.com/v1/chat/completions";
// Define the expected response structure from OpenAI
interface OpenAIProcessedData {
language: string;
messages_sent: number;
sentiment: "positive" | "neutral" | "negative";
escalated: boolean;
forwarded_hr: boolean;
category: string;
questions: string[];
questions: string | string[];
summary: string;
session_id: string;
tokens: number;
tokens_eur: number;
}
/**
@ -37,11 +37,10 @@ async function processTranscriptWithOpenAI(
You are an AI assistant tasked with analyzing chat transcripts.
Extract the following information from the transcript:
1. The primary language used by the user (ISO 639-1 code)
2. Number of messages sent by the user
3. Overall sentiment (positive, neutral, or negative)
4. Whether the conversation was escalated
5. Whether HR contact was mentioned or provided
6. The best-fitting category for the conversation from this list:
2. Overall sentiment (positive, neutral, or negative)
3. Whether the conversation was escalated
4. Whether HR contact was mentioned or provided
5. The best-fitting category for the conversation from this list:
- Schedule & Hours
- Leave & Vacation
- Sick Leave & Recovery
@ -55,20 +54,22 @@ async function processTranscriptWithOpenAI(
- Access & Login
- Social questions
- Unrecognized / Other
7. Up to 5 paraphrased questions asked by the user (in English)
8. A brief summary of the conversation (10-300 characters)
6. A single question or an array of simplified questions asked by the user formulated in English
7. A brief summary of the conversation (10-300 characters)
8. The number of tokens used for the API call
9. The cost of the API call in EUR
Return the data in JSON format matching this schema:
{
"language": "ISO 639-1 code",
"messages_sent": number,
"sentiment": "positive|neutral|negative",
"escalated": boolean,
"forwarded_hr": boolean,
"category": "one of the categories listed above",
"questions": ["question 1", "question 2", ...],
"questions": "a single question or [\"question 1\", \"question 2\", ...]",
"summary": "brief summary",
"session_id": "${sessionId}"
"tokens": number,
"tokens_eur": number
}
`;
@ -124,14 +125,14 @@ function validateOpenAIResponse(
// Check required fields
const requiredFields = [
"language",
"messages_sent",
"sentiment",
"escalated",
"forwarded_hr",
"category",
"questions",
"summary",
"session_id",
"tokens",
"tokens_eur",
];
for (const field of requiredFields) {
@ -147,10 +148,6 @@ function validateOpenAIResponse(
);
}
if (typeof data.messages_sent !== "number" || data.messages_sent < 0) {
throw new Error("Invalid messages_sent. Expected non-negative number");
}
if (!["positive", "neutral", "negative"].includes(data.sentiment)) {
throw new Error(
"Invalid sentiment. Expected 'positive', 'neutral', or 'negative'"
@ -187,8 +184,8 @@ function validateOpenAIResponse(
);
}
if (!Array.isArray(data.questions)) {
throw new Error("Invalid questions. Expected array of strings");
if (typeof data.questions !== "string" && !Array.isArray(data.questions)) {
throw new Error("Invalid questions. Expected string or array of strings");
}
if (
@ -201,8 +198,12 @@ function validateOpenAIResponse(
);
}
if (typeof data.session_id !== "string") {
throw new Error("Invalid session_id. Expected string");
if (typeof data.tokens !== "number" || data.tokens < 0) {
throw new Error("Invalid tokens. Expected non-negative number");
}
if (typeof data.tokens_eur !== "number" || data.tokens_eur < 0) {
throw new Error("Invalid tokens_eur. Expected non-negative number");
}
}
@ -252,26 +253,23 @@ async function processUnprocessedSessions() {
session.transcriptContent
);
// Map sentiment string to float value for compatibility with existing data
const sentimentMap: Record<string, number> = {
positive: 0.8,
neutral: 0.0,
negative: -0.8,
};
// Update the session with processed data
await prisma.session.update({
where: { id: session.id },
data: {
language: processedData.language,
messagesSent: processedData.messages_sent,
sentiment: sentimentMap[processedData.sentiment] || 0,
sentimentCategory: processedData.sentiment,
sentiment: processedData.sentiment,
escalated: processedData.escalated,
forwardedHr: processedData.forwarded_hr,
category: processedData.category,
questions: JSON.stringify(processedData.questions),
questions: processedData.questions,
summary: processedData.summary,
tokens: {
increment: processedData.tokens,
},
tokensEur: {
increment: processedData.tokens_eur,
},
processed: true,
},
});