Skip to content

feat: implement DeepSeek V4 reasoning suffix handling and tests - #4428

Merged
Calcium-Ion merged 1 commit into
QuantumNous:mainfrom
HynoR:feat/dsv4
Apr 24, 2026
Merged

feat: implement DeepSeek V4 reasoning suffix handling and tests#4428
Calcium-Ion merged 1 commit into
QuantumNous:mainfrom
HynoR:feat/dsv4

Conversation

@HynoR

@HynoR HynoR commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

适配deepseek v4 思考后缀,支持 -max (max reasoning) 和 -none (关闭思考) 两种后缀
适配deepseek的 claude 渠道,和 kimi 一样走 /anthropic/v1/message路径

参考的 openai 方法
对写代码的用户和翻译来说,这样按模型名称来区分思考深度更方便。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 IssuesPRs,确认不是重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

(请在此粘贴截图、关键日志或测试报告,以证明变更生效)

截图测试里用的 minimal ,代码实际版本是 none

v4-flash-none (no reasoning)
image

v4-flash (high reasoning)
image

v4-flash (max reasoning)
image

image

Summary by CodeRabbit

  • New Features

    • Added support for six new DeepSeek v4 model variants: deepseek-v4-flash and deepseek-v4-pro with configurable reasoning effort levels (none, max, and default).
    • Enhanced reasoning and thinking parameter handling for request conversions across multiple providers.
  • Bug Fixes

    • Improved error handling in request conversion processes.
  • Refactor

    • Consolidated reasoning effort parsing logic into shared utilities for better consistency across providers.

@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The PR refactors reasoning and thinking suffix parsing across OpenAI and DeepSeek channels. It introduces shared parsing functions in setting/reasoning/suffix.go, adds six new DeepSeek v4 model variants, and updates both deepseek and openai adaptors to use these shared helpers while preserving error handling and updating channel metadata.

Changes

Cohort / File(s) Summary
DeepSeek Adaptor Logic
relay/channel/deepseek/adaptor.go
Enhanced ConvertClaudeRequest and ConvertOpenAIRequest with DeepSeek v4 thinking-suffix parsing, error handling, and conditional updating of request fields (Model, Thinking, OutputConfig) and channel metadata (UpstreamModelName, ReasoningEffort).
DeepSeek Model Expansion
relay/channel/deepseek/constants.go
Extended ModelList with six new DeepSeek v4 model identifiers: deepseek-v4-flash, deepseek-v4-flash-none, deepseek-v4-flash-max, deepseek-v4-pro, deepseek-v4-pro-none, and deepseek-v4-pro-max.
OpenAI Adaptor Refactoring
relay/channel/openai/adaptor.go
Consolidated reasoning-effort suffix parsing by delegating to shared reasoning.ParseOpenAIReasoningEffortFromModelSuffix helper in both ConvertOpenAIRequest and ConvertOpenAIResponsesRequest; removed local parsing implementation.
Shared Reasoning Suffix Helpers
setting/reasoning/suffix.go
Introduced generic TrimEffortSuffixWithSuffixes helper and new exported parsing functions: ParseOpenAIReasoningEffortFromModelSuffix and ParseDeepSeekV4ThinkingSuffix, along with corresponding suffix lists (OpenAIEffortSuffixes, DeepSeekV4EffortSuffixes).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • seefs001
  • Calcium-Ion

Poem

🐰 A hop through suffixes, parsing with care,
DeepSeek v4 thinking floats in the air,
Shared helpers now guide both OpenAI and friends,
From -max to -none, the reasoning extends! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% 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
Title check ✅ Passed The title accurately summarizes the main change: implementing DeepSeek V4 reasoning suffix handling, which is the primary focus across all modified files (constants, suffix parsing, and adaptor updates).
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

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

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.

@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.

🧹 Nitpick comments (3)
relay/channel/deepseek/adaptor.go (2)

96-99: Clarify the info.ChannelMeta != nil gate — or drop it.

The fallback to info.UpstreamModelName is conditioned on info.ChannelMeta != nil. It’s not obvious why ChannelMeta gates trusting UpstreamModelName; typically UpstreamModelName is authoritative regardless. If the intent is just “guard against a stub/zero info in tests,” the info.UpstreamModelName != "" check already covers that. Please either add a short comment explaining the intent or simplify the guard.

Also applies to: 122-126

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

In `@relay/channel/deepseek/adaptor.go` around lines 96 - 99, The current guard
uses info.ChannelMeta != nil before trusting info.UpstreamModelName, which is
confusing; either remove the ChannelMeta nil-check so modelName is set when info
!= nil && info.UpstreamModelName != "" (making UpstreamModelName authoritative),
or keep the check but add a brief comment above the block explaining why
ChannelMeta must be present to trust UpstreamModelName (e.g., tests create stub
infos lacking ChannelMeta). Update both occurrences where modelName is set
before calling reasoning.ParseDeepSeekV4ThinkingSuffix to follow the same
approach and ensure consistent behavior.

94-150: Optional: extract the common model-name/info update boilerplate.

