|
|
@@ -1,91 +1,108 @@
|
|
|
+// 统一的 fetch 包装器:非 2xx 状态码自动抛出错误
|
|
|
+async function apiFetch(url: string, init?: RequestInit): Promise<Response> {
|
|
|
+ const res = await fetch(url, init)
|
|
|
+ if (!res.ok) {
|
|
|
+ try {
|
|
|
+ const err = await res.json()
|
|
|
+ throw new Error(err.detail || err.error || `Request failed: ${res.status}`)
|
|
|
+ } catch (e) {
|
|
|
+ if (e instanceof Error) throw e
|
|
|
+ throw new Error(`Request failed: ${res.status}`)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return res
|
|
|
+}
|
|
|
+
|
|
|
const api = {
|
|
|
// --- Health ---
|
|
|
- health: () => fetch('/health').then(r => r.json()),
|
|
|
+ health: () => apiFetch('/health').then(r => r.json()),
|
|
|
|
|
|
// --- Models ---
|
|
|
models: {
|
|
|
- list: () => fetch('/api/v1/models/').then(r => r.json()) as Promise<ModelInfo[]>,
|
|
|
+ list: () => apiFetch('/api/v1/models/').then(r => r.json()) as Promise<ModelInfo[]>,
|
|
|
download: (modelId: string, useModelscope = false) =>
|
|
|
- fetch('/api/v1/models/download', {
|
|
|
+ apiFetch('/api/v1/models/download', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({ model_id: modelId, use_modelscope: useModelscope }),
|
|
|
}).then(r => r.json()) as Promise<ModelDownloadResponse>,
|
|
|
+ delete: (modelId: string) =>
|
|
|
+ apiFetch(`/api/v1/models/${encodeURIComponent(modelId)}`, { method: 'DELETE' }).then(r => r.json()),
|
|
|
getInfo: (modelId: string) =>
|
|
|
- fetch(`/api/v1/models/${encodeURIComponent(modelId)}`).then(r => r.json()) as Promise<ModelInfo>,
|
|
|
+ apiFetch(`/api/v1/models/${encodeURIComponent(modelId)}`).then(r => r.json()) as Promise<ModelInfo>,
|
|
|
},
|
|
|
|
|
|
// --- Datasets ---
|
|
|
datasets: {
|
|
|
- list: () => fetch('/api/v1/datasets/').then(r => r.json()) as Promise<DatasetInfo[]>,
|
|
|
+ list: () => apiFetch('/api/v1/datasets/').then(r => r.json()) as Promise<DatasetInfo[]>,
|
|
|
upload: (file: File) => {
|
|
|
const form = new FormData()
|
|
|
form.append('file', file)
|
|
|
- return fetch('/api/v1/datasets/upload', { method: 'POST', body: form }).then(r => r.json()) as Promise<DatasetInfo>
|
|
|
+ return apiFetch('/api/v1/datasets/upload', { method: 'POST', body: form }).then(r => r.json()) as Promise<DatasetInfo>
|
|
|
},
|
|
|
download: (datasetId: string, useModelscope = false) =>
|
|
|
- fetch('/api/v1/datasets/download', {
|
|
|
+ apiFetch('/api/v1/datasets/download', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify({ dataset_id: datasetId, use_modelscope: useModelscope }),
|
|
|
}).then(r => r.json()) as Promise<DatasetDownloadResponse>,
|
|
|
preview: (id: string, rows = 10) =>
|
|
|
- fetch(`/api/v1/datasets/${id}/preview?rows=${rows}`).then(r => r.json()) as Promise<DatasetPreview>,
|
|
|
+ apiFetch(`/api/v1/datasets/${id}/preview?rows=${rows}`).then(r => r.json()) as Promise<DatasetPreview>,
|
|
|
validate: (id: string) =>
|
|
|
- fetch(`/api/v1/datasets/${id}/validate`, { method: 'POST' }).then(r => r.json()) as Promise<DatasetValidation>,
|
|
|
+ apiFetch(`/api/v1/datasets/${id}/validate`, { method: 'POST' }).then(r => r.json()) as Promise<DatasetValidation>,
|
|
|
delete: (id: string) =>
|
|
|
- fetch(`/api/v1/datasets/${id}`, { method: 'DELETE' }).then(r => r.json()),
|
|
|
+ apiFetch(`/api/v1/datasets/${id}`, { method: 'DELETE' }).then(r => r.json()),
|
|
|
},
|
|
|
|
|
|
// --- Training ---
|
|
|
training: {
|
|
|
- list: () => fetch('/api/v1/training/jobs').then(r => r.json()) as Promise<TrainingJob[]>,
|
|
|
+ list: () => apiFetch('/api/v1/training/jobs').then(r => r.json()) as Promise<TrainingJob[]>,
|
|
|
create: (cfg: TrainingConfig) =>
|
|
|
- fetch('/api/v1/training/jobs', {
|
|
|
+ apiFetch('/api/v1/training/jobs', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify(cfg),
|
|
|
}).then(r => r.json()) as Promise<TrainingJob>,
|
|
|
get: (id: string) =>
|
|
|
- fetch(`/api/v1/training/jobs/${id}`).then(r => r.json()) as Promise<TrainingJob>,
|
|
|
+ apiFetch(`/api/v1/training/jobs/${id}`).then(r => r.json()) as Promise<TrainingJob>,
|
|
|
cancel: (id: string) =>
|
|
|
- fetch(`/api/v1/training/jobs/${id}/cancel`, { method: 'POST' }).then(r => r.json()),
|
|
|
+ apiFetch(`/api/v1/training/jobs/${id}/cancel`, { method: 'POST' }).then(r => r.json()),
|
|
|
},
|
|
|
|
|
|
// --- Evaluation ---
|
|
|
evaluation: {
|
|
|
run: (cfg: EvalConfig) =>
|
|
|
- fetch('/api/v1/evaluation/run', {
|
|
|
+ apiFetch('/api/v1/evaluation/run', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify(cfg),
|
|
|
}).then(r => r.json()) as Promise<EvalResult>,
|
|
|
results: (id: string) =>
|
|
|
- fetch(`/api/v1/evaluation/${id}/results`).then(r => r.json()) as Promise<EvalResult>,
|
|
|
+ apiFetch(`/api/v1/evaluation/${id}/results`).then(r => r.json()) as Promise<EvalResult>,
|
|
|
},
|
|
|
|
|
|
// --- Deployment ---
|
|
|
deployment: {
|
|
|
export: (cfg: DeployConfig) =>
|
|
|
- fetch('/api/v1/deployment/export', {
|
|
|
+ apiFetch('/api/v1/deployment/export', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify(cfg),
|
|
|
}).then(r => r.json()) as Promise<DeployResponse>,
|
|
|
status: (id: string) =>
|
|
|
- fetch(`/api/v1/deployment/${id}/status`).then(r => r.json()) as Promise<DeployResponse>,
|
|
|
+ apiFetch(`/api/v1/deployment/${id}/status`).then(r => r.json()) as Promise<DeployResponse>,
|
|
|
},
|
|
|
|
|
|
// --- Inference ---
|
|
|
inference: {
|
|
|
generate: (req: InferenceRequest) =>
|
|
|
- fetch('/api/v1/inference/generate', {
|
|
|
+ apiFetch('/api/v1/inference/generate', {
|
|
|
method: 'POST',
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
body: JSON.stringify(req),
|
|
|
}).then(r => r.json()) as Promise<InferenceResponse>,
|
|
|
adapters: () =>
|
|
|
- fetch('/api/v1/inference/adapters').then(r => r.json()) as Promise<AdapterInfo[]>,
|
|
|
+ apiFetch('/api/v1/inference/adapters').then(r => r.json()) as Promise<AdapterInfo[]>,
|
|
|
},
|
|
|
}
|
|
|
|