feat(my-deepagent): v0.4 chat UX boost + A/B live verification

Claude-Code 동급 chat 경험으로 끌어올림 + 7개 핵심 흐름 실제 OpenRouter verify.

A — Live verification (scripts/live_verify.py, 7 PASS, 약 $0.02):
- A1 1-turn chat (CLI-eq) → Haiku 4.5 한국어 응답
- A2 sessions resume → 같은 session_id 재투입 시 LangGraph state 복원
- A3 /skill <name> system inject → SKILL.md ("한국어 haiku 3 lines") 가 정확히
  3행 한국어 시 생성 (LLM 행동 제어 강력한 증거)
- A4 /plan → /approve → LLM plan markdown only, 차단 도구 시도 없음
- A5 /agents spawn → 실제 sub-agent ainvoke + parent stream push
- A6 auto-compaction → 14 메시지 → 4 archive + 77 토큰 summary
- A7 /workflow wiring → role↔persona 매칭 사전 검증

B1 — Markdown rendering:
- app.js pure-JS 미니 파서: 코드 펜스 / ATX 헤더 / ul/ol / `code`/**bold**/
  *italic*/[link](url)
- XSS 정책 유지: createElement + textContent only.  링크 href 는 http(s):
  스킴 강제.

B2 — System event card (collapsible):
- _classifySystemMessage 가 [sub-agent .../workflow .../Earlier conversation
  history/당신은 plan mode/The user APPROVED/skill] 접두사 분류 후 <details>
  카드로 렌더.

B3 — Token streaming via AsyncCallbackHandler:
- ChatOpenAI(streaming=True)
- _StreamingChunkPusher (AsyncCallbackHandler) → asyncio.Queue per session.
- SSE _session_event_stream 이 queue drain → event: chunk SSE.  100ms poll.
- 순서 보장: chunk drain → message rows yield (placeholder 가 메시지로
  교체되기 전에 토큰 visible).
- 라이브: 5 chunk events + 1 final message, "안녕하세요, / 무 / 엇을 도와드 /
  릴까요?" 토큰 단위 push.

B4 — Cancel mid-turn:
- POST /api/sessions/{id}/abort + app.state.pending_per_session 인덱스.
- 새 user 메시지 도착 시 이전 in-flight task 자동 cancel.
- "■ 중단" 버튼 — 대기 중 visible, 완료/취소 시 hide.

B5 — IME composition-safe Enter:
- compositionstart/compositionend 플래그 — 한글 IME 후보 commit Enter 무시.
- Cmd/Ctrl+Enter 는 항상 전송.

DB hot-fix:
- Database.__init__ pool_pre_ping=True — Postgres asyncpg stale connection
  → SSE 부하에서 500 발생 해결.

기타:
- createNewSession 의 repo_path: "" → "." (min_length=1 검증 통과).
- test_conversation_gui.py fake_invoke 가 chunk_queue kwarg 받도록 업데이트.

게이트:
- ruff / format / mypy: PASS (143 source files)
- pytest -q --ignore=tests/integration/test_e2e_workflow.py
  --ignore=tests/integration/test_openrouter_smoke.py: 709 passed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chungyeong
2026-05-18 01:08:40 +09:00
parent 6d371afadd
commit 9a02f22acb
9 changed files with 1169 additions and 57 deletions

View File

