feat(web): chat + llmAdmin api clients with FormData support

This commit is contained in:
2026-05-04 19:48:26 +08:00
parent 8effeee063
commit 00daad07a9
3 changed files with 298 additions and 2 deletions
+122
View File
@@ -0,0 +1,122 @@
import { request } from './client'
export type LLMProvider = 'anthropic' | 'openai' | 'gemini'
export interface LLMEndpoint {
id: string
provider: LLMProvider
display_name: string
base_url: string
created_at: string
updated_at: string
}
export interface ModelCapabilities {
image: boolean
pdf: boolean
audio: boolean
reasoning: boolean
tools: boolean
}
export interface LLMModel {
id: string
endpoint_id: string
model_id: string
display_name: string
capabilities: ModelCapabilities
context_window: number
max_output_tokens: number
prompt_price_per_million_usd: number
completion_price_per_million_usd: number
thinking_price_per_million_usd: number
is_title_generator: boolean
enabled: boolean
sort_order: number
created_at: string
updated_at: string
}
export interface CreateEndpointBody {
provider: LLMProvider
display_name: string
base_url: string
api_key: string
}
export interface UpdateEndpointBody {
display_name?: string
base_url?: string
api_key?: string
}
export interface CreateModelBody {
endpoint_id: string
model_id: string
display_name: string
capabilities?: Partial<ModelCapabilities>
context_window: number
max_output_tokens: number
prompt_price_per_million_usd?: number
completion_price_per_million_usd?: number
thinking_price_per_million_usd?: number
is_title_generator?: boolean
enabled?: boolean
sort_order?: number
}
export type UpdateModelBody = Partial<Omit<CreateModelBody, 'endpoint_id'>>
export type AdminUsageGroupBy = 'user' | 'model' | 'day'
export interface AdminUsageItem {
key: string
prompt_tokens: number
completion_tokens: number
thinking_tokens: number
cost_usd: number
}
const enc = (v: string) => encodeURIComponent(v)
function buildQuery(params: object): string {
const q = new URLSearchParams()
for (const [k, v] of Object.entries(params)) {
if (v === undefined || v === null || v === '') continue
q.set(k, String(v))
}
const s = q.toString()
return s ? '?' + s : ''
}
export const llmAdminApi = {
// ===== Endpoints =====
listEndpoints: () =>
request<{ items: LLMEndpoint[] }>('/api/v1/admin/llm-endpoints').then((r) => r.items),
createEndpoint: (body: CreateEndpointBody) =>
request<LLMEndpoint>('/api/v1/admin/llm-endpoints', { method: 'POST', body }),
updateEndpoint: (id: string, body: UpdateEndpointBody) =>
request<void>(`/api/v1/admin/llm-endpoints/${enc(id)}`, { method: 'PATCH', body }),
deleteEndpoint: (id: string) =>
request<void>(`/api/v1/admin/llm-endpoints/${enc(id)}`, { method: 'DELETE' }),
testEndpoint: (id: string) =>
request<void>(`/api/v1/admin/llm-endpoints/${enc(id)}/test`, { method: 'POST' }),
// ===== Models =====
listModels: (onlyEnabled = false) =>
request<{ items: LLMModel[] }>(
`/api/v1/admin/llm-models${buildQuery({ only_enabled: onlyEnabled })}`,
).then((r) => r.items),
createModel: (body: CreateModelBody) =>
request<LLMModel>('/api/v1/admin/llm-models', { method: 'POST', body }),
updateModel: (id: string, body: UpdateModelBody) =>
request<void>(`/api/v1/admin/llm-models/${enc(id)}`, { method: 'PATCH', body }),
deleteModel: (id: string) =>
request<void>(`/api/v1/admin/llm-models/${enc(id)}`, { method: 'DELETE' }),
// ===== Usage =====
adminUsage: (from: string, to: string, groupBy: AdminUsageGroupBy) =>
request<{ items: AdminUsageItem[] }>(
`/api/v1/admin/usage${buildQuery({ from, to, group_by: groupBy })}`,
),
}