| import { randomUUID } from "node:crypto"; |
| import { NextResponse } from "next/server"; |
|
|
| export const dynamic = "force-dynamic"; |
|
|
| type RunTaskBody = { |
| prompt?: string; |
| }; |
|
|
| type ExecutionStep = { |
| stepId: string; |
| title: string; |
| description: string; |
| kind: "decision" | "tool_call" | "validation"; |
| order: number; |
| }; |
|
|
| type ExecutionPlan = { |
| goal: string; |
| successDefinition: string; |
| steps: ExecutionStep[]; |
| }; |
|
|
| function buildPlan(prompt: string): ExecutionPlan { |
| return { |
| goal: prompt, |
| successDefinition: "Generate a clear, concise response to the prompt.", |
| steps: [ |
| { |
| stepId: "step-1-prep", |
| title: "Prepare context", |
| description: "Read the prompt and set up the execution context.", |
| kind: "decision", |
| order: 1, |
| }, |
| { |
| stepId: "step-2-call", |
| title: "Call LLM provider", |
| description: "Use the configured LLM provider to generate a response.", |
| kind: "tool_call", |
| order: 2, |
| }, |
| { |
| stepId: "step-3-done", |
| title: "Return result", |
| description: "Format and return the final response.", |
| kind: "validation", |
| order: 3, |
| }, |
| ], |
| }; |
| } |
|
|
| function formatTrace(plan: ExecutionPlan): string[] { |
| const trace = [ |
| `Goal: ${plan.goal}`, |
| `Success definition: ${plan.successDefinition}`, |
| ]; |
|
|
| for (const step of plan.steps) { |
| trace.push(`${step.order}. ${step.title} [${step.kind}] — ${step.description}`); |
| } |
|
|
| return trace; |
| } |
|
|
| async function callLlm(prompt: string, systemPrompt: string): Promise<string> { |
| const apiKey = process.env.OPENAI_API_KEY || ""; |
| const model = process.env.HF_DEMO_MODEL || "gpt-4o-mini"; |
| const baseUrl = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1"; |
|
|
| if (!apiKey) { |
| return "LLM API key not configured (OPENAI_API_KEY)."; |
| } |
|
|
| try { |
| const response = await fetch(`${baseUrl}/chat/completions`, { |
| method: "POST", |
| headers: { |
| "Content-Type": "application/json", |
| Authorization: `Bearer ${apiKey}`, |
| }, |
| body: JSON.stringify({ |
| model, |
| messages: [ |
| { role: "system", content: systemPrompt }, |
| { role: "user", content: prompt }, |
| ], |
| temperature: 0.2, |
| max_tokens: 1024, |
| }), |
| }); |
|
|
| const data = (await response.json()) as Record<string, unknown>; |
|
|
| if (!response.ok) { |
| const errorMsg = (data as Record<string, unknown>).error; |
| return `LLM error: ${typeof errorMsg === "object" ? JSON.stringify(errorMsg) : errorMsg}`; |
| } |
|
|
| const choices = data.choices as unknown[]; |
| if (!Array.isArray(choices) || !choices[0]) return "No response from LLM."; |
|
|
| const choice = choices[0] as Record<string, unknown>; |
| const message = choice.message as Record<string, unknown> | undefined; |
| if (!message) return "No content in LLM response."; |
|
|
| const content = message.content; |
| return typeof content === "string" ? content.trim() : ""; |
| } catch (error) { |
| const msg = error instanceof Error ? error.message : String(error); |
| return `Failed to call LLM: ${msg}`; |
| } |
| } |
|
|
| export async function POST(request: Request) { |
| let body: RunTaskBody; |
|
|
| try { |
| body = (await request.json()) as RunTaskBody; |
| } catch { |
| body = {}; |
| } |
|
|
| const prompt = typeof body.prompt === "string" ? body.prompt.trim() : ""; |
|
|
| if (!prompt) { |
| return NextResponse.json( |
| { error: "Prompt is required." }, |
| { status: 400 } |
| ); |
| } |
|
|
| const plan = buildPlan(prompt); |
| const taskId = randomUUID(); |
| const conversationId = randomUUID(); |
| const providerLabel = process.env.LLM_PROVIDER || "openai"; |
|
|
| |
| const finalResult = await callLlm( |
| prompt, |
| "You are a helpful assistant. Provide a clear, concise response." |
| ); |
|
|
| return NextResponse.json({ |
| providerUsed: providerLabel, |
| goal: plan.goal, |
| successDefinition: plan.successDefinition, |
| executionTrace: formatTrace(plan), |
| finalResult: finalResult || `Processed prompt: ${prompt.slice(0, 100)}...`, |
| planNotes: "Hackathon demo using minimal LLM provider abstraction.", |
| taskId, |
| conversationId, |
| }); |
| } |