mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Support per-format provider key auth
This commit is contained in:
@@ -173,6 +173,7 @@ export interface ProviderKeyExport {
|
||||
rate_multipliers?: Record<string, number> | null
|
||||
internal_priority?: number
|
||||
global_priority_by_format?: Record<string, number> | null
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
rpm_limit?: number | null
|
||||
allowed_models?: string[] | null
|
||||
capabilities?: Record<string, boolean>
|
||||
|
||||
@@ -138,7 +138,8 @@ export async function addProviderKey(
|
||||
data: {
|
||||
api_formats: string[] // 支持的 API 格式列表(必填)
|
||||
api_key: string
|
||||
auth_type?: 'api_key' | 'service_account' | 'oauth' // 认证类型
|
||||
auth_type?: 'api_key' | 'service_account' | 'oauth' | 'bearer' // 认证类型
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
auth_config?: Record<string, unknown> // 认证配置(Vertex AI Service Account JSON)
|
||||
name: string
|
||||
rate_multipliers?: Record<string, number> | null // 按 API 格式的成本倍率
|
||||
@@ -167,6 +168,7 @@ export async function updateProviderKey(
|
||||
api_formats: string[] // 支持的 API 格式列表
|
||||
api_key: string
|
||||
auth_type: 'api_key' | 'service_account' | 'oauth' | 'bearer' // 认证类型
|
||||
auth_type_by_format: Record<string, 'api_key' | 'bearer'> | null
|
||||
auth_config: Record<string, unknown> // 认证配置(Vertex AI Service Account JSON)
|
||||
name: string
|
||||
rate_multipliers: Record<string, number> | null // 按 API 格式的成本倍率
|
||||
|
||||
@@ -104,8 +104,9 @@ export interface PoolKeyDetail {
|
||||
key_name: string
|
||||
is_active: boolean
|
||||
auth_type: string
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
credential_kind?: 'raw_secret' | 'oauth_session' | 'service_account' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'unknown' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'mixed' | 'unknown' | string | null
|
||||
oauth_managed?: boolean
|
||||
can_refresh_oauth?: boolean
|
||||
can_export_oauth?: boolean
|
||||
@@ -206,8 +207,9 @@ export interface PoolKeySelectionItem {
|
||||
key_id: string
|
||||
key_name: string
|
||||
auth_type: string
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
credential_kind?: 'raw_secret' | 'oauth_session' | 'service_account' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'unknown' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'mixed' | 'unknown' | string | null
|
||||
oauth_managed?: boolean
|
||||
can_refresh_oauth?: boolean
|
||||
can_export_oauth?: boolean
|
||||
|
||||
@@ -9,47 +9,51 @@ import {
|
||||
sortApiFormats,
|
||||
} from '@/api/endpoints/types'
|
||||
|
||||
const openaiAlias = (kind: string) => ['openai', kind].join(':')
|
||||
const legacyEnumAlias = (kind: string) => ['OPENAI', kind].join('_')
|
||||
|
||||
describe('api format display helpers', () => {
|
||||
it('maps historical OpenAI response aliases to current display names', () => {
|
||||
expect(normalizeApiFormatAlias(openaiAlias('cli'))).toBe(API_FORMATS.OPENAI_RESPONSES)
|
||||
expect(formatApiFormat(openaiAlias('cli'))).toBe('OpenAI Responses')
|
||||
expect(formatApiFormatShort(openaiAlias('cli'))).toBe('OR')
|
||||
|
||||
expect(normalizeApiFormatAlias(openaiAlias('compact'))).toBe(API_FORMATS.OPENAI_RESPONSES_COMPACT)
|
||||
expect(formatApiFormat(openaiAlias('compact'))).toBe('OpenAI Responses Compact')
|
||||
expect(formatApiFormatShort(openaiAlias('compact'))).toBe('ORC')
|
||||
it('normalizes current enum-style names to canonical api format ids', () => {
|
||||
expect(normalizeApiFormatAlias('CLAUDE_MESSAGES')).toBe(API_FORMATS.CLAUDE_MESSAGES)
|
||||
expect(normalizeApiFormatAlias('OPENAI_RESPONSES')).toBe(API_FORMATS.OPENAI_RESPONSES)
|
||||
expect(normalizeApiFormatAlias('OPENAI_RESPONSES_COMPACT')).toBe(API_FORMATS.OPENAI_RESPONSES_COMPACT)
|
||||
expect(normalizeApiFormatAlias('GEMINI_GENERATE_CONTENT')).toBe(API_FORMATS.GEMINI_GENERATE_CONTENT)
|
||||
})
|
||||
|
||||
it('maps historical uppercase enum aliases to current display names', () => {
|
||||
expect(normalizeApiFormatAlias(legacyEnumAlias('CLI'))).toBe(API_FORMATS.OPENAI_RESPONSES)
|
||||
expect(formatApiFormat(legacyEnumAlias('CLI'))).toBe('OpenAI Responses')
|
||||
expect(formatApiFormatShort(legacyEnumAlias('CLI'))).toBe('OR')
|
||||
it('does not remap retired api format ids', () => {
|
||||
expect(normalizeApiFormatAlias('openai:cli')).toBe('openai:cli')
|
||||
expect(formatApiFormat('openai:cli')).toBe('openai:cli')
|
||||
expect(formatApiFormatShort('openai:cli')).toBe('op')
|
||||
|
||||
expect(normalizeApiFormatAlias(legacyEnumAlias('COMPACT'))).toBe(API_FORMATS.OPENAI_RESPONSES_COMPACT)
|
||||
expect(formatApiFormat(legacyEnumAlias('COMPACT'))).toBe('OpenAI Responses Compact')
|
||||
expect(formatApiFormatShort(legacyEnumAlias('COMPACT'))).toBe('ORC')
|
||||
expect(normalizeApiFormatAlias('openai:compact')).toBe('openai:compact')
|
||||
expect(formatApiFormat('openai:compact')).toBe('openai:compact')
|
||||
expect(formatApiFormatShort('openai:compact')).toBe('op')
|
||||
})
|
||||
|
||||
it('sorts historical aliases in the same slot as their current formats', () => {
|
||||
it('does not remap retired enum-style aliases', () => {
|
||||
expect(normalizeApiFormatAlias('OPENAI_CLI')).toBe('openai_cli')
|
||||
expect(formatApiFormat('OPENAI_CLI')).toBe('openai_cli')
|
||||
expect(formatApiFormatShort('OPENAI_CLI')).toBe('op')
|
||||
|
||||
expect(normalizeApiFormatAlias('OPENAI_COMPACT')).toBe('openai_compact')
|
||||
expect(formatApiFormat('OPENAI_COMPACT')).toBe('openai_compact')
|
||||
expect(formatApiFormatShort('OPENAI_COMPACT')).toBe('op')
|
||||
})
|
||||
|
||||
it('sorts only current canonical formats into known slots', () => {
|
||||
expect(sortApiFormats([
|
||||
openaiAlias('compact'),
|
||||
'openai:compact',
|
||||
API_FORMATS.OPENAI,
|
||||
openaiAlias('cli'),
|
||||
API_FORMATS.OPENAI_RESPONSES,
|
||||
])).toEqual([
|
||||
API_FORMATS.OPENAI,
|
||||
openaiAlias('cli'),
|
||||
openaiAlias('compact'),
|
||||
API_FORMATS.OPENAI_RESPONSES,
|
||||
'openai:compact',
|
||||
])
|
||||
})
|
||||
|
||||
it('groups uppercase historical aliases under OpenAI', () => {
|
||||
expect(groupApiFormats([legacyEnumAlias('CLI')])).toEqual([{
|
||||
family: 'openai',
|
||||
label: 'OpenAI',
|
||||
formats: [legacyEnumAlias('CLI')],
|
||||
it('groups retired enum-style aliases as unknown raw families', () => {
|
||||
expect(groupApiFormats(['OPENAI_CLI'])).toEqual([{
|
||||
family: 'openai_cli',
|
||||
label: 'openai_cli',
|
||||
formats: ['OPENAI_CLI'],
|
||||
}])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ export const API_FORMATS = {
|
||||
|
||||
export type APIFormat = typeof API_FORMATS[keyof typeof API_FORMATS]
|
||||
|
||||
// API 格式显示名称映射(按品牌分组:Chat 在前,CLI/Video 在后)
|
||||
// API 格式显示名称映射(按品牌分组)
|
||||
export const API_FORMAT_LABELS: Record<string, string> = {
|
||||
[API_FORMATS.CLAUDE_MESSAGES]: 'Claude Messages',
|
||||
[API_FORMATS.OPENAI]: 'OpenAI Chat',
|
||||
@@ -87,10 +87,8 @@ export const API_FORMAT_FAMILY_LABELS: Record<string, string> = {
|
||||
// Kind 显示名称映射
|
||||
export const API_FORMAT_KIND_LABELS: Record<string, string> = {
|
||||
chat: 'Chat',
|
||||
cli: 'CLI',
|
||||
responses: 'Responses',
|
||||
'responses:compact': 'Responses Compact',
|
||||
compact: 'Compact',
|
||||
messages: 'Messages',
|
||||
generate_content: 'Generate Content',
|
||||
image: 'Image',
|
||||
@@ -110,6 +108,8 @@ export function parseApiFormat(format: string): { family: string; kind: string }
|
||||
|
||||
export function normalizeApiFormatAlias(format: string | null | undefined): string {
|
||||
const raw = format?.trim() ?? ''
|
||||
// Only normalize current enum-style frontend constants. Retired API format ids
|
||||
// are migrated in the database and intentionally do not map at runtime.
|
||||
switch (raw.toUpperCase()) {
|
||||
case 'CLAUDE':
|
||||
case 'CLAUDE_MESSAGES':
|
||||
|
||||
@@ -230,8 +230,9 @@ export interface EndpointAPIKey {
|
||||
api_key_masked: string
|
||||
api_key_plain?: string | null
|
||||
auth_type: 'api_key' | 'service_account' | 'oauth' | 'bearer' // 认证类型(必返回)
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
credential_kind?: 'raw_secret' | 'oauth_session' | 'service_account' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'unknown' | string | null
|
||||
runtime_auth_kind?: 'api_key' | 'bearer' | 'service_account' | 'mixed' | 'unknown' | string | null
|
||||
oauth_managed?: boolean
|
||||
can_refresh_oauth?: boolean
|
||||
can_export_oauth?: boolean
|
||||
@@ -384,7 +385,8 @@ export interface EndpointAPIKeyUpdate {
|
||||
api_formats?: string[] // 支持的 API 格式列表
|
||||
name?: string
|
||||
api_key?: string // 仅在需要更新时提供
|
||||
auth_type?: 'api_key' | 'service_account' | 'oauth' // 认证类型
|
||||
auth_type?: 'api_key' | 'service_account' | 'oauth' | 'bearer' // 认证类型
|
||||
auth_type_by_format?: Record<string, 'api_key' | 'bearer'> | null
|
||||
auth_config?: Record<string, unknown> // 认证配置(Vertex AI Service Account JSON)
|
||||
rate_multipliers?: Record<string, number> | null // 按 API 格式的成本倍率
|
||||
internal_priority?: number
|
||||
|
||||
Reference in New Issue
Block a user