81 lines
2.5 KiB
JavaScript
81 lines
2.5 KiB
JavaScript
const SYSTEM_PREFIX = `You are an email-classification assistant.
|
|
Read the email below and the classification criterion provided by the user.
|
|
`;
|
|
|
|
const DEFAULT_CUSTOM_SYSTEM_PROMPT = "Determine whether the email satisfies the user's criterion.";
|
|
|
|
const SYSTEM_SUFFIX = `
|
|
Return ONLY a JSON object on a single line of the form:
|
|
{"match": true, "reason": "<short explanation>"} - if the email satisfies the criterion
|
|
{"match": false, "reason": "<short explanation>"} - otherwise
|
|
|
|
Do not add any other keys, text, or formatting.`;
|
|
|
|
function extractLastJsonObject(text) {
|
|
let last = null;
|
|
let start = -1;
|
|
let depth = 0;
|
|
let inString = false;
|
|
let escape = false;
|
|
|
|
for (let i = 0; i < text.length; i += 1) {
|
|
const ch = text[i];
|
|
if (inString) {
|
|
if (escape) {
|
|
escape = false;
|
|
continue;
|
|
}
|
|
if (ch === "\\") {
|
|
escape = true;
|
|
continue;
|
|
}
|
|
if (ch === "\"") {
|
|
inString = false;
|
|
}
|
|
continue;
|
|
}
|
|
if (ch === "\"") {
|
|
inString = true;
|
|
continue;
|
|
}
|
|
if (ch === "{") {
|
|
if (depth === 0) {
|
|
start = i;
|
|
}
|
|
depth += 1;
|
|
continue;
|
|
}
|
|
if (ch === "}" && depth > 0) {
|
|
depth -= 1;
|
|
if (depth === 0 && start !== -1) {
|
|
last = text.slice(start, i + 1);
|
|
start = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return last;
|
|
}
|
|
|
|
export const path = '/v1/chat/completions';
|
|
export function buildRequest(text, criterion, config) {
|
|
const payload = {
|
|
messages: [
|
|
{ role: 'system', content: SYSTEM_PREFIX + (config.customSystemPrompt || DEFAULT_CUSTOM_SYSTEM_PROMPT) + SYSTEM_SUFFIX },
|
|
{ role: 'user', content: `**Email Contents**\n\`\`\`\n${text}\n\`\`\`\nClassification Criterion: ${criterion}` }
|
|
], ...config.aiParams
|
|
};
|
|
if (config.model) payload.model = config.model;
|
|
return payload;
|
|
}
|
|
export function parseResponse(result) {
|
|
const content = result?.choices?.[0]?.message?.content;
|
|
if (typeof content !== 'string') throw new Error('Chat response missing text content.');
|
|
const candidate = extractLastJsonObject(content);
|
|
if (!candidate) throw new Error('No JSON object found in AI response.');
|
|
const obj = JSON.parse(candidate);
|
|
const matched = Object.hasOwn(obj, 'match') ? obj.match : obj.matched;
|
|
if (typeof matched !== 'boolean') throw new Error('AI response missing valid match boolean.');
|
|
const reason = obj.reason ?? obj.reasoning ?? obj.explaination;
|
|
return { matched, reason: typeof reason === 'string' ? reason : null, probability: null };
|
|
}
|