route.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. /**
  2. * PBL Runtime Chat API
  3. *
  4. * Handles @mention routing during PBL runtime.
  5. * Students @question or @judge an agent, and this endpoint generates a response.
  6. */
  7. import { NextRequest } from 'next/server';
  8. import { callLLM } from '@/lib/ai/llm';
  9. import type { PBLAgent, PBLIssue } from '@/lib/pbl/types';
  10. import { createLogger } from '@/lib/logger';
  11. import { apiError, apiSuccess } from '@/lib/server/api-response';
  12. import { resolveModelFromHeaders } from '@/lib/server/resolve-model';
  13. const log = createLogger('PBL Chat');
  14. interface PBLChatRequest {
  15. message: string;
  16. agent: PBLAgent;
  17. currentIssue: PBLIssue | null;
  18. recentMessages: { agent_name: string; message: string }[];
  19. userRole: string;
  20. agentType?: 'question' | 'judge';
  21. }
  22. export async function POST(req: NextRequest) {
  23. let agentName: string | undefined;
  24. let resolvedAgentType: string | undefined;
  25. try {
  26. const body = (await req.json()) as PBLChatRequest;
  27. const { message, agent, currentIssue, recentMessages, userRole, agentType } = body;
  28. agentName = agent?.name;
  29. resolvedAgentType = agentType;
  30. if (!message || !agent) {
  31. return apiError('MISSING_REQUIRED_FIELD', 400, 'Message and agent are required');
  32. }
  33. // Get model config from headers
  34. const { model } = await resolveModelFromHeaders(req);
  35. // Build context for the agent, differentiating question vs judge
  36. let issueContext = '';
  37. if (currentIssue) {
  38. issueContext = `\n\n## Current Issue\nTitle: ${currentIssue.title}\nDescription: ${currentIssue.description}\nPerson in Charge: ${currentIssue.person_in_charge}`;
  39. if (currentIssue.generated_questions) {
  40. if (agentType === 'judge') {
  41. issueContext += `\n\nQuestions to Evaluate Against:\n${currentIssue.generated_questions}`;
  42. } else {
  43. issueContext += `\n\nGenerated Questions:\n${currentIssue.generated_questions}`;
  44. }
  45. }
  46. }
  47. const recentContext =
  48. recentMessages.length > 0
  49. ? `\n\n## Recent Conversation\n${recentMessages
  50. .slice(-5)
  51. .map((m) => `${m.agent_name}: ${m.message}`)
  52. .join('\n')}`
  53. : '';
  54. const systemPrompt = `${agent.system_prompt}${issueContext}${recentContext}${userRole ? `\n\nThe student's role is: ${userRole}` : ''}`;
  55. const result = await callLLM(
  56. {
  57. model,
  58. system: systemPrompt,
  59. prompt: message,
  60. },
  61. 'pbl-chat',
  62. );
  63. return apiSuccess({ message: result.text, agentName: agent.name });
  64. } catch (error) {
  65. log.error(
  66. `PBL chat failed [agent="${agentName ?? 'unknown'}", type=${resolvedAgentType ?? 'question'}]:`,
  67. error,
  68. );
  69. return apiError('INTERNAL_ERROR', 500, error instanceof Error ? error.message : String(error));
  70. }
  71. }