applyDeepSeekV4OpenAIThinkingSuffix and applyDeepSeekV4ClaudeThinkingSuffix share the same modelName resolution and info propagation. A tiny helper keeps the two request-type branches focused on their payload-specific mutation.

♻️ Suggested shape
func resolveDeepSeekV4Suffix(info *relaycommon.RelayInfo, modelOnRequest string) (base, thinkingType, effort string, ok bool) {
    name := modelOnRequest
    if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
        name = info.UpstreamModelName
    }
    return reasoning.ParseDeepSeekV4ThinkingSuffix(name)
}

func propagateDeepSeekV4Meta(info *relaycommon.RelayInfo, baseModel, effort string) {
    if info == nil {
        return
    }
    if info.ChannelMeta != nil {
        info.UpstreamModelName = baseModel
    }
    info.ReasoningEffort = effort
}

Then each apply* only performs the request-type-specific marshalling.

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

In `@relay/channel/deepseek/adaptor.go` around lines 94 - 150, Both
applyDeepSeekV4OpenAIThinkingSuffix and applyDeepSeekV4ClaudeThinkingSuffix
duplicate the same model-name resolution and info propagation logic; extract
that boilerplate into helpers like resolveDeepSeekV4Suffix(info
*relaycommon.RelayInfo, modelOnRequest string) (base, thinkingType, effort
string, ok bool) and propagateDeepSeekV4Meta(info *relaycommon.RelayInfo,
baseModel, effort string) to centralize: have each apply* call
resolveDeepSeekV4Suffix instead of repeating the name/select logic and call
propagateDeepSeekV4Meta to update info.UpstreamModelName and
info.ReasoningEffort, leaving each apply* to only handle request-specific
marshaling and field assignment.
setting/reasoning/suffix.go (1)

38-51: Nit: unreachable default branch.

TrimEffortSuffixWithSuffixes is called with DeepSeekV4EffortSuffixes = ["-minimal", "-max"], so when ok == true the returned suffix can only be "minimal" or "max". The default: arm is dead code. It’s harmless, but you can simplify:

♻️ Proposed simplification
-	switch suffix {
-	case "minimal":
-		return baseModel, "disabled", "", true
-	case "max":
-		return baseModel, "enabled", "max", true
-	default:
-		return modelName, "", "", false
-	}
+	if suffix == "minimal" {
+		return baseModel, "disabled", "", true
+	}
+	return baseModel, "enabled", "max", true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@setting/reasoning/suffix.go` around lines 38 - 51, The switch in
ParseDeepSeekV4ThinkingSuffix contains an unreachable default branch because
TrimEffortSuffixWithSuffixes is called with DeepSeekV4EffortSuffixes (only
"minimal" or "max"), so simplify the control flow: in
ParseDeepSeekV4ThinkingSuffix (after calling TrimEffortSuffixWithSuffixes and
the prefix check) replace the switch with explicit handling for "minimal" and
"max" (e.g., if/else or a switch without a default) and return the same values
for those two cases, removing the dead default branch; keep references to
TrimEffortSuffixWithSuffixes and DeepSeekV4EffortSuffixes intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@relay/channel/deepseek/adaptor.go`:
- Around line 96-99: The current guard uses info.ChannelMeta != nil before
trusting info.UpstreamModelName, which is confusing; either remove the
ChannelMeta nil-check so modelName is set when info != nil &&
info.UpstreamModelName != "" (making UpstreamModelName authoritative), or keep
the check but add a brief comment above the block explaining why ChannelMeta
must be present to trust UpstreamModelName (e.g., tests create stub infos
lacking ChannelMeta). Update both occurrences where modelName is set before
calling reasoning.ParseDeepSeekV4ThinkingSuffix to follow the same approach and
ensure consistent behavior.
- Around line 94-150: Both applyDeepSeekV4OpenAIThinkingSuffix and
applyDeepSeekV4ClaudeThinkingSuffix duplicate the same model-name resolution and
info propagation logic; extract that boilerplate into helpers like
resolveDeepSeekV4Suffix(info *relaycommon.RelayInfo, modelOnRequest string)
(base, thinkingType, effort string, ok bool) and propagateDeepSeekV4Meta(info
*relaycommon.RelayInfo, baseModel, effort string) to centralize: have each
apply* call resolveDeepSeekV4Suffix instead of repeating the name/select logic
and call propagateDeepSeekV4Meta to update info.UpstreamModelName and
info.ReasoningEffort, leaving each apply* to only handle request-specific
marshaling and field assignment.

In `@setting/reasoning/suffix.go`:
- Around line 38-51: The switch in ParseDeepSeekV4ThinkingSuffix contains an
unreachable default branch because TrimEffortSuffixWithSuffixes is called with
DeepSeekV4EffortSuffixes (only "minimal" or "max"), so simplify the control
flow: in ParseDeepSeekV4ThinkingSuffix (after calling
TrimEffortSuffixWithSuffixes and the prefix check) replace the switch with
explicit handling for "minimal" and "max" (e.g., if/else or a switch without a
default) and return the same values for those two cases, removing the dead
default branch; keep references to TrimEffortSuffixWithSuffixes and
DeepSeekV4EffortSuffixes intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7d2913a-babc-4511-8cc4-1f342ac18b64

