Skip to content

fix(passthrough): preserve original provider name in route resolution#298

Open
vfeitoza wants to merge 1 commit intoENTERPILOT:mainfrom
vfeitoza:fix/passthrough-preserve-provider-name
Open

fix(passthrough): preserve original provider name in route resolution#298
vfeitoza wants to merge 1 commit intoENTERPILOT:mainfrom
vfeitoza:fix/passthrough-preserve-provider-name

Conversation

@vfeitoza
Copy link
Copy Markdown

@vfeitoza vfeitoza commented May 3, 2026

The semantic enrichment middleware was overwriting info.Provider with the
resolved type (e.g. 'anthropic') before caching, causing passthroughExecutionTarget
to route by type instead of the original configured name (e.g. 'teste').

Added info.ProviderName field to preserve the original route name through the
resolution pipeline. The execution target now prefers the cached provider name
when available, ensuring requests route to the correct configured instance.

Summary by CodeRabbit

  • New Features

    • Added support for name-based provider routing, allowing you to specify a particular configured provider instance by name for pass-through requests instead of relying solely on provider type-based resolution.
  • Improvements

    • Enhanced debug logging for provider resolution to aid in troubleshooting routing behavior.

  The semantic enrichment middleware was overwriting info.Provider with the
  resolved type (e.g. 'anthropic') before caching, causing passthroughExecutionTarget
  to route by type instead of the original configured name (e.g. 'teste').

  Added info.ProviderName field to preserve the original route name through the
  resolution pipeline. The execution target now prefers the cached provider name
  when available, ensuring requests route to the correct configured instance.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 3, 2026

📝 Walkthrough

Walkthrough

A new ProviderName field was added to PassthroughRequest and PassthroughRouteInfo structs to enable name-based routing of configured provider instances. The routing, resolution, enrichment, and service layers were updated to thread this provider name through the passthrough execution pipeline.

Changes

Provider-Instance Name Routing

Layer / File(s) Summary
Data Shape
internal/core/passthrough.go, internal/core/semantic.go
PassthroughRequest and PassthroughRouteInfo both gain new ProviderName string fields for carrying provider instance names through the request pipeline.
Provider Resolution
internal/server/passthrough_provider_resolution.go
resolvePassthroughProvider now includes debug-level logging for resolution attempts and fallback behavior using slog.
Router Routing Logic
internal/providers/router.go
Router.Passthrough now attempts name-based routing via the name registry when req.ProviderName is set, validates the provider implements core.PassthroughProvider, logs resolution details, and falls back to type-based routing if name lookup fails.
Semantic Enrichment
internal/server/passthrough_semantic_enrichment.go
Middleware now assigns info.ProviderName from the resolved provider's ProviderName field when resolution succeeds.
Execution & Service Integration
internal/server/passthrough_execution_helpers.go, internal/server/passthrough_service.go
passthroughExecutionTarget now returns providerName as a separate return value and prefers cached info.ProviderName with fallback to resolved provider name; ProviderPassthrough service now populates req.ProviderName when constructing the passthrough request.
Tests
internal/server/passthrough_execution_helpers_test.go
Test cases updated to destructure the new providerName return value from passthroughExecutionTarget and verify expected provider name resolution.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as PassthroughService
    participant Enrichment as SemanticEnrichment
    participant Resolver as ProviderResolver
    participant Router as Router
    participant Registry as NameRegistry
    participant Provider as PassthroughProvider

    Client->>Server: HTTP request
    Server->>Enrichment: passthrough request (initial)
    Enrichment->>Resolver: resolvePassthroughProvider(providerType)
    Resolver->>Resolver: compute providerType (trimmed)
    Resolver-->>Enrichment: resolved provider info
    Enrichment->>Enrichment: cache info.ProviderName from resolved
    Enrichment-->>Server: enriched route info
    
    Server->>Server: passthroughExecutionTarget()
    Server->>Server: extract providerName (prefer cached, fallback to resolved)
    Server-->>Server: return (providerType, providerName, endpoint, info)
    
    Server->>Server: build PassthroughRequest with ProviderName
    Server->>Router: Router.Passthrough(ctx, providerType, req with ProviderName)
    
    alt req.ProviderName is set
        Router->>Registry: resolve by provider name
        Registry-->>Router: named provider instance
        Router->>Router: verify implements PassthroughProvider
        Router->>Provider: Passthrough(ctx, req)
    else fallback to type-based
        Router->>Router: resolve by providerType
        Router->>Provider: Passthrough(ctx, req)
    end
    
    Provider-->>Router: PassthroughResponse
    Router-->>Server: PassthroughResponse
    Server->>Client: HTTP response
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Labels

