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>
53 lines
1.9 KiB
HTML
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>
|