Skip to content

refactor(virtualmodels): replace dead Provider decorator with one-method chat executor#477

Merged
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/virtualmodels-drop-provider-decorator
Jul 4, 2026
Merged

refactor(virtualmodels): replace dead Provider decorator with one-method chat executor#477
SantiagoDePolonia merged 1 commit into
mainfrom
refactor/virtualmodels-drop-provider-decorator

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Next architecture-review item (doc in #472): the 572-line virtualmodels.Provider decorator mirrored the full ~30-method routable capability surface, but production only ever constructed it once — as the guardrail auxiliary-LLM executor bootstrap in app.go, where the consumed interface (guardrails.ChatCompletionExecutor) has exactly one method. The main request path resolves redirects through Service seams (gateway/interfaces.go), and native batch preparation through BatchPreparer — never through the decorator.

  • New ChatExecutor (redirect rewrite + delegate) replaces the decorator in app.go; it performs the identical rewriteChatRequest the decorator's ChatCompletion did, so guardrail aux calls keep resolving virtual models.
  • The shared redirect-rewrite helpers that BatchPreparer uses move to redirect_rewrite.go; decorator-only helpers (resolveRedirectModel, rewriteResponsesRequest, rewriteEmbeddingRequest) are deleted.
  • Removes the standing obligation to mirror every future provider capability method into the decorator.

Test migration

The decorator was load-bearing in six server test harnesses:

  • Three had explicit-resolver twins already in-tree (literally named ...WithoutProviderDecorator) — the decorator variants are deleted, twins keep the coverage.
  • Three are converted to the production mechanisms: WorkflowResolutionWithResolver(inner, service), and NewBatchPreparer + handler.modelResolver (matching the pattern neighboring batch tests already use).
  • New package-level unit tests cover ChatExecutor (redirect rewrite, concrete-model passthrough, model-not-found short-circuit); the rewriteChatRequest helper test moves over from provider_test.go.

Net −294 lines.

User-visible impact

None. No production behavior change.

Testing

Full go build ./... and go test ./...; go test -race on virtualmodels + server; golangci-lint 0 issues; pre-commit make test-race on the commit.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved model routing for chat requests so redirected or aliased models are resolved more reliably before being sent to the provider.
    • Added safer handling for unsupported models, returning clearer not-found errors instead of forwarding invalid requests.
  • Tests

    • Updated and expanded coverage for model resolution and request rewriting behavior across chat, batch, and file-related flows.

…hod chat executor

The 572-line redirect-aware Provider decorator mirrored the full ~30-method
routable capability surface, but its only production construction was as the
guardrail auxiliary-LLM executor (app.go), a single-method seam superseded by
SetExecutor during wiring. The main request path resolves redirects through
Service seams (gateway/interfaces.go), and native batch preparation through
BatchPreparer — never through the decorator.

Replace it with ChatExecutor (redirect-rewrite + delegate, the one behavior
the bootstrap needs) and keep the shared redirect-rewrite helpers that
BatchPreparer uses in redirect_rewrite.go. Delete the decorator-only helpers
(resolveRedirectModel, rewriteResponsesRequest, rewriteEmbeddingRequest) and
the standing obligation to mirror every new capability method.

Tests: decorator-based server tests either had explicit-resolver twins
(...WithoutProviderDecorator) and are deleted, or are converted to the
production mechanisms (WorkflowResolutionWithResolver, NewBatchPreparer +
handler.modelResolver). New unit tests cover ChatExecutor redirect rewrite,
concrete-model passthrough, and model-not-found; the rewriteChatRequest
helper test moves from provider_test.go.

Net: -294 lines, no production behavior change (the executor performs the
same rewriteChatRequest the decorator's ChatCompletion did).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR removes the virtualmodels.Provider decorator type and replaces the guardrails chat completion path with a new ChatExecutor that resolves redirects and rewrites chat requests directly. Server tests are updated to wire handlers with explicit resolvers and batch preparers instead of provider decoration.

Changes

Provider decorator replacement with ChatExecutor

Layer / File(s) Summary
ChatExecutor and redirect rewrite implementation
internal/virtualmodels/redirect_rewrite.go
Adds ChatExecutor, ChatExecutorProvider interface, constructor, and helpers resolving redirects, validating provider type/support, and rewriting chat requests.
ChatExecutor and rewrite tests
internal/virtualmodels/redirect_rewrite_test.go
Adds mock provider and tests for redirect rewriting, pass-through, unsupported model errors, and request rewrite edge cases.
App guardrails executor wiring
internal/app/app.go
Switches guardrail executor construction from NewProviderWithOptions to NewChatExecutor.
Provider wrapper removal
internal/virtualmodels/provider.go, internal/virtualmodels/provider_test.go
Deletes the legacy Provider wrapper type, its methods, and the associated test.
Server test wiring updates
internal/server/handlers_test.go, internal/server/model_validation_test.go
Removes obsolete alias-resolution tests and rewires batch/file/model-validation tests to use direct handler construction with explicit modelResolver, batchRequestPreparer, and WorkflowResolutionWithResolver.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ChatExecutor
  participant Service
  participant InnerProvider

  Caller->>ChatExecutor: ChatCompletion(req)
  ChatExecutor->>Service: ResolveModelForUserPath(selector)
  Service-->>ChatExecutor: resolved model/provider
  ChatExecutor->>ChatExecutor: validate provider type / support
  alt resolution succeeds
    ChatExecutor->>InnerProvider: ChatCompletion(rewritten req)
    InnerProvider-->>ChatExecutor: ChatResponse
    ChatExecutor-->>Caller: ChatResponse
  else resolution fails
    ChatExecutor-->>Caller: GatewayError (invalid_request/model_not_found)
  end
Loading

Possibly related PRs

  • ENTERPILOT/GoModel#424: Both PRs touch the internal/virtualmodels request-rewrite infrastructure, with the retrieved PR refactoring batch-item rewrite logic and simplifying provider.go that this PR later removes.

Poem

A wrapper hopped away today,
Its Provider cloak tucked far away,
A ChatExecutor takes the lead,
Rewriting routes with redirect speed,
Tests rewired, clean and neat —
This bunny thumps in review-beat! 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main refactor: replacing the Provider decorator with a one-method chat executor.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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/virtualmodels-drop-provider-decorator

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.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 87.71930% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/virtualmodels/redirect_rewrite.go 89.28% 3 Missing and 3 partials ⚠️
internal/app/app.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/server/handlers_test.go`:
- Around line 4096-4098: The test setup for Handler wiring is duplicated in
multiple batch tests, so extract the repeated
NewHandler/modelResolver/batchRequestPreparer initialization into a small helper
such as newBatchTestHandler(inner *mockProvider, service *virtualmodels.Service)
*Handler. Update the affected tests to call that helper instead of repeating the
same three-line block, using the existing Handler and
virtualmodels.NewBatchPreparer setup to keep the wiring in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4fe47690-c13e-46e7-950b-0566d20cf9cb

📥 Commits

Reviewing files that changed from the base of the PR and between 0f9952d and 865e324.

📒 Files selected for processing (7)
  • internal/app/app.go
  • internal/server/handlers_test.go
  • internal/server/model_validation_test.go
  • internal/virtualmodels/provider.go
  • internal/virtualmodels/provider_test.go
  • internal/virtualmodels/redirect_rewrite.go
  • internal/virtualmodels/redirect_rewrite_test.go
💤 Files with no reviewable changes (2)
  • internal/virtualmodels/provider_test.go
  • internal/virtualmodels/provider.go

Comment on lines +4096 to +4098
handler := NewHandler(inner, nil, nil, nil)
handler.modelResolver = service
handler.batchRequestPreparer = virtualmodels.NewBatchPreparer(inner, service)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor duplication in test wiring.

The 3-line NewHandler + modelResolver + batchRequestPreparer wiring block is repeated verbatim in both TestBatches_InputFileRewritesAliasesAndPersistsBatchPreparation and TestBatches_InputFileRejectsDisabledAlias. Consider extracting a small helper (e.g. newBatchTestHandler(inner *mockProvider, service *virtualmodels.Service) *Handler) to avoid repeating this pattern if more call sites are added later.

Also applies to: 4265-4267

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/server/handlers_test.go` around lines 4096 - 4098, The test setup
for Handler wiring is duplicated in multiple batch tests, so extract the
repeated NewHandler/modelResolver/batchRequestPreparer initialization into a
small helper such as newBatchTestHandler(inner *mockProvider, service
*virtualmodels.Service) *Handler. Update the affected tests to call that helper
instead of repeating the same three-line block, using the existing Handler and
virtualmodels.NewBatchPreparer setup to keep the wiring in one place.

@greptile-apps

greptile-apps Bot commented Jul 4, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

Safe to merge with low risk.

The refactor preserves the remaining chat redirect path and keeps production server routing on explicit resolver, authorizer, and batch preparer seams. No blocking correctness or security issues were found.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • A proof for a posted P1 finding was produced and linked to its review comment for reviewer inspection.
  • An end-to-end contract-validation run was completed, including a full build that exited 0 after 87 seconds, a full test that exited 1 after 85 seconds with a failure in TestResolveProviders_NoProvidersNoEnvVars, and a focused fallback validation where all steps exited 0.
  • Artifacts capturing the contract-validation outputs were uploaded and organized to support review.

View all artifacts

T-Rex Ran code and verified through T-Rex

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant App as app.New
participant GR as Guardrails service
participant CE as virtualmodels.ChatExecutor
participant VM as virtualmodels.Service
participant Router as Provider router

App->>CE: NewChatExecutor(router, vm)
App->>GR: New(..., ChatExecutor)
GR->>CE: ChatCompletion(ctx, req)
CE->>VM: ResolveModelForUserPath(ctx, req model/provider)
VM-->>CE: resolved selector or literal selector
CE->>Router: Supports(resolved qualified model)
Router-->>CE: supported
CE->>Router: ChatCompletion(ctx, rewritten req)
Router-->>CE: ChatResponse
CE-->>GR: ChatResponse
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant App as app.New
participant GR as Guardrails service
participant CE as virtualmodels.ChatExecutor
participant VM as virtualmodels.Service
participant Router as Provider router

App->>CE: NewChatExecutor(router, vm)
App->>GR: New(..., ChatExecutor)
GR->>CE: ChatCompletion(ctx, req)
CE->>VM: ResolveModelForUserPath(ctx, req model/provider)
VM-->>CE: resolved selector or literal selector
CE->>Router: Supports(resolved qualified model)
Router-->>CE: supported
CE->>Router: ChatCompletion(ctx, rewritten req)
Router-->>CE: ChatResponse
CE-->>GR: ChatResponse
Loading

Comments Outside Diff (1)

  1. General comment

    P1 Full Go test suite fails in internal/providers

    • Bug
      • The requested go test ./... validation fails because TestResolveProviders_NoProvidersNoEnvVars reports expected empty result, got 1 entries and expected empty filtered raw, got 1 entries, causing gomodel/internal/providers and the overall full suite to fail.
    • Cause
      • The failing assertion is in internal/providers/config_test.go and indicates provider resolution returned one configured provider when the test expected none. This is outside the changed files for the virtualmodels/server/app ChatExecutor refactor, but it blocks the full repository test plan.
    • Fix
      • Investigate why the provider-resolution test environment contains a provider entry when no providers/env vars are expected. Ensure the test clears all relevant provider environment variables/config inputs or update provider resolution behavior if it is incorrectly injecting a default provider.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "refactor(virtualmodels): replace dead Pr..." | Re-trigger Greptile

@SantiagoDePolonia SantiagoDePolonia merged commit e864064 into main Jul 4, 2026
21 checks passed
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.

2 participants