📥 Commits

Reviewing files that changed from the base of the PR and between 2e610e5 and 8d36092.

📒 Files selected for processing (4)
  • relay/channel/deepseek/adaptor.go
  • relay/channel/deepseek/constants.go
  • relay/channel/openai/adaptor.go
  • setting/reasoning/suffix.go

@HynoR
HynoR marked this pull request as ready for review April 24, 2026 07:57

@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.

🧹 Nitpick comments (5)
setting/reasoning/suffix_test.go (1)

55-63: Test name doesn't match what's asserted.

TestTrimEffortSuffixKeepsGeneralSuffixes only asserts the happy path where -max is successfully trimmed — it never exercises a case where a "general" (non-effort) trailing token is kept intact. Consider renaming to reflect what's actually verified (e.g. TestTrimEffortSuffixTrimsMaxSuffix), or extend with a negative case like "claude-opus-4-7" where ok should be false.

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

In `@setting/reasoning/suffix_test.go` around lines 55 - 63, The test name
TestTrimEffortSuffixKeepsGeneralSuffixes is misleading because it only verifies
that TrimEffortSuffix trims a "-max" effort suffix; either rename the test to
reflect the behavior (e.g., TestTrimEffortSuffixTrimsMaxSuffix) or add a second
assertion case that passes a non-effort trailing token (e.g., "claude-opus-4-7")
and asserts ok == false and model equals the original string; update the test
around TrimEffortSuffix accordingly so the name matches the assertions.
relay/channel/deepseek/adaptor_test.go (2)

181-195: URL assertion looks good, but won't catch base-URL variants.

The test hard-codes https://api.deepseek.com as ChannelBaseUrl. If GetRequestURL ever normalizes trailing slashes or concatenates paths differently (e.g. a base URL ending with /), this test wouldn't exercise it. Consider adding one more case with a trailing slash to pin down the joining behavior.

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

In `@relay/channel/deepseek/adaptor_test.go` around lines 181 - 195, Add a second
sub-case to TestGetRequestURLUsesClaudeUpstreamForClaudeRelayFormat that
exercises ChannelBaseUrl with a trailing slash (e.g.
"https://api.deepseek.com/") and asserts GetRequestURL on
(&Adaptor{}).GetRequestURL(info) still returns the same normalized Claude
messages URL ("https://api.deepseek.com/anthropic/v1/messages"); ensure you
reuse the existing RelayInfo setup and only change ChannelBaseUrl to cover
URL-joining behavior and trailing-slash normalization.

63-96: Consider also asserting info.ReasoningEffort in the ignored-suffix path.

In TestConvertOpenAIRequestDeepSeekV4ThinkingSuffixIgnored the test verifies that convertedRequest.ReasoningEffort is preserved as "client-effort", but doesn't check info.ReasoningEffort. Since applyDeepSeekV4OpenAIThinkingSuffix also mutates info.ReasoningEffort on the success path, adding a guard here would lock in the "no-op on unsupported suffix" contract against future regressions.

Proposed addition
 			if convertedRequest.ReasoningEffort != "client-effort" {
 				t.Fatalf("ReasoningEffort = %q, want client-effort", convertedRequest.ReasoningEffort)
 			}
