feat(deepseek): add Passthrough support and PassthroughSemanticEnricher#314
feat(deepseek): add Passthrough support and PassthroughSemanticEnricher#314sonSunnoi wants to merge 6 commits intoENTERPILOT:mainfrom
Conversation
📝 WalkthroughWalkthroughThis PR adds passthrough request support for the DeepSeek provider. It introduces a semantic enricher to map endpoints to operations, implements the Passthrough method with validation, updates configuration defaults to include deepseek, and adds comprehensive test coverage for the new functionality. ChangesDeepSeek Passthrough Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 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 |
Greptile SummaryThis PR adds first-class passthrough support for the DeepSeek provider, enabling
Confidence Score: 4/5Safe to merge; the change is well-scoped and follows the established pattern used by Anthropic and OpenAI providers. The implementation is a clean port of the existing Anthropic/OpenAI passthrough plumbing into DeepSeek, with thorough unit tests covering auth forwarding, header propagation, 4xx preservation, nil guards, and interface compliance. The only omission is that
Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Server as GoModel Server
participant Enricher as PassthroughSemanticEnricher
participant Provider as DeepSeek Provider
participant DS as api.deepseek.com
Client->>Server: POST /p/deepseek/beta/completions
Server->>Server: isEnabledPassthroughProvider("deepseek")
Server->>Server: normalizePassthroughEndpoint("beta/completions")
Server->>Enricher: "Enrich(info{RawEndpoint: "/beta/completions"})"
Enricher-->>Server: "info{SemanticOp: "deepseek.fim_completions", AuditPath: "/beta/completions"}"
Server->>Provider: "Passthrough(ctx, req{Endpoint: "/beta/completions"})"
Provider->>Provider: "DoPassthrough -> setHeaders (Bearer auth)"
Provider->>DS: POST /beta/completions
DS-->>Provider: 200 OK + body
Provider-->>Server: "PassthroughResponse{StatusCode:200, Body}"
Server-->>Client: 200 OK + body (audit logged as /beta/completions)
|
There was a problem hiding this comment.
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)
config/config.go (1)
516-523:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate the
EnabledPassthroughProvidersdefault comment to includedeepseek.Implementation now includes
deepseek, but the field comment above still documents the old default list.Suggested fix
// EnabledPassthroughProviders lists the provider types enabled on // /p/{provider}/... passthrough routes. Default: - // ["openai", "anthropic", "openrouter", "zai", "vllm"]. + // ["openai", "anthropic", "openrouter", "zai", "vllm", "deepseek"]. EnabledPassthroughProviders []string `yaml:"enabled_passthrough_providers" env:"ENABLED_PASSTHROUGH_PROVIDERS"`As per coding guidelines: Keep the implementation explicit and maintainable rather than relying on clever abstractions.
🤖 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 `@config/config.go` around lines 516 - 523, The struct field comment for EnabledPassthroughProviders is out of sync with its default values; update the comment above the EnabledPassthroughProviders field in config/config.go to list the current defaults including "deepseek" (match the slice: "openai", "anthropic", "openrouter", "zai", "vllm", "deepseek") so the docstring accurately reflects the implemented default providers; keep phrasing consistent with surrounding field comments.
🤖 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 `@docs/features/passthrough-api.mdx`:
- Line 148: The env default list uses the variable ENABLED_PASSTHROUGH_PROVIDERS
with value "openai,anthropic,openrouter,zai,vllm,deepseek" but the "Current
limitations" bullet list omits "deepseek", creating an inconsistency; update the
documentation so both places match—either add "deepseek" to the earlier bullet
list describing default-enabled providers or remove "deepseek" from the
ENABLED_PASSTHROUGH_PROVIDERS example value—ensure you modify the referenced
symbol ENABLED_PASSTHROUGH_PROVIDERS and the "Current limitations" provider
bullet so they show the same default set and add a one-line note when/how to
change the default if needed.
In `@internal/providers/deepseek/deepseek.go`:
- Around line 198-205: In Provider.Passthrough, add validation to reject
requests where req.Method or req.Endpoint are empty strings before calling
p.client.DoPassthrough: check req.Method == "" and req.Endpoint == "" (or
either) and return core.NewInvalidRequestError with a clear message and
underlying nil, so empty Method/Endpoint on core.PassthroughRequest are blocked
at the boundary (refer to Provider.Passthrough, core.PassthroughRequest, and
providers.PassthroughEndpoint in the diff).
---
Outside diff comments:
In `@config/config.go`:
- Around line 516-523: The struct field comment for EnabledPassthroughProviders
is out of sync with its default values; update the comment above the
EnabledPassthroughProviders field in config/config.go to list the current
defaults including "deepseek" (match the slice: "openai", "anthropic",
"openrouter", "zai", "vllm", "deepseek") so the docstring accurately reflects
the implemented default providers; keep phrasing consistent with surrounding
field comments.
🪄 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: ab455586-3fe3-4480-a23f-39df8c1f0eca
📒 Files selected for processing (11)
README.mdconfig/config.example.yamlconfig/config.goconfig/config_test.godocs/features/passthrough-api.mdxinternal/providers/deepseek/deepseek.gointernal/providers/deepseek/deepseek_test.gointernal/providers/deepseek/passthrough_semantics.gointernal/providers/deepseek/passthrough_semantics_test.gointernal/server/handlers_test.gointernal/server/passthrough_support.go
| func (p *Provider) Passthrough(ctx context.Context, req *core.PassthroughRequest) (*core.PassthroughResponse, error) { | ||
| if req == nil { | ||
| return nil, core.NewInvalidRequestError("passthrough request is required", nil) | ||
| } | ||
| resp, err := p.client.DoPassthrough(ctx, llmclient.Request{ | ||
| Method: req.Method, | ||
| Endpoint: providers.PassthroughEndpoint(req.Endpoint), | ||
| RawBodyReader: req.Body, |
There was a problem hiding this comment.
Validate empty passthrough method/endpoint before forwarding.
nil is handled, but empty Method/Endpoint can still slip through and lead to unintended upstream calls (for example, implicit/default method behavior). Add explicit validation at this boundary.
Proposed patch
func (p *Provider) Passthrough(ctx context.Context, req *core.PassthroughRequest) (*core.PassthroughResponse, error) {
if req == nil {
return nil, core.NewInvalidRequestError("passthrough request is required", nil)
}
+ if strings.TrimSpace(req.Method) == "" {
+ return nil, core.NewInvalidRequestError("passthrough method is required", nil)
+ }
+ if strings.TrimSpace(req.Endpoint) == "" {
+ return nil, core.NewInvalidRequestError("passthrough endpoint is required", nil)
+ }
resp, err := p.client.DoPassthrough(ctx, llmclient.Request{
Method: req.Method,
Endpoint: providers.PassthroughEndpoint(req.Endpoint),
RawBodyReader: req.Body,
Headers: req.Headers,🤖 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/providers/deepseek/deepseek.go` around lines 198 - 205, In
Provider.Passthrough, add validation to reject requests where req.Method or
req.Endpoint are empty strings before calling p.client.DoPassthrough: check
req.Method == "" and req.Endpoint == "" (or either) and return
core.NewInvalidRequestError with a clear message and underlying nil, so empty
Method/Endpoint on core.PassthroughRequest are blocked at the boundary (refer to
Provider.Passthrough, core.PassthroughRequest, and providers.PassthroughEndpoint
in the diff).
| rawEndpoint: "/v1/models", | ||
| wantAuditPath: "/p/deepseek/v1/models", | ||
| }, | ||
| { | ||
| name: "NormalizedEndpoint takes precedence over RawEndpoint", | ||
| rawEndpoint: "/ignored", |
There was a problem hiding this comment.
Unknown-endpoint case doesn't assert
SemanticOperation is empty
The "unknown endpoint gets prefixed audit path" test case only checks AuditPath but never asserts that SemanticOperation is left at its zero value. If a future change accidentally sets SemanticOperation for the default branch, this test would not catch the regression. Adding wantSemanticOp: "" with an explicit emptiness check for the unknown case would close this gap.
…ng and body forwarding tests
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@docs/features/passthrough-api.mdx`:
- Line 148: The CLAUDE.md provider list is missing "deepseek", causing
inconsistency with defaultEnabledPassthroughProviders and other docs; update the
provider list in CLAUDE.md (around the provider enumeration near line
referenced) to include "deepseek" so it matches ENABLED_PASSTHROUGH_PROVIDERS
and the defaultEnabledPassthroughProviders constant in
internal/server/passthrough_support.go.
In `@internal/providers/deepseek/deepseek_test.go`:
- Around line 281-306: The test TestPassthrough_ForwardsRequestHeaders currently
only asserts Content-Type; modify it to also capture and assert the
Authorization header from the incoming request so custom-header forwarding
doesn't drop auth. In the httptest handler (inside
TestPassthrough_ForwardsRequestHeaders) read r.Header.Get("Authorization") into
a local variable (e.g., gotAuthorization) and after calling provider.Passthrough
assert that gotAuthorization equals the expected "Bearer deepseek-key" (or the
exact expected value used when creating the provider) to ensure
provider.Passthrough preserves Authorization when merging custom Headers.
🪄 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: a644c867-3e0b-445c-aea1-057ae6a12e46
📒 Files selected for processing (3)
config/config.godocs/features/passthrough-api.mdxinternal/providers/deepseek/deepseek_test.go
| ENABLE_PASSTHROUGH_ROUTES=true | ||
| ALLOW_PASSTHROUGH_V1_ALIAS=true | ||
| ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,zai,vllm | ||
| ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,zai,vllm,deepseek |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find all default passthrough provider definitions and verify consistency
echo "=== Searching for defaultEnabledPassthroughProviders definitions ==="
rg -n "defaultEnabledPassthroughProviders" -A 10
echo -e "\n=== Searching for EnabledPassthroughProviders default assignments ==="
rg -n "EnabledPassthroughProviders.*=.*\[\]string" -A 10
echo -e "\n=== Searching for ENABLED_PASSTHROUGH_PROVIDERS in docs/examples ==="
rg -n "ENABLED_PASSTHROUGH_PROVIDERS" --type=yaml --type=mdRepository: ENTERPILOT/GoModel
Length of output: 9672
Update CLAUDE.md to include deepseek in the ENABLED_PASSTHROUGH_PROVIDERS list.
The internal/server/passthrough_support.go still includes "deepseek" in defaultEnabledPassthroughProviders, and this documentation along with README.md list deepseek as enabled. However, CLAUDE.md:115 omits deepseek from the provider list, creating an inconsistency.
🤖 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 `@docs/features/passthrough-api.mdx` at line 148, The CLAUDE.md provider list
is missing "deepseek", causing inconsistency with
defaultEnabledPassthroughProviders and other docs; update the provider list in
CLAUDE.md (around the provider enumeration near line referenced) to include
"deepseek" so it matches ENABLED_PASSTHROUGH_PROVIDERS and the
defaultEnabledPassthroughProviders constant in
internal/server/passthrough_support.go.
| func TestPassthrough_ForwardsRequestHeaders(t *testing.T) { | ||
| var gotContentType string | ||
|
|
||
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
| gotContentType = r.Header.Get("Content-Type") | ||
| w.WriteHeader(http.StatusOK) | ||
| _, _ = w.Write([]byte(`{}`)) | ||
| })) | ||
| defer server.Close() | ||
|
|
||
| provider := NewWithHTTPClient("deepseek-key", server.URL, server.Client(), llmclient.Hooks{}) | ||
|
|
||
| resp, err := provider.Passthrough(context.Background(), &core.PassthroughRequest{ | ||
| Method: http.MethodPost, | ||
| Endpoint: "/beta/completions", | ||
| Body: io.NopCloser(strings.NewReader(`{}`)), | ||
| Headers: http.Header{"Content-Type": []string{"application/json"}}, | ||
| }) | ||
| if err != nil { | ||
| t.Fatalf("Passthrough() error = %v", err) | ||
| } | ||
| defer resp.Body.Close() | ||
| if gotContentType != "application/json" { | ||
| t.Fatalf("Content-Type = %q, want application/json", gotContentType) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Assert auth header survives custom-header forwarding
On Line 297, this test only checks Content-Type. Please also assert that Authorization is still present when custom headers are supplied, so header merge regressions don’t slip through.
Proposed test hardening
func TestPassthrough_ForwardsRequestHeaders(t *testing.T) {
- var gotContentType string
+ var gotContentType, gotAuth, gotTraceID string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotContentType = r.Header.Get("Content-Type")
+ gotAuth = r.Header.Get("Authorization")
+ gotTraceID = r.Header.Get("X-Trace-Id")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{}`))
}))
defer server.Close()
@@
resp, err := provider.Passthrough(context.Background(), &core.PassthroughRequest{
Method: http.MethodPost,
Endpoint: "/beta/completions",
Body: io.NopCloser(strings.NewReader(`{}`)),
- Headers: http.Header{"Content-Type": []string{"application/json"}},
+ Headers: http.Header{
+ "Content-Type": []string{"application/json"},
+ "X-Trace-Id": []string{"trace-123"},
+ },
})
@@
if gotContentType != "application/json" {
t.Fatalf("Content-Type = %q, want application/json", gotContentType)
}
+ if gotAuth != "Bearer deepseek-key" {
+ t.Fatalf("Authorization = %q, want Bearer deepseek-key", gotAuth)
+ }
+ if gotTraceID != "trace-123" {
+ t.Fatalf("X-Trace-Id = %q, want trace-123", gotTraceID)
+ }
}As per coding guidelines **/*_test.go: “Add or update tests for behavior changes. Tests should cover … error handling … and provider-specific parameter mapping.”
📝 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.
| func TestPassthrough_ForwardsRequestHeaders(t *testing.T) { | |
| var gotContentType string | |
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| gotContentType = r.Header.Get("Content-Type") | |
| w.WriteHeader(http.StatusOK) | |
| _, _ = w.Write([]byte(`{}`)) | |
| })) | |
| defer server.Close() | |
| provider := NewWithHTTPClient("deepseek-key", server.URL, server.Client(), llmclient.Hooks{}) | |
| resp, err := provider.Passthrough(context.Background(), &core.PassthroughRequest{ | |
| Method: http.MethodPost, | |
| Endpoint: "/beta/completions", | |
| Body: io.NopCloser(strings.NewReader(`{}`)), | |
| Headers: http.Header{"Content-Type": []string{"application/json"}}, | |
| }) | |
| if err != nil { | |
| t.Fatalf("Passthrough() error = %v", err) | |
| } | |
| defer resp.Body.Close() | |
| if gotContentType != "application/json" { | |
| t.Fatalf("Content-Type = %q, want application/json", gotContentType) | |
| } | |
| } | |
| func TestPassthrough_ForwardsRequestHeaders(t *testing.T) { | |
| var gotContentType, gotAuth, gotTraceID string | |
| server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
| gotContentType = r.Header.Get("Content-Type") | |
| gotAuth = r.Header.Get("Authorization") | |
| gotTraceID = r.Header.Get("X-Trace-Id") | |
| w.WriteHeader(http.StatusOK) | |
| _, _ = w.Write([]byte(`{}`)) | |
| })) | |
| defer server.Close() | |
| provider := NewWithHTTPClient("deepseek-key", server.URL, server.Client(), llmclient.Hooks{}) | |
| resp, err := provider.Passthrough(context.Background(), &core.PassthroughRequest{ | |
| Method: http.MethodPost, | |
| Endpoint: "/beta/completions", | |
| Body: io.NopCloser(strings.NewReader(`{}`)), | |
| Headers: http.Header{ | |
| "Content-Type": []string{"application/json"}, | |
| "X-Trace-Id": []string{"trace-123"}, | |
| }, | |
| }) | |
| if err != nil { | |
| t.Fatalf("Passthrough() error = %v", err) | |
| } | |
| defer resp.Body.Close() | |
| if gotContentType != "application/json" { | |
| t.Fatalf("Content-Type = %q, want application/json", gotContentType) | |
| } | |
| if gotAuth != "Bearer deepseek-key" { | |
| t.Fatalf("Authorization = %q, want Bearer deepseek-key", gotAuth) | |
| } | |
| if gotTraceID != "trace-123" { | |
| t.Fatalf("X-Trace-Id = %q, want trace-123", gotTraceID) | |
| } | |
| } |
🤖 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/providers/deepseek/deepseek_test.go` around lines 281 - 306, The
test TestPassthrough_ForwardsRequestHeaders currently only asserts Content-Type;
modify it to also capture and assert the Authorization header from the incoming
request so custom-header forwarding doesn't drop auth. In the httptest handler
(inside TestPassthrough_ForwardsRequestHeaders) read
r.Header.Get("Authorization") into a local variable (e.g., gotAuthorization) and
after calling provider.Passthrough assert that gotAuthorization equals the
expected "Bearer deepseek-key" (or the exact expected value used when creating
the provider) to ensure provider.Passthrough preserves Authorization when
merging custom Headers.
Summary
Passthrough()on the DeepSeek provider, enabling/p/deepseek/...routes including FIM at/beta/completionsPassthroughSemanticEnricherfor audit-path tagging (deepseek.chat_completions,deepseek.fim_completions), registered inRegistrationdeepseekto the defaultEnabledPassthroughProviderslist across all five locations:config.go,passthrough_support.go,config.example.yaml,README.md,docs/features/passthrough-api.mdxResponses()andStreamResponses()for consistency withCompatibleProvideradaptChatRequestWhy
DeepSeek was the only provider with first-class chat support but no passthrough. This blocked access to DeepSeek-native endpoints (notably
/beta/completionsfor fill-in-the-middle/FIM) without direct API calls. With this PR,/p/deepseek/<any-endpoint>works out of the box.Test Plan
go test ./internal/providers/deepseek/... -race— passthrough auth, header forwarding, 4xx status preservation, nil request, interface compliance, enricher (all cases includingNormalizedEndpointprecedence)go test ./config/... -race— default provider list assertions updatedgo test ./internal/server/... -race—TestProviderPassthrough_RejectsUnsupportedProviderupdated for new default listgo test ./internal/providers/... -race— all 16 provider packages passgo test $(go list ./... | grep -v tests/perf) -race— full suite green (perf tests are pre-existing failures unrelated to this change)Notes
tests/perffailures (TestHotPathPerfGuard) are pre-existing onmainand unrelated to this change (confirmed by running against unmodified code)reasoning_effortvalues continue to pass through (existing intentional behavior, covered byTestNormalizeReasoningEffort)Summary by CodeRabbit
New Features
Documentation
Bug Fixes / Behavior