refactor: no comments left - one-line module docstrings, contracts on public fields only; jobs/job.py; example config and README
This commit is contained in:
1 parent
3915ab48f9
commit
0e07e8409d
65 files changed
+795
-1891
No files matched your search
@@ -1,10 +1,7 @@
|
||||
"""Render Anthropic ``Message`` (and individual user turns) into markdown.
|
||||
"""Render Anthropic ``Message`` (and user turns) into markdown.
|
||||
|
||||
The renderer is one-way: it produces the human-facing artifact in the
|
||||
vault. The parser strips tool/thinking callouts when reshaping the file
|
||||
for backend replay — so what we write here is purely for the human
|
||||
reader (and for the cross-frontend logger, which materializes turns
|
||||
from other frontends).
|
||||
One-way: produces the human-facing artifact only. The parser strips
|
||||
tool/thinking callouts separately when reshaping history for backend replay.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -30,23 +27,11 @@ __all__ = [
|
||||
]
|
||||
|
||||
|
||||
# Empty ``### User:`` block appended after each assistant reply so the
|
||||
# human has an obvious place to type the next turn. Parser drops empty
|
||||
# user blocks, so this doesn't re-trigger dispatch on its own.
|
||||
# Blank line after the header, like every rendered turn - the file stays
|
||||
# symmetric whether the human or the gateway wrote the marker.
|
||||
USER_SCAFFOLD = "### User:\n\n"
|
||||
|
||||
|
||||
# Default 4-backtick fence so tool results that contain literal ```` ``` ````
|
||||
# don't collide. JSON inputs use 3 backticks because they almost never
|
||||
# contain ``` and we get language syntax highlighting in Obsidian for free.
|
||||
FENCE = "````"
|
||||
|
||||
# Input keys we dangle after the tool name in the callout title, best
|
||||
# first — purely cosmetic. ``description`` wins because when a tool
|
||||
# offers one it's a human-written summary of the call, which beats a
|
||||
# truncated shell command or path.
|
||||
_TITLE_KEYS = ("description", "path", "file", "filename", "url", "command", "query")
|
||||
|
||||
|
||||
@@ -61,24 +46,15 @@ def render_assistant_text(text: str) -> str:
|
||||
|
||||
|
||||
def render_assistant_message(message: Message) -> str:
|
||||
"""Render an assistant ``Message`` (with content blocks) into a turn block.
|
||||
"""Render an assistant ``Message`` into a turn block.
|
||||
|
||||
Blocks render in their original order:
|
||||
|
||||
* ``ThinkingBlock`` → ``> [!thinking]-`` collapsed callout
|
||||
* ``TextBlock`` → plain text (the spoken answer)
|
||||
* ``ToolUseBlock`` → nothing (§3.10: tool calls never reach the file;
|
||||
"what the agent is doing" is the activity panel fed by SSE)
|
||||
|
||||
Blank lines separate adjacent blocks; trailing newline guarantees
|
||||
the next ``---`` / ``### User:`` marker lands on its own line.
|
||||
Tool-use blocks render to nothing; only text and thinking content reach
|
||||
the file (see :mod:`.parser` for how the reverse strip works).
|
||||
"""
|
||||
parts: list[str] = ["### Assistant:", ""]
|
||||
for block in message.content:
|
||||
lines = list(_render_block(block))
|
||||
if not lines:
|
||||
# Tool calls render to nothing - no separator for them either,
|
||||
# or every tool leaves a blank line behind.
|
||||
continue
|
||||
parts.extend(lines)
|
||||
parts.append("")
|
||||
@@ -88,19 +64,12 @@ def render_assistant_message(message: Message) -> str:
|
||||
def render_user_param(param: MessageParam) -> str:
|
||||
"""Render a ``MessageParam`` user message into a ``### User:`` block.
|
||||
|
||||
Used by the cross-frontend logger when materializing turns from
|
||||
other frontends. Tool_result blocks in the content list are dropped
|
||||
silently — the markdown view doesn't track them (see ``parser.py``).
|
||||
Tool_result blocks in the content list are dropped silently.
|
||||
"""
|
||||
content = param.get("content", "")
|
||||
if isinstance(content, str):
|
||||
text = content
|
||||
else:
|
||||
# The Anthropic SDK types ``content`` as a union of typed-dict
|
||||
# *Param classes plus pydantic block models — both shapes appear
|
||||
# in practice (raw incoming JSON yields dicts, SDK-built params
|
||||
# yield BaseModels). Treat each entry as a dict-like and pull
|
||||
# ``text`` opportunistically.
|
||||
chunks = [
|
||||
str(blk.get("text", ""))
|
||||
for blk in content
|
||||
@@ -111,11 +80,9 @@ def render_user_param(param: MessageParam) -> str:
|
||||
|
||||
|
||||
def append_to_body(existing: str, new_block: str) -> str:
|
||||
"""Append ``new_block`` to ``existing`` with a decorative HR separator.
|
||||
"""Append ``new_block`` to ``existing`` with a decorative ``---`` separator.
|
||||
|
||||
Preserves the original body verbatim (whitespace, callouts, any
|
||||
formatting the human added). The HR is purely visual: parser ignores
|
||||
it.
|
||||
The separator is visual only; the parser ignores it.
|
||||
"""
|
||||
head = existing.rstrip()
|
||||
if head:
|
||||
@@ -124,18 +91,9 @@ def append_to_body(existing: str, new_block: str) -> str:
|
||||
|
||||
|
||||
def summarize_tool_input(name: str, tool_input: object) -> str:
|
||||
"""Build the ``[!tool]- <summary>`` title string.
|
||||
|
||||
Tries to pick a single salient field (``description``, ``path``,
|
||||
``command``, etc.) from the input dict so the collapsed callout shows
|
||||
something meaningful in Obsidian. Falls back to just the tool name.
|
||||
"""
|
||||
"""Build the ``[!tool]- <summary>`` title, picking one salient input field."""
|
||||
if not isinstance(tool_input, dict):
|
||||
return name
|
||||
# Anthropic ``ToolUseBlock.input`` is typed as ``object`` — the
|
||||
# SDK's runtime value is always a JSON dict (str→Any), so a local
|
||||
# cast keeps the rest of the function readable without sprinkling
|
||||
# per-line type narrowing on every ``.get`` call.
|
||||
d = cast("dict[str, Any]", tool_input)
|
||||
for key in _TITLE_KEYS:
|
||||
value = d.get(key)
|
||||
@@ -145,9 +103,6 @@ def summarize_tool_input(name: str, tool_input: object) -> str:
|
||||
return name
|
||||
|
||||
|
||||
# ---- internals ---------------------------------------------------------
|
||||
|
||||
|
||||
def _render_block(block: object) -> Iterable[str]:
|
||||
if isinstance(block, TextBlock):
|
||||
text = (block.text or "").strip()
|
||||
@@ -157,7 +112,6 @@ def _render_block(block: object) -> Iterable[str]:
|
||||
if isinstance(block, ThinkingBlock):
|
||||
yield from _render_thinking(block.thinking or "")
|
||||
return
|
||||
# Tool-use blocks and unknown block types never reach the file.
|
||||
|
||||
|
||||
def _render_thinking(text: str) -> Iterable[str]:
|
||||
@@ -167,12 +121,7 @@ def _render_thinking(text: str) -> Iterable[str]:
|
||||
|
||||
|
||||
def adaptive_fence(content: str) -> str:
|
||||
"""Return a backtick fence at least one longer than the longest run in ``content``.
|
||||
|
||||
Currently unused (tool *results* aren't persisted yet) — kept here
|
||||
so when result capture lands the rendering side already has the
|
||||
primitive.
|
||||
"""
|
||||
"""Return a backtick fence longer than any backtick run in ``content``."""
|
||||
longest = 0
|
||||
for match in re.finditer(r"`+", content):
|
||||
longest = max(longest, len(match.group(0)))
|
||||
|
||||
Reference in new issue
Block a user