+			if info.ReasoningEffort != "" {
+				t.Fatalf("info.ReasoningEffort = %q, want empty", info.ReasoningEffort)
+			}
 			assertRawThinkingType(t, convertedRequest.THINKING, "client")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/deepseek/adaptor_test.go` around lines 63 - 96, The test
TestConvertOpenAIRequestDeepSeekV4ThinkingSuffixIgnored currently asserts
convertedRequest.ReasoningEffort but not the upstream info.ReasoningEffort; add
an assertion that info.ReasoningEffort remains unchanged (e.g., "client-effort")
after calling (&Adaptor{}).ConvertOpenAIRequest so the ignored-suffix path of
applyDeepSeekV4OpenAIThinkingSuffix is validated; locate the test loop in
TestConvertOpenAIRequestDeepSeekV4ThinkingSuffixIgnored and add a check for
info.ReasoningEffort alongside the existing assertions for
convertedRequest.ReasoningEffort and convertedRequest.Model.
setting/reasoning/suffix.go (2)

30-36: Return order is inverted relative to sibling helper.

ParseOpenAIReasoningEffortFromModelSuffix returns (effort, baseModel) while ParseDeepSeekV4ThinkingSuffix returns (baseModel, thinkingType, effort, ok) — different positional conventions for the same concept. Not a bug (callers use named assignments), but easy to misuse. Consider aligning to (baseModel, effort, ok) for consistency, or at least document the ordering in the function doc.

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

In `@setting/reasoning/suffix.go` around lines 30 - 36, Change
ParseOpenAIReasoningEffortFromModelSuffix to return values in the same
positional order as the sibling helper: (baseModel, effort, ok) instead of
(effort, baseModel). Update the function signature and the return statement to
propagate the ok value returned by TrimEffortSuffixWithSuffixes and return
baseModel first, then effort, then ok; reference TrimEffortSuffixWithSuffixes
and OpenAIEffortSuffixes to locate the call site to adjust.

38-51: Unreachable default branch in switch.

TrimEffortSuffixWithSuffixes only returns ok=true when suffix is one of DeepSeekV4EffortSuffixes ("none" / "max" after the leading - is stripped), so the default arm on line 48-50 can never be hit. It's harmless as defensive code, but worth noting if someone ever extends DeepSeekV4EffortSuffixes — the new suffix would silently fall into default and be reported as unsupported instead of failing loudly. A short comment clarifying the invariant (or a panic/test to guard it) would make the intent explicit.

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

In `@setting/reasoning/suffix.go` around lines 38 - 51, The switch in
ParseDeepSeekV4ThinkingSuffix has an unreachable default because
TrimEffortSuffixWithSuffixes only returns ok=true when suffix is one of
DeepSeekV4EffortSuffixes; update ParseDeepSeekV4ThinkingSuffix to either
(preferred) add a short comment above the switch documenting the invariant that
TrimEffortSuffixWithSuffixes guarantees suffix ∈ DeepSeekV4EffortSuffixes and
thus the default is defensive/unreachable, or (if you want fail-fast) replace
the default branch with a panic or logging fatal that references the unexpected
suffix to surface future changes; reference ParseDeepSeekV4ThinkingSuffix,
TrimEffortSuffixWithSuffixes, and DeepSeekV4EffortSuffixes when applying the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@relay/channel/deepseek/adaptor_test.go`:
- Around line 181-195: Add a second sub-case to
TestGetRequestURLUsesClaudeUpstreamForClaudeRelayFormat that exercises
ChannelBaseUrl with a trailing slash (e.g. "https://api.deepseek.com/") and
asserts GetRequestURL on (&Adaptor{}).GetRequestURL(info) still returns the same
normalized Claude messages URL
("https://api.deepseek.com/anthropic/v1/messages"); ensure you reuse the
existing RelayInfo setup and only change ChannelBaseUrl to cover URL-joining
behavior and trailing-slash normalization.
- Around line 63-96: The test
TestConvertOpenAIRequestDeepSeekV4ThinkingSuffixIgnored currently asserts
convertedRequest.ReasoningEffort but not the upstream info.ReasoningEffort; add
an assertion that info.ReasoningEffort remains unchanged (e.g., "client-effort")
after calling (&Adaptor{}).ConvertOpenAIRequest so the ignored-suffix path of
applyDeepSeekV4OpenAIThinkingSuffix is validated; locate the test loop in
TestConvertOpenAIRequestDeepSeekV4ThinkingSuffixIgnored and add a check for
info.ReasoningEffort alongside the existing assertions for
convertedRequest.ReasoningEffort and convertedRequest.Model.

In `@setting/reasoning/suffix_test.go`:
- Around line 55-63: The test name TestTrimEffortSuffixKeepsGeneralSuffixes is
misleading because it only verifies that TrimEffortSuffix trims a "-max" effort
suffix; either rename the test to reflect the behavior (e.g.,
TestTrimEffortSuffixTrimsMaxSuffix) or add a second assertion case that passes a
non-effort trailing token (e.g., "claude-opus-4-7") and asserts ok == false and
model equals the original string; update the test around TrimEffortSuffix
accordingly so the name matches the assertions.

In `@setting/reasoning/suffix.go`:
- Around line 30-36: Change ParseOpenAIReasoningEffortFromModelSuffix to return
values in the same positional order as the sibling helper: (baseModel, effort,
ok) instead of (effort, baseModel). Update the function signature and the return
statement to propagate the ok value returned by TrimEffortSuffixWithSuffixes and
return baseModel first, then effort, then ok; reference
TrimEffortSuffixWithSuffixes and OpenAIEffortSuffixes to locate the call site to
adjust.
- Around line 38-51: The switch in ParseDeepSeekV4ThinkingSuffix has an
unreachable default because TrimEffortSuffixWithSuffixes only returns ok=true
when suffix is one of DeepSeekV4EffortSuffixes; update
ParseDeepSeekV4ThinkingSuffix to either (preferred) add a short comment above
the switch documenting the invariant that TrimEffortSuffixWithSuffixes
guarantees suffix ∈ DeepSeekV4EffortSuffixes and thus the default is
defensive/unreachable, or (if you want fail-fast) replace the default branch
with a panic or logging fatal that references the unexpected suffix to surface
future changes; reference ParseDeepSeekV4ThinkingSuffix,
TrimEffortSuffixWithSuffixes, and DeepSeekV4EffortSuffixes when applying the
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2170eb1d-cfdd-4c35-8851-8fc3f12a251c

📥 Commits

Reviewing files that changed from the base of the PR and between 8d36092 and 1af6ed0.

