adding movies & web search tool
All checks were successful
Recommender Build and Deploy (internal) / Build Recommender Image (push) Successful in 4m0s
Recommender Build and Deploy (internal) / Deploy Recommender (internal) (push) Successful in 12s

This commit is contained in:
2026-03-26 20:35:22 -03:00
parent 6fdfc3797a
commit 1437092a42
25 changed files with 450 additions and 135 deletions

View File

@@ -5,14 +5,14 @@ import { runInterpreter } from '../agents/interpreter.js';
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 type { CuratorOutput, MediaType, SSEEvent } from '../types/agents.js';
import { generateTitle } from '../agents/titleGenerator.js';
/* -- Agent pipeline --
[1] Interpreter -> gets user input, transforms into structured data
[2] Retrieval -> gets shows from OpenAI (high temperature)
[3] Ranking -> ranks shows based on user input
[4] Curator -> curates shows based on user input
[2] Retrieval -> gets candidates from OpenAI (high temperature)
[3] Ranking -> ranks candidates based on user input
[4] Curator -> curates candidates based on user input
*/
type RecommendationRecord = typeof recommendations.$inferSelect;
@@ -33,8 +33,10 @@ export async function runPipeline(
): Promise<CuratorOutput[]> {
let currentStage: SSEEvent['stage'] = 'interpreter';
const startTime = Date.now();
const mediaType = (rec.media_type ?? 'tv_show') as MediaType;
const useWebSearch = rec.use_web_search ?? false;
log(rec.id, `Starting pipeline for "${rec.title}"${feedbackContext ? ' (with feedback context)' : ''}`);
log(rec.id, `Starting pipeline for "${rec.title}" [${mediaType}${useWebSearch ? ', web_search' : ''}]${feedbackContext ? ' (with feedback context)' : ''}`);
try {
// Set status to running
@@ -54,6 +56,7 @@ export async function runPipeline(
liked_shows: rec.liked_shows,
disliked_shows: rec.disliked_shows,
themes: rec.themes,
media_type: mediaType,
...(feedbackContext !== undefined ? { feedback_context: feedbackContext } : {}),
});
log(rec.id, `Interpreter: done (${Date.now() - t0}ms)`, {
@@ -70,7 +73,7 @@ export async function runPipeline(
log(rec.id, 'Retrieval: start');
sseWrite({ stage: 'retrieval', status: 'start' });
const t1 = Date.now();
const retrievalOutput = await runRetrieval(interpreterOutput, rec.brainstorm_count);
const retrievalOutput = await runRetrieval(interpreterOutput, rec.brainstorm_count, mediaType, useWebSearch);
log(rec.id, `Retrieval: done (${Date.now() - t1}ms) — ${retrievalOutput.candidates.length} candidates`, {
titles: retrievalOutput.candidates.map((c) => c.title),
});
@@ -81,7 +84,7 @@ export async function runPipeline(
log(rec.id, 'Ranking: start');
sseWrite({ stage: 'ranking', status: 'start' });
const t2 = Date.now();
const rankingOutput = await runRanking(interpreterOutput, retrievalOutput);
const rankingOutput = await runRanking(interpreterOutput, retrievalOutput, mediaType);
log(rec.id, `Ranking: done (${Date.now() - t2}ms)`, {
definitely_like: rankingOutput.definitely_like.length,
might_like: rankingOutput.might_like.length,
@@ -95,15 +98,15 @@ export async function runPipeline(
log(rec.id, 'Curator: start');
sseWrite({ stage: 'curator', status: 'start' });
const t3 = Date.now();
const curatorOutput = await runCurator(rankingOutput, interpreterOutput);
log(rec.id, `Curator: done (${Date.now() - t3}ms) — ${curatorOutput.length} shows curated`);
const curatorOutput = await runCurator(rankingOutput, interpreterOutput, mediaType, useWebSearch);
log(rec.id, `Curator: done (${Date.now() - t3}ms) — ${curatorOutput.length} items 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);
aiTitle = await generateTitle(interpreterOutput, mediaType);
log(rec.id, `Title generation: done — "${aiTitle}"`);
} catch (err) {
log(rec.id, `Title generation failed, keeping initial title: ${String(err)}`);