release:internal

Poem

🐰 Through names we route, no type confusion here,
Provider instances now crystal clear!
The router hops with name-based grace,
Each configured instance finds its place. 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: preserving the original provider name during route resolution by adding a ProviderName field to avoid overwriting the original route name with the resolved provider type.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 3, 2026

Greptile Summary

The semantic enrichment middleware was overwriting info.Provider with the resolved type (e.g. \"anthropic\") before caching, causing passthroughExecutionTarget and ultimately Router.Passthrough to route by provider type rather than the original configured instance name (e.g. \"teste\"). This PR fixes the bug by adding a ProviderName field to PassthroughRouteInfo and PassthroughRequest, preserving the original name through the resolution pipeline so named-instance routing works correctly end-to-end.

Confidence Score: 4/5

Safe to merge; the fix is logically sound with only minor test coverage and logging consistency gaps.

All findings are P2: missing unit test for the primary enrichment-cached path and context-less slog.Debug calls. No logic errors or security issues found.

passthrough_execution_helpers_test.go — missing test for the pre-populated info.ProviderName case; passthrough_provider_resolution.go — context-less debug logging.

Important Files Changed

Filename Overview
internal/server/passthrough_semantic_enrichment.go Correctly sets info.ProviderName before overwriting info.Provider with the resolved type — this is the key source-of-truth preservation.
internal/server/passthrough_execution_helpers.go Expands return signature to include providerName; prefers cached info.ProviderName over re-resolved name. Primary fix path (enrichment pre-populated) lacks a unit test.
internal/server/passthrough_provider_resolution.go Adds debug logging; context-less slog.Debug calls are inconsistent with slog.DebugContext usage in router.go.
internal/providers/router.go Adds name-based provider lookup with a type-based fallback in Passthrough; properly guards against nil req and non-PassthroughProvider named providers.
internal/server/passthrough_execution_helpers_test.go Correctly updates call sites and adds providerName assertion for the name-resolved path, but the primary enrichment-cached-name scenario is untested.
internal/core/semantic.go Adds ProviderName field to PassthroughRouteInfo with a clear disambiguating comment; no logic changes.
internal/core/passthrough.go Adds optional ProviderName field to PassthroughRequest for name-based routing in the router layer.
internal/server/passthrough_service.go Threads providerName from passthroughExecutionTarget into PassthroughRequest; straightforward wiring.

Sequence Diagram

sequenceDiagram
    participant Client
    participant SemanticEnrichment as PassthroughSemanticEnrichment
    participant ExecTarget as passthroughExecutionTarget
    participant Service as passthroughService
    participant Router as Router.Passthrough

    Client->>SemanticEnrichment: POST /p/teste/v1/messages
    Note over SemanticEnrichment: info.Provider = "teste"
    SemanticEnrichment->>SemanticEnrichment: resolvePassthroughProvider("teste")<br/>→ {Type:"anthropic", Name:"teste"}
    Note over SemanticEnrichment: info.ProviderName = "teste" ✅<br/>info.Provider = "anthropic"
    SemanticEnrichment->>SemanticEnrichment: CachePassthroughRouteInfo(env, info)
    SemanticEnrichment->>ExecTarget: next(c)
    ExecTarget->>ExecTarget: providerName = info.ProviderName → "teste"
    ExecTarget->>ExecTarget: providerType = resolved.ProviderType → "anthropic"
    ExecTarget-->>Service: (providerType="anthropic", providerName="teste", ...)
    Service->>Router: Passthrough(ctx, "anthropic", req{ProviderName:"teste"})
    Router->>Router: providerByNameRegistry("teste") → named instance
    Router->>Router: named.(PassthroughProvider).Passthrough(ctx, req)
    Router-->>Client: upstream response
Loading

Comments Outside Diff (1)

  1. internal/server/passthrough_execution_helpers_test.go, line 71-102 (link)

    P2 Primary bug path not covered by tests

    The tests only exercise the fallback path where info.ProviderName is empty and providerName is derived from resolved.ProviderName. The core scenario this PR fixes — where the semantic enrichment middleware has already run, setting info.ProviderName before info.Provider is overwritten with the resolved type — has no unit test. Without a test that pre-populates info.ProviderName on the cached PassthroughRouteInfo (e.g., Provider = "anthropic", ProviderName = "teste"), a future refactor could silently break the preference logic (providerName := strings.TrimSpace(info.ProviderName)) and the regression would go undetected.

Reviews (1): Last reviewed commit: "fix(passthrough): preserve original prov..." | Re-trigger Greptile

Comment on lines +24 to +45
providerType := strings.TrimSpace(named.GetProviderTypeForName(routeProvider))
slog.Debug("passthrough provider resolution", "routeProvider", routeProvider, "resolvedType", providerType, "hasNameTypeResolver", true)
if providerType != "" {
return passthroughProviderResolution{
RouteProvider: routeProvider,
ProviderType: providerType,
ProviderName: routeProvider,
}
}
} else {
slog.Debug("passthrough provider resolution", "routeProvider", routeProvider, "hasNameTypeResolver", false)
}
} else {
slog.Debug("passthrough provider resolution", "routeProvider", routeProvider, "provider", "nil")
}