📒 Files selected for processing (4)
  • relay/channel/deepseek/adaptor_test.go
  • relay/channel/deepseek/constants.go
  • setting/reasoning/suffix.go
  • setting/reasoning/suffix_test.go
✅ Files skipped from review due to trivial changes (1)
  • relay/channel/deepseek/constants.go

@Calcium-Ion

Copy link
Copy Markdown
Member

签个名呗大佬

@HynoR
HynoR marked this pull request as draft April 24, 2026 08:37

@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.

🧹 Nitpick comments (3)
setting/reasoning/suffix.go (1)

38-51: Unreachable default branch in switch.

suffix is guaranteed to be either "none" or "max" because TrimEffortSuffixWithSuffixes only returns a non-empty suffix when it matches one of DeepSeekV4EffortSuffixes ({"-none", "-max"}). The default case at Lines 48–49 is dead code. Not a bug, but you could drop it or convert the switch to a plain if/else for clarity.

♻️ Proposed simplification
-	switch suffix {
-	case "none":
-		return baseModel, "disabled", "", true
-	case "max":
-		return baseModel, "enabled", "max", true
-	default:
-		return modelName, "", "", false
-	}
+	if suffix == "none" {
+		return baseModel, "disabled", "", true
+	}
+	return baseModel, "enabled", "max", true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@setting/reasoning/suffix.go` around lines 38 - 51, The switch in
ParseDeepSeekV4ThinkingSuffix has an unreachable default because
TrimEffortSuffixWithSuffixes only yields suffixes from DeepSeekV4EffortSuffixes
("-none","-max"); update ParseDeepSeekV4ThinkingSuffix to remove the dead
default branch and simplify the logic by replacing the switch with an if/else
that checks suffix == "none" and else assumes "max", keeping the existing
early-return guard that uses TrimEffortSuffixWithSuffixes and the "deepseek-v4-"
prefix check.
setting/reasoning/suffix_test.go (1)

5-123: LGTM — good table-driven coverage for both parsers.

Positive and negative cases are well represented, including the tricky "embedded but not suffix" case (gpt-5.1-codex-max) and unsupported-suffix rejections for DeepSeek v4. One optional addition: a test for deepseek-v4-none / deepseek-v4-max (no model family segment between the prefix and the effort suffix), which exercises the HasPrefix(baseModel, "deepseek-v4-") guard — the trimmed base deepseek-v4 lacks the trailing dash and should be rejected.

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

In `@setting/reasoning/suffix_test.go` around lines 5 - 123, Add tests for the
edge cases "deepseek-v4-none" and "deepseek-v4-max" to exercise the
HasPrefix(baseModel, \"deepseek-v4-\") guard in ParseDeepSeekV4ThinkingSuffix;
ensure each new test case asserts that the function returns the original model
unchanged (wantBaseModel equals input) with empty thinkingType/effort and
ok==false so the trimmed base lacking the trailing dash is rejected.
relay/channel/deepseek/adaptor_test.go (1)

13-195: LGTM — thorough adaptor-level coverage.

Good pairing of positive/ignored cases across both OpenAI and Claude conversion paths, plus a targeted check that Claude relay format routes to /anthropic/v1/messages. The "ignored" tests correctly pre-populate client-supplied THINKING / Thinking / OutputConfig values and verify the adaptor leaves them untouched when the suffix doesn't match, which is the most important invariant.

Optional: consider adding one negative case for GetRequestURL that exercises a non-Claude RelayFormat (e.g., default → /v1/chat/completions or /beta/completions) so the routing switch is covered in both directions.

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

In `@relay/channel/deepseek/adaptor_test.go` around lines 13 - 195, Add a
companion negative test to cover the non-Claude routing branch of GetRequestURL:
create a test (e.g.,
TestGetRequestURLUsesDefaultUpstreamForNonClaudeRelayFormat) that constructs a
relaycommon.RelayInfo with RelayFormat not equal to types.RelayFormatClaude (and
set RelayMode to relayconstant.RelayModeChatCompletions or
RelayModeCompletions), provide ChannelMeta.ChannelBaseUrl and RequestURLPath,
call (&Adaptor{}).GetRequestURL(info), assert no error, and assert the returned
URL points to the non-Claude upstream path (e.g., base + /v1/chat/completions or
the expected default completions path); this will mirror
TestGetRequestURLUsesClaudeUpstreamForClaudeRelayFormat but verify the other
switch branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@relay/channel/deepseek/adaptor_test.go`:
- Around line 13-195: Add a companion negative test to cover the non-Claude
routing branch of GetRequestURL: create a test (e.g.,
TestGetRequestURLUsesDefaultUpstreamForNonClaudeRelayFormat) that constructs a
relaycommon.RelayInfo with RelayFormat not equal to types.RelayFormatClaude (and
set RelayMode to relayconstant.RelayModeChatCompletions or
RelayModeCompletions), provide ChannelMeta.ChannelBaseUrl and RequestURLPath,
call (&Adaptor{}).GetRequestURL(info), assert no error, and assert the returned
URL points to the non-Claude upstream path (e.g., base + /v1/chat/completions or
the expected default completions path); this will mirror
TestGetRequestURLUsesClaudeUpstreamForClaudeRelayFormat but verify the other
switch branch.

