feat(chat): 생각 과정(reasoning·도구 스텝) 토글 + 작성 중 스피너·경과 초 — ABAP_OPENCODE 와 같은 모양. 백엔드가 step 이벤트로 흘림. 0.1.2

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
lee-hyeon-cheol
2026-09-21 21:03:51 +09:00
co-authored by Claude Fable 5.1
parent 5ee599fb6b
commit 5277481513
12 changed files with 192 additions and 22 deletions
+11 -2
View File
@@ -134,8 +134,12 @@ class TurnState:
if mid in self.user_message_ids and mid not in self.assistant_message_ids:
return out
pid = props.get("partID", "")
if self.part_types.get(pid) != "text":
return out # reasoning/tool 파트, 또는 아직 타입 모름(최종 스냅샷이 메워줌)
ptype = self.part_types.get(pid)
if ptype == "reasoning":
out.append(("step", {"id": pid, "kind": "reasoning", "text": props["delta"]}))
return out
if ptype != "text":
return out # tool 파트, 또는 아직 타입 모름(최종 스냅샷이 메워줌)
delta = props["delta"]
if pid not in self.text_by_part:
self.part_order.append(pid)
@@ -148,6 +152,11 @@ class TurnState:
part = props.get("part") or {}
if part.get("id"):
self.part_types[part["id"]] = part.get("type", "")
if part.get("type") == "tool":
# 도구 호출 진행 — 화면 "생각 과정" 에 이름·상태만. 입력/출력은 안 보냄(크고 사용자 관심 밖)
st = part.get("state") or {}
out.append(("step", {"id": part.get("id", ""), "kind": "tool", "tool": part.get("tool", ""), "status": st.get("status", ""), "title": st.get("title") or ""}))
return out
if part.get("type") != "text" or part.get("synthetic") or part.get("ignored"):
return out
mid = part.get("messageID", "")