feat: add China LLM providers guided login flow#1254
Conversation
Add a guided login experience for 4 domestic (China) LLM providers in the /login command: DeepSeek, Zhipu GLM, Tongyi Qianwen, and MiMo Xiaomi. Each provider includes model presets with pricing, context windows, and optional Coding Plan integration. - New file: src/utils/chinaLlmProviders.ts — provider preset configs - Modified: src/components/ConsoleOAuthFlow.tsx — 4-step guided flow (select provider → select mode → select model → enter API key) All providers are OpenAI-compatible; credentials saved as OPENAI_BASE_URL + OPENAI_API_KEY under modelType: 'openai'. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds China LLM provider support to the OAuth flow. It introduces a new utility module defining provider types and presets (including URLs and pricing), then integrates a four-step provider setup flow into the existing OAuth component for provider, mode, model, and API key selection. ChangesChina LLM Providers OAuth Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 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)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/components/ConsoleOAuthFlow.tsx (2)
22-22: ⚡ Quick winUse the
src/*alias for the new provider import.This adds another relative import inside
src/, which goes against the repo import-path rule and makes future file moves harder.Suggested change
-import { CHINA_LLM_PROVIDERS, type ProviderPreset, resolveChinaProviderBaseURL } from '../utils/chinaLlmProviders.js'; +import { + CHINA_LLM_PROVIDERS, + type ProviderPreset, + resolveChinaProviderBaseURL, +} from 'src/utils/chinaLlmProviders.js';As per coding guidelines "Import paths must use the
src/*alias (e.g.,import { ... } from 'src/utils/...'); tsconfig mapssrc/*to./src/*."🤖 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 `@src/components/ConsoleOAuthFlow.tsx` at line 22, Replace the relative import in ConsoleOAuthFlow.tsx with the repo alias import: update the import that currently pulls CHINA_LLM_PROVIDERS, ProviderPreset, and resolveChinaProviderBaseURL via a relative path to use the src/* alias (e.g., import { CHINA_LLM_PROVIDERS, type ProviderPreset, resolveChinaProviderBaseURL } from 'src/utils/chinaLlmProviders.js') so the module resolution follows the tsconfig mapping and avoids future breakage when files move.
1425-1428: ⚡ Quick winDon't reintroduce
as anyin the newsrc/save path.The China flow bypasses the settings payload type right at the persistence boundary.
doOpenAISavein this same file already shows the typed pattern to reuse here.As per coding guidelines "Do not use
as anyin production code; useas unknown as SpecificTypefor double assertions or use proper interface definitions."🤖 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 `@src/components/ConsoleOAuthFlow.tsx` around lines 1425 - 1428, The call to updateSettingsForSource in ConsoleOAuthFlow.tsx is bypassing types with "as any"; replace that with the same typed pattern used by doOpenAISave: construct a properly typed payload (or cast via as unknown as YourSettingsType) and pass that instead of using "as any", referencing updateSettingsForSource and doOpenAISave to mirror the typed save path and ensure the modelType/env object conforms to the expected settings interface.
🤖 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 `@src/components/ConsoleOAuthFlow.tsx`:
- Around line 1418-1437: The saved env is merged so an existing OPENAI_AUTH_MODE
can persist; when switching to provider API keys you must explicitly clear it:
add OPENAI_AUTH_MODE: undefined to the env object you pass into
updateSettingsForSource (the env declared before the call to
updateSettingsForSource) so persisted settings remove that key, and after a
successful save also delete process.env.OPENAI_AUTH_MODE (or set it to
undefined) in the success branch where you copy env into process.env to ensure
the in-memory env is cleared as well.
- Around line 1456-1459: The help text always uses provider.apiKeyPage and
provider.keyFormat which is wrong for coding-plan providers (e.g., Qwen, MiMo);
update the rendering in ConsoleOAuthFlow.tsx so the Text nodes that currently
reference provider.apiKeyPage and provider.keyFormat instead first check for and
use the coding-plan metadata for the selected provider (e.g., locate the
provider's plan with id 'coding-plan' or a codingPlan/metadata field on the
provider and read its apiKeyPage/keyFormat) and only fall back to
provider.apiKeyPage/provider.keyFormat if that metadata is absent; update the
three Text lines near the Box flexDirection="column" block to prefer coding-plan
metadata to ensure correct key page and format are shown.
---
Nitpick comments:
In `@src/components/ConsoleOAuthFlow.tsx`:
- Line 22: Replace the relative import in ConsoleOAuthFlow.tsx with the repo
alias import: update the import that currently pulls CHINA_LLM_PROVIDERS,
ProviderPreset, and resolveChinaProviderBaseURL via a relative path to use the
src/* alias (e.g., import { CHINA_LLM_PROVIDERS, type ProviderPreset,
resolveChinaProviderBaseURL } from 'src/utils/chinaLlmProviders.js') so the
module resolution follows the tsconfig mapping and avoids future breakage when
files move.
- Around line 1425-1428: The call to updateSettingsForSource in
ConsoleOAuthFlow.tsx is bypassing types with "as any"; replace that with the
same typed pattern used by doOpenAISave: construct a properly typed payload (or
cast via as unknown as YourSettingsType) and pass that instead of using "as
any", referencing updateSettingsForSource and doOpenAISave to mirror the typed
save path and ensure the modelType/env object conforms to the expected settings
interface.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 91a46235-d775-4aa1-9dc9-e8cedb4c4947
📒 Files selected for processing (2)
src/components/ConsoleOAuthFlow.tsxsrc/utils/chinaLlmProviders.ts
| const env: Record<string, string> = { | ||
| OPENAI_BASE_URL: baseUrl, | ||
| OPENAI_API_KEY: chinaKeyValue.trim(), | ||
| OPENAI_DEFAULT_SONNET_MODEL: modelId, | ||
| OPENAI_DEFAULT_HAIKU_MODEL: modelId, | ||
| OPENAI_DEFAULT_OPUS_MODEL: modelId, | ||
| }; | ||
| const { error } = updateSettingsForSource('userSettings', { | ||
| modelType: 'openai' as any, | ||
| env, | ||
| } as any); | ||
| if (error) { | ||
| setOAuthStatus({ | ||
| state: 'error', | ||
| message: 'Failed to save settings. Please try again.', | ||
| toRetry: { state: 'china_apikey', provider, mode: accessMode, modelId, apiKey: chinaKeyValue }, | ||
| }); | ||
| } else { | ||
| for (const [k, v] of Object.entries(env)) process.env[k] = v; | ||
| logEvent('tengu_china_login_success', {}); |
There was a problem hiding this comment.
Clear stale OPENAI_AUTH_MODE when switching to provider API keys.
updateSettingsForSource merges env objects, so a previous OPENAI_AUTH_MODE=chatgpt survives this write unless you explicitly send undefined. That leaves the persisted China-provider config in the wrong auth mode. The neighboring openai_chat_api save path already handles this correctly.
Suggested change
- const env: Record<string, string> = {
+ const env: Record<string, string | undefined> = {
+ OPENAI_AUTH_MODE: undefined,
OPENAI_BASE_URL: baseUrl,
OPENAI_API_KEY: chinaKeyValue.trim(),
OPENAI_DEFAULT_SONNET_MODEL: modelId,
OPENAI_DEFAULT_HAIKU_MODEL: modelId,
OPENAI_DEFAULT_OPUS_MODEL: modelId,
};
@@
- for (const [k, v] of Object.entries(env)) process.env[k] = v;
+ for (const [k, v] of Object.entries(env)) {
+ if (v === undefined) {
+ delete process.env[k];
+ } else {
+ process.env[k] = v;
+ }
+ }📝 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.
| const env: Record<string, string> = { | |
| OPENAI_BASE_URL: baseUrl, | |
| OPENAI_API_KEY: chinaKeyValue.trim(), | |
| OPENAI_DEFAULT_SONNET_MODEL: modelId, | |
| OPENAI_DEFAULT_HAIKU_MODEL: modelId, | |
| OPENAI_DEFAULT_OPUS_MODEL: modelId, | |
| }; | |
| const { error } = updateSettingsForSource('userSettings', { | |
| modelType: 'openai' as any, | |
| env, | |
| } as any); | |
| if (error) { | |
| setOAuthStatus({ | |
| state: 'error', | |
| message: 'Failed to save settings. Please try again.', | |
| toRetry: { state: 'china_apikey', provider, mode: accessMode, modelId, apiKey: chinaKeyValue }, | |
| }); | |
| } else { | |
| for (const [k, v] of Object.entries(env)) process.env[k] = v; | |
| logEvent('tengu_china_login_success', {}); | |
| const env: Record<string, string | undefined> = { | |
| OPENAI_AUTH_MODE: undefined, | |
| OPENAI_BASE_URL: baseUrl, | |
| OPENAI_API_KEY: chinaKeyValue.trim(), | |
| OPENAI_DEFAULT_SONNET_MODEL: modelId, | |
| OPENAI_DEFAULT_HAIKU_MODEL: modelId, | |
| OPENAI_DEFAULT_OPUS_MODEL: modelId, | |
| }; | |
| const { error } = updateSettingsForSource('userSettings', { | |
| modelType: 'openai' as any, | |
| env, | |
| } as any); | |
| if (error) { | |
| setOAuthStatus({ | |
| state: 'error', | |
| message: 'Failed to save settings. Please try again.', | |
| toRetry: { state: 'china_apikey', provider, mode: accessMode, modelId, apiKey: chinaKeyValue }, | |
| }); | |
| } else { | |
| for (const [k, v] of Object.entries(env)) { | |
| if (v === undefined) { | |
| delete process.env[k]; | |
| } else { | |
| process.env[k] = v; | |
| } | |
| } | |
| logEvent('tengu_china_login_success', {}); |
🤖 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 `@src/components/ConsoleOAuthFlow.tsx` around lines 1418 - 1437, The saved env
is merged so an existing OPENAI_AUTH_MODE can persist; when switching to
provider API keys you must explicitly clear it: add OPENAI_AUTH_MODE: undefined
to the env object you pass into updateSettingsForSource (the env declared before
the call to updateSettingsForSource) so persisted settings remove that key, and
after a successful save also delete process.env.OPENAI_AUTH_MODE (or set it to
undefined) in the success branch where you copy env into process.env to ensure
the in-memory env is cleared as well.
| <Box flexDirection="column" gap={0}> | ||
| <Text dimColor> Get your key: {provider.apiKeyPage}</Text> | ||
| <Text dimColor> {provider.freeTier}</Text> | ||
| <Text dimColor> Key format: {provider.keyFormat}</Text> |
There was a problem hiding this comment.
Use coding-plan metadata in the coding-plan key step.
This help text always shows provider.apiKeyPage and provider.keyFormat. For coding-plan users that is wrong for at least Qwen (sk-sp-...) and MiMo (tp-..., different page), so the guided flow points users at the wrong credentials.
Suggested change
+ const keyPage =
+ accessMode === 'coding-plan' && provider.codingPlan
+ ? provider.codingPlan.purchasePage
+ : provider.apiKeyPage;
+ const keyFormat =
+ accessMode === 'coding-plan' && provider.codingPlan
+ ? provider.codingPlan.keyFormat
+ : provider.keyFormat;
@@
- <Text dimColor> Get your key: {provider.apiKeyPage}</Text>
- <Text dimColor> {provider.freeTier}</Text>
- <Text dimColor> Key format: {provider.keyFormat}</Text>
+ <Text dimColor> Get your key: {keyPage}</Text>
+ <Text dimColor>
+ {accessMode === 'coding-plan' ? 'Use your Coding Plan credential here' : provider.freeTier}
+ </Text>
+ <Text dimColor> Key format: {keyFormat}</Text>📝 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.
| <Box flexDirection="column" gap={0}> | |
| <Text dimColor> Get your key: {provider.apiKeyPage}</Text> | |
| <Text dimColor> {provider.freeTier}</Text> | |
| <Text dimColor> Key format: {provider.keyFormat}</Text> | |
| <Box flexDirection="column" gap={0}> | |
| const keyPage = | |
| accessMode === 'coding-plan' && provider.codingPlan | |
| ? provider.codingPlan.purchasePage | |
| : provider.apiKeyPage; | |
| const keyFormat = | |
| accessMode === 'coding-plan' && provider.codingPlan | |
| ? provider.codingPlan.keyFormat | |
| : provider.keyFormat; | |
| <Text dimColor> Get your key: {keyPage}</Text> | |
| <Text dimColor> | |
| {accessMode === 'coding-plan' ? 'Use your Coding Plan credential here' : provider.freeTier} | |
| </Text> | |
| <Text dimColor> Key format: {keyFormat}</Text> |
🤖 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 `@src/components/ConsoleOAuthFlow.tsx` around lines 1456 - 1459, The help text
always uses provider.apiKeyPage and provider.keyFormat which is wrong for
coding-plan providers (e.g., Qwen, MiMo); update the rendering in
ConsoleOAuthFlow.tsx so the Text nodes that currently reference
provider.apiKeyPage and provider.keyFormat instead first check for and use the
coding-plan metadata for the selected provider (e.g., locate the provider's plan
with id 'coding-plan' or a codingPlan/metadata field on the provider and read
its apiKeyPage/keyFormat) and only fall back to
provider.apiKeyPage/provider.keyFormat if that metadata is absent; update the
three Text lines near the Box flexDirection="column" block to prefer coding-plan
metadata to ensure correct key page and format are shown.
Summary
/logincommand: DeepSeek, Zhipu GLM, Tongyi Qianwen, and MiMo XiaomiOPENAI_BASE_URL+OPENAI_API_KEYundermodelType: 'openai'New file
src/utils/chinaLlmProviders.ts— Provider preset configurations with types, model data, pricing, and helper functionsModified file
src/components/ConsoleOAuthFlow.tsx— Added 4-step guided flow:Providers
Test plan
/loginand verify "China LLM Providers" option appears in the menubun run precheck— all checks pass🤖 Generated with Claude Code
Summary by CodeRabbit