diff --git a/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue b/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue index 271b3478b..705f6d20c 100644 --- a/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue +++ b/packages/stage-pages/src/pages/settings/providers/chat/[providerId].vue @@ -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> } +const { configs: providers } = storeToRefs(providerConfigStore) as { configs: RemovableRef> } 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-...' diff --git a/packages/stage-ui/src/stores/providers/config-defaults.test.ts b/packages/stage-ui/src/stores/providers/config-defaults.test.ts new file mode 100644 index 000000000..0a7d9d9fd --- /dev/null +++ b/packages/stage-ui/src/stores/providers/config-defaults.test.ts @@ -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', + }) + }) +}) diff --git a/packages/stage-ui/src/stores/providers/config-defaults.ts b/packages/stage-ui/src/stores/providers/config-defaults.ts new file mode 100644 index 000000000..04a21f9de --- /dev/null +++ b/packages/stage-ui/src/stores/providers/config-defaults.ts @@ -0,0 +1,9 @@ +export function normalizeProviderConfigDefaults(config: Record, defaultOptions: Record) { + 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]] + })) +} diff --git a/packages/stage-ui/src/stores/providers/provider.ts b/packages/stage-ui/src/stores/providers/provider.ts index 3284f4309..778b7bc71 100644 --- a/packages/stage-ui/src/stores/providers/provider.ts +++ b/packages/stage-ui/src/stores/providers/provider.ts @@ -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) {