mirror of
https://github.com/whit3rabbit/anyllm-proxy.git
synced 2026-09-22 00:00:50 +00:00
feat: add managed backend types, queries, and provider field schema
- Add CatalogProvider, ManagedBackend, CreateManagedBackendRequest, UpdateManagedBackendRequest to api/types.ts - Add useManagedBackends, useCreateManagedBackend, useUpdateManagedBackend, useDeleteManagedBackend hooks to api/queries.ts - Create utils/providerFields.ts with getProviderFields() mapping protocol+auth to FieldDef[] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0ecd5aeab6
commit
887661f6dc
+8
-8
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ import type {
|
||||
Backend, ConfigResponse, ObservabilityResponse,
|
||||
ModelsResponse, AuditResponse, TrafficResponse, UptimeResponse,
|
||||
EnvImportResponse, ProxyStatus, DiscoverResponse,
|
||||
ManagedBackend, CreateManagedBackendRequest, UpdateManagedBackendRequest,
|
||||
} from './types'
|
||||
|
||||
// ── Status ───────────────────────────────────────────────────────────────────
|
||||
@@ -232,6 +233,43 @@ export function useImportEnv() {
|
||||
})
|
||||
}
|
||||
|
||||
// ── Managed backends ──────────────────────────────────────────────────────────
|
||||
|
||||
export function useManagedBackends() {
|
||||
return useQuery<{ backends: ManagedBackend[] }>({
|
||||
queryKey: ['managed-backends'],
|
||||
queryFn: () => apiFetch('/admin/api/backends/managed'),
|
||||
staleTime: Infinity,
|
||||
})
|
||||
}
|
||||
|
||||
export function useCreateManagedBackend() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation<ManagedBackend, Error, CreateManagedBackendRequest>({
|
||||
mutationFn: (data) =>
|
||||
mutatingFetch<ManagedBackend>('POST', '/admin/api/backends/managed', data),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['managed-backends'] }) },
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateManagedBackend() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation<ManagedBackend, Error, { name: string; data: UpdateManagedBackendRequest }>({
|
||||
mutationFn: ({ name, data }) =>
|
||||
mutatingFetch<ManagedBackend>('PUT', `/admin/api/backends/managed/${name}`, data),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['managed-backends'] }) },
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeleteManagedBackend() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation<void, Error, string>({
|
||||
mutationFn: (name) =>
|
||||
mutatingFetch<void>('DELETE', `/admin/api/backends/managed/${name}`),
|
||||
onSuccess: () => { qc.invalidateQueries({ queryKey: ['managed-backends'] }) },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the current effective env as a .anyllm.env file.
|
||||
* Not a hook — call directly from an event handler.
|
||||
|
||||
@@ -239,6 +239,54 @@ export interface EnvImportError {
|
||||
warnings: EnvWarning[]
|
||||
}
|
||||
|
||||
// --- Provider catalog ---
|
||||
|
||||
export interface CatalogProvider {
|
||||
id: string
|
||||
name: string
|
||||
protocol: string
|
||||
auth: string
|
||||
default_base_url: string
|
||||
env_vars: string[]
|
||||
}
|
||||
|
||||
// --- Managed backends ---
|
||||
|
||||
export interface ManagedBackend {
|
||||
id: string
|
||||
name: string
|
||||
provider_id: string
|
||||
api_key_set: boolean
|
||||
aws_creds_set: boolean
|
||||
api_base: string | null
|
||||
deployment: string | null
|
||||
api_version: string | null
|
||||
project: string | null
|
||||
region: string | null
|
||||
rpm: number | null
|
||||
tpm: number | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface CreateManagedBackendRequest {
|
||||
name: string
|
||||
provider_id: string
|
||||
api_key?: string
|
||||
api_base?: string
|
||||
deployment?: string
|
||||
api_version?: string
|
||||
project?: string
|
||||
region?: string
|
||||
aws_access_key_id?: string
|
||||
aws_secret_access_key?: string
|
||||
aws_session_token?: string
|
||||
rpm?: number
|
||||
tpm?: number
|
||||
}
|
||||
|
||||
export type UpdateManagedBackendRequest = Partial<Omit<CreateManagedBackendRequest, 'name'>>
|
||||
|
||||
// --- WebSocket events ---
|
||||
|
||||
export type WSEvent =
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import { CatalogProvider } from '../api/types'
|
||||
|
||||
export type FieldGroup = 'auth' | 'endpoint' | 'limits'
|
||||
|
||||
export interface FieldDef {
|
||||
name: string
|
||||
label: string
|
||||
type: 'text' | 'password' | 'url' | 'number'
|
||||
required: boolean
|
||||
placeholder?: string
|
||||
hint?: string
|
||||
group: FieldGroup
|
||||
}
|
||||
|
||||
export function getProviderFields(provider: CatalogProvider): FieldDef[] {
|
||||
const fields: FieldDef[] = []
|
||||
const firstEnvVar = provider.env_vars.length > 0 ? provider.env_vars[0] : null
|
||||
|
||||
const { protocol, auth, default_base_url } = provider
|
||||
|
||||
if (protocol === 'openai_compat' && auth === 'bearer') {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
...(firstEnvVar ? { hint: `or set ${firstEnvVar} env var` } : {}),
|
||||
})
|
||||
fields.push({
|
||||
name: 'api_base',
|
||||
label: 'API Base URL',
|
||||
type: 'url',
|
||||
required: false,
|
||||
group: 'endpoint',
|
||||
...(default_base_url ? { placeholder: default_base_url } : {}),
|
||||
})
|
||||
} else if (protocol === 'openai_compat' && auth === 'none') {
|
||||
fields.push({
|
||||
name: 'api_base',
|
||||
label: 'API Base URL',
|
||||
type: 'url',
|
||||
required: false,
|
||||
group: 'endpoint',
|
||||
...(default_base_url ? { placeholder: default_base_url } : {}),
|
||||
})
|
||||
} else if (protocol === 'azure_openai' && auth === 'azure_api_key') {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'Azure API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
})
|
||||
fields.push({
|
||||
name: 'api_base',
|
||||
label: 'Endpoint URL',
|
||||
type: 'url',
|
||||
required: true,
|
||||
placeholder: 'https://<resource>.openai.azure.com',
|
||||
group: 'endpoint',
|
||||
})
|
||||
fields.push({
|
||||
name: 'deployment',
|
||||
label: 'Deployment Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
group: 'endpoint',
|
||||
})
|
||||
fields.push({
|
||||
name: 'api_version',
|
||||
label: 'API Version',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: '2024-08-01-preview',
|
||||
group: 'endpoint',
|
||||
})
|
||||
} else if (protocol === 'vertex_ai' && auth === 'google_api_key') {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
})
|
||||
fields.push({
|
||||
name: 'project',
|
||||
label: 'GCP Project ID',
|
||||
type: 'text',
|
||||
required: true,
|
||||
group: 'endpoint',
|
||||
})
|
||||
fields.push({
|
||||
name: 'region',
|
||||
label: 'GCP Region',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'us-central1',
|
||||
group: 'endpoint',
|
||||
})
|
||||
} else if (protocol === 'bedrock_native' && auth === 'aws_sigv4') {
|
||||
fields.push({
|
||||
name: 'aws_access_key_id',
|
||||
label: 'AWS Access Key ID',
|
||||
type: 'text',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
})
|
||||
fields.push({
|
||||
name: 'aws_secret_access_key',
|
||||
label: 'AWS Secret Access Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
})
|
||||
fields.push({
|
||||
name: 'aws_session_token',
|
||||
label: 'AWS Session Token',
|
||||
type: 'password',
|
||||
required: false,
|
||||
group: 'auth',
|
||||
})
|
||||
fields.push({
|
||||
name: 'region',
|
||||
label: 'AWS Region',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'us-east-1',
|
||||
group: 'endpoint',
|
||||
})
|
||||
} else if ((protocol === 'gemini_openai' || protocol === 'gemini_native') && auth === 'google_api_key') {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
...(firstEnvVar ? { hint: `or set ${firstEnvVar} env var` } : {}),
|
||||
})
|
||||
} else if (protocol === 'anthropic_native' && auth === 'bearer') {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
...(firstEnvVar ? { hint: `or set ${firstEnvVar} env var` } : {}),
|
||||
})
|
||||
} else {
|
||||
// Fallback
|
||||
if (auth.includes('bearer')) {
|
||||
fields.push({
|
||||
name: 'api_key',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
group: 'auth',
|
||||
})
|
||||
}
|
||||
if (auth === 'none' || auth.includes('bearer')) {
|
||||
fields.push({
|
||||
name: 'api_base',
|
||||
label: 'API Base URL',
|
||||
type: 'url',
|
||||
required: false,
|
||||
group: 'endpoint',
|
||||
...(default_base_url ? { placeholder: default_base_url } : {}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// All providers get rate/token limits
|
||||
fields.push({
|
||||
name: 'rpm',
|
||||
label: 'Rate Limit (req/min)',
|
||||
type: 'number',
|
||||
required: false,
|
||||
group: 'limits',
|
||||
})
|
||||
fields.push({
|
||||
name: 'tpm',
|
||||
label: 'Token Limit (tokens/min)',
|
||||
type: 'number',
|
||||
required: false,
|
||||
group: 'limits',
|
||||
})
|
||||
|
||||
return fields
|
||||
}
|
||||
Reference in New Issue
Block a user