Skip to content

refactor: extract openai_compat, fix bugs, and modernize Go idioms#7

Merged
yanmxa merged 9 commits intomainfrom
refactor/pr5-valuable
Apr 5, 2026
Merged

refactor: extract openai_compat, fix bugs, and modernize Go idioms#7
yanmxa merged 9 commits intomainfrom
refactor/pr5-valuable

Conversation

@yanmxa
Copy link
Copy Markdown
Owner

@yanmxa yanmxa commented Apr 5, 2026

Summary

Cherry-picked the valuable parts of PR #5 (originally refactor/deep-cleanup) onto the new main after PR #6 merged:

  • internal/provider/openai_compat — new shared package eliminating ~600 lines of duplication across openai/moonshot/alibaba providers (ConvertMessages, ConvertTools, MapFinishReason, ExtractReasoningContent)
  • internal/tool/util.go — param helpers (requireString, getString, getBool, etc.) applied across 20+ tool files to replace repetitive type-assertion boilerplate
  • fix: slice aliasing race in store.SetTokenLimit — concurrent readers could observe partial writes without holding a lock
  • fix: unchecked errors across mcp, cron, image, hooks, and app packages
  • refactor: permission alias migration — replace deprecated config.PermissionAllow/Deny with config.Allow/Deny
  • refactor: dead code removalcontainsIgnoreCase, unused style functions, installScopeIdx field
  • refactor: session loader consolidationloadWithoutLock() delegates to loadFromFile(), single canonical JSONL parser
  • refactor: Anthropic tool conversion — extracted convertAnthropicTools() helper; tool conversion loop now 5 lines instead of 25
  • refactor: sort modernizationsort.Sliceslices.SortFunc across providers and session store
  • staticcheck fixes — empty if-err branches, redundant embedded fields

Skipped: the original first commit (ed0adce architecture cleanup) which was entirely superseded by PR #6.

Test plan

  • go build ./... passes
  • go test ./... passes

Summary by Sourcery

Extract shared OpenAI-compatible message/tool conversion into a reusable helper package, apply new parameter utility helpers across tools, fix concurrency and error-handling issues, and modernize assorted Go patterns and APIs.

New Features:

  • Introduce internal/provider/openai_compat package to centralize conversion of messages, tools, finish reasons, and reasoning_content handling for OpenAI-compatible providers.
  • Add internal/tool/util helpers for parameter validation, typed extraction, and request ID generation used across multiple tools.

Bug Fixes:

  • Prevent slice-aliasing races in provider.Store.SetTokenLimit by copying model caches before mutation.
  • Ensure multiple resources (files, pipes, HTTP bodies, transports) are closed and errors are checked or safely ignored where previously omitted.
  • Improve robustness of image loading, MCP tool refresh, and plugin initialization by handling and propagating errors more consistently.

Enhancements:

  • Refactor OpenAI, Moonshot, and Alibaba providers to use shared OpenAI compatibility helpers, reducing duplication and standardizing behavior.
  • Consolidate session JSONL loading through a single canonical loader with shared metadata application logic.
  • Simplify Anthropic tool conversion with a dedicated helper and normalize JSON Schema handling.
  • Replace ad-hoc parameter parsing in many tools with shared helpers, improving readability and consistency of validation and defaults.
  • Modernize sorting and map/slice utilities across the codebase using slices.SortFunc, slices.Sorted, and maps.Copy.
  • Clarify and deprecate legacy permission aliases in favor of config.Allow/Deny/Ask while updating call sites.
  • Tighten types by using any instead of interface{}, and improve struct field alignment and comments for clarity.
  • Improve various app, hook, MCP, and plugin flows by treating some failures as non-fatal and ensuring best-effort cleanup.
  • Remove unused code and fields related to plugins, plan prompt styling, and agent CLI helpers.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added public APIs to access and modify conversation messages, token usage, and message history.
    • Added new OpenAI-compatible helper utilities for improved provider integration.
  • Bug Fixes

    • Fixed control-flow logic in markdown rendering for proper error handling.
    • Image references now match case-insensitively for file extensions.
  • Improvements

    • Enhanced parameter validation and error messages across multiple tools.
    • Improved error handling and resource cleanup throughout the application.
    • Better error context when loading images.
    • Simplified permission enum handling.
  • Chores

    • Modernized code to use Go 1.18+ and 1.21+ features for cleaner syntax.

