fitaiProto/apps/admin/src/app/api/recommendations/generate/route.ts
echo 28b5b52a8f context extended
maybe need polishing
2025-11-26 01:13:11 +01:00

192 lines
7.2 KiB
TypeScript

import { NextResponse } from "next/server";
import { getDatabase } from "@/lib/database";
import { buildAIContext } from "@/lib/ai/ai-context";
import { buildEnhancedPrompt, buildBasicPrompt } from "@/lib/ai/prompt-builder";
export async function POST(req: Request) {
try {
const { userId, useExternalModel } = await req.json();
if (!userId) {
return NextResponse.json({ error: "User ID is required" }, { status: 400 });
}
const db = await getDatabase();
// Fetch fitness profile
const profile = await db.getFitnessProfileByUserId(userId);
if (!profile) {
return NextResponse.json(
{ error: "Fitness profile not found for this user" },
{ status: 404 }
);
}
// Build AI context with goals and recommendations
let prompt: string;
try {
const context = await buildAIContext(userId);
prompt = buildEnhancedPrompt(context);
console.log('Using enhanced AI context with goals and history');
} catch (error) {
// Fallback to basic prompt if context building fails
console.warn('Failed to build AI context, using basic prompt:', error);
prompt = buildBasicPrompt(profile);
}
let parsedResponse;
if (useExternalModel) {
// Use DeepSeek AI
const deepseekApiKey = process.env.DEEPSEEK_API_KEY;
if (!deepseekApiKey) {
return NextResponse.json(
{ error: "DeepSeek API key not configured" },
{ status: 500 }
);
}
console.log("Using DeepSeek AI model...");
const deepseekResponse = await fetch("https://api.deepseek.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${deepseekApiKey}`,
},
body: JSON.stringify({
model: "deepseek-chat",
messages: [
{
role: "system",
content: "You are a professional fitness trainer and nutritionist. Always respond with valid JSON only, no markdown or code blocks."
},
{
role: "user",
content: prompt
}
],
temperature: 0.7,
max_tokens: 1000,
}),
});
if (!deepseekResponse.ok) {
const errorText = await deepseekResponse.text();
console.error("DeepSeek API error:", errorText);
return NextResponse.json(
{ error: "Failed to generate recommendation from DeepSeek AI" },
{ status: 500 }
);
}
const deepseekData = await deepseekResponse.json();
console.log("Raw DeepSeek Response:", deepseekData);
try {
const content = deepseekData.choices[0].message.content;
let cleanResponse = content.trim();
// Remove markdown code blocks if present
if (cleanResponse.startsWith("```json")) {
cleanResponse = cleanResponse.replace(/^```json\s*/, "").replace(/\s*```$/, "");
} else if (cleanResponse.startsWith("```")) {
cleanResponse = cleanResponse.replace(/^```\s*/, "").replace(/\s*```$/, "");
}
// Find the first '{' and last '}' to extract the JSON object
const firstBrace = cleanResponse.indexOf("{");
const lastBrace = cleanResponse.lastIndexOf("}");
if (firstBrace !== -1 && lastBrace !== -1) {
cleanResponse = cleanResponse.substring(firstBrace, lastBrace + 1);
}
parsedResponse = JSON.parse(cleanResponse);
} catch (e) {
console.error("Failed to parse DeepSeek response:", deepseekData);
return NextResponse.json(
{ error: "Invalid response format from DeepSeek AI" },
{ status: 500 }
);
}
} else {
// Use local Ollama
console.log("Using local Ollama model...");
const ollamaResponse = await fetch("http://localhost:11434/api/generate", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemma3:latest",
prompt: prompt,
stream: false,
format: "json",
}),
});
if (!ollamaResponse.ok) {
console.error("Ollama API error:", await ollamaResponse.text());
return NextResponse.json(
{ error: "Failed to generate recommendation from Ollama" },
{ status: 500 }
);
}
const aiData = await ollamaResponse.json();
console.log("Raw Ollama Response:", aiData.response);
try {
let cleanResponse = aiData.response.trim();
// Remove markdown code blocks if present
if (cleanResponse.startsWith("```json")) {
cleanResponse = cleanResponse.replace(/^```json\s*/, "").replace(/\s*```$/, "");
} else if (cleanResponse.startsWith("```")) {
cleanResponse = cleanResponse.replace(/^```\s*/, "").replace(/\s*```$/, "");
}
// Find the first '{' and last '}' to extract the JSON object
const firstBrace = cleanResponse.indexOf("{");
const lastBrace = cleanResponse.lastIndexOf("}");
if (firstBrace !== -1 && lastBrace !== -1) {
cleanResponse = cleanResponse.substring(firstBrace, lastBrace + 1);
}
parsedResponse = JSON.parse(cleanResponse);
} catch (e) {
console.error("Failed to parse Ollama response:", aiData.response);
return NextResponse.json(
{ error: "Invalid response format from Ollama" },
{ status: 500 }
);
}
}
// Save to database
const recommendation = await db.createRecommendation({
id: crypto.randomUUID(),
userId,
fitnessProfileId: profile.userId,
type: 'ai_plan',
content: parsedResponse.recommendationText,
activityPlan: parsedResponse.activityPlan,
dietPlan: parsedResponse.dietPlan,
status: 'pending'
});
return NextResponse.json(recommendation);
} catch (error) {
console.error("Error generating recommendation:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}