feat(ConfigManager): auto fetch model list for OpenAI compatible
This commit is contained in:
@ -1,8 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { config } = useConfigStore()
|
interface OpenAICompatibleModel {
|
||||||
|
id: string
|
||||||
|
object: string
|
||||||
|
}
|
||||||
|
interface OpenAICompatibleModelsResponse {
|
||||||
|
object: string
|
||||||
|
data: OpenAICompatibleModel[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const { config, aiApiBase } = useConfigStore()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
const showModal = ref(false)
|
const showModal = ref(false)
|
||||||
|
const loadingAiModels = ref(false)
|
||||||
|
const aiModelOptions = ref<string[]>([])
|
||||||
|
|
||||||
const aiProviderOptions = computed(() => [
|
const aiProviderOptions = computed(() => [
|
||||||
{
|
{
|
||||||
@ -18,6 +29,54 @@
|
|||||||
aiProviderOptions.value.find((o) => o.value === config.ai.provider),
|
aiProviderOptions.value.find((o) => o.value === config.ai.provider),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Try to find available AI models based on selected provider
|
||||||
|
const debouncedListAiModels = useDebounceFn(async () => {
|
||||||
|
if (!config.ai.apiKey) return
|
||||||
|
if (!aiApiBase || !aiApiBase.startsWith('http')) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
loadingAiModels.value = true
|
||||||
|
const result: OpenAICompatibleModelsResponse = await $fetch(
|
||||||
|
`${aiApiBase}/models`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${config.ai.apiKey}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
`Found ${result.data.length} AI models for provider ${config.ai.provider}`,
|
||||||
|
)
|
||||||
|
aiModelOptions.value = result.data.map((m) => m.id)
|
||||||
|
// Auto-select the current model
|
||||||
|
if (!aiModelOptions.value.includes(config.ai.model)) {
|
||||||
|
aiModelOptions.value.unshift(config.ai.model)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Fetch AI models failed`, error)
|
||||||
|
if (config.ai.model) {
|
||||||
|
aiModelOptions.value = [config.ai.model]
|
||||||
|
} else {
|
||||||
|
aiModelOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingAiModels.value = false
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [
|
||||||
|
config.ai.provider,
|
||||||
|
config.ai.apiKey,
|
||||||
|
config.ai.apiBase,
|
||||||
|
showModal.value,
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
if (!showModal.value) return
|
||||||
|
debouncedListAiModels()
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
show() {
|
show() {
|
||||||
showModal.value = true
|
showModal.value = true
|
||||||
@ -58,11 +117,15 @@
|
|||||||
:placeholder="selectedAiProvider?.apiBasePlaceholder"
|
:placeholder="selectedAiProvider?.apiBasePlaceholder"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
<UFormField label="Model" required>
|
<UFormField :label="$t('settings.ai.model')" required>
|
||||||
<UInput
|
<UInputMenu
|
||||||
v-model="config.ai.model"
|
v-model="config.ai.model"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
placeholder="Model name"
|
:items="aiModelOptions"
|
||||||
|
:placeholder="$t('settings.ai.model')"
|
||||||
|
:loading="loadingAiModels"
|
||||||
|
create-item
|
||||||
|
@create="aiModelOptions.push($event)"
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</UFormField>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,7 +6,7 @@ export const useAiModel = () => {
|
|||||||
case 'openai-compatible':
|
case 'openai-compatible':
|
||||||
const openai = createOpenAI({
|
const openai = createOpenAI({
|
||||||
apiKey: config.config.ai.apiKey,
|
apiKey: config.config.ai.apiKey,
|
||||||
baseURL: config.config.ai.apiBase || 'https://api.openai.com/v1', // TODO: better default
|
baseURL: config.aiApiBase,
|
||||||
})
|
})
|
||||||
return openai(config.config.ai.model)
|
return openai(config.config.ai.model)
|
||||||
default:
|
default:
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
import type { ResearchResult } from '~/lib/deep-research'
|
import type { ResearchResult } from '~/lib/deep-research'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const config = useConfigStore()
|
const { config } = useConfigStore()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
|
||||||
const configManagerRef = ref<InstanceType<typeof ConfigManager>>()
|
const configManagerRef = ref<InstanceType<typeof ConfigManager>>()
|
||||||
@ -77,8 +77,8 @@ ${feedback.value
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function generateFeedback(data: ResearchInputData) {
|
async function generateFeedback(data: ResearchInputData) {
|
||||||
const aiConfig = config.config.ai
|
const aiConfig = config.ai
|
||||||
const webSearchConfig = config.config.webSearch
|
const webSearchConfig = config.webSearch
|
||||||
|
|
||||||
if (!aiConfig.model || !aiConfig.apiKey || !webSearchConfig.apiKey) {
|
if (!aiConfig.model || !aiConfig.apiKey || !webSearchConfig.apiKey) {
|
||||||
toast.add({
|
toast.add({
|
||||||
|
@ -30,5 +30,9 @@ export const useConfigStore = defineStore('config', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return { config: skipHydrate(config) }
|
const aiApiBase = computed(() => {
|
||||||
|
return config.value.ai.apiBase || 'https://api.openai.com/v1'
|
||||||
|
})
|
||||||
|
|
||||||
|
return { config: skipHydrate(config), aiApiBase }
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user