yanmxa and others added 9 commits April 4, 2026 22:10
- Move ToolError and generateRequestID from edit.go to util.go so all
  tools in the package share a single definition rather than relying on
  cross-file visibility
- Add panic guard to Registry.Register for empty tool names (programming
  error caught at startup)
- Fix ImageRefPattern to use (?i) flag so uppercase extensions (.PNG,
  .JPG) are matched correctly
- Add file path context to image load errors for easier debugging
- Mark PermissionAllow/Deny/Ask/PermissionResult aliases as Deprecated
  via godoc comments
- Modernize extractBashCommands to use strings.SplitSeq

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SetTokenLimit was modifying ModelInfo slice elements via a range-copy
of ModelCache. The range copy shares the underlying array with the
stored data, so concurrent readers holding a slice from GetCachedModels
could observe partial writes without holding any lock.

Fix: copy the slice into a fresh allocation before mutation, then store
the new slice back. Also modernize GetConnections to use maps.Copy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dead code removal:
- Delete unused containsIgnoreCase in cmd/gen/agent.go (and the strings import)
- Delete unused getPlanSeparatorStyle/getPlanTitleStyle in prompt_plan.go
- Remove unused installScopes/installScopeIdx fields from plugin model

Code quality:
- Replace empty if-err branch with _ = in plugin/integration.go
- Simplify Extra loop to append(parts, s.Extra...) in system.go
- Inline blank-identifier assignment in askuser.go
- Use UpdateMsg(u) type conversion in progress.go (same underlying type)
- Fix capitalized error string in exa.go
- Convert if-else chains to switch in header.go and skill/loader.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MCP client:
- Explicitly discard transport.Close() errors in Connect() error paths
  (cleanup is best-effort; original error is already being returned)
- Fix handleNotification goroutine to check ListTools error and return
  early rather than silently skipping the onToolsChanged callback
- Replace interface{} with any in helper functions (Go 1.18+ idiom)
- Explicitly discard Disconnect() errors in RemoveServer/DisconnectAll
  and in ConnectServers cleanup (cleanup-path, errors are non-actionable)
- Explicitly discard io.Copy drain errors in HTTP/SSE transports
- Use defer func() { _ = r.Close() }() for deferred close in SSE readLoop

Other:
- Explicitly discard rand.Read error in cron/store.go generateID
- Explicitly discard json.Unmarshal/os.WriteFile in mcp/config.go
- Explicitly discard os.Remove in image/clipboard.go temp file cleanup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…check

Permission constants:
- Replace config.PermissionAllow/Deny/Ask with config.Allow/Deny/Ask
  in app/tool/run.go (following the Deprecated: godoc annotations)

Unchecked errors (best-effort cleanups):
- hooks/engine.go: discard stdinPipe.Close() errors (cleanup path)
- mcp/transport/stdio.go: discard stdin/stdout Close, Signal, and Kill
  errors during server shutdown (best-effort termination)
- app/mcp/commands.go: discard Close/Remove errors on tmpFile and
  use defer func(){_ = os.Remove(...)}() for cleanup defers
- app/mcp/model.go: discard Disconnect error in HandleDisconnect

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace empty if-err {} branches with _ = ... in plugin code
- Remove redundant embedded field name StylePrimitive in markdown.go
  glamour style configuration (can use promoted fields directly)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Discard config.Reload() returns with _, _ = (reload is best-effort)
- Discard os.Remove errors in todostore.go cleanup paths
- Discard os.Remove error in cmd/gen/mcp.go editor failure cleanup
- Use _, _ = fmt.Fprintln and _ = w.Flush() in history.go write path
- Explicitly discard tty.WriteString, tty.Close, and exec.Run in
  handler_command.go /clear command (terminal ops, errors non-actionable)
