@@ -258,6 +258,119 @@ ${truncatedContent}
258258 } ;
259259}
260260
261+ /**
262+ * ์ฌ๋ฌ ์๋ฃจ์
ํ์ผ์ ์ ๊ทผ๋ฒ ์ผ์น ์ฌ๋ถ๋ฅผ ํ ๋ฒ์ API ํธ์ถ๋ก ์ผ๊ด ๋ถ์.
263+ * subrequest ์๋ฅผ ์ค์ด๊ธฐ ์ํด ํ์ผ๋น ๊ฐ๋ณ ํธ์ถ ๋์ ๋ฐฐ์น๋ก ์ฒ๋ฆฌํ๋ค.
264+ *
265+ * @param {Array<{problemName: string, fileContent: string, problemInfo: object}> } items
266+ * @param {string } apiKey - OpenAI API ํค
267+ * @returns {Promise<{results: Array<{matches: boolean, explanation: string}>, usage: object|null}> }
268+ */
269+ export async function generateBatchApproachAnalysis ( items , apiKey ) {
270+ if ( items . length === 0 ) return { results : [ ] , usage : null } ;
271+
272+ // ๋จ๊ฑด์ด๋ฉด ๊ธฐ์กด ํจ์ ์์
273+ if ( items . length === 1 ) {
274+ const { fileContent, problemName, problemInfo } = items [ 0 ] ;
275+ const result = await generateApproachAnalysis ( fileContent , problemName , problemInfo , apiKey ) ;
276+ return {
277+ results : [ { matches : result . matches , explanation : result . explanation } ] ,
278+ usage : result . usage ?? null ,
279+ } ;
280+ }
281+
282+ const systemPrompt = `You are an algorithm analysis expert. You will receive multiple problems. For each one, determine if the submitted code matches the intended approach.
283+
284+ Respond with a JSON object containing a "results" array with exactly ${ items . length } entries, in the same order as the input:
285+ {
286+ "results": [
287+ { "matches": true, "explanation": "ํ๊ตญ์ด 1๋ฌธ์ฅ, 80์ ์ด๋ด" },
288+ ...
289+ ]
290+ }
291+
292+ Rules:
293+ - matches=true if the core data structure or algorithm matches the intended approach
294+ - matches=false if brute force was used when an optimized approach was intended
295+ - Keep each explanation to 1 sentence in Korean, 80 characters or fewer
296+ - You MUST return exactly ${ items . length } results` ;
297+
298+ const MAX_BATCH_FILE_SIZE = 5000 ;
299+
300+ const problemSections = items . map ( ( { problemName, fileContent, problemInfo } , i ) => {
301+ const truncated = fileContent . slice ( 0 , MAX_BATCH_FILE_SIZE ) ;
302+ return `## ๋ฌธ์ ${ i + 1 } : ${ problemName }
303+ - ๋์ด๋: ${ problemInfo . difficulty }
304+ - ์นดํ
๊ณ ๋ฆฌ: ${ ( problemInfo . categories || [ ] ) . join ( ", " ) }
305+ - ์๋๋ ์ ๊ทผ๋ฒ: ${ problemInfo . intended_approach }
306+
307+ \`\`\`
308+ ${ truncated }
309+ \`\`\`` ;
310+ } ) ;
311+
312+ const userPrompt = problemSections . join ( "\n\n" ) +
313+ `\n\n์ ${ items . length } ๊ฐ ์ฝ๋๊ฐ ๊ฐ๊ฐ ์๋๋ ์ ๊ทผ๋ฒ๊ณผ ์ผ์นํ๋์ง ๋ถ์ํด์ฃผ์ธ์.` ;
314+
315+ const response = await fetch ( "https://api.openai.com/v1/chat/completions" , {
316+ method : "POST" ,
317+ headers : {
318+ Authorization : `Bearer ${ apiKey } ` ,
319+ "Content-Type" : "application/json" ,
320+ } ,
321+ body : JSON . stringify ( {
322+ model : "gpt-4.1-nano" ,
323+ messages : [
324+ { role : "system" , content : systemPrompt } ,
325+ { role : "user" , content : userPrompt } ,
326+ ] ,
327+ response_format : { type : "json_object" } ,
328+ max_tokens : 200 * items . length ,
329+ temperature : 0.2 ,
330+ } ) ,
331+ } ) ;
332+
333+ if ( ! response . ok ) {
334+ const error = await response . text ( ) ;
335+ throw new Error ( `OpenAI batch API error: ${ error } ` ) ;
336+ }
337+
338+ const data = await response . json ( ) ;
339+ const content = data . choices [ 0 ] ?. message ?. content ;
340+
341+ if ( ! content ) {
342+ throw new Error ( "Empty response from OpenAI batch analysis" ) ;
343+ }
344+
345+ let parsed ;
346+ try {
347+ parsed = JSON . parse ( content ) ;
348+ } catch {
349+ throw new Error ( `OpenAI returned invalid JSON: ${ content . slice ( 0 , 200 ) } ` ) ;
350+ }
351+
352+ const rawResults = parsed . results ;
353+ if ( ! Array . isArray ( rawResults ) ) {
354+ throw new Error ( `OpenAI did not return a results array` ) ;
355+ }
356+
357+ if ( rawResults . length !== items . length ) {
358+ console . warn (
359+ `[generateBatchApproachAnalysis] Expected ${ items . length } results, got ${ rawResults . length } `
360+ ) ;
361+ }
362+
363+ const results = items . map ( ( _ , i ) => {
364+ const r = rawResults [ i ] ;
365+ return {
366+ matches : r ?. matches === true ,
367+ explanation : typeof r ?. explanation === "string" ? r . explanation : "" ,
368+ } ;
369+ } ) ;
370+
371+ return { results, usage : data . usage ?? null } ;
372+ }
373+
261374/**
262375 * ์๋ฃจ์
์ ์๊ฐ/๊ณต๊ฐ ๋ณต์ก๋ ๋ถ์.
263376 * ์ฌ์ฉ์๊ฐ ์ฝ๋ ์ด๋๊ฐ์ ์์ ํฌ๋งท์ผ๋ก ๋จ๊ธด TC/SC ์ฃผ์์ ํจ๊ป ์ถ์ถํ์ฌ
0 commit comments