mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-05 21:50:46 +08:00
fix(api-server): rotate comma-separated provider API keys (#14346)
### What this PR does Before this PR: The local OpenAI-compatible API server (http://127.0.0.1:23333) forwarded `provider.apiKey` verbatim to the upstream client. For providers configured with multiple comma-separated keys (e.g. `key1,key2,key3`), the whole string was sent as the bearer token, causing upstream 403 `Authorization failed` even though the same provider worked from the Cherry Studio UI chat path. After this PR: The API server now takes the first comma-separated key before constructing the upstream client, in the two call sites that previously used the raw string: - `src/main/apiServer/services/chat-completion.ts` — the OpenAI client built for `/v1/chat/completions`. - `src/main/apiServer/routes/knowledge/handlers.ts` — the embed/rerank provider config used by the knowledge routes. This matches the existing main-process convention at `src/main/services/OpenClawService.ts:783`: ```ts let apiKey = provider.apiKey ? provider.apiKey.split(',')[0].trim() : '' ``` Fixes #14344 ### Why we need it and why it was done in this way The UI chat path rotates comma-separated keys via `getRotatedApiKey` (which depends on `window.keyv`, a renderer-only API). The API server path didn't split the string at all, producing inconsistent behavior. Rather than introduce a second rotation implementation in the main process, this PR matches the existing main-process convention of taking the first key. That fixes the reported 403 with a minimal, one-line change per call site — appropriate for a frozen-main hotfix. The following tradeoffs were made: - No rotation in main. Users with multiple keys will always hit the first one from the API server. Rotation remains UI-only; reaching parity would require extracting a shared helper and replacing the `window.keyv` dependency, which is a refactor blocked by the main-branch freeze policy. The following alternatives were considered: - A main-side rotation helper with per-provider in-memory state. Rejected: duplicates renderer logic, expands scope beyond the bug fix, and contradicts the existing `OpenClawService` pattern. - Extracting a shared rotation helper into `packages/shared`. Deferred to v2: requires replacing `window.keyv` with a runtime-agnostic store. Links to places where the discussion took place: https://github.com/CherryHQ/cherry-studio/issues/14344 ### Breaking changes None. Single-key providers behave identically; multi-key providers now succeed instead of returning 403. Behavior matches `OpenClawService` (always first key). ### Special notes for your reviewer Two commits on this branch: 1. Initial attempt that added a main-side rotation helper and tests. 2. Follow-up that removes the helper and switches to the `.split(',')[0].trim()` pattern matching `OpenClawService`. Recommend squash-merge so the final history shows just the one-line-per-call-site fix. ### Checklist - [x] PR: The PR description is expressive enough and will help future contributors - [x] Code: Write code that humans can understand and Keep it simple - [x] Refactor: You have left the code cleaner than you found it (Boy Scout Rule) - [x] Upgrade: Impact of this change on upgrade flows was considered and addressed if required - [x] Documentation: A user-guide update was considered and is present (link) or not required. Check this only when the PR introduces or changes a user-facing feature or behavior. - [x] Self-review: I have reviewed my own code before requesting review from others ### Release note ```release-note Fix the local OpenAI-compatible API server (http://127.0.0.1:23333) returning 403 Authorization failed for providers configured with multiple comma-separated API keys. The API server now takes the first key, matching the existing main-process behavior used by other services. ``` --------- Signed-off-by: suyao <sy20010504@gmail.com>
This commit is contained in:
@@ -142,8 +142,12 @@ async function getProviderConfig(providerId: string): Promise<{ apiKey: string;
|
||||
baseURL = baseURL.replace(/\/+$/, '')
|
||||
baseURL = baseURL.replace(/#$/, '')
|
||||
|
||||
// If multiple API keys are configured (comma-separated), use the first one.
|
||||
// Matches the main-process convention in OpenClawService.
|
||||
const apiKey = provider.apiKey ? provider.apiKey.split(',')[0].trim() : ''
|
||||
|
||||
return {
|
||||
apiKey: provider.apiKey || '',
|
||||
apiKey,
|
||||
baseURL
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,13 @@ export class ChatCompletionService {
|
||||
|
||||
const modelId = modelValidation.modelId!
|
||||
|
||||
// If multiple API keys are configured (comma-separated), use the first one.
|
||||
// Matches the main-process convention in OpenClawService.
|
||||
const apiKey = provider.apiKey ? provider.apiKey.split(',')[0].trim() : ''
|
||||
|
||||
const client = new OpenAI({
|
||||
baseURL: provider.apiHost,
|
||||
apiKey: provider.apiKey
|
||||
apiKey
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user