Skip to content

Commit 540317a

Browse files
committed
fix: correct is_valid mapping and show provider status in UI
Fix is_valid field mapping to read is_vaild (legacy typo) from the API response. Fix save payloads to send is_vaild: 2 (VaildStatus.is_valid). Update sidebar, BYOK detail, and default model dropdown to show red indicators for invalid providers and warning styling on the dropdown trigger. Show inline API key error when provider is invalidated.
1 parent 7668468 commit 540317a

1 file changed

Lines changed: 55 additions & 14 deletions

File tree

src/pages/Setting/Models.tsx

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ export default function SettingModels() {
244244
apiKey: found.api_key || '',
245245
// Fall back to provider's default API host if endpoint_url is empty
246246
apiHost: found.endpoint_url || item.apiHost,
247-
is_valid: !!found?.is_valid,
247+
is_valid: found?.is_vaild === 2,
248248
prefer: found.prefer ?? false,
249249
model_type: found.model_type ?? '',
250250
externalConfig: fi.externalConfig
@@ -375,6 +375,15 @@ export default function SettingModels() {
375375
return t('setting.select-default-model');
376376
};
377377

378+
const isDefaultModelInvalid = (): boolean => {
379+
// Check for custom model preference
380+
const preferredIdx = form.findIndex((f) => f.prefer);
381+
if (preferredIdx !== -1) {
382+
return form[preferredIdx].is_valid === false;
383+
}
384+
return false;
385+
};
386+
378387
// Check if a model is configured
379388
const isModelConfigured = (
380389
category: 'cloud' | 'custom' | 'local',
@@ -559,7 +568,7 @@ export default function SettingModels() {
559568
provider_name: item.id,
560569
api_key: form[idx].apiKey,
561570
endpoint_url: form[idx].apiHost,
562-
is_valid: form[idx].is_valid,
571+
is_vaild: 2,
563572
model_type: form[idx].model_type,
564573
};
565574
if (externalConfig) {
@@ -590,7 +599,7 @@ export default function SettingModels() {
590599
apiKey: found.api_key || '',
591600
// Fall back to provider's default API host if endpoint_url is empty
592601
apiHost: found.endpoint_url || item.apiHost,
593-
is_valid: !!found.is_valid,
602+
is_valid: found.is_vaild === 2,
594603
prefer: found.prefer ?? false,
595604
externalConfig: fi.externalConfig
596605
? fi.externalConfig.map((ec) => {
@@ -734,7 +743,7 @@ export default function SettingModels() {
734743
provider_name: localPlatform,
735744
api_key: 'not-required',
736745
endpoint_url: currentEndpoint, // Save base URL without specific endpoints
737-
is_valid: true,
746+
is_vaild: 2,
738747
model_type: currentType,
739748
encrypted_config: {
740749
model_platform: localPlatform,
@@ -1037,7 +1046,8 @@ export default function SettingModels() {
10371046
modelId: string | null,
10381047
isActive: boolean,
10391048
isSubItem: boolean = false,
1040-
isConfigured: boolean = false
1049+
isConfigured: boolean = false,
1050+
isValid: boolean = true
10411051
) => {
10421052
const modelImage = getModelImage(modelId);
10431053
const fallbackIcon =
@@ -1078,9 +1088,12 @@ export default function SettingModels() {
10781088
{label}
10791089
</span>
10801090
</div>
1081-
{isConfigured && (
1091+
{isConfigured && isValid && (
10821092
<div className="m-1 h-2 w-2 rounded-full bg-text-success" />
10831093
)}
1094+
{isConfigured && !isValid && (
1095+
<div className="m-1 h-2 w-2 rounded-full bg-text-error" />
1096+
)}
10841097
</button>
10851098
);
10861099
};
@@ -1300,8 +1313,10 @@ export default function SettingModels() {
13001313
: t('setting.set-as-default')}
13011314
</Button>
13021315
)}
1303-
{form[idx].provider_id ? (
1316+
{form[idx].provider_id && form[idx].is_valid !== false ? (
13041317
<div className="h-2 w-2 shrink-0 rounded-full bg-text-success" />
1318+
) : form[idx].provider_id && form[idx].is_valid === false ? (
1319+
<div className="h-2 w-2 shrink-0 rounded-full bg-text-error" />
13051320
) : (
13061321
<div className="h-2 w-2 shrink-0 rounded-full bg-text-label opacity-10" />
13071322
)}
@@ -1318,8 +1333,19 @@ export default function SettingModels() {
13181333
type={showApiKey[idx] ? 'text' : 'password'}
13191334
size="default"
13201335
title={t('setting.api-key-setting')}
1321-
state={errors[idx]?.apiKey ? 'error' : 'default'}
1322-
note={errors[idx]?.apiKey ?? undefined}
1336+
state={
1337+
errors[idx]?.apiKey
1338+
? 'error'
1339+
: form[idx].provider_id && form[idx].is_valid === false
1340+
? 'error'
1341+
: 'default'
1342+
}
1343+
note={
1344+
errors[idx]?.apiKey ??
1345+
(form[idx].provider_id && form[idx].is_valid === false
1346+
? t('setting.api-key-invalid-or-expired')
1347+
: undefined)
1348+
}
13231349
placeholder={` ${t('setting.enter-your-api-key')} ${
13241350
item.name
13251351
} ${t('setting.key')}`}
@@ -1718,11 +1744,19 @@ export default function SettingModels() {
17181744
</div>
17191745
<DropdownMenu>
17201746
<DropdownMenuTrigger asChild>
1721-
<button className="flex w-fit items-center justify-between gap-2 rounded-lg border-[0.5px] border-solid border-border-success bg-surface-success px-3 py-1 font-semibold text-text-success transition-colors hover:opacity-70 active:opacity-90">
1747+
<button
1748+
className={`flex w-fit items-center justify-between gap-2 rounded-lg border-[0.5px] border-solid px-3 py-1 font-semibold transition-colors hover:opacity-70 active:opacity-90 ${
1749+
isDefaultModelInvalid()
1750+
? 'border-border-warning bg-surface-warning text-text-warning'
1751+
: 'border-border-success bg-surface-success text-text-success'
1752+
}`}
1753+
>
17221754
<span className="whitespace-nowrap text-body-sm">
17231755
{getDefaultModelDisplayText()}
17241756
</span>
1725-
<ChevronDown className="h-4 w-4 flex-shrink-0 text-text-success" />
1757+
<ChevronDown
1758+
className={`h-4 w-4 flex-shrink-0 ${isDefaultModelInvalid() ? 'text-text-warning' : 'text-text-success'}`}
1759+
/>
17261760
</button>
17271761
</DropdownMenuTrigger>
17281762
<DropdownMenuContent align="end" className="w-[180px]">
@@ -1766,6 +1800,7 @@ export default function SettingModels() {
17661800
{items.map((item, idx) => {
17671801
const isConfigured = !!form[idx]?.provider_id;
17681802
const isPreferred = form[idx]?.prefer;
1803+
const isValid = form[idx]?.is_valid !== false;
17691804
const modelImage = getModelImage(item.id);
17701805

17711806
return (
@@ -1802,11 +1837,16 @@ export default function SettingModels() {
18021837
<div className="h-2 w-2 rounded-full bg-text-label opacity-10" />
18031838
)}
18041839
{isPreferred && (
1805-
<Check className="h-4 w-4 text-text-success" />
1840+
<Check
1841+
className={`h-4 w-4 ${isValid ? 'text-text-success' : 'text-text-error'}`}
1842+
/>
18061843
)}
1807-
{isConfigured && !isPreferred && (
1844+
{isConfigured && !isPreferred && isValid && (
18081845
<div className="h-2 w-2 rounded-full bg-text-success" />
18091846
)}
1847+
{isConfigured && !isPreferred && !isValid && (
1848+
<div className="h-2 w-2 rounded-full bg-text-error" />
1849+
)}
18101850
</div>
18111851
</DropdownMenuItem>
18121852
);
@@ -1932,7 +1972,8 @@ export default function SettingModels() {
19321972
item.id,
19331973
selectedTab === `byok-${item.id}`,
19341974
true,
1935-
!!form[idx].provider_id
1975+
!!form[idx].provider_id,
1976+
form[idx].is_valid !== false
19361977
)
19371978
)}
19381979
</div>

0 commit comments

Comments
 (0)