In `@setting/reasoning/suffix_test.go`:
- Around line 5-123: Add tests for the edge cases "deepseek-v4-none" and
"deepseek-v4-max" to exercise the HasPrefix(baseModel, \"deepseek-v4-\") guard
in ParseDeepSeekV4ThinkingSuffix; ensure each new test case asserts that the
function returns the original model unchanged (wantBaseModel equals input) with
empty thinkingType/effort and ok==false so the trimmed base lacking the trailing
dash is rejected.

In `@setting/reasoning/suffix.go`:
- Around line 38-51: The switch in ParseDeepSeekV4ThinkingSuffix has an
unreachable default because TrimEffortSuffixWithSuffixes only yields suffixes
from DeepSeekV4EffortSuffixes ("-none","-max"); update
ParseDeepSeekV4ThinkingSuffix to remove the dead default branch and simplify the
logic by replacing the switch with an if/else that checks suffix == "none" and
else assumes "max", keeping the existing early-return guard that uses
TrimEffortSuffixWithSuffixes and the "deepseek-v4-" prefix check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aca89e1d-8a1f-4e44-aabd-3f12b75dafec

📥 Commits

Reviewing files that changed from the base of the PR and between 1af6ed0 and d814e89.

📒 Files selected for processing (6)
  • relay/channel/deepseek/adaptor.go
  • relay/channel/deepseek/adaptor_test.go
  • relay/channel/deepseek/constants.go
  • relay/channel/openai/adaptor.go
  • setting/reasoning/suffix.go
  • setting/reasoning/suffix_test.go
✅ Files skipped from review due to trivial changes (1)
  • relay/channel/deepseek/constants.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • relay/channel/openai/adaptor.go
  • relay/channel/deepseek/adaptor.go

@HynoR
HynoR marked this pull request as ready for review April 24, 2026 08:49
@HynoR

HynoR commented Apr 24, 2026

Copy link
Copy Markdown
Contributor Author

签个名呗大佬

好了,用错电脑commit 了

@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: 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 `@relay/channel/deepseek/adaptor.go`:
- Around line 94-150: The helpers applyDeepSeekV4OpenAIThinkingSuffix and
applyDeepSeekV4ClaudeThinkingSuffix currently unconditionally overwrite
reasoning fields; change them to merge instead: in
applyDeepSeekV4OpenAIThinkingSuffix only set request.THINKING.Type when
request.THINKING is nil or its Type is empty (preserve existing budget_tokens
and other keys) and only set request.ReasoningEffort when it is empty; in
applyDeepSeekV4ClaudeThinkingSuffix only set request.Thinking.Type when
request.Thinking is nil or its Type is empty (preserve existing BudgetTokens),
and instead of setting request.OutputConfig = nil or replacing it entirely,
update or clear only the "effort" entry (preserve other OutputConfig fields) —
keep updating info.UpstreamModelName and info.ReasoningEffort as before. Ensure
marshaling logic respects existing non-empty fields and avoids discarding
caller-supplied BudgetTokens or other custom fields.
- Around line 36-39: The current branch silently returns convertedRequest when
the type assertion to *dto.ClaudeRequest fails, which skips the DeepSeek V4
"-none"/"-max" suffix handling; update ConvertClaudeRequest usage so the suffix
is handled deterministically: either perform the "-none"/"-max" model-name
normalization on the original req.Model before calling
claude.Adaptor.ConvertClaudeRequest, or if you must inspect the adaptor result,
replace the silent fallthrough in the claudeRequest type-assertion block with an
explicit error return (or fallback that extracts the model field) so that
dto.ClaudeRequest, convertedRequest, or the original request always goes through
the suffix-stripping logic (referencing ConvertClaudeRequest, convertedRequest,
claudeRequest, and dto.ClaudeRequest).
🪄 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: CHILL

Plan: Pro

Run ID: c680aa6d-bb7d-4497-9809-7ee94321ec86

📥 Commits

Reviewing files that changed from the base of the PR and between d814e89 and 435d7ae.

📒 Files selected for processing (4)
  • relay/channel/deepseek/adaptor.go
  • relay/channel/deepseek/constants.go
  • relay/channel/openai/adaptor.go
  • setting/reasoning/suffix.go
✅ Files skipped from review due to trivial changes (1)
  • relay/channel/deepseek/constants.go

Comment on lines +36 to +39
claudeRequest, ok := convertedRequest.(*dto.ClaudeRequest)
if !ok {
return convertedRequest, nil
}

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 | 🟡 Minor

Silent fallthrough may leave the -none/-max suffix unhandled.

If claude.Adaptor.ConvertClaudeRequest ever returns a non-*dto.ClaudeRequest payload (e.g. a future refactor returns a raw []byte/map), this branch returns the converted value unchanged and the DeepSeek V4 suffix handling is silently skipped. The -none/-max-suffixed model name would then be forwarded upstream, producing an obscure "invalid model" error on /anthropic/v1/messages rather than a clear local error.

Consider either asserting the type or applying the suffix handling on the raw req.Model before calling the claude adaptor.

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

In `@relay/channel/deepseek/adaptor.go` around lines 36 - 39, The current branch
silently returns convertedRequest when the type assertion to *dto.ClaudeRequest
fails, which skips the DeepSeek V4 "-none"/"-max" suffix handling; update
ConvertClaudeRequest usage so the suffix is handled deterministically: either
perform the "-none"/"-max" model-name normalization on the original req.Model
before calling claude.Adaptor.ConvertClaudeRequest, or if you must inspect the
adaptor result, replace the silent fallthrough in the claudeRequest
type-assertion block with an explicit error return (or fallback that extracts
the model field) so that dto.ClaudeRequest, convertedRequest, or the original
request always goes through the suffix-stripping logic (referencing
ConvertClaudeRequest, convertedRequest, claudeRequest, and dto.ClaudeRequest).

Comment on lines +94 to +150
func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) error {
modelName := request.Model
if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
modelName = info.UpstreamModelName
}
baseModel, thinkingType, effort, ok := reasoning.ParseDeepSeekV4ThinkingSuffix(modelName)
if !ok {
return nil
}
thinking, err := common.Marshal(map[string]string{
"type": thinkingType,
})
if err != nil {
return fmt.Errorf("error marshalling thinking: %w", err)
}
request.Model = baseModel
request.THINKING = thinking
request.ReasoningEffort = effort
if info != nil {
if info.ChannelMeta != nil {
info.UpstreamModelName = baseModel
}
info.ReasoningEffort = effort
}
return nil
}

