fix(stage-*) Fix Inablility to verify openrouter and remove api key requirement on OpenAI compatible #566 (#582)

This commit is contained in:
Ilya Bogdanov
2025-09-14 20:55:24 +08:00
committed by GitHub
parent 697d680417
commit 8d9abc1a04
6 changed files with 53 additions and 6 deletions
@@ -115,6 +115,7 @@ const {
>
<ProviderApiKeyInput
v-model="apiKey"
:required="false"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
@@ -115,6 +115,7 @@ const {
>
<ProviderApiKeyInput
v-model="apiKey"
:required="false"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
@@ -139,6 +139,7 @@ function handleResetSettings() {
>
<ProviderApiKeyInput
v-model="apiKey"
:required="false"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
@@ -83,6 +83,7 @@ function handleResetSettings() {
>
<ProviderApiKeyInput
v-model="apiKey"
:required="false"
:provider-name="providerMetadata?.localizedName"
placeholder="sk-..."
/>
+47 -1
View File
@@ -252,7 +252,53 @@ export const useProvidersStore = defineStore('providers', () => {
description: 'openrouter.ai',
defaultBaseUrl: 'https://openrouter.ai/api/v1/',
creator: createOpenRouter,
validation: ['health', 'model_list', 'chat_completions'],
validation: ['health', 'model_list'],
validators: {
validateProviderConfig: async (config) => {
const errors: Error[] = []
if (!config.apiKey) {
errors.push(new Error('API Key is required'))
}
if (!config.baseUrl) {
errors.push(new Error('Base URL is required'))
}
if (errors.length > 0) {
return { errors, reason: errors.map(e => e.message).join(', '), valid: false }
}
if (!isUrl(config.baseUrl as string) || new URL(config.baseUrl as string).host.length === 0) {
errors.push(new Error('Base URL is not absolute. Check your input.'))
}
if (!(config.baseUrl as string).endsWith('/')) {
errors.push(new Error('Base URL must end with a trailing slash (/).'))
}
if (errors.length > 0) {
return { errors, reason: errors.map(e => e.message).join(', '), valid: false }
}
const response = await fetch(`${config.baseUrl as string}chat/completions`, { headers: { Authorization: `Bearer ${config.apiKey}` }, method: 'POST', body: `{"model": "test","messages": [{"role": "user","content": "Hello, world"}],"stream": false}` })
const responseJson = await response.json()
if (!responseJson.user_id) {
return {
errors: [new Error(`OpenRouterError: ${responseJson.error.message}`)],
reason: `OpenRouterError: ${responseJson.error.message}`,
valid: false,
}
}
return {
errors: [],
reason: '',
valid: true,
}
},
},
}),
'app-local-audio-speech': buildOpenAICompatibleProvider({
id: 'app-local-audio-speech',
@@ -54,9 +54,6 @@ export function buildOpenAICompatibleProvider(
validateProviderConfig: async (config: Record<string, unknown>) => {
const errors: Error[] = []
if (!config.apiKey) {
errors.push(new Error('API key is required'))
}
if (!config.baseUrl) {
errors.push(new Error('Base URL is required'))
}
@@ -83,7 +80,7 @@ export function buildOpenAICompatibleProvider(
if (validationChecks.includes('health')) {
try {
responseChat = await fetch(`${config.baseUrl as string}chat/completions`, { headers: { Authorization: `Bearer ${config.apiKey}`, ...additionalHeaders }, method: 'POST' })
responseChat = await fetch(`${config.baseUrl as string}chat/completions`, { headers: { Authorization: `Bearer ${config.apiKey}`, ...additionalHeaders }, method: 'POST', body: '{"model": "test"}' })
responseModelList = await fetch(`${config.baseUrl as string}models`, { headers: { Authorization: `Bearer ${config.apiKey}`, ...additionalHeaders } })
if (!([200, 400, 401].includes(responseChat.status) || [200, 400, 401].includes(responseModelList.status))) {
@@ -119,7 +116,7 @@ export function buildOpenAICompatibleProvider(
try {
let response = responseChat
if (!response) {
response = await fetch(`${config.baseUrl as string}chat/completions`, { headers: { Authorization: `Bearer ${config.apiKey}`, ...additionalHeaders }, method: 'POST' })
response = await fetch(`${config.baseUrl as string}chat/completions`, { headers: { Authorization: `Bearer ${config.apiKey}`, ...additionalHeaders }, method: 'POST', body: '{"model": "test"}' })
}
if (!response.ok) {