@@ -2,6 +2,70 @@
## [Unreleased]
### Added
- **v0.4 chat UX boost + A/B live verification** — Claude-Code 동급의 chat
경험으로 끌어올림 + 7개 핵심 흐름을 실제 OpenRouter 로 verify.
**A — Live verification (`scripts/live_verify.py`, 7 PASS)**:
- A1 1-turn chat (CLI-eq) → Anthropic Haiku 4.5 한국어 응답
- A2 sessions resume → 같은 session_id 재투입 시 LangGraph thread state 복원
- A3 `/skill <name>` system inject → SKILL.md ("한국어 haiku 3 lines") 가
실제로 LLM 행동을 제어 (정확히 3행 한국어 시 출력)
- A4 `/plan → /approve` → LLM 이 plan markdown 만 생성, 차단 도구 시도 없음
- A5 `/agents spawn` → 실제 sub-agent ainvoke + 결과 parent stream push
- A6 auto-compaction → 14 메시지 → 4 archive + 77 토큰 summary
- A7 `/workflow` wiring → role↔persona 매칭 사전 검증
- 총 비용 약 \$0.02.
**B1 — Markdown rendering** in conversation.html:
- `app.js` 의 pure-JS 미니 마크다운 파서 — 코드 펜스, ATX 헤더, ul/ol,
inline `code`/**bold**/*italic*/[link](url) 지원.
- XSS 정책 유지: `createElement + textContent` 만 사용, `innerHTML`
금지. 링크 href 는 `http(s):` 스킴으로 강제 제한.
- `style.css``.md-p`, `.md-h`, `.md-ul`, `.md-ol`, `.md-code`
스타일 추가. user bubble (brown 배경) 안에서도 코드/링크 가독성 유지.
**B2 — System event card** (collapsible):
- `_classifySystemMessage` 가 system content 의 접두사를 보고
"Sub-agent result / Workflow started / Compaction summary / Plan mode
activated / Approved plan / Skill activated" 등으로 분류 → `<details>`
카드로 렌더. 채팅 thread 가 이벤트 메시지로 도배되지 않음.
**B3 — Token streaming via AsyncCallbackHandler**:
- `session.py:resolve_model_instance``ChatOpenAI(streaming=True)`.
- `api/agent_runner._StreamingChunkPusher` (AsyncCallbackHandler) 가
`on_llm_new_token` 마다 `asyncio.Queue``{"type":"delta","text":...}`
push.
- `api/routes/sessions._session_event_stream` 이 queue 를 drain 해 SSE
`event: chunk` 로 전송. Poll interval 100ms. 순서 보장: chunk 먼저
drain → message rows 후 yield (placeholder 가 메시지로 교체되기 전에
토큰이 시각적으로 흐르도록).
- 프론트엔드 `app.js``appendStreamDelta` 가 chunk 를 placeholder 에
누적; 최종 `message` SSE 가 도착하면 markdown-rendered bubble 로 교체.
- 라이브 verify: 5 chunk events + 1 final message, OpenRouter Haiku 응답
"안녕하세요, / 무 / 엇을 도와드 / 릴까요?" 토큰 단위 push 확인.
**B4 — Cancel mid-turn** (`POST /api/sessions/{id}/abort`):
- `app.state.pending_per_session: dict[session_id, Task]` 인덱스 +
`_remove_from_session_map` done-callback.
- 새 user 메시지 도착 시 이전 in-flight task 자동 cancel (Claude Code parity).
- 프론트엔드 우하단 "**■ 중단**" 버튼 — 대기 중 visible, 완료/취소 시 hide.
**B5 — IME composition-safe Enter**:
- 한글/일본어/중국어 IME 입력 중 Enter 가 후보 commit 용으로 쓰일 때
전송되지 않도록 `compositionstart` / `compositionend` 플래그. 순수
Enter 만 무시, Cmd/Ctrl+Enter 는 우선 적용.
**DB hot-fix** (v0.4 chat UX 라운드 도중 발견):
- `Database.__init__``pool_pre_ping=True` — Postgres asyncpg pool 이
idle/network blip 후 stale connection 을 넘기던 문제 (SSE 0.5s poll
부하에서 500 발생) 해결.
**새 테스트** (정확한 인보크 시그니처 sync + 기존 통합 보존):
- `tests/integration/test_conversation_gui.py``fake_invoke` 스텁이
`chunk_queue` kwarg 도 받도록 업데이트.
- 전체 회귀: 709 passed (no new failures).
### Added
- **v0.4 — Workflow generator UI + hot-reload + UX polish**. 사용자가 직접
YAML 을 작성하지 않고도 브라우저에서 새 워크플로우 템플릿을 만들고 즉시