Files
connectai/tests/liveReasoningFilter.test.ts
T
koriweb 47b3b9f93a v2.2.299~307: 아키텍처 수렴 + 채팅 정리 + 벤치마킹 강화 + Claude 구독 엔진
- v2.2.299 아키텍처 수렴: coreChat 통일(엔진 휴리스틱 3벌 제거), 기업 모드
  검색 오케스트레이터 승격, lib/execUtil(실행 래퍼 6곳·Python 탐지 3벌 단일화),
  lib/kstSchedule(워처 4개 nowInKst 통합), estimateTokens 통합, 설정 접근 규칙 명문화
- v2.2.300 채팅 화면 정리: LiveReasoningFilter(스트리밍 중 <think>/Harmony 추론
  토큰 단위 차단), 확신도·검토요청 footer 기본 숨김(계산·Reflection 은 유지)
- v2.2.301 문맥·의도 이해: [답변 전 이해 원칙] 상시 주입, 워크플로우 의도 브리핑,
  Report QA 루프(규칙 레지스트리+실측치+회귀 게이트, 블로그_v3 개념 이식)
- v2.2.302 /benchmark 비즈니스 렌즈(가격·수익·운영)+빌드 프롬프트 모드+QA 연계
- v2.2.303 handoff 모드(측정치 무손실 인수인계 문서)+/claude(Claude Code 터미널 위임)
- v2.2.304 이식성: 지식 경로 두뇌-상대 규약(pickWikiDir 상대 해석), 이사 체크리스트
- v2.2.305 Claude 구독 엔진: claude: 프로바이더(CLI 위임, 모델 드롭다운 자동 노출,
  coreChat 지원 — 워크플로우·QA도 구독 모델 가능)
- v2.2.306 Tone Guard: AI 상투어 금지 레지스트리(상담사 화법 실사례 8종+대조 예시)
- v2.2.307 /benchmark 레이아웃 골격(sectionRoles 결정론 분석, 롤링 배너 즉답),
  파트별 실패 격리, 합성 타임아웃 120→300초

검증: tsc 무오류 + jest 888 통과 + esbuild 정상

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:02:15 +09:00

66 lines
2.9 KiB
TypeScript

import { LiveReasoningFilter } from '../src/lib/contextBuilders/liveReasoningFilter';
/** 토큰 배열을 순서대로 밀어넣고 화면에 방출된 텍스트를 합쳐 반환. */
function run(tokens: string[]): string {
const f = new LiveReasoningFilter();
let out = '';
for (const t of tokens) out += f.push(t);
out += f.flush();
return out;
}
describe('LiveReasoningFilter — 스트리밍 중 추론 구간 실시간 차단', () => {
test('일반 텍스트는 그대로 통과한다', () => {
expect(run(['안녕', '하세요. ', '무엇을 도와드릴까요?'])).toBe('안녕하세요. 무엇을 도와드릴까요?');
});
test('<think> 블록은 통째로 숨긴다', () => {
expect(run(['<think>사용자가 인사했다</think>', '반갑습니다.'])).toBe('반갑습니다.');
});
test('토큰 경계에 걸쳐 쪼개진 <think> 도 잡는다', () => {
expect(run(['<th', 'ink>내부 ', '추론</thi', 'nk>답', '변입니다'])).toBe('답변입니다');
});
test('<thinking> 과 <analysis> 변형도 처리한다', () => {
expect(run(['<thinking>...</thinking>', 'A'])).toBe('A');
expect(run(['<analysis>...</analysis>', 'B'])).toBe('B');
});
test('본문 중간의 think 블록도 앞뒤 본문을 보존한다', () => {
expect(run(['결론은 X입니다. ', '<think>검산</think>', '근거는 Y입니다.']))
.toBe('결론은 X입니다. 근거는 Y입니다.');
});
test('Harmony thought 채널은 숨기고 final 채널 본문만 통과한다', () => {
const tokens = [
'<|channel|>thought The user said "안녕". This is a greeting.',
'<|channel|>final<|message|>',
'반갑습니다. 무엇을 도와드릴까요?',
];
expect(run(tokens)).toBe('반갑습니다. 무엇을 도와드릴까요?');
});
test('채널 마커가 토큰 경계에 걸쳐도 잡는다', () => {
const tokens = ['<|chan', 'nel|>thought 내부 추론 텍스트 ', '<|channel|>fin', 'al<|mess', 'age|>답변'];
expect(run(tokens)).toBe('답변');
});
test('thought 채널이 <|end|> 로 끝나면 이후는 visible', () => {
expect(run(['<|channel|>thought 추론<|end|>', '본문'])).toBe('본문');
});
test('닫히지 않은 think 는 아무것도 방출하지 않는다 (최종 streamReplace 가 정리)', () => {
expect(run(['<think>끝나지 않는 추론...'])).toBe('');
});
test('마커가 아닌 < 문자는 보류 후 정상 방출된다', () => {
expect(run(['a < b 이고 ', 'c > d 입니다'])).toBe('a < b 이고 c > d 입니다');
expect(run(['제네릭 Array<string> 타입'])).toBe('제네릭 Array<string> 타입');
});
test('스트림이 잠재 마커 접두사로 끝나면 flush 로 회수된다', () => {
expect(run(['답변 끝 <'])).toBe('답변 끝 <');
});
});