call-ai
Version:
Lightweight library for making AI API calls with streaming support
144 lines • 6.68 kB
JavaScript
import { keyStore, globalDebug, isNewKeyError, refreshApiKey } from "./key-management.js";
import { CallAIError } from "./types.js";
async function handleApiError(ierror, context, debug = globalDebug, options = {}) {
const error = ierror;
const errorMessage = error?.message || String(error);
const status = error?.status ||
error?.statusCode ||
error?.response?.status ||
(errorMessage.match(/status: (\d+)/i)?.[1] && parseInt(errorMessage.match(/status: (\d+)/i)?.[1] ?? "500"));
const isMissingKeyError = errorMessage.includes("API key is required");
if (debug) {
console.error(`[callAi:error] ${context} error:`, {
message: errorMessage,
status,
name: error?.name,
cause: error?.cause,
isMissingKey: isMissingKeyError,
});
}
if (options.skipRefresh) {
throw error;
}
const needsNewKey = isNewKeyError(error, debug) || isMissingKeyError;
if (needsNewKey) {
if (debug) {
console.log(`[callAi:key-refresh] Error suggests API key issue, attempting refresh...`);
}
try {
const currentKey = options.apiKey || keyStore().current;
const endpoint = options.endpoint || keyStore().refreshEndpoint;
const refreshToken = options.refreshToken || keyStore().refreshToken;
try {
const { apiKey, topup } = await refreshApiKey(options, currentKey, endpoint, refreshToken, debug);
if (keyStore().current !== apiKey) {
keyStore().current = apiKey;
}
if (debug) {
console.log(`[callAi:key-refresh] ${topup ? "Topped up" : "Refreshed"} API key successfully`);
}
return;
}
catch (initialRefreshError) {
if (options.updateRefreshToken && refreshToken) {
if (debug) {
console.log(`[callAi:key-refresh] Initial refresh failed, attempting to update refresh token`);
}
try {
const newRefreshToken = await options.updateRefreshToken(refreshToken);
if (newRefreshToken && newRefreshToken !== refreshToken) {
if (debug) {
console.log(`[callAi:key-refresh] Got new refresh token, retrying key refresh`);
}
keyStore().refreshToken = newRefreshToken;
const { apiKey, topup } = await refreshApiKey(options, currentKey, endpoint, newRefreshToken, debug);
if (keyStore().current !== apiKey) {
keyStore().current = apiKey;
}
if (debug) {
console.log(`[callAi:key-refresh] ${topup ? "Topped up" : "Refreshed"} API key successfully with new refresh token`);
}
return;
}
else {
if (debug) {
console.log(`[callAi:key-refresh] No new refresh token provided or same token returned, cannot retry`);
}
throw initialRefreshError;
}
}
catch (tokenUpdateError) {
if (debug) {
console.error(`[callAi:key-refresh] Failed to update refresh token:`, tokenUpdateError);
}
throw initialRefreshError;
}
}
else {
throw initialRefreshError;
}
}
}
catch (refreshError) {
if (debug) {
console.error(`[callAi:key-refresh] API key refresh failed:`, refreshError);
}
const detailedError = new CallAIError({
message: `${errorMessage} (Key refresh failed: ${refreshError instanceof Error ? refreshError.message : String(refreshError)})`,
originalError: error,
refreshError,
status: status || 401,
contentType: "text/plain",
});
throw detailedError;
}
}
const detailedError = new CallAIError({
message: `${context}: ${errorMessage}`,
originalError: error,
status: status || 500,
errorType: error.name || "Error",
});
throw detailedError;
}
async function checkForInvalidModelError(response, model, debug = globalDebug) {
if (response.status < 400 || response.status >= 500) {
return { isInvalidModel: false };
}
const responseClone = response.clone();
let errorData;
try {
errorData = await responseClone.json();
}
catch (e) {
try {
const text = await responseClone.text();
errorData = { error: text };
}
catch (e) {
errorData = { error: `Error ${response.status}: ${response.statusText}` };
}
}
const isInvalidModelError = response.status === 404 ||
response.status === 400 ||
(errorData &&
((typeof errorData.error === "string" &&
(errorData.error.toLowerCase().includes("model") ||
errorData.error.toLowerCase().includes("engine") ||
errorData.error.toLowerCase().includes("not found") ||
errorData.error.toLowerCase().includes("invalid") ||
errorData.error.toLowerCase().includes("unavailable"))) ||
(errorData.error?.message &&
typeof errorData.error.message === "string" &&
(errorData.error.message.toLowerCase().includes("model") ||
errorData.error.message.toLowerCase().includes("engine") ||
errorData.error.message.toLowerCase().includes("not found") ||
errorData.error.message.toLowerCase().includes("invalid") ||
errorData.error.message.toLowerCase().includes("unavailable")))));
if (debug && isInvalidModelError) {
console.log(`[callAi:model-fallback] Detected invalid model error for "${model}":`, errorData);
}
return { isInvalidModel: isInvalidModelError, errorData };
}
export { handleApiError, checkForInvalidModelError };
//# sourceMappingURL=error-handling.js.map