|
|
|
|
|
const ClientLLM = (apiKey) => { |
|
let abortController = null; |
|
let isCancelled = false; |
|
|
|
const createResult = (ok, response = null, error = null) => { |
|
return { |
|
ok, |
|
response, |
|
error, |
|
}; |
|
}; |
|
|
|
const createError = (message, type, code, details) => ({ |
|
message: message || null, |
|
type: type || null, |
|
code: code || null, |
|
details: { |
|
message: details?.message || null, |
|
type: details?.type || null, |
|
param: details?.param || null, |
|
code: details?.code || null, |
|
}, |
|
}); |
|
|
|
const handleHttpError = async (response) => { |
|
const errorMessages = { |
|
400: "Richiesta non valida", |
|
401: "Non autorizzato - Controlla la API key", |
|
403: "Accesso negato", |
|
404: "Endpoint non trovato", |
|
429: "Troppe richieste - Rate limit superato", |
|
500: "Errore interno del server", |
|
503: "Servizio non disponibile", |
|
}; |
|
|
|
let detailsContent; |
|
let errorType = "HTTPError"; |
|
let message = errorMessages[response.status] || `Errore HTTP ${response.status}`; |
|
|
|
try { |
|
if (response.headers.get("Content-Type")?.includes("application/json")) { |
|
detailsContent = await response.json(); |
|
if (response.status === 400 && detailsContent) { |
|
const errorMsg = typeof detailsContent.error === "string" ? detailsContent.error : detailsContent.message || detailsContent.error?.message; |
|
if ( |
|
errorMsg && |
|
(errorMsg.includes("token limit") || errorMsg.includes("token exceeded") || errorMsg.includes("input too long") || errorMsg.includes("context length") || errorMsg.includes("max tokens")) |
|
) { |
|
message = "Input troppo lungo - Superato il limite di token"; |
|
errorType = "TokenLimitError"; |
|
} |
|
} |
|
} else { |
|
detailsContent = await response.text(); |
|
if (response.status === 400 && (detailsContent.includes("token limit") || detailsContent.includes("input too long") || detailsContent.includes("context length"))) { |
|
message = "Input troppo lungo - Superato il limite di token"; |
|
errorType = "TokenLimitError"; |
|
} |
|
} |
|
} catch (e) { |
|
detailsContent = { message: "Impossibile estrarre i dettagli dell'errore" }; |
|
} |
|
return createError(message, errorType, response.status, typeof detailsContent === "string" ? { message: detailsContent } : detailsContent); |
|
}; |
|
|
|
const handleNetworkError = (error) => { |
|
if (error.name === "AbortError") { |
|
if (isCancelled) { |
|
return createError("Richiesta annullata dall'utente", "CancellationError", 499, { message: "La richiesta è stata interrotta volontariamente dall'utente" }); |
|
} else { |
|
return createError("Richiesta interrotta per timeout", "TimeoutError", 408, { message: "La richiesta è stata interrotta a causa di un timeout", isTimeout: true }); |
|
} |
|
} |
|
if (error.name === "TypeError" && error.message.includes("Failed to fetch")) { |
|
return createError("Errore di rete", "NetworkError", 0, { message: "Impossibile raggiungere il server. Controlla la connessione." }); |
|
} |
|
return createError("Errore imprevisto", error.name || "UnknownError", 500, { message: error.message || "Si è verificato un errore sconosciuto" }); |
|
}; |
|
|
|
const sendRequest = async (url, payload, requestTimeout = 60) => { |
|
isCancelled = false; |
|
abortController = new AbortController(); |
|
const actualTimeoutMs = requestTimeout * 1000; |
|
|
|
const timeoutId = setTimeout(() => { |
|
if (abortController) { |
|
abortController.abort(); |
|
} |
|
}, actualTimeoutMs); |
|
|
|
try { |
|
const response = await fetch(url, { |
|
method: "POST", |
|
headers: { |
|
Authorization: `Bearer ${apiKey}`, |
|
"Content-Type": "application/json", |
|
}, |
|
body: JSON.stringify(payload), |
|
signal: abortController.signal, |
|
}); |
|
|
|
if (isCancelled) { |
|
const cancelledError = createError("Richiesta annullata", "CancellationError", 499, { message: "La richiesta è stata interrotta volontariamente dall'utente" }); |
|
return createResult(false, null, cancelledError); |
|
} |
|
if (!response.ok) { |
|
console.error("error ok=false\n", response); |
|
const err = await handleHttpError(response); |
|
return createResult(false, null, err); |
|
} |
|
const respJson = await response.json(); |
|
return createResult(true, respJson, null); |
|
} catch (error) { |
|
const err = handleNetworkError(error); |
|
console.error("error network:\n", error); |
|
return createResult(false, null, err); |
|
} finally { |
|
clearTimeout(timeoutId); |
|
abortController = null; |
|
} |
|
}; |
|
|
|
const cancelRequest = () => { |
|
isCancelled = true; |
|
if (abortController) { |
|
abortController.abort(); |
|
abortController = null; |
|
return true; |
|
} |
|
return false; |
|
}; |
|
|
|
return { |
|
sendRequest, |
|
createError, |
|
cancelRequest, |
|
}; |
|
}; |
|
|