fix(phase2): 타로 interpret 견고성 — maxOutputTokens 8192 + wall-clock 가드로 호출 상한 축소
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,11 +16,16 @@ loadDotenv({ path: resolve(process.cwd(), '.env.local'), override: true });
|
||||
|
||||
// 모델 우선순위 — 강력한 순서 (이 API 키로 접근 가능한 모델만) — 사주 analyze와 동일 폴백 목록
|
||||
const MODELS = [
|
||||
{ id: 'gemini-2.5-pro', maxTokens: 4096 },
|
||||
{ id: 'gemini-2.5-flash', maxTokens: 4096 },
|
||||
{ id: 'gemini-2.0-flash', maxTokens: 4096 },
|
||||
{ id: 'gemini-2.5-pro', maxTokens: 8192 },
|
||||
{ id: 'gemini-2.5-flash', maxTokens: 8192 },
|
||||
{ id: 'gemini-2.0-flash', maxTokens: 8192 },
|
||||
] as const;
|
||||
|
||||
// wall-clock 예산 — maxDuration(60s)보다 여유 있게 끊어 graceful 502를 반환
|
||||
const TIME_BUDGET_MS = 45_000;
|
||||
// 최악 호출 수 상한 — 모델 폴백 × 검증 실패 reroll을 합쳐도 이 값을 넘지 않음
|
||||
const MAX_ATTEMPTS = 3;
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// 1) 인증 — 로그인 사용자만 (Gemini API 무단 호출 방지)
|
||||
const supabase = await createClient();
|
||||
@@ -71,10 +76,21 @@ export async function POST(request: Request) {
|
||||
context_meta,
|
||||
});
|
||||
|
||||
// 5) 호출 — 모델 폴백 × 검증 실패 시 사유 주입 reroll(최대 2 시도)
|
||||
// 5) 호출 — 모델 폴백 + 검증 실패 시 같은 모델로 1회 reroll
|
||||
// wall-clock 45s 예산과 총 호출 3회 상한으로 최악 케이스를 조기 종료(→ 502)
|
||||
const startedAt = Date.now();
|
||||
let feedback = '';
|
||||
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||
for (const { id: modelId, maxTokens } of MODELS) {
|
||||
let attempts = 0;
|
||||
|
||||
modelLoop:
|
||||
for (const { id: modelId, maxTokens } of MODELS) {
|
||||
// retry 0: 최초 시도, retry 1: 검증 실패 시에만 같은 모델로 1회 reroll
|
||||
for (let retry = 0; retry < 2; retry += 1) {
|
||||
if (attempts >= MAX_ATTEMPTS || Date.now() - startedAt > TIME_BUDGET_MS) {
|
||||
break modelLoop;
|
||||
}
|
||||
attempts += 1;
|
||||
|
||||
try {
|
||||
const model = genAI.getGenerativeModel({
|
||||
model: modelId,
|
||||
@@ -100,9 +116,12 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ interpretation_json: parsed, model: modelId });
|
||||
}
|
||||
|
||||
// 검증 실패 — 사유를 피드백으로 주입해 같은 모델로 1회 reroll(retry 루프 계속)
|
||||
feedback = invalid ?? 'JSON 파싱 실패';
|
||||
} catch (modelError) {
|
||||
// 호출 자체의 예외(레이트리밋 등)는 reroll하지 않고 바로 다음 모델로 폴백
|
||||
feedback = modelError instanceof Error ? modelError.message : 'model error';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user