return passthroughProviderResolution{
result := passthroughProviderResolution{
RouteProvider: routeProvider,
ProviderType: routeProvider,
ProviderName: workflowProviderNameForType(provider, routeProvider),
}
slog.Debug("passthrough provider resolution fallback", "routeProvider", routeProvider, "providerType", result.ProviderType, "providerName", result.ProviderName)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Context-less slog.Debug calls lose request-scoped attributes

The new slog.Debug(...) calls use the global logger without a context, so request-scoped values (trace IDs, request IDs, etc.) are not attached. router.go uses slog.DebugContext(ctx, ...) for the same operation. resolvePassthroughProvider currently has no context.Context parameter; if this logging proves useful operationally, threading a context through would make these entries correlate with the caller's trace.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/providers/router.go`:
- Around line 772-795: Add regression tests under **/*_test.go that exercise
Router.Passthrough’s new name-first routing: (1) name hit — register a provider
implementing core.PassthroughProvider, call Router.Passthrough with
req.ProviderName set and assert the provider’s Passthrough is invoked and
response normalized; (2) name miss — ensure providerByNameRegistry returns nil
and Router falls back to resolvePassthroughProvider, asserting the fallback
provider is used and behavior matches existing tests; (3) name hit but provider
lacks PassthroughProvider — register a provider by name that does NOT implement
core.PassthroughProvider, call Router.Passthrough with that name and assert it
falls back to type resolution (or returns correct error/behavior). Use
Router.Passthrough, providerByNameRegistry, resolvePassthroughProvider,
core.PassthroughProvider, core.PassthroughRequest and core.PassthroughResponse
in assertions and cover request translation, response normalization, and error
handling per guidelines.

In `@internal/server/passthrough_semantic_enrichment.go`:
- Around line 43-45: resolvePassthroughProvider can return an empty ProviderName
in fallback cases, so don't unconditionally overwrite info.ProviderName; update
the assignment in the passthrough logic (where resolvePassthroughProvider(...)
is called and info.ProviderName/info.Provider are set) to only set
info.ProviderName when resolved.ProviderName is non-empty, while still assigning
info.Provider = resolved.ProviderType as before to preserve provider type
routing and avoid wiping the original route name before caching.
🪄 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: e166c22d-4b23-4a1e-9a75-f730050a9c0f

📥 Commits

Reviewing files that changed from the base of the PR and between 7bb6190 and 8f526a3.

📒 Files selected for processing (8)
  • internal/core/passthrough.go
  • internal/core/semantic.go
  • internal/providers/router.go
  • internal/server/passthrough_execution_helpers.go
  • internal/server/passthrough_execution_helpers_test.go
  • internal/server/passthrough_provider_resolution.go
  • internal/server/passthrough_semantic_enrichment.go
  • internal/server/passthrough_service.go

Comment on lines 772 to 795
func (r *Router) Passthrough(ctx context.Context, providerType string, req *core.PassthroughRequest) (*core.PassthroughResponse, error) {
pp, err := r.resolvePassthroughProvider(providerType)
if err != nil {
return nil, err
var pp core.PassthroughProvider
if req != nil && strings.TrimSpace(req.ProviderName) != "" {
slog.DebugContext(ctx, "passthrough routing by name", "providerName", req.ProviderName, "providerType", providerType)
if p := r.providerByNameRegistry(strings.TrimSpace(req.ProviderName)); p != nil {
if named, ok := p.(core.PassthroughProvider); ok {
pp = named
slog.DebugContext(ctx, "passthrough routed by name", "providerName", req.ProviderName)
} else {
slog.DebugContext(ctx, "passthrough provider found by name but does not implement PassthroughProvider", "providerName", req.ProviderName)
}
} else {
slog.DebugContext(ctx, "passthrough provider not found by name, falling back to type", "providerName", req.ProviderName, "providerType", providerType)
}
}
if pp == nil {
var err error
pp, err = r.resolvePassthroughProvider(providerType)
if err != nil {
return nil, err
}
}
return pp.Passthrough(ctx, req)
}
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.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add regression tests for the new name-first passthrough branch.

Router.Passthrough() now changes routing precedence, so please cover:

  • name hit,
  • name miss, and
  • name hit where the provider does not implement PassthroughProvider.

As per coding guidelines, **/*_test.go: Add or update tests for behavior changes. Tests should cover request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/providers/router.go` around lines 772 - 795, Add regression tests
under **/*_test.go that exercise Router.Passthrough’s new name-first routing:
(1) name hit — register a provider implementing core.PassthroughProvider, call
Router.Passthrough with req.ProviderName set and assert the provider’s
Passthrough is invoked and response normalized; (2) name miss — ensure
providerByNameRegistry returns nil and Router falls back to
resolvePassthroughProvider, asserting the fallback provider is used and behavior
matches existing tests; (3) name hit but provider lacks PassthroughProvider —
register a provider by name that does NOT implement core.PassthroughProvider,
call Router.Passthrough with that name and assert it falls back to type
resolution (or returns correct error/behavior). Use Router.Passthrough,
providerByNameRegistry, resolvePassthroughProvider, core.PassthroughProvider,
core.PassthroughRequest and core.PassthroughResponse in assertions and cover
request translation, response normalization, and error handling per guidelines.

Comment on lines 43 to 45
if resolved := resolvePassthroughProvider(provider, info.Provider); resolved.ProviderType != "" {
info.ProviderName = resolved.ProviderName
info.Provider = resolved.ProviderType
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve ProviderName when the resolver cannot supply one.

resolvePassthroughProvider() can return an empty ProviderName in the fallback path. Assigning it unconditionally here can wipe out the original route name before caching, which defeats the new name-based passthrough routing.

🔧 Suggested fix
 if resolved := resolvePassthroughProvider(provider, info.Provider); resolved.ProviderType != "" {
-    info.ProviderName = resolved.ProviderName
+    if resolved.ProviderName != "" {
+        info.ProviderName = resolved.ProviderName
+    }
     info.Provider = resolved.ProviderType
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if resolved := resolvePassthroughProvider(provider, info.Provider); resolved.ProviderType != "" {
info.ProviderName = resolved.ProviderName
info.Provider = resolved.ProviderType
if resolved := resolvePassthroughProvider(provider, info.Provider); resolved.ProviderType != "" {
if resolved.ProviderName != "" {
info.ProviderName = resolved.ProviderName
}
info.Provider = resolved.ProviderType
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/server/passthrough_semantic_enrichment.go` around lines 43 - 45,
resolvePassthroughProvider can return an empty ProviderName in fallback cases,
so don't unconditionally overwrite info.ProviderName; update the assignment in
the passthrough logic (where resolvePassthroughProvider(...) is called and
info.ProviderName/info.Provider are set) to only set info.ProviderName when
resolved.ProviderName is non-empty, while still assigning info.Provider =
resolved.ProviderType as before to preserve provider type routing and avoid
wiping the original route name before caching.

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