semillero-2-AI-ATS/app/api/candidates/route.ts
2026-06-09 10:25:06 -04:00

111 lines
3.2 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { createServerSupabaseClient } from "@/lib/supabase";
interface CandidateScore {
id: string;
candidate_id: string;
ai_score: number;
evaluation: {
summary: string;
classification: string;
suggestions: string;
riskLevel: string;
};
created_at: string;
}
interface RankedCandidate {
id: string;
name: string;
contact_info: {
email: string;
phone: string;
};
similarity?: number;
scores?: CandidateScore[];
}
export async function GET(request: NextRequest) {
try {
const supabase = createServerSupabaseClient();
const jobId = request.nextUrl.searchParams.get("jobId");
if (jobId) {
// 1. Fetch the job embedding
const { data: job, error: jobError } = await supabase
.from("jobs")
.select("embedding")
.eq("id", jobId)
.single();
if (jobError || !job) {
return NextResponse.json({ error: "Job not found or error fetching job" }, { status: 404 });
}
if (!job.embedding) {
return NextResponse.json({ error: "Job embedding not generated yet" }, { status: 400 });
}
// 2. Query similarity ranking using match_candidates rpc
const { data: rankedCandidates, error: matchError } = await supabase.rpc(
"match_candidates",
{
query_embedding: job.embedding,
match_threshold: -1.0,
match_count: 50,
}
);
if (matchError) {
return NextResponse.json({ error: matchError.message }, { status: 500 });
}
const candidatesList = (rankedCandidates as unknown as RankedCandidate[]) || [];
// 3. Fetch scores for these matched candidates to return AI scores/details
if (candidatesList.length > 0) {
const candidateIds = candidatesList.map((c) => c.id);
const { data: scores, error: scoresError } = await supabase
.from("scores")
.select("*")
.in("candidate_id", candidateIds);
if (!scoresError && scores) {
const typedScores = (scores as unknown as CandidateScore[]) || [];
// Merge scores into rankedCandidates
const scoresMap = new Map<string, CandidateScore[]>();
typedScores.forEach((s) => {
const list = scoresMap.get(s.candidate_id) || [];
list.push(s);
scoresMap.set(s.candidate_id, list);
});
candidatesList.forEach((c) => {
c.scores = scoresMap.get(c.id) || [];
});
} else {
candidatesList.forEach((c) => {
c.scores = [];
});
}
}
return NextResponse.json(candidatesList);
} else {
// Fetch all candidates sorted by created_at descending
const { data: candidates, error } = await supabase
.from("candidates")
.select("*, scores(*)")
.order("created_at", { ascending: false });
if (error) {
return NextResponse.json({ error: error.message }, { status: 500 });
}
return NextResponse.json(candidates);
}
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : "Internal Server Error";
return NextResponse.json({ error: errorMessage }, { status: 500 });
}
}