fix(stage-ui): incorrect DeepSeek provider instance normalized (#2287)

## Summary
- show the DeepSeek thinking-mode control for configured provider
instances whose id is generated
- compare provider configs after filling omitted schema defaults so new
defaults like `thinkingMode: auto` do not mark old empty DeepSeek
configs as configured

## Tests
- `pnpm -rF @proj-airi/stage-ui run test:run
src/stores/providers/config-defaults.test.ts
src/libs/providers/providers/deepseek/index.test.ts`
- `pnpm exec moeru-lint
packages/stage-ui/src/stores/providers/config-defaults.ts
packages/stage-ui/src/stores/providers/config-defaults.test.ts
packages/stage-ui/src/stores/providers/provider.ts
packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue`
- `git diff --check`

Co-authored-by: 冼健聪 <mark.xian@evenrealities.com>
This commit is contained in:
Mark Xian
2026-08-19 17:27:41 +08:00
committed by GitHub
co-authored by 冼健聪
parent 75277ce901
commit db89aaeee3
4 changed files with 43 additions and 5 deletions
@@ -14,6 +14,7 @@ import { useProviderValidation } from '@proj-airi/stage-ui/composables/use-provi
import { getDefinedProvider } from '@proj-airi/stage-ui/libs'
import { useConsciousnessStore } from '@proj-airi/stage-ui/stores/modules/consciousness'
import { useProviderConfigStore } from '@proj-airi/stage-ui/stores/providers/config'
import { useProviderStore } from '@proj-airi/stage-ui/stores/providers/provider'
import { FieldCombobox } from '@proj-airi/ui'
import { storeToRefs } from 'pinia'
import { computed } from 'vue'
@@ -21,10 +22,12 @@ import { useRoute } from 'vue-router'
const route = useRoute()
const providerId = route.params.providerId as string
const providerStore = useProviderConfigStore()
const providerConfigStore = useProviderConfigStore()
const providersStore = useProviderStore()
const consciousnessStore = useConsciousnessStore()
const { configs: providers } = storeToRefs(providerStore) as { configs: RemovableRef<Record<string, any>> }
const { configs: providers } = storeToRefs(providerConfigStore) as { configs: RemovableRef<Record<string, any>> }
const { activeProvider } = storeToRefs(consciousnessStore)
const providerDefinition = computed(() => providersStore.findProviderDefinition(providerId))
// Define computed properties for credentials
const apiKey = computed({
@@ -54,7 +57,7 @@ const thinkingMode = computed({
},
})
const supportsDeepSeekThinkingMode = computed(() => providerId === 'deepseek')
const supportsDeepSeekThinkingMode = computed(() => providerDefinition.value?.id === 'deepseek')
// Use the composable to get validation logic and state
const {
@@ -74,7 +77,7 @@ const {
} = useProviderValidation(providerId)
const apiKeyPlaceholder = computed(() => {
const definition = getDefinedProvider(providerId)
const definition = providerDefinition.value ?? getDefinedProvider(providerId)
if (!definition?.createProviderConfig)
return 'sk-...'
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest'
import { normalizeProviderConfigDefaults } from './config-defaults'
describe('normalizeProviderConfigDefaults', () => {
it('treats omitted schema defaults as unchanged provider config', () => {
expect(normalizeProviderConfigDefaults(
{ baseUrl: 'https://api.deepseek.com/' },
{ baseUrl: 'https://api.deepseek.com/', thinkingMode: 'auto' },
)).toEqual({
baseUrl: 'https://api.deepseek.com/',
thinkingMode: 'auto',
})
})
it('keeps explicit non-default values dirty', () => {
expect(normalizeProviderConfigDefaults(
{ baseUrl: 'https://api.deepseek.com/', thinkingMode: 'disable' },
{ baseUrl: 'https://api.deepseek.com/', thinkingMode: 'auto' },
)).toEqual({
baseUrl: 'https://api.deepseek.com/',
thinkingMode: 'disable',
})
})
})
@@ -0,0 +1,9 @@
export function normalizeProviderConfigDefaults(config: Record<string, unknown>, defaultOptions: Record<string, unknown>) {
const keys = [...new Set([...Object.keys(defaultOptions), ...Object.keys(config)])]
return Object.fromEntries(keys.map((key) => {
if (Object.hasOwn(config, key))
return [key, config[key]]
return [key, defaultOptions[key]]
}))
}
@@ -32,6 +32,7 @@ import {
} from '../../libs/providers'
import { selectProviderMetadata, selectProvidersMetadata } from '../../libs/providers/metadata'
import { useProviderConfigStore } from './config'
import { normalizeProviderConfigDefaults } from './config-defaults'
export type { ModelInfo, VoiceInfo } from '../../libs/providers/types'
@@ -836,7 +837,7 @@ export const useProviderStore = defineStore('provider', () => {
return false
const defaultOptions = getDefaultProviderConfig(providerId)
return JSON.stringify(config) !== JSON.stringify(defaultOptions)
return JSON.stringify(normalizeProviderConfigDefaults(config, defaultOptions)) !== JSON.stringify(defaultOptions)
}
function shouldListProvider(providerId: string) {