func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *dto.ClaudeRequest) error {
modelName := request.Model
if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
modelName = info.UpstreamModelName
}
baseModel, thinkingType, effort, ok := reasoning.ParseDeepSeekV4ThinkingSuffix(modelName)
if !ok {
return nil
}
request.Model = baseModel
request.Thinking = &dto.Thinking{Type: thinkingType}
if effort == "" {
request.OutputConfig = nil
} else {
outputConfig, err := common.Marshal(map[string]string{
"effort": effort,
})
if err != nil {
return fmt.Errorf("error marshalling output_config: %w", err)
}
request.OutputConfig = outputConfig
}
if info != nil {
if info.ChannelMeta != nil {
info.UpstreamModelName = baseModel
}
info.ReasoningEffort = effort
}
return nil
}

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 | 🟡 Minor

Consider preserving caller-supplied Thinking / THINKING / OutputConfig fields.

Both helpers unconditionally clobber reasoning-related request fields whenever the suffix matches:

  • applyDeepSeekV4OpenAIThinkingSuffix (L103-111) replaces request.THINKING with {"type": thinkingType} and overwrites request.ReasoningEffort, even if the caller already set THINKING with budget_tokens or a custom ReasoningEffort.
  • applyDeepSeekV4ClaudeThinkingSuffix (L131-142) replaces request.Thinking with &dto.Thinking{Type: ...} (drops BudgetTokens etc.), and for the -none case sets request.OutputConfig = nil, silently discarding anything the caller put there; for -max it overwrites OutputConfig with just {"effort":"max"}.

If the intended semantic is "the suffix is the sole signal and overrides client input", this is fine but worth documenting. Otherwise, prefer merging (only set fields that are currently zero/nil, e.g. preserve existing BudgetTokens, only clear OutputConfig.effort rather than the whole struct).

