initial commit
This commit is contained in:
83
packages/backend/src/agents/ranking.ts
Normal file
83
packages/backend/src/agents/ranking.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { openai } from '../agent.js';
|
||||
import type { InterpreterOutput, RetrievalOutput, RankingOutput } from '../types/agents.js';
|
||||
|
||||
export async function runRanking(
|
||||
interpreter: InterpreterOutput,
|
||||
retrieval: RetrievalOutput,
|
||||
): Promise<RankingOutput> {
|
||||
// Phase 1: Pre-filter — remove avoidance violations
|
||||
const avoidList = interpreter.avoid.map((a) => a.toLowerCase());
|
||||
const filtered = retrieval.candidates.filter((c) => {
|
||||
const text = (c.title + ' ' + c.reason).toLowerCase();
|
||||
return !avoidList.some((a) => text.includes(a));
|
||||
});
|
||||
|
||||
// Phase 2: Chunked ranking — split into groups of ~15
|
||||
const CHUNK_SIZE = 15;
|
||||
const chunks: typeof filtered[] = [];
|
||||
for (let i = 0; i < filtered.length; i += CHUNK_SIZE) {
|
||||
chunks.push(filtered.slice(i, i + CHUNK_SIZE));
|
||||
}
|
||||
|
||||
const allBuckets: RankingOutput = {
|
||||
definitely_like: [],
|
||||
might_like: [],
|
||||
questionable: [],
|
||||
will_not_like: [],
|
||||
};
|
||||
|
||||
for (const chunk of chunks) {
|
||||
const chunkTitles = chunk.map((c) => `- ${c.title}: ${c.reason}`).join('\n');
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5.4',
|
||||
temperature: 0.2,
|
||||
service_tier: 'flex',
|
||||
response_format: { type: 'json_object' },
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a TV show ranking critic. Assign each show to exactly one of four confidence buckets based on how well it matches the user's preferences.
|
||||
|
||||
Buckets:
|
||||
- "definitely_like": Near-perfect match to all preferences
|
||||
- "might_like": Strong match to most preferences
|
||||
- "questionable": Partial alignment, some aspects don't match
|
||||
- "will_not_like": Likely mismatch, conflicts with preferences or avoidance criteria
|
||||
|
||||
Your output MUST be valid JSON:
|
||||
{
|
||||
"definitely_like": string[],
|
||||
"might_like": string[],
|
||||
"questionable": string[],
|
||||
"will_not_like": string[]
|
||||
}
|
||||
|
||||
Every show in the input must appear in exactly one bucket. Use the title exactly as given.`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `User preferences:
|
||||
Liked shows: ${JSON.stringify(interpreter.liked)}
|
||||
Themes: ${JSON.stringify(interpreter.themes)}
|
||||
Character preferences: ${JSON.stringify(interpreter.character_preferences)}
|
||||
Tone: ${JSON.stringify(interpreter.tone)}
|
||||
Avoid: ${JSON.stringify(interpreter.avoid)}
|
||||
|
||||
Rank these shows:
|
||||
${chunkTitles}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const content = response.choices[0]?.message?.content ?? '{}';
|
||||
const chunkResult = JSON.parse(content) as Partial<RankingOutput>;
|
||||
|
||||
allBuckets.definitely_like.push(...(chunkResult.definitely_like ?? []));
|
||||
allBuckets.might_like.push(...(chunkResult.might_like ?? []));
|
||||
allBuckets.questionable.push(...(chunkResult.questionable ?? []));
|
||||
allBuckets.will_not_like.push(...(chunkResult.will_not_like ?? []));
|
||||
}
|
||||
|
||||
return allBuckets;
|
||||
}
|
||||
Reference in New Issue
Block a user