v2.2.256: 코어 채팅 큰 입력 청킹·통합 + 실제 컨텍스트 창 정렬 + 모델 핸들 race 수정
큰 입력 시 "Failed to acquire LM Studio model handle … Operation canceled" 로 턴 전체가 죽던 문제를 3계층으로 해결. 일반 채팅(코어 경로)은 그동안 단일 예산 호출이라 약한 모델·큰 입력에서 무너졌다 — 그 갭을 메움. - 핸들 race 수정: getModelHandle 을 재시도 루프 안으로 이동. 취소/죽은-핸들 류 에러는 SDK 재생성 후 1회 자동 재시도(실제 사용자 취소는 존중). 라이프 사이클의 동시 로드가 abort 되며 SDK 가 coalesce 한 JIT 조회까지 죽던 것. - Phase 1 실제 창 정렬: llm.getContextLength()(캐시)로 실측 창에 예산 클램프. 설정값보다 작은 창으로 로드된 경우 서버 truncation/빈 답변 차단. 배지에 표시. - Phase 2 코어 Map-Reduce: 단일 입력이 (유효 창 × ratio) 초과 시 청크→질의 인지형 추출→통합. 부분/전체 폴백, 무관 시 정직 신호. 동시성 기본 2. - Phase 3 메타 노출: 진행/결과 배지 표시, [조각 k] 출처 옵트인. 신규 설정 5종. /meet·/review 전용 경로는 불변. 테스트 +25건, 전체 684 통과. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+30
-2
@@ -444,13 +444,38 @@
|
||||
if (b.droppedHistory > 0) parts.push(`기록 −${b.droppedHistory}`);
|
||||
if (b.systemTruncated) parts.push('컨텍스트 일부 생략');
|
||||
if (b.cappedForSmallModel) parts.push('🔻 작은 모델 모드');
|
||||
if (b.windowMismatch && typeof b.actualContextLength === 'number') parts.push('⚠ 실제 창 ' + fmtK(b.actualContextLength) + '↓');
|
||||
if (b.tight) parts.push('⚠ 컨텍스트 거의 가득');
|
||||
const warn = b.tight || b.systemTruncated;
|
||||
const warn = b.tight || b.systemTruncated || b.windowMismatch;
|
||||
ctxBadge.textContent = parts.join(' · ');
|
||||
ctxBadge.className = 'ctx-badge' + (warn ? ' warn' : ' ok');
|
||||
// New turn starts → drop stale stats from the previous answer.
|
||||
lastLmStats = null;
|
||||
ctxBadge.title = `model: ${b.model || ''}${b.paramB != null ? ' (~' + b.paramB + 'B)' : ''}\n입력 ≈ ${b.inputTokens} tokens (시스템 ${b.systemTokens}, 기록 ${b.historyKept}개)\n출력 상한 ${b.maxOutputTokens} tokens / 유효 context window ${b.contextLength} tokens${b.cappedForSmallModel ? ' (작은 모델용 축소; 설정값 ' + b.nominalContextLength + ')' : ''}`;
|
||||
const mismatchNote = (b.windowMismatch && typeof b.actualContextLength === 'number')
|
||||
? `\n⚠ 모델이 실제로는 ${b.actualContextLength} 토큰 창으로 로드됨 (설정 ${b.nominalContextLength}). 그 한도에 맞춰 예산함.`
|
||||
: '';
|
||||
ctxBadge.title = `model: ${b.model || ''}${b.paramB != null ? ' (~' + b.paramB + 'B)' : ''}\n입력 ≈ ${b.inputTokens} tokens (시스템 ${b.systemTokens}, 기록 ${b.historyKept}개)\n출력 상한 ${b.maxOutputTokens} tokens / 유효 context window ${b.contextLength} tokens${b.cappedForSmallModel ? ' (작은 모델용 축소; 설정값 ' + b.nominalContextLength + ')' : ''}${mismatchNote}`;
|
||||
}
|
||||
function renderMapReduceStatus(v) {
|
||||
if (!ctxBadge || !v) return;
|
||||
if (v.phase === 'start') {
|
||||
ctxBadge.textContent = '🧩 큰 입력을 조각으로 나눠 관련 내용 추출 중…';
|
||||
ctxBadge.className = 'ctx-badge warn';
|
||||
ctxBadge.title = '입력이 컨텍스트 창보다 커서 청크→추출→통합(map-reduce)으로 처리 중입니다.';
|
||||
} else if (v.phase === 'done') {
|
||||
if (v.allIrrelevant) {
|
||||
ctxBadge.textContent = '🧩 추출 결과: 요청 관련 내용 없음';
|
||||
ctxBadge.className = 'ctx-badge warn';
|
||||
ctxBadge.title = '긴 입력의 모든 조각에서 요청과 직접 관련된 내용을 찾지 못했습니다. 원본을 그대로(예산 내에서 잘라) 전달합니다.';
|
||||
} else {
|
||||
ctxBadge.textContent = `🧩 ${v.chunkCount}조각 → ${v.relevantCount}조각 추출·통합`;
|
||||
ctxBadge.className = 'ctx-badge ok';
|
||||
ctxBadge.title = `큰 입력을 ${v.chunkCount}개 조각으로 나눠 그중 ${v.relevantCount}개에서 관련 내용을 추출·통합했습니다.`;
|
||||
}
|
||||
} else if (v.phase === 'error') {
|
||||
ctxBadge.textContent = '🧩 분할 처리 실패 — 단발 처리로 진행';
|
||||
ctxBadge.className = 'ctx-badge warn';
|
||||
}
|
||||
}
|
||||
function renderLmStudioStats(s) {
|
||||
if (!ctxBadge || !s) return;
|
||||
@@ -995,6 +1020,9 @@
|
||||
case 'lmStudioStats':
|
||||
renderLmStudioStats(msg.value);
|
||||
break;
|
||||
case 'mapReduceStatus':
|
||||
renderMapReduceStatus(msg.value);
|
||||
break;
|
||||
case 'usedScope': {
|
||||
let target = streamBody && streamBody._parent;
|
||||
if (!target) {
|
||||
|
||||
Reference in New Issue
Block a user