- Use defer func() { _ = log.Sync() }() in main.go
- Use defer func() { _ = resp.Body.Close() }() in webfetch.go

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New internal/provider/openai_compat package eliminates ~600 lines of
  duplication across openai/moonshot/alibaba providers:
  ConvertMessages, ConvertTools, MapFinishReason, ExtractReasoningContent
  are shared; each provider injects only its provider-specific behavior
  (e.g. reasoning_content) via a pluggable convertAssistant function.

- New param helpers in internal/tool/util.go (requireString, getString,
  getBool, getFloat64, getInt) applied across all 20+ tool files to
  replace repetitive type-assertion boilerplate and float64/int dual
  checks from JSON unmarshaling.

- Replace remaining interface{} with any in types, MCP transport, and
  tool interface definitions.

- Add zap.Error logging to compactAndReplace (was silently swallowing
  compaction failures).

- Minor modernizations: bytes.IndexByte for binary detection in read.go,
  max(min(...)) for textarea height clamping, min() for timeout clamping
  in bash.go and taskoutput.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on, modernize sorts

Session store:
- loadWithoutLock() now delegates to loadFromFile() — single canonical
  JSONL parser instead of two nearly-identical 50-line functions
- Extract applyMetadata() helper to copy EntryMetadata_ fields into
  SessionMetadata, eliminating the duplicated field-by-field block
- Replace sort.Slice with slices.SortFunc (time.Compare) for List() and
  listByScanning()

Anthropic provider:
- Extract convertAnthropicTools() and anyStrings() helpers; the tool
  conversion loop is now 5 lines instead of 25
- anyStrings() normalises the JSON Schema "required" field which may
  arrive as []string or []any depending on the caller

Core package:
- Add godoc to Messages(), SetMessages(), Tokens(), AddUser(),
  AddToolResult()
- Clarify DecideCompletion() precedence: tool calls always execute even
  when stop_reason == "max_tokens"

Provider sorts:
- Replace sort.Slice/sort.Strings with slices.SortFunc / slices.Sorted
  across alibaba, moonshot, openai, google providers and streamutil
  (AddToolCallsSorted, AddToolCallsByKey now use maps.Keys + slices.Sorted)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Apr 5, 2026

🧙 Sourcery has finished reviewing your pull request!


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 5, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a5cb96e-33df-46a2-adb8-4b7af49fdbfe

📥 Commits

Reviewing files that changed from the base of the PR and between 643be3b and 0a7975a.