♻️ Example: preserve existing Thinking fields
-	request.Model = baseModel
-	request.Thinking = &dto.Thinking{Type: thinkingType}
+	request.Model = baseModel
+	if request.Thinking == nil {
+		request.Thinking = &dto.Thinking{}
+	}
+	request.Thinking.Type = thinkingType
📝 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
func applyDeepSeekV4OpenAIThinkingSuffix(info *relaycommon.RelayInfo, request *dto.GeneralOpenAIRequest) error {
modelName := request.Model
if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
modelName = info.UpstreamModelName
}
baseModel, thinkingType, effort, ok := reasoning.ParseDeepSeekV4ThinkingSuffix(modelName)
if !ok {
return nil
}
thinking, err := common.Marshal(map[string]string{
"type": thinkingType,
})
if err != nil {
return fmt.Errorf("error marshalling thinking: %w", err)
}
request.Model = baseModel
request.THINKING = thinking
request.ReasoningEffort = effort
if info != nil {
if info.ChannelMeta != nil {
info.UpstreamModelName = baseModel
}
info.ReasoningEffort = effort
}
return nil
}
func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *dto.ClaudeRequest) error {
modelName := request.Model
if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
modelName = info.UpstreamModelName
}
baseModel, thinkingType, effort, ok := reasoning.ParseDeepSeekV4ThinkingSuffix(modelName)
if !ok {
return nil
}
request.Model = baseModel
request.Thinking = &dto.Thinking{Type: thinkingType}
if effort == "" {
request.OutputConfig = nil
} else {
outputConfig, err := common.Marshal(map[string]string{
"effort": effort,
})
if err != nil {
return fmt.Errorf("error marshalling output_config: %w", err)
}
request.OutputConfig = outputConfig
}
if info != nil {
if info.ChannelMeta != nil {
info.UpstreamModelName = baseModel
}
info.ReasoningEffort = effort
}
return nil
}
func applyDeepSeekV4ClaudeThinkingSuffix(info *relaycommon.RelayInfo, request *dto.ClaudeRequest) error {
modelName := request.Model
if info != nil && info.ChannelMeta != nil && info.UpstreamModelName != "" {
modelName = info.UpstreamModelName
}
baseModel, thinkingType, effort, ok := reasoning.ParseDeepSeekV4ThinkingSuffix(modelName)
if !ok {
return nil
}
request.Model = baseModel
if request.Thinking == nil {
request.Thinking = &dto.Thinking{}
}
request.Thinking.Type = thinkingType
if effort == "" {
request.OutputConfig = nil
} else {
outputConfig, err := common.Marshal(map[string]string{
"effort": effort,
})
if err != nil {
return fmt.Errorf("error marshalling output_config: %w", err)
}
request.OutputConfig = outputConfig
}
if info != nil {
if info.ChannelMeta != nil {
info.UpstreamModelName = baseModel
}
info.ReasoningEffort = effort
}
return nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@relay/channel/deepseek/adaptor.go` around lines 94 - 150, The helpers
applyDeepSeekV4OpenAIThinkingSuffix and applyDeepSeekV4ClaudeThinkingSuffix
currently unconditionally overwrite reasoning fields; change them to merge
instead: in applyDeepSeekV4OpenAIThinkingSuffix only set request.THINKING.Type
when request.THINKING is nil or its Type is empty (preserve existing
budget_tokens and other keys) and only set request.ReasoningEffort when it is
empty; in applyDeepSeekV4ClaudeThinkingSuffix only set request.Thinking.Type
when request.Thinking is nil or its Type is empty (preserve existing
BudgetTokens), and instead of setting request.OutputConfig = nil or replacing it
entirely, update or clear only the "effort" entry (preserve other OutputConfig
fields) — keep updating info.UpstreamModelName and info.ReasoningEffort as
before. Ensure marshaling logic respects existing non-empty fields and avoids
discarding caller-supplied BudgetTokens or other custom fields.

@Calcium-Ion
Calcium-Ion merged commit 8993386 into QuantumNous:main Apr 24, 2026
1 check passed
@HynoR
HynoR deleted the feat/dsv4 branch April 28, 2026 02:09
@CodeCroon

CodeCroon commented Apr 30, 2026

Copy link
Copy Markdown

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

@HynoR

HynoR commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

只做了openai接口,a社转换没适配🌚,晚点pr

@HynoR

HynoR commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

能麻烦截个图看你是怎么接deepseek的吗,这边复现一下找找最佳方案

@CodeCroon

Copy link
Copy Markdown

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

能麻烦截个图看你是怎么接deepseek的吗,这边复现一下找找最佳方案

只做了openai接口 这个是指的上游还是下游?我下游 anthropic ,上游 openai/anthropic 都是一样的反应。

image image

@HynoR

HynoR commented Apr 30, 2026

Copy link
Copy Markdown
Contributor Author

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

能麻烦截个图看你是怎么接deepseek的吗,这边复现一下找找最佳方案

只做了openai接口 这个是指的上游还是下游?我下游 anthropic ,上游 openai/anthropic 都是一样的反应。

image image

okok提个issue,我这几天修

@CodeCroon

Copy link
Copy Markdown

@HynoR 佬,我现在的版本是v1.0.0-rc.1,上游渠道用的是https://api.deepseek.com/anthropic,使用 ClaudeCode 接入 new-api。

  • 试了只加deepseek-v4-flash,调用deepseek-v4-flash-none会提示没有模型:API Error: 503 No available channel for model deepseek-v4-flash-none under group vip (distributor) (request id: 202604300247175249889528268d9d6MugLm4sm). This is a server-side issue, usually temporary — try again in a moment. If it persists, check status.claude.com.
  • 试了加deepseek-v4-flashdeepseek-v4-flash-none,调用deepseek-v4-flash-none上游会报错:API Error: 400 {"error":{"type":"invalid_request_error","message":"The supported API model names are deepseek-v4-pro or deepseek-v4-flash, but you passed deepseek-v4-flash-none. (request id: 202604300243481791806818268d9d6ilG5BywC)"},"type":"error"}

是我的姿势不对吗?

能麻烦截个图看你是怎么接deepseek的吗,这边复现一下找找最佳方案

只做了openai接口 这个是指的上游还是下游?我下游 anthropic ,上游 openai/anthropic 都是一样的反应。
image image

okok提个issue,我这几天修

感谢佬的付出,已提 issue #4562

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.

3 participants