-
Notifications
You must be signed in to change notification settings - Fork 55
Refactor content formatting to use dispatcher pattern #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Replace the if/elif type dispatch chain in HtmlRenderer._format_message_content()
with a declarative dispatcher pattern in the base Renderer class:
- Add _build_dispatcher() method to base Renderer returning type->formatter mapping
- Add format_content() method with MRO walking for automatic fallback to parent handlers
- HtmlRenderer overrides _build_dispatcher() with all content type formatters
- Complex formatters (UserTextContent, ToolResultContentModel) use instance methods
- Simple formatters use lambdas with cast() for type safety
Benefits:
- Explicit & declarative - all handlers visible in one dict
- MRO-aware fallback - handlers for parent classes serve as defaults
- Easy subclass customization via {**super()._build_dispatcher(), ...}
- Clean separation - dispatch logic in base, handlers in subclass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
WalkthroughIntroduces a dispatcher-based formatting and chunked-content model: Renderer gains init, _build_dispatcher, and format_content; message content is split into chunks and parsed into new content-item models (TextContent/ImageContent/UserSlashCommandContent). HtmlRenderer and HTML formatters switched to dispatcher handlers; parser and models updated for interleaved items. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120+ minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This refactoring fixes a bug where user images were lost during rendering and simplifies the message processing pipeline. Key changes: - Add chunk_message_content() to split content into special vs regular chunks - UserTextContent and AssistantTextContent now use items lists to preserve content order (text + images interleaved) - Remove ContentBlock from ContentItem union, use only our own types - Simplify parser to not depend on Anthropic SDK types - Simplify _process_regular_message by filtering empty text in chunking - Move TextContent, ImageContent, ImageSource earlier in models.py 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
Introduce a dedicated content model for slash command expanded prompts (isMeta=True) instead of reusing UserTextContent. This follows the same pattern as CompactedSummaryContent and UserMemoryContent. Changes: - Add UserSlashCommandContent(text: str) model in models.py - Update parse_user_message_content() to accept is_slash_command param - Add format_user_slash_command_content() formatter - Add dispatcher entry for UserSlashCommandContent - Update _process_regular_message to detect content type - Update dev-docs/messages.md to distinguish isMeta vs tags variants 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
claude_code_log/html/user_formatters.py (1)
194-211: Docstring mentions fallback that isn't implemented.Similar to
assistant_formatters.py, the docstring at lines 200-205 describes "Falls back to legacy text-only behavior whenitemsis None" but the code unconditionally iteratescontent.items. Either update the docstring or add the fallback logic.
🧹 Nitpick comments (4)
claude_code_log/html/user_formatters.py (1)
213-228: Same pattern issue:elsebranch assumesTextContentwithout type verification.Similar to
assistant_formatters.py, theelsebranch at line 224 assumes any non-IdeNotificationContent, non-ImageContentitem isTextContent. If an unexpected type is passed, accessingitem.textcould fail.Consider adding an explicit type check for robustness.
🔎 Proposed fix
+from ..models import TextContent # add to imports if not presentfor item in content.items: if isinstance(item, IdeNotificationContent): notifications = format_ide_notification_content(item) parts.extend(notifications) elif isinstance(item, ImageContent): parts.append(format_image_content(item)) - else: # TextContent + elif isinstance(item, TextContent): # Regular user text as preformatted if item.text.strip(): parts.append(format_user_text_content(item.text)) + # Other types silently skippedclaude_code_log/html/renderer.py (1)
128-140: Consider adding a docstring note about the ToolResultContent vs ToolResultContentModel distinction.The method creates a
ToolResultContentfromToolResultContentModelto delegate toformat_tool_result_content. A brief comment explaining why this wrapper exists (the model carries rendering context likefile_pathandtool_namethat the rawToolResultContentlacks) would help future maintainers.claude_code_log/renderer.py (2)
2148-2151: UUID generation for multi-chunk messages could benefit from a more deterministic approach.When a message produces multiple chunks, each chunk gets a UUID like
{original_uuid}-chunk-{chunk_idx}. This is reasonable, but if the same message is re-rendered, the UUIDs will match, which is good for consistency. However, consider whether the chunk index should be based on position among all chunks or just among chunks of the same type.
2218-2224: UUID generation for tool messages uses tool_use_id or fallback.Using
tool_use_idwhen available provides meaningful IDs for tool messages. The fallback{msg_uuid}-tool-{len(template_messages)}ensures uniqueness but depends on the current list length, which could be fragile if the processing order changes. Consider usingchunk_idxinstead for consistency.🔎 Suggested improvement for more deterministic tool UUIDs
# Generate unique UUID for this tool message # Use tool_use_id if available, otherwise fall back to msg UUID + index tool_uuid = ( tool_result.tool_use_id if tool_result.tool_use_id - else f"{msg_uuid}-tool-{len(template_messages)}" + else f"{msg_uuid}-tool-{chunk_idx}" )
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
claude_code_log/html/assistant_formatters.py(1 hunks)claude_code_log/html/renderer.py(5 hunks)claude_code_log/html/user_formatters.py(3 hunks)claude_code_log/models.py(6 hunks)claude_code_log/parser.py(6 hunks)claude_code_log/renderer.py(9 hunks)dev-docs/messages.md(3 hunks)test/test_ide_tags.py(3 hunks)test/test_renderer.py(1 hunks)test/test_user_renderer.py(3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: Use ruff for code formatting and linting, with ruff check --fix for automatic fixes
Use pyright and mypy for type checking in Python code
Target Python 3.10+ with support for modern Python features and type hints
Files:
claude_code_log/html/renderer.pytest/test_ide_tags.pyclaude_code_log/parser.pytest/test_user_renderer.pyclaude_code_log/models.pyclaude_code_log/renderer.pyclaude_code_log/html/user_formatters.pytest/test_renderer.pyclaude_code_log/html/assistant_formatters.py
test/**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Organize tests into categories with pytest markers to avoid async event loop conflicts: unit tests (no mark), TUI tests (@pytest.mark.tui), browser tests (@pytest.mark.browser), and snapshot tests
Files:
test/test_ide_tags.pytest/test_user_renderer.pytest/test_renderer.py
claude_code_log/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use dateparser for natural language date parsing to support date range filtering with expressions like 'today', 'yesterday', 'last week', and relative dates
Files:
claude_code_log/parser.pyclaude_code_log/models.pyclaude_code_log/renderer.py
claude_code_log/models.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use Pydantic models for parsing and validating transcript JSON data, including TranscriptEntry (union of UserTranscriptEntry, AssistantTranscriptEntry, SummaryTranscriptEntry), UsageInfo, and ContentItem
Files:
claude_code_log/models.py
claude_code_log/renderer.py
📄 CodeRabbit inference engine (CLAUDE.md)
Use mistune for quick Markdown rendering with syntax highlighting support in server-side template rendering
Files:
claude_code_log/renderer.py
🧠 Learnings (3)
📚 Learning: 2025-11-30T17:16:32.495Z
Learnt from: CR
Repo: daaain/claude-code-log PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T17:16:32.495Z
Learning: Applies to claude_code_log/renderer.py : Use mistune for quick Markdown rendering with syntax highlighting support in server-side template rendering
Applied to files:
claude_code_log/html/renderer.py
📚 Learning: 2025-11-30T17:16:32.495Z
Learnt from: CR
Repo: daaain/claude-code-log PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-30T17:16:32.495Z
Learning: Applies to claude_code_log/models.py : Use Pydantic models for parsing and validating transcript JSON data, including TranscriptEntry (union of UserTranscriptEntry, AssistantTranscriptEntry, SummaryTranscriptEntry), UsageInfo, and ContentItem
Applied to files:
claude_code_log/html/renderer.pyclaude_code_log/parser.pyclaude_code_log/models.pydev-docs/messages.md
📚 Learning: 2025-12-09T23:52:47.578Z
Learnt from: daaain
Repo: daaain/claude-code-log PR: 59
File: test/test_cache.py:135-165
Timestamp: 2025-12-09T23:52:47.578Z
Learning: SQLite supports NULLS FIRST and NULLS LAST in ORDER BY since v3.30.0 (Oct 2019). Do not flag SQL that uses these clauses as an error when reviewing Python tests or code that interacts with SQLite. If reviewing SQL strings, verify the target SQLite version supports NULLS FIRST/LAST and ensure the syntax is used correctly for the database in use.
Applied to files:
test/test_ide_tags.pytest/test_user_renderer.pytest/test_renderer.py
🧬 Code graph analysis (6)
test/test_ide_tags.py (1)
claude_code_log/models.py (5)
IdeNotificationContent(258-272)UserTextContent(309-323)TextContent(282-286)ImageContent(297-305)AssistantTextContent(334-350)
claude_code_log/parser.py (1)
claude_code_log/models.py (9)
UserSlashCommandContent(221-229)ThinkingContent(773-776)CompactedSummaryContent(196-205)UserMemoryContent(209-217)UserTextContent(309-323)TextContent(282-286)ImageContent(297-305)IdeNotificationContent(258-272)ToolUseContent(739-762)
test/test_user_renderer.py (1)
claude_code_log/models.py (2)
TextContent(282-286)UserTextContent(309-323)
claude_code_log/renderer.py (5)
claude_code_log/models.py (13)
UserSlashCommandContent(221-229)CompactedSummaryContent(196-205)UserMemoryContent(209-217)MessageType(16-51)AssistantTextContent(334-350)ToolUseContent(739-762)ToolResultContent(765-770)ThinkingContent(773-776)TextContent(282-286)MessageContent(84-94)QueueOperationTranscriptEntry(885-902)UnknownContent(369-376)MessageModifiers(55-73)claude_code_log/parser.py (1)
parse_user_message_content(354-433)claude_code_log/utils.py (1)
format_timestamp(21-32)claude_code_log/tui.py (1)
format_timestamp(469-482)claude_code_log/html/renderer.py (1)
_build_dispatcher(97-126)
claude_code_log/html/user_formatters.py (3)
claude_code_log/models.py (5)
ImageContent(297-305)SlashCommandContent(136-145)UserMemoryContent(209-217)UserSlashCommandContent(221-229)IdeNotificationContent(258-272)claude_code_log/html/assistant_formatters.py (1)
format_image_content(87-97)claude_code_log/html/utils.py (1)
render_markdown_collapsible(222-260)
test/test_renderer.py (2)
claude_code_log/models.py (5)
TextContent(282-286)ToolUseContent(739-762)ThinkingContent(773-776)ImageContent(297-305)ImageSource(289-294)claude_code_log/renderer.py (1)
chunk_message_content(847-894)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
- GitHub Check: test (windows-latest, 3.13)
- GitHub Check: test (windows-latest, 3.14)
- GitHub Check: test (windows-latest, 3.11)
- GitHub Check: test (windows-latest, 3.12)
- GitHub Check: test (ubuntu-latest, 3.13)
- GitHub Check: test (ubuntu-latest, 3.10)
- GitHub Check: test (windows-latest, 3.10)
- GitHub Check: test (ubuntu-latest, 3.11)
- GitHub Check: test (ubuntu-latest, 3.14)
- GitHub Check: test (ubuntu-latest, 3.12)
🔇 Additional comments (29)
test/test_user_renderer.py (2)
210-212: LGTM!The test correctly verifies the new items-based
UserTextContentstructure, checking that items is a list with oneTextContentelement containing the expected text.
291-312: LGTM!The test updates correctly construct
UserTextContentwithitems=[TextContent(...)]and properly test the HTML escaping behavior through the formatting pipeline.dev-docs/messages.md (2)
171-178: LGTM!The documentation correctly describes the new
UserSlashCommandContentmodel with itstext: strfield and clarifies that these are LLM-generated markdown instruction prompts rendered as collapsible markdown.
24-24: Minor: Data flow diagram shows both content types for slash commands.Line 24 lists both
UserSlashCommandContent (isMeta)andSlashCommandContent (<command-name> tags)which correctly reflects the two different slash command content types. This is accurate and helpful for understanding the distinction.test/test_renderer.py (6)
373-390: LGTM!The test correctly verifies that consecutive text items are grouped into a single list chunk, matching the expected behavior from
chunk_message_content.
392-408: LGTM!Good test coverage for the tool_use chunking behavior - verifies that tool_use content is isolated into its own chunk with surrounding text in separate list chunks.
410-424: LGTM!Correctly tests that thinking content forms its own chunk, separate from text items.
426-444: LGTM!Comprehensive test for interleaved content types, verifying the expected
[text], thinking, [text], tool_usechunking pattern.
446-466: LGTM!Correctly verifies that images are treated as "regular" items (not "special") and remain grouped with text in the same chunk.
468-473: LGTM!Simple but important edge case test for empty input returning an empty list.
test/test_ide_tags.py (2)
314-330: LGTM! Test correctly validates the new items-based content model.The assertions properly verify the ordering and types of items returned by
parse_user_message_content: IDE notification first, remaining text second, image third. This aligns with the updatedUserTextContentmodel that uses an interleaveditemslist.
352-354: LGTM! Test updated to use the new items-based AssistantTextContent.The test correctly constructs
AssistantTextContentwith anitemslist containingTextContent, matching the model changes inmodels.py.claude_code_log/html/renderer.py (2)
97-126: Well-structured dispatcher implementation with clear content-type mappings.The dispatcher pattern provides a clean, declarative mapping from content types to formatters. The use of
partial(format_thinking_content, line_threshold=10)elegantly binds the threshold parameter. Using an instance method forToolResultContentModelenables access to additional context.
158-159: LGTM! Clean integration with base class dispatcher.The change from direct
_format_message_contentcall toself.format_content(msg)properly delegates to the MRO-aware dispatcher in the baseRendererclass.claude_code_log/parser.py (4)
73-81: Simplified and more robust text extraction.The updated logic handles both
ThinkingContentinstances and duck-typed thinking items viagetattr(item, "type", None). Usinghasattr(item, "text")for text extraction is cleaner than the previous approach.
378-387: Good early return for slash command content.The
is_slash_commandpath correctly combines all text items and returns aUserSlashCommandContentimmediately. Theif all_textcheck prevents returning empty content.
406-433: Well-implemented items-based content parsing with proper order preservation.The loop correctly:
- Extracts IDE notifications from text content first
- Adds remaining text as
TextContentonly if non-empty- Handles both native
ImageContentand Anthropic SDK image types
428-430: No action needed. Anthropic SDK responses are Pydantic models, and model_dump() is the standard method to convert Pydantic models to dictionaries. The SDK maintains compatibility across Pydantic v1 and v2 through a compatibility layer, with model_dump() working across both versions. The runtime checks (hasattr(item, "source")andgetattr(item, "type", None) == "image") provide appropriate defensive validation before calling the method.claude_code_log/models.py (5)
220-229: LGTM! Clean model for slash command expanded prompts.The
UserSlashCommandContentdataclass correctly captures LLM-generated instruction text from slash commands. The docstring clearly explains the use case and references the formatter location.
275-306: Well-defined content item models with proper Pydantic validation.The
TextContent,ImageSource, andImageContentmodels provide strong typing for content arrays. UsingLiteral["text"],Literal["base64"], andLiteral["image"]for type discrimination enables clean pattern matching.
320-323: Thepyright: ignorecomment is appropriate for this pattern.The union type in a dataclass field with
field(default_factory=list)triggers pyright warnings that aren't actionable. The ignore is correctly scoped to just this line.
347-350: Consistent pattern with UserTextContent for assistant items.The
AssistantTextContent.itemsfield mirrors the pattern used inUserTextContent, enabling unified handling of interleaved text and image content.
779-786: Verify ImageContent is correctly included in ContentItem union.The
ContentItemunion now includesImageContent, which is necessary for the items-based content models. This aligns with the model definitions earlier in the file.claude_code_log/renderer.py (6)
831-844: Good type alias and helper function for content chunking.The
ContentChunktype alias and_is_special_itemhelper cleanly distinguish between regular items (text/image) that accumulate and special items (tool_use, tool_result, thinking) that become standalone chunks.
847-894: Well-implemented chunking algorithm with clear documentation.The
chunk_message_contentfunction correctly:
- Accumulates regular items into list chunks
- Flushes accumulated items before special items
- Skips empty text content
- Handles empty input gracefully
The docstring example clearly illustrates the expected behavior.
708-753: Updated_process_regular_messagecorrectly handles items-based content.The function now:
- Accepts
items: list[ContentItem]instead of extracting text- Creates
UserSlashCommandContentfor slash command prompts viaparse_user_message_content- Creates
AssistantTextContentdirectly from items for assistant messagesThe type ignore on line 752 is appropriate since the items have already been filtered to text/image content.
2074-2076: Token usage correctly shown only on the first content chunk.The
token_shownflag ensures token usage is displayed once per message, even when the message produces multiple chunks. This prevents duplicate token counts in the UI.
2381-2417: Well-designed dispatcher pattern in base Renderer class.The implementation:
- Caches the dispatcher dict in
__init__for efficiency- Provides
_build_dispatcheras an override point for subclasses- Walks the content type's MRO to find handlers, enabling inheritance-based fallbacks
- Returns empty string if no handler found (safe default)
The MRO walking is a nice touch that allows registering a handler for a parent class (like
MessageContent) as a catch-all.
2003-2010: Edge case: String content wrapping creates nested list.When
message_contentis a string, it's wrapped as[[TextContent(...)]](a list containing a list with one item). This is correct sincechunk_message_contentexpects a list, and this will be treated as a single regular chunk. The logic is sound.
| When `items` is set, iterates through the content items preserving order: | ||
| - TextContent: Rendered as markdown with collapsible support | ||
| - ImageContent: Rendered as inline <img> tag with base64 data URL | ||
| Falls back to legacy text-only behavior when `items` is None. | ||
| Args: | ||
| content: AssistantTextContent with the text to render | ||
| content: AssistantTextContent with text/items to render | ||
| line_threshold: Number of lines before content becomes collapsible | ||
| preview_line_count: Number of preview lines to show when collapsed | ||
| Returns: | ||
| HTML string with markdown-rendered, optionally collapsible content | ||
| """ | ||
| return render_markdown_collapsible( | ||
| content.text, | ||
| "assistant-text", | ||
| line_threshold=line_threshold, | ||
| preview_line_count=preview_line_count, | ||
| ) | ||
| parts: list[str] = [] | ||
| for item in content.items: | ||
| if isinstance(item, ImageContent): | ||
| parts.append(format_image_content(item)) | ||
| else: # TextContent | ||
| if item.text.strip(): | ||
| text_html = render_markdown_collapsible( | ||
| item.text, | ||
| "assistant-text", | ||
| line_threshold=line_threshold, | ||
| preview_line_count=preview_line_count, | ||
| ) | ||
| parts.append(text_html) | ||
| return "\n".join(parts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstring claims fallback behavior that isn't implemented.
The docstring at line 38 states "Falls back to legacy text-only behavior when items is None", but the code unconditionally iterates over content.items without checking for None. If items can be None (as the docstring implies), this would raise a TypeError.
Either:
- Remove the fallback claim from the docstring if
itemsis always populated, or - Add the fallback logic if backward compatibility is needed.
Additionally, the else branch at line 52 assumes any non-ImageContent item is TextContent, which could cause AttributeError if an unexpected type is encountered. Consider adding an explicit type check.
🔎 Proposed fix with explicit type handling
from ..models import (
AssistantTextContent,
ImageContent,
+ TextContent,
ThinkingContentModel,
UnknownContent,
) parts: list[str] = []
for item in content.items:
if isinstance(item, ImageContent):
parts.append(format_image_content(item))
- else: # TextContent
+ elif isinstance(item, TextContent):
if item.text.strip():
text_html = render_markdown_collapsible(
item.text,
"assistant-text",
line_threshold=line_threshold,
preview_line_count=preview_line_count,
)
parts.append(text_html)
+ # Other types are silently skipped
return "\n".join(parts)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In claude_code_log/html/assistant_formatters.py around lines 34 to 61, the
docstring claims a fallback when content.items is None but the code
unconditionally iterates content.items and assumes non-ImageContent are
TextContent; update the implementation to match the docstring by checking if
content.items is None and fall back to the legacy text-only rendering path
(render content.text via render_markdown_collapsible), or if you prefer to keep
items always present remove the fallback claim from the docstring; additionally
replace the unguarded else with an explicit isinstance(item, TextContent) branch
(render it) and otherwise skip/log unexpected item types to avoid
AttributeError.
| def format_user_slash_command_content(content: UserSlashCommandContent) -> str: | ||
| """Format slash command expanded prompt (isMeta) as HTML. | ||
| These are LLM-generated instruction text from slash commands, | ||
| rendered as collapsible markdown. | ||
| Args: | ||
| content: UserSlashCommandContent with markdown text | ||
| Returns: | ||
| HTML string with collapsible markdown rendering | ||
| """ | ||
| return render_markdown_collapsible( | ||
| content.text, | ||
| "slash-command", | ||
| line_threshold=30, | ||
| preview_line_count=10, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New function format_user_slash_command_content is not exported in __all__.
The function is defined but not included in the __all__ list at lines 374-385. If this function is intended to be part of the public API (as indicated by the AI summary), it should be added to the exports.
🔎 Proposed fix
__all__ = [
# Formatting functions
"format_slash_command_content",
"format_command_output_content",
"format_bash_input_content",
"format_bash_output_content",
"format_user_text_content",
"format_user_text_model_content",
"format_compacted_summary_content",
"format_user_memory_content",
"format_ide_notification_content",
+ "format_user_slash_command_content",
]🤖 Prompt for AI Agents
In claude_code_log/html/user_formatters.py around lines 268 and the export block
at lines 374-385, the new function format_user_slash_command_content is defined
but not exported; add "format_user_slash_command_content" to the module's
__all__ list in the export block so the function is part of the public API,
placing it alongside the other formatter names and keeping the list
alphabetized/consistent with the existing style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll let CodeRabbit comment on the nits, but from a high-level perspective it makes sense!
I had one thought, if you feel like checking which types we could replace with ones from the Anthropic SDK that could be a useful refactor, for example our ImageSource could be their Base64ImageSourceParam
Thanks for the feedback! I was about to merge this one, as I have pending changes that will go into another PR (to keep each reasonably small), and after that, I'm about to embark on yet an even bigger refactoring...
Well, actually as you will see in e2e7e9f, I went the opposite direction, as from my perspective, mixing our own types and the Anthropic ones was adding complexity for no apparent benefits (that I or my Claudes could see). We had from_anthropic/to_anthropic methods that were never called, for example. |
Yeah, I guess another option is to rip those out completely 🤷 I included them thinking that using existing Anthropic types would make life easier, but I think they don't always fit and Claude is almost always too lazy to look them up, so maybe more hassle than what they worth... |
Replace the if/elif type dispatch chain in HtmlRenderer._format_message_content() with a declarative dispatcher pattern in the base Renderer class:
Benefits:
🤖 Generated with Claude Code
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.