@@ -27,7 +27,7 @@ import { postLearningStatus } from "./learning-status.js";
2727/**
2828 * GitHub webhook 이벤트 처리
2929 */
30- export async function handleWebhook ( request , env ) {
30+ export async function handleWebhook ( request , env , ctx ) {
3131 try {
3232 const payload = await request . json ( ) ;
3333 const eventType = request . headers . get ( "X-GitHub-Event" ) ;
@@ -54,7 +54,7 @@ export async function handleWebhook(request, env) {
5454 return handleProjectsV2ItemEvent ( payload , env ) ;
5555
5656 case "pull_request" :
57- return handlePullRequestEvent ( payload , env ) ;
57+ return handlePullRequestEvent ( payload , env , ctx ) ;
5858
5959 case "issue_comment" :
6060 return handleIssueCommentEvent ( payload , env ) ;
@@ -240,7 +240,7 @@ async function getChangedFilenames(repoOwner, repoName, baseSha, headSha, appTok
240240 * - opened/reopened: Week 설정 체크 + 알고리즘 패턴 태깅 (전체 파일)
241241 * - synchronize: 알고리즘 패턴 태깅만 (변경된 파일만, Week 체크 스킵)
242242 */
243- async function handlePullRequestEvent ( payload , env ) {
243+ async function handlePullRequestEvent ( payload , env , ctx ) {
244244 const action = payload . action ;
245245
246246 // opened, reopened, synchronize 액션만 처리
@@ -284,8 +284,51 @@ async function handlePullRequestEvent(payload, env) {
284284 console . log ( `PR synchronized: #${ prNumber } ` ) ;
285285 }
286286
287- // 알고리즘 패턴 태깅 (OPENAI_API_KEY 있을 때만)
288- if ( env . OPENAI_API_KEY ) {
287+ // AI 핸들러들을 별도 Worker 호출로 디스패치 (각각 독립적인 subrequest 예산)
288+ if ( env . OPENAI_API_KEY && env . INTERNAL_SECRET ) {
289+ const baseUrl = env . WORKER_URL || "https://github.daleseo.workers.dev" ;
290+
291+ const dispatchHeaders = {
292+ "Content-Type" : "application/json" ,
293+ "X-Internal-Secret" : env . INTERNAL_SECRET ,
294+ } ;
295+
296+ const commonPayload = { repoOwner, repoName, prNumber } ;
297+
298+ // 패턴 태깅 디스패치
299+ ctx . waitUntil (
300+ fetch ( `${ baseUrl } /internal/tag-patterns` , {
301+ method : "POST" ,
302+ headers : dispatchHeaders ,
303+ body : JSON . stringify ( {
304+ ...commonPayload ,
305+ headSha : pr . head . sha ,
306+ prData : pr ,
307+ } ) ,
308+ } ) . catch ( ( err ) =>
309+ console . error ( `[dispatch] tagPatterns failed: ${ err . message } ` )
310+ )
311+ ) ;
312+
313+ // 학습 현황 디스패치
314+ ctx . waitUntil (
315+ fetch ( `${ baseUrl } /internal/learning-status` , {
316+ method : "POST" ,
317+ headers : dispatchHeaders ,
318+ body : JSON . stringify ( {
319+ ...commonPayload ,
320+ username : pr . user . login ,
321+ } ) ,
322+ } ) . catch ( ( err ) =>
323+ console . error ( `[dispatch] learningStatus failed: ${ err . message } ` )
324+ )
325+ ) ;
326+
327+ console . log ( `[handlePullRequestEvent] Dispatched 2 AI handlers for PR #${ prNumber } ` ) ;
328+ } else if ( env . OPENAI_API_KEY ) {
329+ // INTERNAL_SECRET 미설정 시 기존 방식으로 폴백 (동일 invocation에서 순차 실행)
330+ console . warn ( "[handlePullRequestEvent] INTERNAL_SECRET not set, running handlers in-process" ) ;
331+
289332 try {
290333 // synchronize일 때만 변경 파일 목록 추출 (최적화: #7)
291334 let changedFilenames = null ;
@@ -314,24 +357,12 @@ async function handlePullRequestEvent(payload, env) {
314357 ) ;
315358 } catch ( error ) {
316359 console . error ( `[handlePullRequestEvent] tagPatterns failed: ${ error . message } ` ) ;
317- // 패턴 태깅 실패는 전체 흐름을 중단시키지 않음
318360 }
319- }
320361
321- // 학습 현황 댓글 (OPENAI_API_KEY 있을 때만)
322- if ( env . OPENAI_API_KEY ) {
323362 try {
324- await postLearningStatus (
325- repoOwner ,
326- repoName ,
327- prNumber ,
328- pr . user . login ,
329- appToken ,
330- env . OPENAI_API_KEY
331- ) ;
363+ await postLearningStatus ( repoOwner , repoName , prNumber , pr . user . login , appToken , env . OPENAI_API_KEY ) ;
332364 } catch ( error ) {
333365 console . error ( `[handlePullRequestEvent] learningStatus failed: ${ error . message } ` ) ;
334- // 학습 현황 실패는 전체 흐름을 중단시키지 않음
335366 }
336367 }
337368
0 commit comments