특정 uid로 실제 도구·모델 루프를 돌려 호출된 도구·토큰·속도·캐시 히트를 보고하는 /debug/chatToolsSheet, /debug/chatProbe(Sheet) 엔드포인트를 추가한다 (요청별 model 오버라이드로 A/B 가능). prod에 같은 검증을 돌리는 scripts/chat-tools-sheet.ts도 추가한다. 무인증 + 임의 uid의 개인 데이터를 노출하는 검수용 임시 엔드포인트다.
133 lines
5.8 KiB
TypeScript
133 lines
5.8 KiB
TypeScript
import { onRequest } from "firebase-functions/https";
|
|
import { logger } from "firebase-functions";
|
|
import { runDailyArchive } from "../scheduled/dailyArchive";
|
|
import { forceSyncDay } from "../services/gameSyncService";
|
|
import { runChatToolsSheet, renderSheetMarkdown } from "../services/chatToolsSheetService";
|
|
import { runChatProbe, runChatProbeBatch, renderProbeMarkdown } from "../services/chatProbeService";
|
|
import { sendError } from "../middleware/errors";
|
|
import { daysAgoKst, parseDateString } from "../types/dateString";
|
|
|
|
/**
|
|
* 임시 디버그 핸들러. 인증 없음 — 운영 안정화 후 제거할 것.
|
|
*
|
|
* - GET/POST `/debug/forceSync` — 어제(KST) 기준으로 라이브 데이터 sync 실행.
|
|
* - GET/POST `/debug/forceSync?date=YYYY-MM-DD` — 지정한 날짜를 강제 sync.
|
|
* - GET/POST `/debug/dailyArchive` — 어제(KST) 기준으로 dailyArchive 본체 실행.
|
|
* - GET/POST `/debug/dailyArchive?date=YYYY-MM-DD` — 지정한 날짜를 archive 대상으로 실행.
|
|
* - GET `/debug/chatToolsSheet?uid=UID` — 그 유저 기준으로 짹 도구 전체를 실호출한
|
|
* 결과 시트(JSON)를 반환. `&format=md`면 Markdown, `&player=`·`&date=`로 케이스 추가.
|
|
* - GET `/debug/chatProbe?uid=UID&q=문장` — 자연어 문장 1개를 실제 모델+도구 루프로
|
|
* 돌리고, 모델이 고른 도구·인자(toolCalls)와 최종 응답을 반환. `&model=`로 테스트 모델 지정.
|
|
* - GET `/debug/chatProbeSheet?uid=UID` — 기본 자연어 문장 세트를 배치로 프로브한
|
|
* 결과 시트(토큰·속도·캐시 포함). `&format=md`면 Markdown, `&model=<모델ID>`로 A/B
|
|
* (미지정 시 기본 lite. 타임아웃은 모든 모델 공평하게 24초).
|
|
* ⚠️ 무인증 + 임의 uid의 개인 데이터(출석·예측)를 노출하고 실제 Vertex 토큰을
|
|
* 소모한다 — 검수용 임시 엔드포인트다.
|
|
*/
|
|
export const debug = onRequest({ timeoutSeconds: 300 }, async (req, res) => {
|
|
const segs = req.path.replace(/^\/+|\/+$/g, "").split("/");
|
|
const tail = segs.slice(-1)[0];
|
|
|
|
try {
|
|
if (tail === "forceSync") {
|
|
const dateParam =
|
|
(typeof req.query.date === "string" && req.query.date) ||
|
|
(typeof req.body?.date === "string" && req.body.date) ||
|
|
undefined;
|
|
const dateString = dateParam ?
|
|
parseDateString(dateParam) :
|
|
daysAgoKst(1);
|
|
const ymd = dateString.replace(/-/g, "");
|
|
|
|
logger.info(`debug.forceSync triggered manually (ymd=${ymd})`);
|
|
const result = await forceSyncDay(ymd);
|
|
res.status(200).json({ ymd, ...result });
|
|
return;
|
|
}
|
|
|
|
if (tail === "dailyArchive") {
|
|
const dateParam =
|
|
(typeof req.query.date === "string" && req.query.date) ||
|
|
(typeof req.body?.date === "string" && req.body.date) ||
|
|
undefined;
|
|
const overrideDate = dateParam ? parseDateString(dateParam) : undefined;
|
|
|
|
logger.info(
|
|
`debug.dailyArchive triggered manually (date=${overrideDate ?? "yesterday"})`
|
|
);
|
|
const result = await runDailyArchive(overrideDate);
|
|
res.status(200).json(result);
|
|
return;
|
|
}
|
|
|
|
if (tail === "chatToolsSheet" || tail === "chat-tools-sheet") {
|
|
const uid =
|
|
(typeof req.query.uid === "string" && req.query.uid) ||
|
|
(typeof req.body?.uid === "string" && req.body.uid) ||
|
|
"";
|
|
if (!uid) {
|
|
res.status(400).json({ error: "uid is required (e.g. ?uid=<UID>)" });
|
|
return;
|
|
}
|
|
const player = typeof req.query.player === "string" ? req.query.player : undefined;
|
|
const date = typeof req.query.date === "string" ? req.query.date : undefined;
|
|
const format = typeof req.query.format === "string" ? req.query.format : "json";
|
|
|
|
logger.info(`debug.chatToolsSheet triggered (uid=${uid})`);
|
|
const sheet = await runChatToolsSheet(uid, { player, date });
|
|
|
|
if (format === "md" || format === "markdown") {
|
|
res.status(200).type("text/markdown; charset=utf-8").send(renderSheetMarkdown(sheet));
|
|
return;
|
|
}
|
|
res.status(200).json(sheet);
|
|
return;
|
|
}
|
|
|
|
if (tail === "chatProbe") {
|
|
const uid =
|
|
(typeof req.query.uid === "string" && req.query.uid) ||
|
|
(typeof req.body?.uid === "string" && req.body.uid) ||
|
|
"";
|
|
const q =
|
|
(typeof req.query.q === "string" && req.query.q) ||
|
|
(typeof req.body?.q === "string" && req.body.q) ||
|
|
"";
|
|
if (!uid || !q) {
|
|
res.status(400).json({ error: "uid and q are required (e.g. ?uid=<UID>&q=<문장>)" });
|
|
return;
|
|
}
|
|
const model = typeof req.query.model === "string" ? req.query.model : undefined;
|
|
logger.info(`debug.chatProbe triggered (uid=${uid}, model=${model ?? "default"})`);
|
|
const result = await runChatProbe(uid, q, model);
|
|
res.status(200).json(result);
|
|
return;
|
|
}
|
|
|
|
if (tail === "chatProbeSheet") {
|
|
const uid =
|
|
(typeof req.query.uid === "string" && req.query.uid) ||
|
|
(typeof req.body?.uid === "string" && req.body.uid) ||
|
|
"";
|
|
if (!uid) {
|
|
res.status(400).json({ error: "uid is required (e.g. ?uid=<UID>)" });
|
|
return;
|
|
}
|
|
const format = typeof req.query.format === "string" ? req.query.format : "json";
|
|
const model = typeof req.query.model === "string" ? req.query.model : undefined;
|
|
logger.info(`debug.chatProbeSheet triggered (uid=${uid}, model=${model ?? "default"})`);
|
|
const sheet = await runChatProbeBatch(uid, undefined, model);
|
|
if (format === "md" || format === "markdown") {
|
|
res.status(200).type("text/markdown; charset=utf-8").send(renderProbeMarkdown(sheet));
|
|
return;
|
|
}
|
|
res.status(200).json(sheet);
|
|
return;
|
|
}
|
|
|
|
res.status(404).json({ error: `Unknown: ${req.method} ${req.path}` });
|
|
} catch (err) {
|
|
sendError(res, err);
|
|
}
|
|
});
|