feat: Bridge 타깃 토글 + /research 제거 + 환각·오염 방지 강화 (v2.2.205)
- Datacollect Bridge 로컬/NAS 타깃 토글(Settings 패널) + NAS URL/x-bridge-token. 기본 local = 현행 동작 유지. (백엔드 NAS 분리 준비) - /research(NotebookLM) 제거 — 로컬 Datacollect 앱 전용으로 분리. - 에러로그 오염 차단: STT/스택트레이스/에러덤프를 장기기억 채굴 제외 + 자동 추출 항목 14일 TTL(참조 시 슬라이딩 연장). 기존·수동 항목 무영향. - 컨텍스트 [주제] 태깅 + 교차오염 방지 경계 지침. - "확인 불가" 사실 날조 금지 규칙(R7과 구분). - /meet STT 오타 보정: 철자 정규화 허용하되 사실 날조는 차단. 타입체크 + 407 테스트 통과. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* Datacollect handlers — /research · /benchmark · /youtube · /blog · /wikify · /meet.
|
||||
* Datacollect handlers — /benchmark · /youtube · /blog · /wikify · /meet.
|
||||
* (/research(NotebookLM)는 v2.2.205 에서 제거 — 로컬 Datacollect 앱 전용으로 분리)
|
||||
*
|
||||
* v2.2.201 에서 slashRouter.ts 에서 분리. Datacollect bridge (port 3002) 통합
|
||||
* 슬래시 명령 클러스터 — NotebookLM Deep Research · Playwright 웹 스캔 ·
|
||||
@@ -33,100 +34,6 @@ import {
|
||||
parseActionItems,
|
||||
} from './scheduling/calendarHelpers';
|
||||
|
||||
// ───────────────────────────── /research ─────────────────────────────
|
||||
|
||||
async function runResearch(topic: string, view: Webview | undefined): Promise<boolean> {
|
||||
if (!topic) {
|
||||
chunk(view, `사용법: \`/research <주제>\`\n주제를 입력해 NotebookLM Deep Research를 시작합니다.\n`);
|
||||
return true;
|
||||
}
|
||||
|
||||
chunk(view, `🚀 **Research 시작**: \`${topic}\`\n\n`);
|
||||
const start = await bridgeFetch<{ success: boolean; notebookId: string; taskId: string }>(
|
||||
BRIDGE_API.research.start,
|
||||
{ method: 'POST', body: JSON.stringify({ topic }) },
|
||||
{ timeoutMs: 60_000 },
|
||||
);
|
||||
chunk(view, `- notebookId: \`${start.notebookId}\`\n- taskId: \`${start.taskId}\`\n\n⏳ 상태 polling (5초 간격, 최대 10분)…\n`);
|
||||
|
||||
const deadline = Date.now() + 10 * 60_000;
|
||||
const HEARTBEAT_MS = 30_000;
|
||||
const MAX_CONSECUTIVE_FAILS = 5;
|
||||
const COMPLETED_SET = new Set(['completed', 'done', 'success', 'finished']);
|
||||
const FAILED_SET = new Set(['failed', 'error', 'cancelled', 'canceled', 'aborted']);
|
||||
|
||||
let lastStatus = '';
|
||||
let lastChangeAt = Date.now();
|
||||
let consecutiveFails = 0;
|
||||
let pollCount = 0;
|
||||
let researchOk = false;
|
||||
while (Date.now() < deadline) {
|
||||
await new Promise(r => setTimeout(r, 5_000));
|
||||
pollCount++;
|
||||
let st: { success: boolean; result: any } | undefined;
|
||||
try {
|
||||
st = await bridgeFetch<{ success: boolean; result: any }>(
|
||||
`${BRIDGE_API.research.status}?notebookId=${encodeURIComponent(start.notebookId)}&taskId=${encodeURIComponent(start.taskId)}`,
|
||||
{ method: 'GET' },
|
||||
{ timeoutMs: 60_000 },
|
||||
);
|
||||
consecutiveFails = 0;
|
||||
} catch (e: any) {
|
||||
consecutiveFails++;
|
||||
if (consecutiveFails >= MAX_CONSECUTIVE_FAILS) {
|
||||
chunk(view, `\n❌ Status polling 연속 실패 ${consecutiveFails}회 — bridge 가 응답하지 않습니다. 중단합니다.\n(원인: ${e?.message || String(e)})\n`);
|
||||
return true;
|
||||
}
|
||||
chunk(view, `\n · status 호출 실패 ${consecutiveFails}/${MAX_CONSECUTIVE_FAILS} (${e?.message || 'unknown'})\n`);
|
||||
continue;
|
||||
}
|
||||
const status = String(st.result?.status || st.result || '').trim().toLowerCase();
|
||||
if (status && status !== lastStatus) {
|
||||
chunk(view, ` · ${status}\n`);
|
||||
lastStatus = status;
|
||||
lastChangeAt = Date.now();
|
||||
} else if (Date.now() - lastChangeAt > HEARTBEAT_MS) {
|
||||
chunk(view, ` · ⏳ 대기 중 (${Math.round((Date.now() - lastChangeAt) / 1000)}s, 폴링 ${pollCount}회)\n`);
|
||||
lastChangeAt = Date.now();
|
||||
}
|
||||
if (COMPLETED_SET.has(status)) { researchOk = true; break; }
|
||||
if (FAILED_SET.has(status)) {
|
||||
chunk(view, `\n❌ Research 실패: ${JSON.stringify(st.result).slice(0, 400)}\n`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!researchOk) {
|
||||
chunk(view, `\n❌ 10분 polling 후에도 완료 신호가 오지 않았습니다 (마지막 status: \`${lastStatus || '(없음)'}\`). 중단합니다.\n`);
|
||||
return true;
|
||||
}
|
||||
|
||||
chunk(view, `\n📥 import…\n`);
|
||||
await bridgeFetch(BRIDGE_API.research.import, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ notebookId: start.notebookId, taskId: start.taskId }),
|
||||
}, {
|
||||
timeoutMs: 300_000,
|
||||
onHeartbeat: (elapsedMs) => chunk(view, ` · import 진행 중 (${Math.round(elapsedMs / 1000)}s)\n`),
|
||||
});
|
||||
|
||||
chunk(view, `🧪 synthesize…\n\n`);
|
||||
const synth = await bridgeFetch<{ success: boolean; markdown?: string; result?: string }>(
|
||||
BRIDGE_API.research.synthesize,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ notebookId: start.notebookId, topic, rootTopic: topic, includeKnowledgeConnections: true }),
|
||||
},
|
||||
{
|
||||
timeoutMs: 600_000,
|
||||
onHeartbeat: (elapsedMs) => chunk(view, ` · synthesize LLM 작업 중 (${Math.round(elapsedMs / 1000)}s)\n`),
|
||||
},
|
||||
);
|
||||
const md = synth.markdown || synth.result || '(빈 응답)';
|
||||
chunk(view, `---\n\n${md}\n`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ───────────────────────────── /benchmark ─────────────────────────────
|
||||
|
||||
async function runBenchmark(arg: string, view: Webview | undefined): Promise<boolean> {
|
||||
@@ -749,7 +656,9 @@ async function runMeet(arg: string, view: Webview | undefined, context?: vscode.
|
||||
|
||||
// ─── 등록 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
registerSlashCommand({ name: '/research', description: 'NotebookLM Deep Research 호출 후 위키 합성', handler: runResearch });
|
||||
// /research(NotebookLM Deep Research)는 v2.2.205 에서 제거 — NotebookLM 은 로컬
|
||||
// Datacollect 앱 전용으로 분리(Chrome/Google 로그인 의존). ASTRA 백엔드는 NAS 경량
|
||||
// Bridge 로 운영 가능해야 하므로 brower-auth 가 필요한 명령은 두지 않는다.
|
||||
registerSlashCommand({ name: '/benchmark', description: 'Playwright 웹 벤치마크 + 4-렌즈 LLM 분석', handler: runBenchmark });
|
||||
registerSlashCommand({ name: '/youtube', description: 'YouTube 단일 영상 또는 채널/플레이리스트 분석', handler: runYoutube });
|
||||
registerSlashCommand({ name: '/blog', description: 'Blog Pipeline 안내 (Datacollect 별도 흐름)', handler: runBlog });
|
||||
|
||||
Reference in New Issue
Block a user