Files
dev-puppeteer/my-deepagent/static/conversation.html
chungyeong 9a02f22acb 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>
2026-05-18 01:08:40 +09:00

53 lines
1.9 KiB
HTML

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>my-deepagent · 대화</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body data-page="conversation">
<header>
<h1><a href="/">my-deepagent</a></h1>
<nav>
<a href="/" class="nav-primary">세션 목록</a>
<a href="/conversation.html" class="active nav-primary">대화</a>
<a href="/runs.html" class="nav-secondary">Runs</a>
<a href="/new.html" class="nav-secondary">워크플로우 실행</a>
</nav>
</header>
<main class="conversation-main">
<div id="error" class="error-banner" style="display:none"></div>
<!-- Top bar: session picker + new conversation button -->
<div class="conv-topbar">
<label for="session-picker" class="conv-label">세션</label>
<select id="session-picker" class="conv-picker">
<option value="">(세션 선택…)</option>
</select>
<button id="new-session-btn" type="button" class="conv-action-btn">새 대화</button>
<span class="conv-session-state" id="session-state-pill"></span>
</div>
<!-- Message thread -->
<div id="messages" class="messages-thread">
<div class="conv-empty" id="conv-empty">대화를 시작하려면 위에서 세션을 선택하거나 "새 대화"를 누르세요.</div>
</div>
<!-- Input bar -->
<form id="message-form" class="conv-input-bar">
<textarea
id="message-input"
rows="2"
placeholder="메시지를 입력하세요… (Cmd/Ctrl+Enter 로 전송)"
autocomplete="off"
disabled
></textarea>
<button id="send-btn" type="submit" disabled>전송</button>
<button id="abort-btn" type="button" disabled style="display:none">⏹ 중단</button>
</form>
</main>
<script src="/static/app.js"></script>
</body>
</html>