Add SystemOne classification support and release version 4.0.0

This commit is contained in:
Jordan Wages 2026-10-07 23:57:49 -05:00
commit 9a5404cb62
16 changed files with 923 additions and 579 deletions

View file

@ -0,0 +1,81 @@
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 };
}

View file

@ -0,0 +1,17 @@
export const path = '/v1/systemone';
export function buildRequest(text, criterion, config) {
const payload = {
state: text,
questions: { match: { type: 'noul', instructions: `Does this email satisfy the following criterion: ${criterion}` } }
};
if (config.model) payload.model = config.model;
return payload;
}
export function parseResponse(result) {
const answer = result?.answers?.match;
if (answer?.type !== 'noul' || typeof answer.noul !== 'number' ||
!Number.isFinite(answer.noul) || answer.noul < 0 || answer.noul > 1) {
throw new Error('SystemOne response must contain answers.match with type noul and a probability from 0 to 1.');
}
return { matched: null, reason: null, probability: answer.noul };
}