feat(frontend): add kiro api and model presets

This commit is contained in:
nianzs
2026-04-29 16:29:35 +08:00
parent 05bc424c9a
commit e8fc09869b
6 changed files with 369 additions and 4 deletions
@@ -50,6 +50,15 @@ describe('useModelWhitelist', () => {
expect(models.indexOf('gemini-2.5-flash-image')).toBeLessThan(models.indexOf('gemini-2.5-flash-lite'))
})
it('kiro 模型列表不暴露旧的 -agentic / -chat 后缀', () => {
const models = getModelsByPlatform('kiro')
expect(models).toContain('claude-sonnet-4-6')
expect(models).toContain('claude-sonnet-4-6-thinking')
expect(models).not.toContain('claude-sonnet-4-6-chat')
expect(models.every((model) => !model.endsWith('-agentic') && !model.endsWith('-chat'))).toBe(true)
})
it('whitelist 模式会忽略通配符条目', () => {
const mapping = buildModelMappingObject('whitelist', ['claude-*', 'gemini-3.1-flash-image'], [])
expect(mapping).toEqual({
+191
View File
@@ -0,0 +1,191 @@
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin'
import type { KiroTokenInfo } from '@/api/admin/kiro'
export function useKiroOAuth() {
const appStore = useAppStore()
const { t } = useI18n()
const authUrl = ref('')
const sessionId = ref('')
const state = ref('')
const loading = ref(false)
const error = ref('')
const resetState = () => {
authUrl.value = ''
sessionId.value = ''
state.value = ''
loading.value = false
error.value = ''
}
const generateAuthUrl = async (
proxyId: number | null | undefined,
provider: 'Google' | 'Github' = 'Google'
): Promise<boolean> => {
loading.value = true
error.value = ''
authUrl.value = ''
sessionId.value = ''
state.value = ''
try {
const response = await adminAPI.kiro.generateAuthUrl({
proxy_id: proxyId || undefined,
provider
})
authUrl.value = response.auth_url
sessionId.value = response.session_id
state.value = response.state
return true
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.authFailed')
appStore.showError(error.value)
return false
} finally {
loading.value = false
}
}
const generateIDCAuthUrl = async (
params: { proxyId?: number | null; startUrl?: string; region?: string }
): Promise<boolean> => {
loading.value = true
error.value = ''
authUrl.value = ''
sessionId.value = ''
state.value = ''
try {
const response = await adminAPI.kiro.generateIDCAuthUrl({
proxy_id: params.proxyId || undefined,
start_url: params.startUrl,
region: params.region
})
authUrl.value = response.auth_url
sessionId.value = response.session_id
state.value = response.state
return true
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.authFailed')
appStore.showError(error.value)
return false
} finally {
loading.value = false
}
}
const exchangeAuthCode = async (params: {
code: string
sessionId: string
state: string
callbackPath?: string
loginOption?: string
proxyId?: number | null
}): Promise<KiroTokenInfo | null> => {
loading.value = true
error.value = ''
try {
return await adminAPI.kiro.exchangeCode({
session_id: params.sessionId,
state: params.state,
code: params.code.trim(),
callback_path: params.callbackPath,
login_option: params.loginOption,
proxy_id: params.proxyId || undefined
})
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.authFailed')
appStore.showError(error.value)
return null
} finally {
loading.value = false
}
}
const validateRefreshToken = async (payload: {
refreshToken: string
authMethod?: string
provider?: string
clientId?: string
clientSecret?: string
startUrl?: string
region?: string
profileArn?: string
proxyId?: number | null
}): Promise<KiroTokenInfo | null> => {
loading.value = true
error.value = ''
try {
return await adminAPI.kiro.refreshToken({
refresh_token: payload.refreshToken.trim(),
auth_method: payload.authMethod,
provider: payload.provider,
client_id: payload.clientId,
client_secret: payload.clientSecret,
start_url: payload.startUrl,
region: payload.region,
profile_arn: payload.profileArn,
proxy_id: payload.proxyId || undefined
})
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.authFailed')
return null
} finally {
loading.value = false
}
}
const importToken = async (
tokenJSON: string,
deviceRegistrationJSON?: string
): Promise<KiroTokenInfo | null> => {
loading.value = true
error.value = ''
try {
return await adminAPI.kiro.importToken({
token_json: tokenJSON,
device_registration_json: deviceRegistrationJSON
})
} catch (err: any) {
error.value = err.response?.data?.detail || t('admin.accounts.oauth.authFailed')
appStore.showError(error.value)
return null
} finally {
loading.value = false
}
}
const buildCredentials = (tokenInfo: KiroTokenInfo): Record<string, unknown> => ({
access_token: tokenInfo.access_token,
refresh_token: tokenInfo.refresh_token,
profile_arn: tokenInfo.profile_arn,
expires_at: tokenInfo.expires_at,
auth_method: tokenInfo.auth_method,
provider: tokenInfo.provider,
client_id: tokenInfo.client_id,
client_secret: tokenInfo.client_secret,
client_id_hash: tokenInfo.client_id_hash,
email: tokenInfo.email,
start_url: tokenInfo.start_url,
region: tokenInfo.region
})
return {
authUrl,
sessionId,
state,
loading,
error,
resetState,
generateAuthUrl,
generateIDCAuthUrl,
exchangeAuthCode,
validateRefreshToken,
importToken,
buildCredentials
}
}
+41 -2
View File
@@ -76,6 +76,19 @@ const antigravityModels = [
'tab_flash_lite_preview'
]
const kiroModels = [
'claude-opus-4-6',
'claude-opus-4-6-thinking',
'claude-sonnet-4-6',
'claude-sonnet-4-6-thinking',
'claude-opus-4-5-20251101',
'claude-opus-4-5-20251101-thinking',
'claude-sonnet-4-5-20250929',
'claude-sonnet-4-5-20250929-thinking',
'claude-haiku-4-5-20251001',
'claude-haiku-4-5-20251001-thinking'
]
// 智谱 GLM
const zhipuModels = [
'glm-4', 'glm-4v', 'glm-4-plus', 'glm-4-0520',
@@ -298,6 +311,19 @@ const antigravityPresetMappings = [
{ label: 'Opus 4.7', from: 'claude-opus-4-7', to: 'claude-opus-4-7', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' }
]
const kiroPresetMappings = [
{ label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'claude-opus-4.6', color: 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-300' },
{ label: 'Opus 4.6 Thinking', from: 'claude-opus-4-6-thinking', to: 'claude-opus-4.6', color: 'bg-yellow-100 text-yellow-700 hover:bg-yellow-200 dark:bg-yellow-900/30 dark:text-yellow-300' },
{ label: 'Sonnet 4.6', from: 'claude-sonnet-4-6', to: 'claude-sonnet-4.6', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-300' },
{ label: 'Sonnet 4.6 Thinking', from: 'claude-sonnet-4-6-thinking', to: 'claude-sonnet-4.6', color: 'bg-orange-100 text-orange-700 hover:bg-orange-200 dark:bg-orange-900/30 dark:text-orange-300' },
{ label: 'Opus 4.5', from: 'claude-opus-4-5-20251101', to: 'claude-opus-4.5', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-300' },
{ label: 'Opus 4.5 Thinking', from: 'claude-opus-4-5-20251101-thinking', to: 'claude-opus-4.5', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-300' },
{ label: 'Sonnet 4.5', from: 'claude-sonnet-4-5-20250929', to: 'claude-sonnet-4.5', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-300' },
{ label: 'Sonnet 4.5 Thinking', from: 'claude-sonnet-4-5-20250929-thinking', to: 'claude-sonnet-4.5', color: 'bg-cyan-100 text-cyan-700 hover:bg-cyan-200 dark:bg-cyan-900/30 dark:text-cyan-300' },
{ label: 'Haiku 4.5', from: 'claude-haiku-4-5-20251001', to: 'claude-haiku-4.5', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-300' },
{ label: 'Haiku 4.5 Thinking', from: 'claude-haiku-4-5-20251001-thinking', to: 'claude-haiku-4.5', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-300' }
]
// Bedrock 预设映射(与后端 DefaultBedrockModelMapping 保持一致)
const bedrockPresetMappings = [
{ label: 'Opus 4.6', from: 'claude-opus-4-6', to: 'us.anthropic.claude-opus-4-6-v1', color: 'bg-pink-100 text-pink-700 hover:bg-pink-200 dark:bg-pink-900/30 dark:text-pink-400' },
@@ -308,15 +334,18 @@ const bedrockPresetMappings = [
{ label: 'Haiku 4.5', from: 'claude-haiku-4-5', to: 'us.anthropic.claude-haiku-4-5-20251001-v1:0', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' },
]
const kiroDefaultMappings = kiroPresetMappings.map(({ from, to }) => ({ from, to }))
// Antigravity 默认映射(从后端 API 获取,与 constants.go 保持一致)
// 使用 fetchAntigravityDefaultMappings() 异步获取
import { getAntigravityDefaultModelMapping } from '@/api/admin/accounts'
let _antigravityDefaultMappingsCache: { from: string; to: string }[] | null = null
let _kiroDefaultMappingsCache: { from: string; to: string }[] | null = null
export async function fetchAntigravityDefaultMappings(): Promise<{ from: string; to: string }[]> {
if (_antigravityDefaultMappingsCache !== null) {
return _antigravityDefaultMappingsCache
return _antigravityDefaultMappingsCache.map(({ from, to }) => ({ from, to }))
}
try {
const mapping = await getAntigravityDefaultModelMapping()
@@ -325,7 +354,15 @@ export async function fetchAntigravityDefaultMappings(): Promise<{ from: string;
console.warn('[fetchAntigravityDefaultMappings] API failed, using empty fallback', e)
_antigravityDefaultMappingsCache = []
}
return _antigravityDefaultMappingsCache
return _antigravityDefaultMappingsCache.map(({ from, to }) => ({ from, to }))
}
export async function fetchKiroDefaultMappings(): Promise<{ from: string; to: string }[]> {
if (_kiroDefaultMappingsCache !== null) {
return _kiroDefaultMappingsCache.map(({ from, to }) => ({ from, to }))
}
_kiroDefaultMappingsCache = kiroDefaultMappings.map(({ from, to }) => ({ from, to }))
return _kiroDefaultMappingsCache.map(({ from, to }) => ({ from, to }))
}
// =====================
@@ -354,6 +391,7 @@ export function getModelsByPlatform(platform: string): string[] {
case 'claude': return claudeModels
case 'gemini': return geminiModels
case 'antigravity': return antigravityModels
case 'kiro': return kiroModels
case 'zhipu': return zhipuModels
case 'qwen': return qwenModels
case 'deepseek': return deepseekModels
@@ -378,6 +416,7 @@ export function getPresetMappingsByPlatform(platform: string) {
if (platform === 'openai') return openaiPresetMappings
if (platform === 'gemini') return geminiPresetMappings
if (platform === 'antigravity') return antigravityPresetMappings
if (platform === 'kiro') return kiroPresetMappings
if (platform === 'bedrock') return bedrockPresetMappings
return anthropicPresetMappings
}