⛔ Files ignored due to path filters (4)
  • cmd/gen/agent.go is excluded by !**/gen/**
  • cmd/gen/main.go is excluded by !**/gen/**
  • cmd/gen/mcp.go is excluded by !**/gen/**
  • cmd/gen/plugin.go is excluded by !**/gen/**
📒 Files selected for processing (85)
  • internal/app/agent/model.go
  • internal/app/approval/model.go
  • internal/app/conversation/model.go
  • internal/app/handler_approval.go
  • internal/app/handler_command.go
  • internal/app/input/input.go
  • internal/app/mcp/commands.go
  • internal/app/mcp/model.go
  • internal/app/memory/model.go
  • internal/app/mode/model.go
  • internal/app/mode/prompt_plan.go
  • internal/app/plugin/load.go
  • internal/app/plugin/model.go
  • internal/app/plugin/reset.go
  • internal/app/provider/state.go
  • internal/app/render/markdown.go
  • internal/app/session/model.go
  • internal/app/skill/model.go
  • internal/app/themeselect/model.go
  • internal/app/tool/model.go
  • internal/app/tool/run.go
  • internal/config/loader.go
  • internal/config/permission.go
  • internal/config/settings.go
  • internal/config/suggestion.go
  • internal/core/core.go
  • internal/cron/cron.go
  • internal/cron/store.go
  • internal/hooks/engine.go
  • internal/hooks/types.go
  • internal/image/clipboard.go
  • internal/mcp/caller.go
  • internal/mcp/client.go
  • internal/mcp/config.go
  • internal/mcp/registry.go
  • internal/mcp/transport/http.go
  • internal/mcp/transport/interface.go
  • internal/mcp/transport/sse.go
  • internal/mcp/transport/stdio.go
  • internal/mcp/types.go
  • internal/options/run.go
  • internal/plugin/integration.go
  • internal/provider/alibaba/client.go
  • internal/provider/anthropic/client.go
  • internal/provider/google/client.go
  • internal/provider/moonshot/client.go
  • internal/provider/openai/client.go
  • internal/provider/openai_compat/convert.go
  • internal/provider/search/exa.go
  • internal/provider/store.go
  • internal/provider/streamutil/streamutil.go
  • internal/provider/types.go
  • internal/session/store.go
  • internal/skill/loader.go
  • internal/system/system.go
  • internal/tool/agent.go
  • internal/tool/askuser.go
  • internal/tool/bash.go
  • internal/tool/croncreate.go
  • internal/tool/crondelete.go
  • internal/tool/edit.go
  • internal/tool/enterplanmode.go
  • internal/tool/enterworktree.go
  • internal/tool/exitplanmode.go
  • internal/tool/exitworktree.go
  • internal/tool/glob.go
  • internal/tool/grep.go
  • internal/tool/read.go
  • internal/tool/registry.go
  • internal/tool/skill.go
  • internal/tool/taskoutput.go
  • internal/tool/taskstop.go
  • internal/tool/todocreate.go
  • internal/tool/todoget.go
  • internal/tool/todostore.go
  • internal/tool/todoupdate.go
  • internal/tool/toolsearch.go
  • internal/tool/types.go
  • internal/tool/ui/header.go
  • internal/tool/util.go
  • internal/tool/webfetch.go
  • internal/tool/websearch.go
  • internal/tool/write.go
  • internal/ui/history/history.go
  • internal/ui/progress/progress.go

📝 Walkthrough

Walkthrough

This PR applies systematic code quality improvements across the codebase: converting interface{} to Go 1.18+ any type alias, standardizing error handling by explicitly discarding ignored errors, refactoring tool parameter parsing to use shared helpers, introducing a new OpenAI-compatible provider abstraction, and improving code readability through formatting adjustments and utility simplifications.

Changes

Cohort / File(s) Summary
Type Alias Modernization
internal/app/approval/model.go, internal/app/mode/model.go, internal/mcp/transport/interface.go, internal/mcp/types.go, internal/provider/types.go, internal/tool/types.go
Updated interface{} to any type alias in struct fields and function parameters across message types and provider interfaces, leveraging Go 1.18+ standard.
Error Handling Standardization
internal/app/handler_approval.go, internal/app/handler_command.go, internal/app/mcp/commands.go, internal/app/mcp/model.go, internal/app/plugin/load.go, internal/cron/store.go, internal/hooks/engine.go, internal/image/clipboard.go, internal/mcp/caller.go, internal/mcp/client.go, internal/mcp/config.go, internal/mcp/registry.go, internal/mcp/transport/*, internal/ui/history/history.go, internal/tool/bash.go, internal/tool/todostore.go, internal/tool/webfetch.go
Explicitly discard ignored error return values using _, _ = ... or defer func() { _ = ... }() patterns instead of implicit ignoring, making intentional error suppression clear.
Tool Parameter Parsing Refactoring
internal/tool/agent.go, internal/tool/askuser.go, internal/tool/bash.go, internal/tool/croncreate.go, internal/tool/crondelete.go, internal/tool/edit.go, internal/tool/enterplanmode.go, internal/tool/enterworktree.go, internal/tool/exitplanmode.go, internal/tool/exitworktree.go, internal/tool/glob.go, internal/tool/grep.go, internal/tool/read.go, internal/tool/skill.go, internal/tool/taskoutput.go, internal/tool/taskstop.go, internal/tool/todocreate.go, internal/tool/todoget.go, internal/tool/todoupdate.go, internal/tool/toolsearch.go, internal/tool/websearch.go, internal/tool/write.go
Consolidated parameter extraction from direct type assertions to shared helper functions (getString, getBool, getInt, requireString, getFloat64) for consistency and clearer error handling.
New OpenAI-Compatible Provider Abstraction
internal/provider/openai_compat/convert.go
Added new package with shared conversion utilities for OpenAI-compatible message/tool/finish-reason transformations, supporting code reuse across multiple provider implementations.
Provider Client Refactoring
internal/provider/alibaba/client.go, internal/provider/anthropic/client.go, internal/provider/moonshot/client.go, internal/provider/openai/client.go
Refactored message and tool conversion logic to delegate to openai_compat utilities, reducing code duplication and improving maintainability across OpenAI-compatible providers.
Sorting Modernization
internal/provider/google/client.go, internal/provider/streamutil/streamutil.go, internal/session/store.go
Replaced sort.Slice with Go 1.21+ slices.SortFunc and cmp.Compare utilities for cleaner, more idiomatic sorting.
Core API Expansion
internal/core/core.go
Added public methods (SetMessages, Tokens, AddUser, AddToolResult) to Loop for improved session control and message manipulation, with read-only semantics documented for Messages().
Utility Module Addition
internal/tool/util.go
Added new shared utilities module with ToolError type and parameter extraction helpers (requireString, getString, getBool, getInt, getFloat64, generateRequestID).
Logic Simplification & Code Cleanup
internal/app/input/input.go, internal/app/render/markdown.go, internal/config/permission.go, internal/input/input.go, internal/plugin/integration.go, internal/provider/search/exa.go, internal/provider/store.go, internal/skill/loader.go, internal/system/system.go, internal/tool/ui/header.go, internal/tool/registry.go, internal/tool/mode/prompt_plan.go
Pattern refactoring (iterator-based loops, case-insensitive regex, error wrapping, preallocation, switch statements) and removal of unused code without altering core behavior.
Formatting & Alignment Changes
internal/app/agent/model.go, internal/app/conversation/model.go, internal/app/memory/model.go, internal/app/session/model.go, internal/app/skill/model.go, internal/app/themeselect/model.go, internal/app/tool/model.go, internal/config/loader.go, internal/config/settings.go, internal/config/suggestion.go, internal/cron/cron.go, internal/hooks/types.go, internal/options/run.go, internal/provider/state.go
Adjusted whitespace/field alignment in struct definitions and constant declarations for improved readability; no functional changes.
Specific Logic Updates
internal/app/mode/prompt_plan.go, internal/app/plugin/model.go, internal/app/plugin/reset.go, internal/config/permission.go, internal/mcp/client.go, internal/ui/progress/progress.go
Removed unused helper functions, eliminated unused state fields, refactored permission constant deprecation documentation, simplified control flow in UI/protocol handling.

Sequence Diagram(s)

No sequence diagrams generated. Changes consist primarily of type alias updates, error handling standardization, parameter parsing consolidation, and utility refactoring—without significant new control flow or multi-component interactions warranting visualization.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Refactor/architecture analysis #4: Shares modifications to internal/config/permission.go with overlapping restructuring of permission constant handling and potential security helper extraction.

Poem

🐰 Hop along with types so clean,
interface{} to any seen,
Errors now explicit, no more hide,
Tools extract with helpers wide,
OpenAI-compat brings shared delight! 🚀

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/pr5-valuable

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@yanmxa yanmxa merged commit 07ececa into main Apr 5, 2026
2 of 4 checks passed
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • In internal/config/permission.go, extractBashCommands now uses strings.SplitSeq with range (for part := range strings.SplitSeq(...)), but the standard library strings package has no SplitSeq and range over a function call like this is invalid; this will fail to compile unless there is a custom strings implementation, so either restore strings.Split or implement/rename the intended helper.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In internal/config/permission.go, extractBashCommands now uses strings.SplitSeq with range (`for part := range strings.SplitSeq(...)`), but the standard library strings package has no SplitSeq and range over a function call like this is invalid; this will fail to compile unless there is a custom strings implementation, so either restore strings.Split or implement/rename the intended helper.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@yanmxa yanmxa deleted the refactor/pr5-valuable branch April 21, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant