Add CLIProxyAPI provider support (#375)
This commit is contained in:
@@ -42,6 +42,7 @@ const modelOptions = ref<Array<{ label: string; value: string }>>([])
|
||||
const CODEX_KEY = 'openai-codex'
|
||||
const NOUS_KEY = 'nous'
|
||||
const COPILOT_KEY = 'copilot'
|
||||
const CLIPROXYAPI_KEY = 'cliproxyapi'
|
||||
const ALIBABA_CODING_KEY = 'alibaba-coding-plan'
|
||||
const ALIBABA_CODING_REGIONS = {
|
||||
intl: 'https://coding-intl.dashscope.aliyuncs.com/v1',
|
||||
@@ -51,6 +52,7 @@ const ALIBABA_CODING_REGIONS = {
|
||||
const isCodex = computed(() => selectedPreset.value === CODEX_KEY)
|
||||
const isNous = computed(() => selectedPreset.value === NOUS_KEY)
|
||||
const isCopilot = computed(() => selectedPreset.value === COPILOT_KEY)
|
||||
const isCliproxyApi = computed(() => selectedPreset.value === CLIPROXYAPI_KEY)
|
||||
const isAlibabaCoding = computed(() => selectedPreset.value === ALIBABA_CODING_KEY)
|
||||
const alibabaCodingRegion = ref<'intl' | 'cn'>('intl')
|
||||
|
||||
@@ -171,7 +173,7 @@ async function handleSave() {
|
||||
message.warning(t('models.baseUrlRequired'))
|
||||
return
|
||||
}
|
||||
if (!formData.value.api_key.trim()) {
|
||||
if (!formData.value.api_key.trim() && !isCliproxyApi.value) {
|
||||
message.warning(t('models.apiKeyRequired'))
|
||||
return
|
||||
}
|
||||
@@ -344,7 +346,7 @@ function handleClose() {
|
||||
/>
|
||||
</NFormItem>
|
||||
|
||||
<NFormItem v-if="!isCodex && !isNous" :label="t('models.apiKey')" required>
|
||||
<NFormItem v-if="!isCodex && !isNous" :label="t('models.apiKey')" :required="!isCliproxyApi">
|
||||
<NInput
|
||||
v-model:value="formData.api_key"
|
||||
type="password"
|
||||
|
||||
@@ -192,6 +192,19 @@ export const PROVIDER_PRESETS: ProviderPreset[] = [
|
||||
'deepseek/deepseek-v3.2',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'CLIProxyAPI',
|
||||
value: 'cliproxyapi',
|
||||
base_url: 'http://127.0.0.1:8317/v1',
|
||||
models: [
|
||||
'gpt-5.5',
|
||||
'gpt-5-codex',
|
||||
'claude-sonnet-4-6',
|
||||
'claude-sonnet-4-5-20250929',
|
||||
'gemini-3.1-pro-preview',
|
||||
'gemini-2.5-pro',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'OpenCode Zen',
|
||||
value: 'opencode-zen',
|
||||
|
||||
@@ -134,13 +134,13 @@ export async function getAvailable(ctx: any) {
|
||||
}
|
||||
if (Object.keys(modelMeta).length === 0) modelMeta = undefined
|
||||
}
|
||||
} else if (providerKey === 'openrouter') {
|
||||
// OpenRouter has 200+ models — fetch dynamically like Copilot
|
||||
} else if (providerKey === 'openrouter' || providerKey === 'cliproxyapi') {
|
||||
// OpenRouter and local CLIProxyAPI expose dynamic OpenAI-compatible /models catalogs.
|
||||
if (envMapping.api_key_env) {
|
||||
const orKey = envGetValue(envMapping.api_key_env)
|
||||
if (orKey) {
|
||||
const apiKey = envGetValue(envMapping.api_key_env)
|
||||
if (apiKey) {
|
||||
try {
|
||||
const fetched = await fetchProviderModels(baseUrl, orKey, true)
|
||||
const fetched = await fetchProviderModels(baseUrl, apiKey, providerKey === 'openrouter')
|
||||
if (fetched.length > 0) modelsList = fetched
|
||||
} catch { /* ignore — leave empty, won't show */ }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import * as hermesCli from '../../services/hermes/hermes-cli'
|
||||
import { readConfigYaml, writeConfigYaml, saveEnvValue, PROVIDER_ENV_MAP } from '../../services/config-helpers'
|
||||
import { logger } from '../../services/logger'
|
||||
|
||||
const OPTIONAL_API_KEY_PROVIDERS = new Set(['cliproxyapi'])
|
||||
|
||||
function buildProviderEntry(name: string, base_url: string, api_key: string, model: string, context_length?: number) {
|
||||
const entry: any = { name, base_url, api_key, model }
|
||||
if (context_length && context_length > 0) {
|
||||
@@ -20,7 +22,7 @@ export async function create(ctx: any) {
|
||||
if (!name || !base_url || !model) {
|
||||
ctx.status = 400; ctx.body = { error: 'Missing name, base_url, or model' }; return
|
||||
}
|
||||
if (!api_key) {
|
||||
if (!api_key && !OPTIONAL_API_KEY_PROVIDERS.has(String(providerKey || ''))) {
|
||||
ctx.status = 400; ctx.body = { error: 'Missing API key' }; return
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -24,6 +24,7 @@ export const PROVIDER_ENV_MAP: Record<string, { api_key_env: string; base_url_en
|
||||
gemini: { api_key_env: 'GEMINI_API_KEY', base_url_env: '' },
|
||||
kilocode: { api_key_env: 'KILO_API_KEY', base_url_env: '' },
|
||||
'ai-gateway': { api_key_env: 'AI_GATEWAY_API_KEY', base_url_env: '' },
|
||||
cliproxyapi: { api_key_env: '', base_url_env: '' },
|
||||
'opencode-zen': { api_key_env: 'OPENCODE_API_KEY', base_url_env: '' },
|
||||
'opencode-go': { api_key_env: 'OPENCODE_API_KEY', base_url_env: '' },
|
||||
huggingface: { api_key_env: 'HF_TOKEN', base_url_env: '' },
|
||||
|
||||
@@ -205,6 +205,20 @@ export const PROVIDER_PRESETS: ProviderPreset[] = [
|
||||
'deepseek/deepseek-v3.2',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'CLIProxyAPI',
|
||||
value: 'cliproxyapi',
|
||||
builtin: true,
|
||||
base_url: 'http://127.0.0.1:8317/v1',
|
||||
models: [
|
||||
'gpt-5.5',
|
||||
'gpt-5-codex',
|
||||
'claude-sonnet-4-6',
|
||||
'claude-sonnet-4-5-20250929',
|
||||
'gemini-3.1-pro-preview',
|
||||
'gemini-2.5-pro',
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'OpenCode Zen',
|
||||
value: 'opencode-zen',
|
||||
|
||||
Reference in New Issue
Block a user