From 33eece331a7d10d0eed660d21096879a4d350464 Mon Sep 17 00:00:00 2001 From: h Date: Fri, 22 May 2026 22:48:12 +0200 Subject: [PATCH] feat: add more logging --- src/beaver_gateway/backends/claude_code.py | 34 +++++++++++- .../frontends/markdown/frontend.py | 54 +++++++++++++++++++ uv.lock | 4 +- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/src/beaver_gateway/backends/claude_code.py b/src/beaver_gateway/backends/claude_code.py index 4967f23..173122b 100644 --- a/src/beaver_gateway/backends/claude_code.py +++ b/src/beaver_gateway/backends/claude_code.py @@ -27,6 +27,7 @@ agent's ``system_prompt`` is the canonical identity of the agent. from __future__ import annotations import json +import logging import uuid from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any, Self @@ -67,6 +68,9 @@ if TYPE_CHECKING: from beaver_gateway.core.events import MessageStreamEvent +_log = logging.getLogger("beaver_gateway.backends.claude_code") + + __all__ = ["ClaudeCodeBackendAdapter", "TurnCapture"] @@ -257,12 +261,24 @@ class ClaudeCodeBackendAdapter: ) raise ValueError(msg) + msgs_list: list[Mapping[str, Any]] = list(messages) + _log.info( + "complete: agent=%s n_messages=%d capture=%s live_sessions=%d", + agent.name, + len(msgs_list), + capture is not None, + self._backend.live_session_count, + ) + message_id = f"msg_{uuid.uuid4().hex}" yield build_message_start(message_id=message_id, model=agent.model) next_index = 0 stop_reason: str | None = None usage: Mapping[str, Any] | None = None + n_text = 0 + n_thinking = 0 + n_tool_use = 0 # We keep raw events so we can hand them to # ``synthesize_turn_messages`` after the stream closes — the # markdown frontend stores the result in its conversation @@ -271,10 +287,16 @@ class ClaudeCodeBackendAdapter: # are silently discarded from the wire but kept here. raw_events: list[Any] = [] - async for event in self._backend.complete(list(messages)): + async for event in self._backend.complete(msgs_list): raw_events.append(event) if isinstance(event, AssistantMessage): for block in event.content: + if isinstance(block, TextBlock): + n_text += 1 + elif isinstance(block, ThinkingBlock): + n_thinking += 1 + elif isinstance(block, ToolUseBlock): + n_tool_use += 1 for ev in _emit_block(block, next_index): yield ev next_index += 1 @@ -298,6 +320,16 @@ class ClaudeCodeBackendAdapter: if capture is not None: capture.synthesized_messages = synthesize_turn_messages(raw_events) + _log.info( + "complete: agent=%s DONE text=%d thinking=%d tool_use=%d stop=%s synth=%d", + agent.name, + n_text, + n_thinking, + n_tool_use, + stop_reason, + len(capture.synthesized_messages) if capture is not None else 0, + ) + yield build_message_delta( stop_reason=_map_stop_reason(stop_reason), usage=_normalize_usage(usage) ) diff --git a/src/beaver_gateway/frontends/markdown/frontend.py b/src/beaver_gateway/frontends/markdown/frontend.py index b5657e3..1e9c6b9 100644 --- a/src/beaver_gateway/frontends/markdown/frontend.py +++ b/src/beaver_gateway/frontends/markdown/frontend.py @@ -571,7 +571,24 @@ class MarkdownFrontend(Frontend): conv, conv_external_id, stored_msgs = await self._resolve_conversation( runtime=runtime, metadata=parsed.metadata, agent_name=agent.name ) + _log.info( + "chat/stream: file=%s conv_external_id=%s conv_id=%d " + "stored_msgs=%d incoming_turns=%d", + filename, + conv_external_id, + conv.id or -1, + len(stored_msgs), + len(parsed.turns), + ) outcome = diff_and_fork(stored=stored_msgs, incoming=parsed.turns) + _log.info( + "chat/stream: file=%s diff_and_fork divergence=%s " + "backend_msgs=%d persist_msgs=%d", + filename, + outcome.divergence_index, + len(outcome.messages), + len(outcome.persist_messages), + ) capture: TurnCapture | None = ( TurnCapture() if isinstance(backend, ClaudeCodeBackendAdapter) else None ) @@ -579,6 +596,12 @@ class MarkdownFrontend(Frontend): kwargs: dict[str, Any] = {} if capture is not None: kwargs["capture"] = capture + _log.info( + "chat/stream: file=%s calling backend.complete agent=%s capture=%s", + filename, + agent.name, + capture is not None, + ) events = backend.complete( agent=agent, messages=outcome.messages, system=None, **kwargs ) @@ -791,10 +814,30 @@ class MarkdownFrontend(Frontend): conv = await load_conversation( session, frontend="markdown", external_id=lookup_id ) + if conv is None: + _log.info( + "_resolve_conversation: frontmatter conv_id=%s " + "not found in DB, will mint new", + lookup_id, + ) + else: + _log.info( + "_resolve_conversation: LOADED existing conv " + "id=%d external_id=%s", + conv.id or -1, + conv.external_id, + ) if conv is None: conv = await mint_conversation( session, frontend="markdown", agent_name=agent_name ) + _log.info( + "_resolve_conversation: MINTED new conv " + "id=%d external_id=%s agent=%s", + conv.id or -1, + conv.external_id, + agent_name, + ) if conv.id is None: msg = "conversation row missing primary key after commit" raise RuntimeError(msg) @@ -824,10 +867,21 @@ class MarkdownFrontend(Frontend): else _fallback_synthesized(message) ) canonical = [*persist_messages, new_user_msg, *synthesized] + _log.info( + "_persist_canonical_history: conv_id=%d writing %d msgs " + "(prior=%d + new_user + synth=%d)", + conversation_id, + len(canonical), + len(persist_messages), + len(synthesized), + ) async with runtime.db.session() as session: await rewrite_messages( session, conversation_id=conversation_id, messages=canonical ) + _log.info( + "_persist_canonical_history: conv_id=%d DB committed", conversation_id + ) def _resolve_path(self, filename: str) -> Path: """Resolve ``filename`` under the vault; reject escapes.""" diff --git a/uv.lock b/uv.lock index 5660069..b848a09 100644 --- a/uv.lock +++ b/uv.lock @@ -287,7 +287,7 @@ local = [ { name = "raycast-api", version = "0.1.0", source = { editable = "../raycast-api" } }, ] prod = [ - { name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#21dc26810b8241c2e317b1134042d5c96af0f7b1" } }, + { name = "claude-code-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#d05c8dd613d94a03d9b3a2a4fb364ee3e15dada0" } }, { name = "raycast-api", version = "0.1.0", source = { git = "https://git.kotikot.com/beaver/raycast-api.git#e73894c8e435da5c0709f92f69f11bcd0dab9afe" } }, ] @@ -419,7 +419,7 @@ wheels = [ [[package]] name = "claude-code-api" version = "0.1.0" -source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#21dc26810b8241c2e317b1134042d5c96af0f7b1" } +source = { git = "https://git.kotikot.com/beaver/claude-code-api.git#d05c8dd613d94a03d9b3a2a4fb364ee3e15dada0" } resolution-markers = [ "python_full_version >= '3.14'", "python_full_version < '3.14'",