Feature/support deepseek#3519
Conversation
…tions - Added DeepSeekKey configuration structure for managing DeepSeek Web tokens with optional routing overrides. - Implemented sanitization for DeepSeek keys in configuration loading. - Registered DeepSeek models in the model registry and updated model handling logic. - Enhanced synthesizer to create Auth entries for DeepSeek tokens. - Updated relevant areas to support DeepSeek in OAuth model aliasing and API key resolution. Closes: router-for-me#3486
…urations - Updated README to reflect the addition of DeepSeek Web as a compatible provider. - Added configuration examples for `deepseek-api-key` and listed built-in model IDs. - Clarified existing support for OpenAI, Gemini, Claude, Codex, and Grok APIs.
|
This pull request targeted The base branch has been automatically changed to |
There was a problem hiding this comment.
Code Review
This pull request introduces support for the DeepSeek Web Provider, implementing a new executor, configuration handling, and model definitions. Key features include a custom Proof-of-Work (PoW) solver, SSE parsing for streaming responses, and specialized HTTP transport. The reviewer identified a critical bug in the Keccak round loop that would cause PoW verification to fail. Other feedback suggests performance improvements through HTTP client reuse and pre-compiled regular expressions, as well as better error handling for response body decompression.
| a15, a16, a17, a18, a19 := s[15], s[16], s[17], s[18], s[19] | ||
| a20, a21, a22, a23, a24 := s[20], s[21], s[22], s[23], s[24] | ||
|
|
||
| for r := 1; r < 24; r++ { |
There was a problem hiding this comment.
The Keccak round loop starts at index 1 and performs only 23 rounds. Standard Keccak-f[1600] (which the keccakRoundConstants array and the permutation logic are based on) requires 24 rounds, typically indexed 0 to 23. Skipping the first round constant (0x0000000000000001) will result in incorrect hash values, which will cause Proof-of-Work verification to fail against the real DeepSeek API.
| for r := 1; r < 24; r++ { | |
| for r := 0; r < 24; r++ { |
| func NewClients(cfg *config.Config, auth *cliproxyauth.Auth) Clients { | ||
| proxyURL := "" | ||
| if auth != nil { | ||
| proxyURL = strings.TrimSpace(auth.ProxyURL) | ||
| } | ||
| if proxyURL == "" && cfg != nil { | ||
| proxyURL = strings.TrimSpace(cfg.ProxyURL) | ||
| } | ||
| return Clients{ | ||
| Regular: newFingerprintClient(60*time.Second, proxyURL), | ||
| Stream: newFingerprintClient(0, proxyURL), | ||
| Fallback: newFallbackClient(60*time.Second, proxyURL), | ||
| FallbackStream: newFallbackClient(0, proxyURL), | ||
| } | ||
| } |
There was a problem hiding this comment.
Creating new HTTP clients and transports for every request is inefficient as it prevents connection reuse and increases overhead due to repeated TLS handshakes. http.Client and http.Transport should be long-lived and reused across requests. Consider caching these clients within the DeepSeekExecutor, potentially keyed by the proxyURL if it varies per credential.
| }() | ||
| helps.RecordAPIResponseMetadata(ctx, e.cfg, flow.response.StatusCode, flow.response.Header.Clone()) | ||
| if flow.response.StatusCode < 200 || flow.response.StatusCode >= 300 { | ||
| body, _ := dshelp.ReadResponseBody(flow.response) |
There was a problem hiding this comment.
| t3 := binary.LittleEndian.Uint64(ta[24:]) | ||
|
|
||
| prefix := []byte(salt + "_" + strconv.FormatInt(expireAt, 10) + "_") | ||
| const rate = 136 |
| } | ||
|
|
||
| func findToolCallBlock(text string) (string, int, int) { | ||
| re := regexp.MustCompile(`(?is)<(?:\|DSML\|)?tool_calls\b[^>]*>.*?</(?:\|DSML\|)?tool_calls>`) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5dcdbc9515
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| token, _, _ := e.resolveCredentials(auth) | ||
| if token != "" { | ||
| return false | ||
| } |
There was a problem hiding this comment.
Re-authenticate when a stored DeepSeek token expires
This guard skips PrepareRequestAuth whenever any token is present, so login-based DeepSeek auth (email/mobile + password) will keep using an expired bearer token and never refresh it during normal request preparation. In the common case where a token is cached in metadata and later expires, requests will repeatedly fail with 401 until the token is manually removed or external refresh metadata is configured.
Useful? React with 👍 / 👎.
| clients := dshelp.NewClients(e.cfg, auth) | ||
| return clients.Regular.Do(httpReq) |
There was a problem hiding this comment.
Use a no-timeout client for DeepSeek HttpRequest
HttpRequest always dispatches through clients.Regular, which is constructed with a 60-second http.Client.Timeout. For any long-lived SSE/raw forwarding path that uses HttpRequest, the client will abort at ~60s with a context deadline error even though the stream is healthy, which can truncate DeepSeek responses in production.
Useful? React with 👍 / 👎.
…r handling - Introduced a mutex and a cache for clients in DeepSeekExecutor to optimize client management. - Refactored client creation logic to utilize cached clients based on proxy URL. - Improved error handling for response body reading in Execute and ExecuteStream methods. - Updated DeepSeek hashing functions to use a constant for the rate, enhancing code clarity and maintainability. - Replaced regex instantiation with precompiled regex variables for performance optimization in prompt handling.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c9c418f6c5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| body["usage"] = usage | ||
| } | ||
| raw, _ := json.Marshal(body) | ||
| return append([]byte("data: "), raw...) |
There was a problem hiding this comment.
Emit plain chunk JSON from DeepSeek stream adapter
openAIStreamChunk currently prefixes each payload with data: , and forwardDeepSeekStream also emits data: [DONE]; this breaks OpenAI chat streaming because the OpenAI handlers already add SSE framing (data: ...\n\n) around every chunk. On /v1/chat/completions streams with the DeepSeek executor, clients receive malformed lines like data: data: {...} (and duplicated/invalid done markers), which causes standard SSE/OpenAI parsers to fail.
Useful? React with 👍 / 👎.
Summary