Refactor form components and improve API error handling

This commit is contained in:
2025-09-21 21:33:53 +02:00
parent b665194307
commit 501afb7f0d
16 changed files with 663 additions and 184 deletions

View File

@@ -1,4 +1,5 @@
import type { CreateInstanceOptions, Instance } from "@/types/instance";
import { handleApiError } from "./errorUtils";
const API_BASE = "/api/v1";
@@ -30,25 +31,8 @@ async function apiCall<T>(
headers,
});
// Handle authentication errors
if (response.status === 401) {
throw new Error('Authentication required');
}
if (!response.ok) {
// Try to get error message from response
let errorMessage = `HTTP ${response.status}`;
try {
const errorText = await response.text();
if (errorText) {
errorMessage += `: ${errorText}`;
}
} catch {
// If we can't read the error, just use status
}
throw new Error(errorMessage);
}
// Handle errors using centralized error handler
await handleApiError(response);
// Handle empty responses (like DELETE)
if (response.status === 204) {

View File

@@ -0,0 +1,32 @@
/**
* Parses error response from API calls and returns a formatted error message
*/
export async function parseErrorResponse(response: Response): Promise<string> {
let errorMessage = `HTTP ${response.status}`
try {
const errorText = await response.text()
if (errorText) {
errorMessage += `: ${errorText}`
}
} catch {
// If we can't read the error, just use status
}
return errorMessage
}
/**
* Handles common API call errors and throws appropriate Error objects
*/
export async function handleApiError(response: Response): Promise<void> {
// Handle authentication errors
if (response.status === 401) {
throw new Error('Authentication required')
}
if (!response.ok) {
const errorMessage = await parseErrorResponse(response)
throw new Error(errorMessage)
}
}

View File

@@ -20,7 +20,6 @@ export const basicFieldsConfig: Record<string, {
label: string
description?: string
placeholder?: string
required?: boolean
}> = {
auto_restart: {
label: 'Auto Restart',
@@ -56,13 +55,11 @@ const basicLlamaCppFieldsConfig: Record<string, {
label: string
description?: string
placeholder?: string
required?: boolean
}> = {
model: {
label: 'Model Path',
placeholder: '/path/to/model.gguf',
description: 'Path to the model file',
required: true
description: 'Path to the model file'
},
hf_repo: {
label: 'Hugging Face Repository',
@@ -86,13 +83,11 @@ const basicMlxFieldsConfig: Record<string, {
label: string
description?: string
placeholder?: string
required?: boolean
}> = {
model: {
label: 'Model',
placeholder: 'mlx-community/Mistral-7B-Instruct-v0.3-4bit',
description: 'The path to the MLX model weights, tokenizer, and config',
required: true
description: 'The path to the MLX model weights, tokenizer, and config'
},
temp: {
label: 'Temperature',
@@ -126,13 +121,11 @@ const basicVllmFieldsConfig: Record<string, {
label: string
description?: string
placeholder?: string
required?: boolean
}> = {
model: {
label: 'Model',
placeholder: 'microsoft/DialoGPT-medium',
description: 'The name or path of the Hugging Face model to use',
required: true
description: 'The name or path of the Hugging Face model to use'
},
tensor_parallel_size: {
label: 'Tensor Parallel Size',
@@ -146,11 +139,23 @@ const basicVllmFieldsConfig: Record<string, {
}
}
// Backend field configuration lookup
const backendFieldConfigs = {
mlx_lm: basicMlxFieldsConfig,
vllm: basicVllmFieldsConfig,
llama_cpp: basicLlamaCppFieldsConfig,
} as const
const backendFieldGetters = {
mlx_lm: getAllMlxFieldKeys,
vllm: getAllVllmFieldKeys,
llama_cpp: getAllLlamaCppFieldKeys,
} as const
function isBasicField(key: keyof CreateInstanceOptions): boolean {
return key in basicFieldsConfig
}
export function getBasicFields(): (keyof CreateInstanceOptions)[] {
return Object.keys(basicFieldsConfig) as (keyof CreateInstanceOptions)[]
}
@@ -159,29 +164,18 @@ export function getAdvancedFields(): (keyof CreateInstanceOptions)[] {
return getAllFieldKeys().filter(key => !isBasicField(key))
}
export function getBasicBackendFields(backendType?: string): string[] {
if (backendType === 'mlx_lm') {
return Object.keys(basicMlxFieldsConfig)
} else if (backendType === 'vllm') {
return Object.keys(basicVllmFieldsConfig)
} else if (backendType === 'llama_cpp') {
return Object.keys(basicLlamaCppFieldsConfig)
}
// Default to LlamaCpp for backward compatibility
return Object.keys(basicLlamaCppFieldsConfig)
const normalizedType = (backendType || 'llama_cpp') as keyof typeof backendFieldConfigs
const config = backendFieldConfigs[normalizedType] || basicLlamaCppFieldsConfig
return Object.keys(config)
}
export function getAdvancedBackendFields(backendType?: string): string[] {
if (backendType === 'mlx_lm') {
return getAllMlxFieldKeys().filter(key => !(key in basicMlxFieldsConfig))
} else if (backendType === 'vllm') {
return getAllVllmFieldKeys().filter(key => !(key in basicVllmFieldsConfig))
} else if (backendType === 'llama_cpp') {
return getAllLlamaCppFieldKeys().filter(key => !(key in basicLlamaCppFieldsConfig))
}
// Default to LlamaCpp for backward compatibility
return getAllLlamaCppFieldKeys().filter(key => !(key in basicLlamaCppFieldsConfig))
const normalizedType = (backendType || 'llama_cpp') as keyof typeof backendFieldGetters
const fieldGetter = backendFieldGetters[normalizedType] || getAllLlamaCppFieldKeys
const basicConfig = backendFieldConfigs[normalizedType] || basicLlamaCppFieldsConfig
return fieldGetter().filter(key => !(key in basicConfig))
}
// Combined backend fields config for use in BackendFormField
@@ -189,7 +183,6 @@ export const basicBackendFieldsConfig: Record<string, {
label: string
description?: string
placeholder?: string
required?: boolean
}> = {
...basicLlamaCppFieldsConfig,
...basicMlxFieldsConfig,