feature: new changes!
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { openai } from '../agent.js';
|
||||
import type { InterpreterOutput, RetrievalOutput } from '../types/agents.js';
|
||||
|
||||
export async function runRetrieval(input: InterpreterOutput): Promise<RetrievalOutput> {
|
||||
export async function runRetrieval(input: InterpreterOutput, brainstormCount = 100): Promise<RetrievalOutput> {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5.4',
|
||||
temperature: 0.9,
|
||||
@@ -10,7 +10,7 @@ export async function runRetrieval(input: InterpreterOutput): Promise<RetrievalO
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `You are a TV show candidate generator. Your goal is to brainstorm a LARGE, DIVERSE pool of 60–80 TV show candidates that match the user's structured preferences.
|
||||
content: `You are a TV show candidate generator. Your goal is to brainstorm a LARGE, DIVERSE pool of ${brainstormCount} TV show candidates that match the user's structured preferences.
|
||||
|
||||
Your output MUST be valid JSON matching this schema:
|
||||
{
|
||||
@@ -25,7 +25,7 @@ Rules:
|
||||
- Each "reason" should briefly explain why the show matches the preferences
|
||||
- Avoid duplicates
|
||||
- Include shows from different decades, countries, and networks
|
||||
- Aim for 60–80 candidates minimum`,
|
||||
- Aim for ${brainstormCount} candidates minimum`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
|
||||
28
packages/backend/src/agents/titleGenerator.ts
Normal file
28
packages/backend/src/agents/titleGenerator.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { openai } from '../agent.js';
|
||||
import type { InterpreterOutput } from '../types/agents.js';
|
||||
|
||||
export async function generateTitle(interpreter: InterpreterOutput): Promise<string> {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-4o-mini',
|
||||
temperature: 0.7,
|
||||
service_tier: 'flex',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: `Generate a concise 5-8 word title for a TV show recommendation session.
|
||||
Capture the essence of the user's taste — genre, tone, key themes.
|
||||
Respond with ONLY the title. No quotes, no trailing punctuation.
|
||||
Examples: "Dark Crime Dramas With Moral Ambiguity", "Cozy British Mysteries With Quirky Detectives"`,
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: `Liked: ${JSON.stringify(interpreter.liked)}
|
||||
Themes: ${JSON.stringify(interpreter.themes)}
|
||||
Tone: ${JSON.stringify(interpreter.tone)}
|
||||
Character preferences: ${JSON.stringify(interpreter.character_preferences)}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return (response.choices[0]?.message?.content ?? '').trim() || 'My Recommendation Session';
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export const recommendations = pgTable('recommendations', {
|
||||
liked_shows: text('liked_shows').notNull().default(''),
|
||||
disliked_shows: text('disliked_shows').notNull().default(''),
|
||||
themes: text('themes').notNull().default(''),
|
||||
brainstorm_count: integer('brainstorm_count').notNull().default(100),
|
||||
recommendations: jsonb('recommendations').$type<CuratorOutput[]>(),
|
||||
status: text('status').notNull().default('pending'),
|
||||
created_at: timestamp('created_at').defaultNow().notNull(),
|
||||
|
||||
@@ -6,6 +6,7 @@ import { runRetrieval } from '../agents/retrieval.js';
|
||||
import { runRanking } from '../agents/ranking.js';
|
||||
import { runCurator } from '../agents/curator.js';
|
||||
import type { CuratorOutput, SSEEvent } from '../types/agents.js';
|
||||
import { generateTitle } from '../agents/titleGenerator.js';
|
||||
|
||||
/* -- Agent pipeline --
|
||||
[1] Interpreter -> gets user input, transforms into structured data
|
||||
@@ -69,7 +70,7 @@ export async function runPipeline(
|
||||
log(rec.id, 'Retrieval: start');
|
||||
sseWrite({ stage: 'retrieval', status: 'start' });
|
||||
const t1 = Date.now();
|
||||
const retrievalOutput = await runRetrieval(interpreterOutput);
|
||||
const retrievalOutput = await runRetrieval(interpreterOutput, rec.brainstorm_count);
|
||||
log(rec.id, `Retrieval: done (${Date.now() - t1}ms) — ${retrievalOutput.candidates.length} candidates`, {
|
||||
titles: retrievalOutput.candidates.map((c) => c.title),
|
||||
});
|
||||
@@ -98,11 +99,21 @@ export async function runPipeline(
|
||||
log(rec.id, `Curator: done (${Date.now() - t3}ms) — ${curatorOutput.length} shows curated`);
|
||||
sseWrite({ stage: 'curator', status: 'done', data: curatorOutput });
|
||||
|
||||
// Generate AI title
|
||||
let aiTitle: string = rec.title;
|
||||
try {
|
||||
log(rec.id, 'Title generation: start');
|
||||
aiTitle = await generateTitle(interpreterOutput);
|
||||
log(rec.id, `Title generation: done — "${aiTitle}"`);
|
||||
} catch (err) {
|
||||
log(rec.id, `Title generation failed, keeping initial title: ${String(err)}`);
|
||||
}
|
||||
|
||||
// Save results to DB
|
||||
log(rec.id, 'Saving results to DB');
|
||||
await db
|
||||
.update(recommendations)
|
||||
.set({ recommendations: curatorOutput, status: 'done' })
|
||||
.set({ recommendations: curatorOutput, status: 'done', title: aiTitle })
|
||||
.where(eq(recommendations.id, rec.id));
|
||||
|
||||
sseWrite({ stage: 'complete', status: 'done' });
|
||||
|
||||
@@ -13,6 +13,7 @@ export default async function recommendationsRoute(fastify: FastifyInstance) {
|
||||
liked_shows?: string;
|
||||
disliked_shows?: string;
|
||||
themes?: string;
|
||||
brainstorm_count?: number;
|
||||
};
|
||||
|
||||
const title = (body.main_prompt ?? '')
|
||||
@@ -21,6 +22,9 @@ export default async function recommendationsRoute(fastify: FastifyInstance) {
|
||||
.slice(0, 5)
|
||||
.join(' ');
|
||||
|
||||
const rawCount = Number(body.brainstorm_count ?? 100);
|
||||
const brainstorm_count = Number.isFinite(rawCount) ? Math.min(200, Math.max(50, rawCount)) : 100;
|
||||
|
||||
const [rec] = await db
|
||||
.insert(recommendations)
|
||||
.values({
|
||||
@@ -29,6 +33,7 @@ export default async function recommendationsRoute(fastify: FastifyInstance) {
|
||||
liked_shows: body.liked_shows ?? '',
|
||||
disliked_shows: body.disliked_shows ?? '',
|
||||
themes: body.themes ?? '',
|
||||
brainstorm_count,
|
||||
status: 'pending',
|
||||
})
|
||||
.returning({ id: recommendations.id });
|
||||
|
||||
Reference in New Issue
Block a user