11import { NextRequest , NextResponse } from 'next/server' ;
2- import OpenAI , { AzureOpenAI } from 'openai' ;
32import fs from 'fs' ;
43import path from 'path' ;
54import { getAppSessionFromRequest , getChatSession , validateAppSession } from '@/lib/session' ;
@@ -9,17 +8,14 @@ import { trace, Span } from '@opentelemetry/api';
98import { getExerciseByNameWithResponse } from '@/lib/exercise-file-manager' ;
109import { executePython } from './codeExecutionTool' ;
1110import { runOpenAI } from './openaiRunner' ;
11+ import { decrypt , encrypt } from '@/lib/encryption' ;
1212
1313const tracer = trace . getTracer ( 'ai-workshop-chat' ) ;
1414
15- // In-memory store for session ID -> OpenAI response ID mapping
16- // TODO: Persist this to a database later
17- const sessionResponseMap = new Map < string , { previousResponseId : string ; sessionInstanceId : string } > ( ) ;
18-
1915/**
2016 * @route POST /api/chat
2117 * @desc Send a chat message
22- * @body { message: string, resetConversation: boolean }
18+ * @body { message: string, encryptedPreviousResponseId: string | undefined }
2319 * @urlParam exercise: string
2420 * @response 200 { response: string } or 400 { error: string }
2521 * @access Protected (any authenticated user/workshop)
@@ -34,7 +30,7 @@ export async function POST(request: NextRequest) {
3430 }
3531
3632 // Get body payload and query string parameters
37- const { message, resetConversation } = await request . json ( ) ;
33+ const { message, encryptedPreviousResponseId } = await request . json ( ) ;
3834 if ( ! message || typeof message !== 'string' ) {
3935 return NextResponse . json ( { error : 'Message is required' } , { status : 400 } ) ;
4036 }
@@ -60,14 +56,15 @@ export async function POST(request: NextRequest) {
6056 await session . save ( ) ;
6157 }
6258
63- // Handle conversation reset if requested
64- if ( resetConversation === true ) {
65- sessionResponseMap . delete ( sessionId ) ;
59+ let previousResponseId : string | undefined = undefined ;
60+ if ( encryptedPreviousResponseId ) {
61+ // console.log('Received encryptedPreviousResponseId:', encryptedPreviousResponseId);
62+ try {
63+ previousResponseId = decrypt ( encryptedPreviousResponseId , Buffer . from ( process . env . PREVIOUS_RESPONSE_ID_SECRET ! , 'hex' ) ) ;
64+ } catch {
65+ }
6666 }
6767
68- // Get previous response ID from in-memory store
69- let { previousResponseId, sessionInstanceId } = sessionResponseMap . get ( sessionId ) || { previousResponseId : undefined , sessionInstanceId : crypto . randomUUID ( ) } ;
70-
7168 // Read system prompt
7269 const systemPromptPath = path . join ( process . cwd ( ) , 'prompts' , exerciseData . folder , exerciseData . system_prompt_file ) ;
7370 let systemPrompt = await fs . promises . readFile ( systemPromptPath , { encoding : 'utf-8' } ) ;
@@ -105,24 +102,35 @@ Achtung! Die Dateien haben mehr Zeilen als hier gezeigt. Alle Dateien sind im Or
105102 const result = await executePython (
106103 script ,
107104 exerciseData . data_files . map ( f => path . join ( process . cwd ( ) , 'prompts' , exerciseData . folder , f ) ) ,
108- sessionInstanceId
105+ sessionId
109106 ) ;
110107 if ( result . resultFiles ) {
111108 for ( const resultFile of result . resultFiles ) {
109+ // TODO: handle non-image files differently (see GH issue #29)
112110 const markdownImage = `` ;
113111 const data = JSON . stringify ( { delta : `\n\n${ markdownImage } \n\n` } ) ;
114112 controller . enqueue ( encoder . encode ( `data: ${ data } \n\n` ) ) ;
115113 }
116114 }
115+ if ( result . stdout || result . stderr ) {
116+ // TODO: send the output as a collapsed section (see GH issue #17)
117+ }
117118 return JSON . stringify ( result ) ;
118119 } ,
119120 welcomeMessage
120121 ) ;
121122
122- sessionResponseMap . set ( sessionId , {
123- previousResponseId : newPreviousResponseId ,
124- sessionInstanceId,
125- } ) ;
123+ // Update session with new response ID
124+ // Encrypt previousResponseId before sending to client
125+ let encryptedResponseId : string | undefined ;
126+ // console.log('New previousResponseId:', newPreviousResponseId);
127+ // console.log('Encryption key:', process.env.PREVIOUS_RESPONSE_ID_SECRET);
128+ // Revert key.toString('hex') first
129+
130+ encryptedResponseId = encrypt ( newPreviousResponseId , Buffer . from ( process . env . PREVIOUS_RESPONSE_ID_SECRET ! , 'hex' ) ) ;
131+
132+ const data = JSON . stringify ( { encryptedResponseId } ) ;
133+ controller . enqueue ( encoder . encode ( `data: ${ data } \n\n` ) ) ;
126134
127135 controller . enqueue ( encoder . encode ( `data: [DONE]\n\n` ) ) ;
128136 } catch ( error ) {
0 commit comments