From 047646b1d242fa7573cca58f0bab61da6338341f Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Thu, 29 May 2025 15:28:56 +0200 Subject: [PATCH 01/10] narrower error types --- packages/inference/src/error.ts | 87 +++++++++++++ .../src/lib/getInferenceProviderMapping.ts | 59 ++++++--- .../inference/src/lib/getProviderHelper.ts | 7 +- .../inference/src/lib/makeRequestOptions.ts | 59 +++++---- .../src/providers/black-forest-labs.ts | 3 +- packages/inference/src/providers/fal-ai.ts | 28 ++-- packages/inference/src/utils/request.ts | 93 +++++++++++--- .../inference/src/vendor/type-fest/basic.ts | 31 +++++ .../src/vendor/type-fest/license-cc0 | 121 ++++++++++++++++++ .../src/vendor/type-fest/license-mit | 9 ++ 10 files changed, 414 insertions(+), 83 deletions(-) create mode 100644 packages/inference/src/error.ts create mode 100644 packages/inference/src/vendor/type-fest/basic.ts create mode 100644 packages/inference/src/vendor/type-fest/license-cc0 create mode 100644 packages/inference/src/vendor/type-fest/license-mit diff --git a/packages/inference/src/error.ts b/packages/inference/src/error.ts new file mode 100644 index 0000000000..409785c45d --- /dev/null +++ b/packages/inference/src/error.ts @@ -0,0 +1,87 @@ +import type { JsonObject } from "./vendor/type-fest/basic.js"; + +/** + * Base class for all inference-related errors. + */ +export abstract class HfInferenceError extends Error { + constructor(message: string) { + super(message); + this.name = "HfInferenceError"; + } +} + +export class HfInferenceInputError extends HfInferenceError { + constructor(message: string) { + super(message); + this.name = "InputError"; + } +} + +interface HttpRequest { + url: string; + method: string; + headers?: Record; + body?: JsonObject; +} + +interface HttpResponse { + requestId: string; + status: number; + body: JsonObject | string; +} + +abstract class HfInferenceHttpRequestError extends HfInferenceError { + httpRequest: HttpRequest; + httpResponse: HttpResponse; + constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { + super(message); + this.httpRequest = { + ...httpRequest, + ...(httpRequest.headers ? { + headers: { + ...httpRequest.headers, + ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), + /// redact authentication in the request headers + }, + } : undefined) + }; + this.httpResponse = httpResponse; + } +} + +/** + * Thrown when the HTTP request to the provider fails, e.g. due to API issues or server errors. + */ +export class HfInferenceProviderApiError extends HfInferenceHttpRequestError { + constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { + super(message, httpRequest, httpResponse); + this.name = "ProviderApiError"; + } +} + +/** + * Thrown when the HTTP request to the hub fails, e.g. due to API issues or server errors. + */ +export class HfInferenceHubApiError extends HfInferenceHttpRequestError { + constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { + super(message, httpRequest, httpResponse); + this.name = "HubApiError"; + } +} + +/** + * Thrown when the inference output returned by the provider is invalid / does not match the expectations + */ +export class HfInferenceProviderOutputError extends HfInferenceError { + httpRequest: HttpRequest; + httpResponse: HttpResponse; + error: string | JsonObject; + constructor(message: string, httpRequest: { url: string; method: string; headers: Record; body: JsonObject }, httpResponse: { requestId: string; status: number; headers: Record; body: JsonObject | string }, error: string | JsonObject) { + super(message); + this.name = "ProviderOutputError"; + this.httpRequest = httpRequest; + this.httpResponse = httpResponse; + this.error = error; + } +} + diff --git a/packages/inference/src/lib/getInferenceProviderMapping.ts b/packages/inference/src/lib/getInferenceProviderMapping.ts index 8d2777efb7..f962c90e2b 100644 --- a/packages/inference/src/lib/getInferenceProviderMapping.ts +++ b/packages/inference/src/lib/getInferenceProviderMapping.ts @@ -4,6 +4,7 @@ import { HARDCODED_MODEL_INFERENCE_MAPPING } from "../providers/consts.js"; import { EQUIVALENT_SENTENCE_TRANSFORMERS_TASKS } from "../providers/hf-inference.js"; import type { InferenceProvider, InferenceProviderOrPolicy, ModelId } from "../types.js"; import { typedInclude } from "../utils/typedInclude.js"; +import { HfInferenceHubApiError, HfInferenceInputError } from "../error.js"; export const inferenceProviderMappingCache = new Map(); @@ -32,27 +33,49 @@ export async function fetchInferenceProviderMappingForModel( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion inferenceProviderMapping = inferenceProviderMappingCache.get(modelId)!; } else { + const url = `${HF_HUB_URL}/api/models/${modelId}?expand[]=inferenceProviderMapping`; const resp = await (options?.fetch ?? fetch)( - `${HF_HUB_URL}/api/models/${modelId}?expand[]=inferenceProviderMapping`, + url, { headers: accessToken?.startsWith("hf_") ? { Authorization: `Bearer ${accessToken}` } : {}, } ); - if (resp.status === 404) { - throw new Error(`Model ${modelId} does not exist`); + if (!resp.ok) { + if (resp.headers.get("Content-Type")?.startsWith("application/json")) { + const error = await resp.json(); + if ("error" in error && typeof error.error === "string") { + throw new HfInferenceHubApiError( + `Failed to fetch inference provider mapping for model ${modelId}: ${error.error}`, + { url, method: "GET" }, + { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: error } + ); + } + } else { + throw new HfInferenceHubApiError( + `Failed to fetch inference provider mapping for model ${modelId}`, + { url, method: "GET" }, + { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } + ); + } } - inferenceProviderMapping = await resp - .json() - .then((json) => json.inferenceProviderMapping) - .catch(() => null); - - if (inferenceProviderMapping) { - inferenceProviderMappingCache.set(modelId, inferenceProviderMapping); + let payload: { inferenceProviderMapping?: InferenceProviderMapping } | null = null; + try { + payload = await resp.json(); + } catch { + throw new HfInferenceHubApiError( + `Failed to fetch inference provider mapping for model ${modelId}: malformed API response, invalid JSON`, + { url, method: "GET" }, + { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } + ); } - } - - if (!inferenceProviderMapping) { - throw new Error(`We have not been able to find inference provider information for model ${modelId}.`); + if (!payload?.inferenceProviderMapping) { + throw new HfInferenceHubApiError( + `We have not been able to find inference provider information for model ${modelId}.`, + { url, method: "GET" }, + { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } + ); + } + inferenceProviderMapping = payload.inferenceProviderMapping; } return inferenceProviderMapping; } @@ -83,7 +106,7 @@ export async function getInferenceProviderMapping( ? EQUIVALENT_SENTENCE_TRANSFORMERS_TASKS : [params.task]; if (!typedInclude(equivalentTasks, providerMapping.task)) { - throw new Error( + throw new HfInferenceInputError( `Model ${params.modelId} is not supported for task ${params.task} and provider ${params.provider}. Supported task: ${providerMapping.task}.` ); } @@ -104,7 +127,7 @@ export async function resolveProvider( ): Promise { if (endpointUrl) { if (provider) { - throw new Error("Specifying both endpointUrl and provider is not supported."); + throw new HfInferenceInputError("Specifying both endpointUrl and provider is not supported."); } /// Defaulting to hf-inference helpers / API return "hf-inference"; @@ -117,13 +140,13 @@ export async function resolveProvider( } if (provider === "auto") { if (!modelId) { - throw new Error("Specifying a model is required when provider is 'auto'"); + throw new HfInferenceInputError("Specifying a model is required when provider is 'auto'"); } const inferenceProviderMapping = await fetchInferenceProviderMappingForModel(modelId); provider = Object.keys(inferenceProviderMapping)[0] as InferenceProvider | undefined; } if (!provider) { - throw new Error(`No Inference Provider available for model ${modelId}.`); + throw new HfInferenceInputError(`No Inference Provider available for model ${modelId}.`); } return provider; } diff --git a/packages/inference/src/lib/getProviderHelper.ts b/packages/inference/src/lib/getProviderHelper.ts index 9afcb8980f..dbc5cc5da7 100644 --- a/packages/inference/src/lib/getProviderHelper.ts +++ b/packages/inference/src/lib/getProviderHelper.ts @@ -48,6 +48,7 @@ import * as Replicate from "../providers/replicate.js"; import * as Sambanova from "../providers/sambanova.js"; import * as Together from "../providers/together.js"; import type { InferenceProvider, InferenceProviderOrPolicy, InferenceTask } from "../types.js"; +import { HfInferenceInputError } from "../error.js"; export const PROVIDERS: Record>> = { "black-forest-labs": { @@ -281,14 +282,14 @@ export function getProviderHelper( return new HFInference.HFInferenceTask(); } if (!task) { - throw new Error("you need to provide a task name when using an external provider, e.g. 'text-to-image'"); + throw new HfInferenceInputError("you need to provide a task name when using an external provider, e.g. 'text-to-image'"); } if (!(provider in PROVIDERS)) { - throw new Error(`Provider '${provider}' not supported. Available providers: ${Object.keys(PROVIDERS)}`); + throw new HfInferenceInputError(`Provider '${provider}' not supported. Available providers: ${Object.keys(PROVIDERS)}`); } const providerTasks = PROVIDERS[provider]; if (!providerTasks || !(task in providerTasks)) { - throw new Error( + throw new HfInferenceInputError( `Task '${task}' not supported for provider '${provider}'. Available tasks: ${Object.keys(providerTasks ?? {})}` ); } diff --git a/packages/inference/src/lib/makeRequestOptions.ts b/packages/inference/src/lib/makeRequestOptions.ts index 7973d944ba..0c435d34d8 100644 --- a/packages/inference/src/lib/makeRequestOptions.ts +++ b/packages/inference/src/lib/makeRequestOptions.ts @@ -5,6 +5,7 @@ import type { InferenceProviderModelMapping } from "./getInferenceProviderMappin import { getInferenceProviderMapping } from "./getInferenceProviderMapping.js"; import type { getProviderHelper } from "./getProviderHelper.js"; import { isUrl } from "./isUrl.js"; +import { HfInferenceHubApiError, HfInferenceInputError } from "../error.js"; /** * Lazy-loaded from huggingface.co/api/tasks when needed @@ -33,10 +34,10 @@ export async function makeRequestOptions( // Validate inputs if (args.endpointUrl && provider !== "hf-inference") { - throw new Error(`Cannot use endpointUrl with a third-party provider.`); + throw new HfInferenceInputError(`Cannot use endpointUrl with a third-party provider.`); } if (maybeModel && isUrl(maybeModel)) { - throw new Error(`Model URLs are no longer supported. Use endpointUrl instead.`); + throw new HfInferenceInputError(`Model URLs are no longer supported. Use endpointUrl instead.`); } if (args.endpointUrl) { @@ -51,38 +52,38 @@ export async function makeRequestOptions( } if (!maybeModel && !task) { - throw new Error("No model provided, and no task has been specified."); + throw new HfInferenceInputError("No model provided, and no task has been specified."); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const hfModel = maybeModel ?? (await loadDefaultModel(task!)); if (providerHelper.clientSideRoutingOnly && !maybeModel) { - throw new Error(`Provider ${provider} requires a model ID to be passed directly.`); + throw new HfInferenceInputError(`Provider ${provider} requires a model ID to be passed directly.`); } const inferenceProviderMapping = providerHelper.clientSideRoutingOnly ? ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - providerId: removeProviderPrefix(maybeModel!, provider), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - hfModelId: maybeModel!, - status: "live", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + providerId: removeProviderPrefix(maybeModel!, provider), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + hfModelId: maybeModel!, + status: "live", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + task: task!, + } satisfies InferenceProviderModelMapping) + : await getInferenceProviderMapping( + { + modelId: hfModel, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion task: task!, - } satisfies InferenceProviderModelMapping) - : await getInferenceProviderMapping( - { - modelId: hfModel, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - task: task!, - provider, - accessToken: args.accessToken, - }, - { fetch: options?.fetch } - ); + provider, + accessToken: args.accessToken, + }, + { fetch: options?.fetch } + ); if (!inferenceProviderMapping) { - throw new Error(`We have not been able to find inference provider information for model ${hfModel}.`); + throw new HfInferenceInputError(`We have not been able to find inference provider information for model ${hfModel}.`); } // Use the sync version with the resolved model @@ -122,9 +123,8 @@ export function makeRequestOptionsFromResolvedModel( if (providerHelper.clientSideRoutingOnly) { // Closed-source providers require an accessToken (cannot be routed). if (accessToken && accessToken.startsWith("hf_")) { - throw new Error(`Provider ${provider} is closed-source and does not support HF tokens.`); + throw new HfInferenceInputError(`Provider ${provider} is closed-source and does not support HF tokens.`); } - return "provider-key"; } if (accessToken) { return accessToken.startsWith("hf_") ? "hf-token" : "provider-key"; @@ -197,23 +197,28 @@ async function loadDefaultModel(task: InferenceTask): Promise { } const taskInfo = tasks[task]; if ((taskInfo?.models.length ?? 0) <= 0) { - throw new Error(`No default model defined for task ${task}, please define the model explicitly.`); + throw new HfInferenceInputError(`No default model defined for task ${task}, please define the model explicitly.`); } return taskInfo.models[0].id; } async function loadTaskInfo(): Promise> { - const res = await fetch(`${HF_HUB_URL}/api/tasks`); + const url = `${HF_HUB_URL}/api/tasks`; + const res = await fetch(url); if (!res.ok) { - throw new Error("Failed to load tasks definitions from Hugging Face Hub."); + throw new HfInferenceHubApiError( + "Failed to load tasks definitions from Hugging Face Hub.", + { url, method: "GET" }, + { requestId: res.headers.get("x-request-id") ?? "", status: res.status, body: await res.text() }, + ); } return await res.json(); } function removeProviderPrefix(model: string, provider: string): string { if (!model.startsWith(`${provider}/`)) { - throw new Error(`Models from ${provider} must be prefixed by "${provider}/". Got "${model}".`); + throw new HfInferenceInputError(`Models from ${provider} must be prefixed by "${provider}/". Got "${model}".`); } return model.slice(provider.length + 1); } diff --git a/packages/inference/src/providers/black-forest-labs.ts b/packages/inference/src/providers/black-forest-labs.ts index 8399ccb562..e5f08d1748 100644 --- a/packages/inference/src/providers/black-forest-labs.ts +++ b/packages/inference/src/providers/black-forest-labs.ts @@ -14,6 +14,7 @@ * * Thanks! */ +import { HfInferenceInputError } from "../error.js"; import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { delay } from "../utils/delay.js"; @@ -52,7 +53,7 @@ export class BlackForestLabsTextToImageTask extends TaskProviderHelper implement makeRoute(params: UrlParams): string { if (!params) { - throw new Error("Params are required"); + throw new HfInferenceInputError("Params are required"); } return `/v1/${params.model}`; } diff --git a/packages/inference/src/providers/fal-ai.ts b/packages/inference/src/providers/fal-ai.ts index 975b9cda5c..c9ef09ec55 100644 --- a/packages/inference/src/providers/fal-ai.ts +++ b/packages/inference/src/providers/fal-ai.ts @@ -30,6 +30,7 @@ import { } from "./providerHelper.js"; import { HF_HUB_URL } from "../config.js"; import type { AutomaticSpeechRecognitionArgs } from "../tasks/audio/automaticSpeechRecognition.js"; +import { HfInferenceInputError, HfInferenceProviderApiError } from "../error.js"; export interface FalAiQueueOutput { request_id: string; @@ -159,9 +160,8 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe let status = response.status; const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ - parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" + }`; // extracting the provider model id for status and result urls // from the response as it might be different from the mapped model in `url` @@ -232,12 +232,12 @@ export class FalAIAutomaticSpeechRecognitionTask extends FalAITask implements Au const blob = "data" in args && args.data instanceof Blob ? args.data : "inputs" in args ? args.inputs : undefined; const contentType = blob?.type; if (!contentType) { - throw new Error( + throw new HfInferenceInputError( `Unable to determine the input's content-type. Make sure your are passing a Blob when using provider fal-ai.` ); } if (!FAL_AI_SUPPORTED_BLOB_TYPES.includes(contentType)) { - throw new Error( + throw new HfInferenceInputError( `Provider fal-ai does not support blob type ${contentType} - supported content types are: ${FAL_AI_SUPPORTED_BLOB_TYPES.join( ", " )}` @@ -267,17 +267,19 @@ export class FalAITextToSpeechTask extends FalAITask { `Expected { audio: { url: string } } format from Fal.ai Text-to-Speech, got: ${JSON.stringify(response)}` ); } + const urlResponse = await fetch(res.audio.url); + if (!urlResponse.ok) { + throw new HfInferenceProviderApiError(`Failed to fetch audio from ${res.audio.url}: ${urlResponse.statusText}`, + { url: res.audio.url, method: "GET", headers: { "Content-Type": "application/json" } }, + { requestId: urlResponse.headers.get("x-request-id") ?? "", status: urlResponse.status, body: await urlResponse.text() } + ); + } try { - const urlResponse = await fetch(res.audio.url); - if (!urlResponse.ok) { - throw new Error(`Failed to fetch audio from ${res.audio.url}: ${urlResponse.statusText}`); - } return await urlResponse.blob(); } catch (error) { - throw new InferenceOutputError( - `Error fetching or processing audio from Fal.ai Text-to-Speech URL: ${res.audio.url}. ${ - error instanceof Error ? error.message : String(error) - }` + throw new HfInferenceProviderApiError(`Failed to fetch audio from ${res.audio.url}: ${error instanceof Error ? error.message : String(error)}`, + { url: res.audio.url, method: "GET", headers: { "Content-Type": "application/json" } }, + { requestId: urlResponse.headers.get("x-request-id") ?? "", status: urlResponse.status, body: await urlResponse.text() } ); } } diff --git a/packages/inference/src/utils/request.ts b/packages/inference/src/utils/request.ts index e4aa24073a..08ba930590 100644 --- a/packages/inference/src/utils/request.ts +++ b/packages/inference/src/utils/request.ts @@ -3,6 +3,8 @@ import { makeRequestOptions } from "../lib/makeRequestOptions.js"; import type { InferenceTask, Options, RequestArgs } from "../types.js"; import type { EventSourceMessage } from "../vendor/fetch-event-source/parse.js"; import { getLines, getMessages } from "../vendor/fetch-event-source/parse.js"; +import { HfInferenceProviderApiError } from "../error.js"; +import type { JsonObject } from "../vendor/type-fest/basic.js"; export interface ResponseWrapper { data: T; @@ -12,6 +14,15 @@ export interface ResponseWrapper { }; } +function requestArgsToJson(args: RequestArgs): JsonObject { + // Convert the entire args object to a JSON-serializable format + const argsWithData = args as RequestArgs & { data?: Blob | ArrayBuffer }; + return JSON.parse(JSON.stringify({ + ...argsWithData, + data: argsWithData.data ? "[Blob or ArrayBuffer]" : null + })) as JsonObject; +} + /** * Primitive to make custom calls to the inference provider */ @@ -39,18 +50,32 @@ export async function innerRequest( if (["application/json", "application/problem+json"].some((ct) => contentType?.startsWith(ct))) { const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { - throw new Error( - `Server ${args.model} does not seem to support chat completion. Error: ${JSON.stringify(output.error)}` + throw new HfInferenceProviderApiError( + `Provider ${args.provider} does not seem to support chat completion for model ${args.model} . Error: ${JSON.stringify(output.error)}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, ); } - if (output.error || output.detail) { - throw new Error(JSON.stringify(output.error ?? output.detail)); + if (typeof output.error === "string" || typeof output.detail === "string") { + throw new HfInferenceProviderApiError( + `Failed to perform inference: ${output.error ?? output.detail}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } else { - throw new Error(output); + throw new HfInferenceProviderApiError( + `Failed to perform inference: an HTTP error occurred when requesting the provider.`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } } const message = contentType?.startsWith("text/plain;") ? await response.text() : undefined; - throw new Error(message ?? "An error occurred while fetching the blob"); + throw new HfInferenceProviderApiError( + `Failed to perform inference: ${message ?? "an HTTP error occurred when requesting the provider"}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: message ?? "" } + ); } if (response.headers.get("Content-Type")?.startsWith("application/json")) { @@ -85,26 +110,48 @@ export async function* innerStreamingRequest( if (response.headers.get("Content-Type")?.startsWith("application/json")) { const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { - throw new Error(`Server ${args.model} does not seem to support chat completion. Error: ${output.error}`); + throw new HfInferenceProviderApiError( + `Provider ${args.provider} does not seem to support chat completion for model ${args.model} . Error: ${JSON.stringify(output.error)}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } if (typeof output.error === "string") { - throw new Error(output.error); + throw new HfInferenceProviderApiError( + `Failed to perform inference: ${output.error}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } if (output.error && "message" in output.error && typeof output.error.message === "string") { /// OpenAI errors - throw new Error(output.error.message); + throw new HfInferenceProviderApiError( + `Failed to perform inference: ${output.error.message}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } // Sambanova errors if (typeof output.message === "string") { - throw new Error(output.message); + throw new HfInferenceProviderApiError( + `Failed to perform inference: ${output.message}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + ); } } - throw new Error(`Server response contains error: ${response.status}`); + throw new HfInferenceProviderApiError( + `Failed to perform inference: an HTTP error occurred when requesting the provider.`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: "" } + ); } if (!response.headers.get("content-type")?.startsWith("text/event-stream")) { - throw new Error( - `Server does not support event stream content type, it returned ` + response.headers.get("content-type") + throw new HfInferenceProviderApiError( + `Failed to perform inference: server does not support event stream content type, it returned ` + response.headers.get("content-type"), + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: "" } ); } @@ -122,8 +169,8 @@ export async function* innerStreamingRequest( const onChunk = getLines( getMessages( - () => {}, - () => {}, + () => { }, + () => { }, onEvent ) ); @@ -146,12 +193,16 @@ export async function* innerStreamingRequest( typeof data.error === "string" ? data.error : typeof data.error === "object" && - data.error && - "message" in data.error && - typeof data.error.message === "string" - ? data.error.message - : JSON.stringify(data.error); - throw new Error(`Error forwarded from backend: ` + errorStr); + data.error && + "message" in data.error && + typeof data.error.message === "string" + ? data.error.message + : JSON.stringify(data.error); + throw new HfInferenceProviderApiError( + `Failed to perform inference: an occurred while streaming the response: ${errorStr}`, + { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: data }, + ); } yield data as T; } diff --git a/packages/inference/src/vendor/type-fest/basic.ts b/packages/inference/src/vendor/type-fest/basic.ts new file mode 100644 index 0000000000..3fa40a0399 --- /dev/null +++ b/packages/inference/src/vendor/type-fest/basic.ts @@ -0,0 +1,31 @@ +/** +Matches a JSON object. + +This type can be useful to enforce some input to be JSON-compatible or as a super-type to be extended from. Don't use this as a direct return type as the user would have to double-cast it: `jsonObject as unknown as CustomResponse`. Instead, you could extend your CustomResponse type from it to ensure your type only uses JSON-compatible types: `interface CustomResponse extends JsonObject { … }`. + +@category JSON +*/ +export type JsonObject = { [Key in string]: JsonValue } & { [Key in string]?: JsonValue | undefined }; + +/** +Matches a JSON array. + +@category JSON +*/ +export type JsonArray = JsonValue[] | readonly JsonValue[]; + +/** +Matches any valid JSON primitive value. + +@category JSON +*/ +export type JsonPrimitive = string | number | boolean | null; + +/** +Matches any valid JSON value. + +@see `Jsonify` if you need to transform a type to one that is assignable to `JsonValue`. + +@category JSON +*/ +export type JsonValue = JsonPrimitive | JsonObject | JsonArray; diff --git a/packages/inference/src/vendor/type-fest/license-cc0 b/packages/inference/src/vendor/type-fest/license-cc0 new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/packages/inference/src/vendor/type-fest/license-cc0 @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/packages/inference/src/vendor/type-fest/license-mit b/packages/inference/src/vendor/type-fest/license-mit new file mode 100644 index 0000000000..fa7ceba3eb --- /dev/null +++ b/packages/inference/src/vendor/type-fest/license-mit @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 6beab3391d23c6744f7f7225338f7c419d4f5d16 Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Thu, 29 May 2025 15:52:17 +0200 Subject: [PATCH 02/10] update provider-specific code to use the new error types --- packages/inference/src/error.ts | 8 +- packages/inference/src/index.ts | 2 +- .../inference/src/lib/InferenceOutputError.ts | 8 -- .../src/providers/black-forest-labs.ts | 10 +- packages/inference/src/providers/fal-ai.ts | 30 ++--- .../inference/src/providers/featherless-ai.ts | 10 +- .../inference/src/providers/hf-inference.ts | 109 ++++++++---------- .../inference/src/providers/hyperbolic.ts | 13 +-- packages/inference/src/providers/nebius.ts | 4 +- packages/inference/src/providers/novita.ts | 25 ++-- packages/inference/src/providers/nscale.ts | 4 +- packages/inference/src/providers/ovhcloud.ts | 10 +- .../inference/src/providers/providerHelper.ts | 8 +- packages/inference/src/providers/replicate.ts | 8 +- packages/inference/src/providers/sambanova.ts | 7 +- packages/inference/src/providers/together.ts | 6 +- .../tasks/audio/automaticSpeechRecognition.ts | 4 +- 17 files changed, 125 insertions(+), 141 deletions(-) delete mode 100644 packages/inference/src/lib/InferenceOutputError.ts diff --git a/packages/inference/src/error.ts b/packages/inference/src/error.ts index 409785c45d..53c2d05b7f 100644 --- a/packages/inference/src/error.ts +++ b/packages/inference/src/error.ts @@ -73,15 +73,9 @@ export class HfInferenceHubApiError extends HfInferenceHttpRequestError { * Thrown when the inference output returned by the provider is invalid / does not match the expectations */ export class HfInferenceProviderOutputError extends HfInferenceError { - httpRequest: HttpRequest; - httpResponse: HttpResponse; - error: string | JsonObject; - constructor(message: string, httpRequest: { url: string; method: string; headers: Record; body: JsonObject }, httpResponse: { requestId: string; status: number; headers: Record; body: JsonObject | string }, error: string | JsonObject) { + constructor(message: string) { super(message); this.name = "ProviderOutputError"; - this.httpRequest = httpRequest; - this.httpResponse = httpResponse; - this.error = error; } } diff --git a/packages/inference/src/index.ts b/packages/inference/src/index.ts index 93ed1cd54e..c898cb680f 100644 --- a/packages/inference/src/index.ts +++ b/packages/inference/src/index.ts @@ -1,5 +1,5 @@ export { InferenceClient, InferenceClientEndpoint, HfInference } from "./InferenceClient.js"; -export { InferenceOutputError } from "./lib/InferenceOutputError.js"; +export * from "./error.js" export * from "./types.js"; export * from "./tasks/index.js"; import * as snippets from "./snippets/index.js"; diff --git a/packages/inference/src/lib/InferenceOutputError.ts b/packages/inference/src/lib/InferenceOutputError.ts deleted file mode 100644 index 0765b99944..0000000000 --- a/packages/inference/src/lib/InferenceOutputError.ts +++ /dev/null @@ -1,8 +0,0 @@ -export class InferenceOutputError extends TypeError { - constructor(message: string) { - super( - `Invalid inference output: ${message}. Use the 'request' method with the same parameters to do a custom call with no type checking.` - ); - this.name = "InferenceOutputError"; - } -} diff --git a/packages/inference/src/providers/black-forest-labs.ts b/packages/inference/src/providers/black-forest-labs.ts index e5f08d1748..ec1b12325f 100644 --- a/packages/inference/src/providers/black-forest-labs.ts +++ b/packages/inference/src/providers/black-forest-labs.ts @@ -14,8 +14,7 @@ * * Thanks! */ -import { HfInferenceInputError } from "../error.js"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; +import { HfInferenceInputError, HfInferenceProviderApiError, HfInferenceProviderOutputError } from "../error.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { delay } from "../utils/delay.js"; import { omit } from "../utils/omit.js"; @@ -71,7 +70,10 @@ export class BlackForestLabsTextToImageTask extends TaskProviderHelper implement urlObj.searchParams.set("attempt", step.toString(10)); const resp = await fetch(urlObj, { headers: { "Content-Type": "application/json" } }); if (!resp.ok) { - throw new InferenceOutputError("Failed to fetch result from black forest labs API"); + throw new HfInferenceProviderApiError("Failed to fetch result from black forest labs API", + { url: urlObj.toString(), method: "GET", headers: { "Content-Type": "application/json" } }, + { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } + ); } const payload = await resp.json(); if ( @@ -93,6 +95,6 @@ export class BlackForestLabsTextToImageTask extends TaskProviderHelper implement return await image.blob(); } } - throw new InferenceOutputError("Failed to fetch result from black forest labs API"); + throw new HfInferenceProviderOutputError(`Timed out while waiting for the result from black forest labs API - aborting after 5 attempts`); } } diff --git a/packages/inference/src/providers/fal-ai.ts b/packages/inference/src/providers/fal-ai.ts index c9ef09ec55..db2be03214 100644 --- a/packages/inference/src/providers/fal-ai.ts +++ b/packages/inference/src/providers/fal-ai.ts @@ -17,7 +17,6 @@ import { base64FromBytes } from "../utils/base64FromBytes.js"; import type { AutomaticSpeechRecognitionOutput } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import { isUrl } from "../lib/isUrl.js"; import type { BodyParams, HeaderParams, ModelId, RequestArgs, UrlParams } from "../types.js"; import { delay } from "../utils/delay.js"; @@ -30,7 +29,7 @@ import { } from "./providerHelper.js"; import { HF_HUB_URL } from "../config.js"; import type { AutomaticSpeechRecognitionArgs } from "../tasks/audio/automaticSpeechRecognition.js"; -import { HfInferenceInputError, HfInferenceProviderApiError } from "../error.js"; +import { HfInferenceInputError, HfInferenceProviderApiError, HfInferenceProviderOutputError } from "../error.js"; export interface FalAiQueueOutput { request_id: string; @@ -123,7 +122,7 @@ export class FalAITextToImageTask extends FalAITask implements TextToImageTaskHe return await urlResponse.blob(); } - throw new InferenceOutputError("Expected Fal.ai text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Fal.ai text-to-image API"); } } @@ -151,11 +150,11 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe headers?: Record ): Promise { if (!url || !headers) { - throw new InferenceOutputError("URL and headers are required for text-to-video task"); + throw new HfInferenceInputError("URL and headers are required for text-to-video task"); } const requestId = response.request_id; if (!requestId) { - throw new InferenceOutputError("No request ID found in the response"); + throw new HfInferenceProviderOutputError("Received malformed response from Fal.ai text-to-video API: no request ID found in the response"); } let status = response.status; @@ -176,12 +175,15 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe const statusResponse = await fetch(statusUrl, { headers }); if (!statusResponse.ok) { - throw new InferenceOutputError("Failed to fetch response status from fal-ai API"); + throw new HfInferenceProviderApiError("Failed to fetch response status from fal-ai API", + { url: statusUrl, method: "GET" }, + { requestId: statusResponse.headers.get("x-request-id") ?? "", status: statusResponse.status, body: await statusResponse.text() } + ); } try { status = (await statusResponse.json()).status; } catch (error) { - throw new InferenceOutputError("Failed to parse status response from fal-ai API"); + throw new HfInferenceProviderOutputError("Failed to parse status response from fal-ai API: received malformed response"); } } @@ -190,7 +192,7 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe try { result = await resultResponse.json(); } catch (error) { - throw new InferenceOutputError("Failed to parse result response from fal-ai API"); + throw new HfInferenceProviderOutputError("Failed to parse result response from fal-ai API: received malformed response"); } if ( typeof result === "object" && @@ -205,8 +207,8 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe const urlResponse = await fetch(result.video.url); return await urlResponse.blob(); } else { - throw new InferenceOutputError( - "Expected { video: { url: string } } result format, got instead: " + JSON.stringify(result) + throw new HfInferenceProviderOutputError( + `Received malformed response from Fal.ai text-to-video API: expected { video: { url: string } } result format, got instead: ${JSON.stringify(result)}` ); } } @@ -221,8 +223,8 @@ export class FalAIAutomaticSpeechRecognitionTask extends FalAITask implements Au override async getResponse(response: unknown): Promise { const res = response as FalAIAutomaticSpeechRecognitionOutput; if (typeof res?.text !== "string") { - throw new InferenceOutputError( - `Expected { text: string } format from Fal.ai Automatic Speech Recognition, got: ${JSON.stringify(response)}` + throw new HfInferenceProviderOutputError( + `Received malformed response from Fal.ai Automatic Speech Recognition API: expected { text: string } format, got instead: ${JSON.stringify(response)}` ); } return { text: res.text }; @@ -263,8 +265,8 @@ export class FalAITextToSpeechTask extends FalAITask { override async getResponse(response: unknown): Promise { const res = response as FalAITextToSpeechOutput; if (typeof res?.audio?.url !== "string") { - throw new InferenceOutputError( - `Expected { audio: { url: string } } format from Fal.ai Text-to-Speech, got: ${JSON.stringify(response)}` + throw new HfInferenceProviderOutputError( + `Received malformed response from Fal.ai Text-to-Speech API: expected { audio: { url: string } } format, got instead: ${JSON.stringify(response)}` ); } const urlResponse = await fetch(res.audio.url); diff --git a/packages/inference/src/providers/featherless-ai.ts b/packages/inference/src/providers/featherless-ai.ts index 2c838ce12e..f55eae7fe4 100644 --- a/packages/inference/src/providers/featherless-ai.ts +++ b/packages/inference/src/providers/featherless-ai.ts @@ -4,10 +4,10 @@ import type { TextGenerationOutput, TextGenerationOutputFinishReason, } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams } from "../types.js"; import { BaseConversationalTask, BaseTextGenerationTask } from "./providerHelper.js"; import { omit } from "../utils/omit.js"; +import { HfInferenceProviderOutputError } from "../error.js"; interface FeatherlessAITextCompletionOutput extends Omit { choices: Array<{ @@ -38,9 +38,9 @@ export class FeatherlessAITextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: params.args.parameters.max_new_tokens, - ...omit(params.args.parameters, "max_new_tokens"), - } + max_tokens: params.args.parameters.max_new_tokens, + ...omit(params.args.parameters, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; @@ -58,6 +58,6 @@ export class FeatherlessAITextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new InferenceOutputError("Expected Featherless AI text generation response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Featherless AI text generation API"); } } diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index 099d50e6da..e387a839cd 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -34,7 +34,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; +import { HfInferenceProviderOutputError } from "../error.js"; import type { TabularClassificationOutput } from "../tasks/tabular/tabularClassification.js"; import type { BodyParams, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -127,7 +127,7 @@ export class HFInferenceTextToImageTask extends HFInferenceTask implements TextT outputType?: "url" | "blob" ): Promise { if (!response) { - throw new InferenceOutputError("response is undefined"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-image API: response is undefined"); } if (typeof response == "object") { if ("data" in response && Array.isArray(response.data) && response.data[0].b64_json) { @@ -154,7 +154,7 @@ export class HFInferenceTextToImageTask extends HFInferenceTask implements TextT } return response; } - throw new InferenceOutputError("Expected a Blob "); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-image API: expected a Blob"); } } @@ -195,13 +195,12 @@ export class HFInferenceTextGenerationTask extends HFInferenceTask implements Te if (Array.isArray(res) && res.every((x) => "generated_text" in x && typeof x?.generated_text === "string")) { return (res as TextGenerationOutput[])?.[0]; } - throw new InferenceOutputError("Expected Array<{generated_text: string}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-speech API: expected Array<{generated_text: string}>"); } } export class HFInferenceAudioClassificationTask extends HFInferenceTask implements AudioClassificationTaskHelper { override async getResponse(response: unknown): Promise { - // Add type checking/validation for the 'unknown' input if ( Array.isArray(response) && response.every( @@ -209,18 +208,15 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen typeof x === "object" && x !== null && typeof x.label === "string" && typeof x.score === "number" ) ) { - // If validation passes, it's safe to return as AudioClassificationOutput return response; } - // If validation fails, throw an error - throw new InferenceOutputError("Expected Array<{label: string, score: number}> but received different format"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-classification API: expected Array<{label: string, score: number}> but received different format"); } } export class HFInferenceAutomaticSpeechRecognitionTask extends HFInferenceTask - implements AutomaticSpeechRecognitionTaskHelper -{ + implements AutomaticSpeechRecognitionTaskHelper { override async getResponse(response: AutomaticSpeechRecognitionOutput): Promise { return response; } @@ -229,16 +225,16 @@ export class HFInferenceAutomaticSpeechRecognitionTask return "data" in args ? args : { - ...omit(args, "inputs"), - data: args.inputs, - }; + ...omit(args, "inputs"), + data: args.inputs, + }; } } export class HFInferenceAudioToAudioTask extends HFInferenceTask implements AudioToAudioTaskHelper { override async getResponse(response: AudioToAudioOutput[]): Promise { if (!Array.isArray(response)) { - throw new InferenceOutputError("Expected Array"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-to-audio API: expected Array"); } if ( !response.every((elem): elem is AudioToAudioOutput => { @@ -254,7 +250,7 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi ); }) ) { - throw new InferenceOutputError("Expected Array<{label: string, audio: Blob}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-to-audio API: expected Array<{label: string, audio: Blob}>"); } return response; } @@ -262,8 +258,7 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi export class HFInferenceDocumentQuestionAnsweringTask extends HFInferenceTask - implements DocumentQuestionAnsweringTaskHelper -{ + implements DocumentQuestionAnsweringTaskHelper { override async getResponse( response: DocumentQuestionAnsweringOutput ): Promise { @@ -281,7 +276,7 @@ export class HFInferenceDocumentQuestionAnsweringTask ) { return response[0]; } - throw new InferenceOutputError("Expected Array<{answer: string, end: number, score: number, start: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference document-question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>"); } } @@ -298,7 +293,7 @@ export class HFInferenceFeatureExtractionTask extends HFInferenceTask implements if (Array.isArray(response) && isNumArrayRec(response, 3, 0)) { return response; } - throw new InferenceOutputError("Expected Array"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference feature-extraction API: expected Array"); } } @@ -307,7 +302,7 @@ export class HFInferenceImageClassificationTask extends HFInferenceTask implemen if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new InferenceOutputError("Expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-classification API: expected Array<{label: string, score: number}>"); } } @@ -324,14 +319,14 @@ export class HFInferenceImageSegmentationTask extends HFInferenceTask implements ) { return response; } - throw new InferenceOutputError("Expected Array<{label: string, mask: string, score: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-segmentation API: expected Array<{label: string, mask: string, score: number}>"); } } export class HFInferenceImageToTextTask extends HFInferenceTask implements ImageToTextTaskHelper { override async getResponse(response: ImageToTextOutput): Promise { if (typeof response?.generated_text !== "string") { - throw new InferenceOutputError("Expected {generated_text: string}"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-to-text API: expected {generated_text: string}"); } return response; } @@ -359,7 +354,7 @@ export class HFInferenceImageToImageTask extends HFInferenceTask implements Imag if (response instanceof Blob) { return response; } - throw new InferenceOutputError("Expected Blob"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-to-image API: expected Blob"); } } @@ -379,21 +374,20 @@ export class HFInferenceObjectDetectionTask extends HFInferenceTask implements O ) { return response; } - throw new InferenceOutputError( - "Expected Array<{label: string, score: number, box: {xmin: number, ymin: number, xmax: number, ymax: number}}>" + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference object-detection API: expected Array<{label: string, score: number, box: {xmin: number, ymin: number, xmax: number, ymax: number}}>" ); } } export class HFInferenceZeroShotImageClassificationTask extends HFInferenceTask - implements ZeroShotImageClassificationTaskHelper -{ + implements ZeroShotImageClassificationTaskHelper { override async getResponse(response: ZeroShotImageClassificationOutput): Promise { if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new InferenceOutputError("Expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference zero-shot-image-classification API: expected Array<{label: string, score: number}>"); } } @@ -403,7 +397,7 @@ export class HFInferenceTextClassificationTask extends HFInferenceTask implement if (Array.isArray(output) && output.every((x) => typeof x?.label === "string" && typeof x.score === "number")) { return output; } - throw new InferenceOutputError("Expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-classification API: expected Array<{label: string, score: number}>"); } } @@ -414,24 +408,24 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements if ( Array.isArray(response) ? response.every( - (elem) => - typeof elem === "object" && - !!elem && - typeof elem.answer === "string" && - typeof elem.end === "number" && - typeof elem.score === "number" && - typeof elem.start === "number" - ) + (elem) => + typeof elem === "object" && + !!elem && + typeof elem.answer === "string" && + typeof elem.end === "number" && + typeof elem.score === "number" && + typeof elem.start === "number" + ) : typeof response === "object" && - !!response && - typeof response.answer === "string" && - typeof response.end === "number" && - typeof response.score === "number" && - typeof response.start === "number" + !!response && + typeof response.answer === "string" && + typeof response.end === "number" && + typeof response.score === "number" && + typeof response.start === "number" ) { return Array.isArray(response) ? response[0] : response; } - throw new InferenceOutputError("Expected Array<{answer: string, end: number, score: number, start: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>"); } } @@ -449,8 +443,8 @@ export class HFInferenceFillMaskTask extends HFInferenceTask implements FillMask ) { return response; } - throw new InferenceOutputError( - "Expected Array<{score: number, sequence: string, token: number, token_str: string}>" + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference fill-mask API: expected Array<{score: number, sequence: string, token: number, token_str: string}>" ); } } @@ -470,7 +464,7 @@ export class HFInferenceZeroShotClassificationTask extends HFInferenceTask imple ) { return response; } - throw new InferenceOutputError("Expected Array<{labels: string[], scores: number[], sequence: string}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference zero-shot-classification API: expected Array<{labels: string[], scores: number[], sequence: string}>"); } } @@ -479,7 +473,7 @@ export class HFInferenceSentenceSimilarityTask extends HFInferenceTask implement if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new InferenceOutputError("Expected Array"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference sentence-similarity API: expected Array"); } } @@ -510,8 +504,8 @@ export class HFInferenceTableQuestionAnsweringTask extends HFInferenceTask imple ) { return Array.isArray(response) ? response[0] : response; } - throw new InferenceOutputError( - "Expected {aggregator: string, answer: string, cells: string[], coordinates: number[][]}" + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference table-question-answering API: expected {aggregator: string, answer: string, cells: string[], coordinates: number[][]}" ); } } @@ -531,8 +525,8 @@ export class HFInferenceTokenClassificationTask extends HFInferenceTask implemen ) { return response; } - throw new InferenceOutputError( - "Expected Array<{end: number, entity_group: string, score: number, start: number, word: string}>" + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference token-classification API: expected Array<{end: number, entity_group: string, score: number, start: number, word: string}>" ); } } @@ -542,7 +536,7 @@ export class HFInferenceTranslationTask extends HFInferenceTask implements Trans if (Array.isArray(response) && response.every((x) => typeof x?.translation_text === "string")) { return response?.length === 1 ? response?.[0] : response; } - throw new InferenceOutputError("Expected Array<{translation_text: string}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference translation API: expected Array<{translation_text: string}>"); } } @@ -551,7 +545,7 @@ export class HFInferenceSummarizationTask extends HFInferenceTask implements Sum if (Array.isArray(response) && response.every((x) => typeof x?.summary_text === "string")) { return response?.[0]; } - throw new InferenceOutputError("Expected Array<{summary_text: string}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference summarization API: expected Array<{summary_text: string}>"); } } @@ -566,14 +560,13 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new InferenceOutputError("Expected Array"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference tabular-classification API: expected Array"); } } export class HFInferenceVisualQuestionAnsweringTask extends HFInferenceTask - implements VisualQuestionAnsweringTaskHelper -{ + implements VisualQuestionAnsweringTaskHelper { override async getResponse(response: VisualQuestionAnsweringOutput): Promise { if ( Array.isArray(response) && @@ -584,7 +577,7 @@ export class HFInferenceVisualQuestionAnsweringTask ) { return response[0]; } - throw new InferenceOutputError("Expected Array<{answer: string, score: number}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference visual-question-answering API: expected Array<{answer: string, score: number}>"); } } @@ -593,7 +586,7 @@ export class HFInferenceTabularRegressionTask extends HFInferenceTask implements if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new InferenceOutputError("Expected Array"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference tabular-regression API: expected Array"); } } diff --git a/packages/inference/src/providers/hyperbolic.ts b/packages/inference/src/providers/hyperbolic.ts index 7dffbbe157..1d38e86647 100644 --- a/packages/inference/src/providers/hyperbolic.ts +++ b/packages/inference/src/providers/hyperbolic.ts @@ -15,7 +15,6 @@ * Thanks! */ import type { ChatCompletionOutput, TextGenerationOutput } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams, UrlParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { @@ -24,7 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; - +import { HfInferenceProviderOutputError } from "../error.js"; const HYPERBOLIC_API_BASE_URL = "https://api.hyperbolic.xyz"; export interface HyperbolicTextCompletionOutput extends Omit { @@ -57,9 +56,9 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { messages: [{ content: params.args.inputs, role: "user" }], ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), ...omit(params.args, ["inputs", "parameters"]), model: params.model, @@ -79,7 +78,7 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { }; } - throw new InferenceOutputError("Expected Hyperbolic text generation response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Hyperbolic text generation API"); } } @@ -121,6 +120,6 @@ export class HyperbolicTextToImageTask extends TaskProviderHelper implements Tex return fetch(`data:image/jpeg;base64,${response.images[0].image}`).then((res) => res.blob()); } - throw new InferenceOutputError("Expected Hyperbolic text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Hyperbolic text-to-image API"); } } diff --git a/packages/inference/src/providers/nebius.ts b/packages/inference/src/providers/nebius.ts index a301c15195..a30423e6d4 100644 --- a/packages/inference/src/providers/nebius.ts +++ b/packages/inference/src/providers/nebius.ts @@ -15,7 +15,6 @@ * Thanks! */ import type { FeatureExtractionOutput } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { @@ -25,6 +24,7 @@ import { type FeatureExtractionTaskHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; +import { HfInferenceProviderOutputError } from "../error.js"; const NEBIUS_API_BASE_URL = "https://api.studio.nebius.ai"; @@ -92,7 +92,7 @@ export class NebiusTextToImageTask extends TaskProviderHelper implements TextToI return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new InferenceOutputError("Expected Nebius text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Nebius text-to-image API"); } } diff --git a/packages/inference/src/providers/novita.ts b/packages/inference/src/providers/novita.ts index 2009ffa3ac..bb12dca00d 100644 --- a/packages/inference/src/providers/novita.ts +++ b/packages/inference/src/providers/novita.ts @@ -14,7 +14,6 @@ * * Thanks! */ -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import { isUrl } from "../lib/isUrl.js"; import type { TextToVideoArgs } from "../tasks/index.js"; import type { BodyParams, UrlParams } from "../types.js"; @@ -26,6 +25,7 @@ import { TaskProviderHelper, type TextToVideoTaskHelper, } from "./providerHelper.js"; +import { HfInferenceInputError, HfInferenceProviderApiError, HfInferenceProviderOutputError } from "../error.js"; const NOVITA_API_BASE_URL = "https://api.novita.ai"; @@ -78,17 +78,16 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV headers?: Record ): Promise { if (!url || !headers) { - throw new InferenceOutputError("URL and headers are required for text-to-video task"); + throw new HfInferenceInputError("URL and headers are required for text-to-video task"); } const taskId = response.task_id; if (!taskId) { - throw new InferenceOutputError("No task ID found in the response"); + throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: no task ID found in the response"); } const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ - parsedUrl.host === "router.huggingface.co" ? "/novita" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/novita" : "" + }`; const resultUrl = `${baseUrl}/v3/async/task-result?task_id=${taskId}`; let status = ""; @@ -98,7 +97,11 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV await delay(500); const resultResponse = await fetch(resultUrl, { headers }); if (!resultResponse.ok) { - throw new InferenceOutputError("Failed to fetch task result"); + throw new HfInferenceProviderApiError( + "Failed to fetch task result", + { url: resultUrl, method: "GET", headers }, + { requestId: resultResponse.headers.get("x-request-id") ?? "", status: resultResponse.status, body: await resultResponse.text() } + ); } try { taskResult = await resultResponse.json(); @@ -113,15 +116,15 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV ) { status = taskResult.task.status; } else { - throw new InferenceOutputError("Failed to get task status"); + throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: failed to get task status"); } } catch (error) { - throw new InferenceOutputError("Failed to parse task result"); + throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: failed to parse task result"); } } if (status === "TASK_STATUS_FAILED") { - throw new InferenceOutputError("Task failed"); + throw new HfInferenceProviderOutputError("Novita text-to-video task failed"); } if ( @@ -139,7 +142,7 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV const urlResponse = await fetch(taskResult.videos[0].video_url); return await urlResponse.blob(); } else { - throw new InferenceOutputError("Expected { videos: [{ video_url: string }] }"); + throw new HfInferenceProviderOutputError(`Received malformed response from Novita text-to-video API: expected { videos: [{ video_url: string }] } format, got instead: ${JSON.stringify(taskResult)}`); } } } diff --git a/packages/inference/src/providers/nscale.ts b/packages/inference/src/providers/nscale.ts index 92f1b3a9d7..7c686b1b05 100644 --- a/packages/inference/src/providers/nscale.ts +++ b/packages/inference/src/providers/nscale.ts @@ -15,10 +15,10 @@ * Thanks! */ import type { TextToImageInput } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { BaseConversationalTask, TaskProviderHelper, type TextToImageTaskHelper } from "./providerHelper.js"; +import { HfInferenceProviderOutputError } from "../error.js"; const NSCALE_API_BASE_URL = "https://inference.api.nscale.com"; @@ -74,6 +74,6 @@ export class NscaleTextToImageTask extends TaskProviderHelper implements TextToI return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new InferenceOutputError("Expected Nscale text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Nscale text-to-image API"); } } diff --git a/packages/inference/src/providers/ovhcloud.ts b/packages/inference/src/providers/ovhcloud.ts index b0e5d21080..e8a8f48f23 100644 --- a/packages/inference/src/providers/ovhcloud.ts +++ b/packages/inference/src/providers/ovhcloud.ts @@ -17,10 +17,10 @@ import { BaseConversationalTask, BaseTextGenerationTask } from "./providerHelper.js"; import type { ChatCompletionOutput, TextGenerationOutput, TextGenerationOutputFinishReason } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import type { TextGenerationInput } from "@huggingface/tasks"; +import { HfInferenceProviderOutputError } from "../error.js"; const OVHCLOUD_API_BASE_URL = "https://oai.endpoints.kepler.ai.cloud.ovh.net"; @@ -50,9 +50,9 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; @@ -70,6 +70,6 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new InferenceOutputError("Expected OVHcloud text generation response format"); + throw new HfInferenceProviderOutputError("Received malformed response from OVHcloud text generation API"); } } diff --git a/packages/inference/src/providers/providerHelper.ts b/packages/inference/src/providers/providerHelper.ts index 00923d8a0c..2bbfdbb329 100644 --- a/packages/inference/src/providers/providerHelper.ts +++ b/packages/inference/src/providers/providerHelper.ts @@ -46,7 +46,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; +import { HfInferenceProviderOutputError } from "../error.js"; import type { AudioToAudioOutput } from "../tasks/audio/audioToAudio.js"; import type { BaseArgs, BodyParams, HeaderParams, InferenceProvider, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -61,7 +61,7 @@ export abstract class TaskProviderHelper { readonly provider: InferenceProvider, private baseUrl: string, readonly clientSideRoutingOnly: boolean = false - ) {} + ) { } /** * Return the response in the expected format. @@ -320,7 +320,7 @@ export class BaseConversationalTask extends TaskProviderHelper implements Conver return response; } - throw new InferenceOutputError("Expected ChatCompletionOutput"); + throw new HfInferenceProviderOutputError("Expected ChatCompletionOutput"); } } @@ -353,6 +353,6 @@ export class BaseTextGenerationTask extends TaskProviderHelper implements TextGe return res[0]; } - throw new InferenceOutputError("Expected Array<{generated_text: string}>"); + throw new HfInferenceProviderOutputError("Expected Array<{generated_text: string}>"); } } diff --git a/packages/inference/src/providers/replicate.ts b/packages/inference/src/providers/replicate.ts index 7a908cb617..e4810afefa 100644 --- a/packages/inference/src/providers/replicate.ts +++ b/packages/inference/src/providers/replicate.ts @@ -14,7 +14,7 @@ * * Thanks! */ -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; +import { HfInferenceProviderOutputError } from "../error.js"; import { isUrl } from "../lib/isUrl.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { omit } from "../utils/omit.js"; @@ -99,7 +99,7 @@ export class ReplicateTextToImageTask extends ReplicateTask implements TextToIma return await urlResponse.blob(); } - throw new InferenceOutputError("Expected Replicate text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-image API"); } } @@ -132,7 +132,7 @@ export class ReplicateTextToSpeechTask extends ReplicateTask { } } } - throw new InferenceOutputError("Expected Blob or object with output"); + throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-speech API"); } } @@ -149,6 +149,6 @@ export class ReplicateTextToVideoTask extends ReplicateTask implements TextToVid return await urlResponse.blob(); } - throw new InferenceOutputError("Expected { output: string }"); + throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-video API"); } } diff --git a/packages/inference/src/providers/sambanova.ts b/packages/inference/src/providers/sambanova.ts index efc41fe7da..789fabf9ff 100644 --- a/packages/inference/src/providers/sambanova.ts +++ b/packages/inference/src/providers/sambanova.ts @@ -14,12 +14,11 @@ * * Thanks! */ -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; - import type { FeatureExtractionOutput } from "@huggingface/tasks"; import type { BodyParams } from "../types.js"; import type { FeatureExtractionTaskHelper } from "./providerHelper.js"; import { BaseConversationalTask, TaskProviderHelper } from "./providerHelper.js"; +import { HfInferenceProviderOutputError } from "../error.js"; export class SambanovaConversationalTask extends BaseConversationalTask { constructor() { @@ -40,8 +39,8 @@ export class SambanovaFeatureExtractionTask extends TaskProviderHelper implement if (typeof response === "object" && "data" in response && Array.isArray(response.data)) { return response.data.map((item) => item.embedding); } - throw new InferenceOutputError( - "Expected Sambanova feature-extraction (embeddings) response format to be {'data' : list of {'embedding' : number[]}}" + throw new HfInferenceProviderOutputError( + "Received malformed response from Sambanova feature-extraction (embeddings) API" ); } diff --git a/packages/inference/src/providers/together.ts b/packages/inference/src/providers/together.ts index 350b3c677d..2520b9027c 100644 --- a/packages/inference/src/providers/together.ts +++ b/packages/inference/src/providers/together.ts @@ -15,7 +15,6 @@ * Thanks! */ import type { ChatCompletionOutput, TextGenerationOutput, TextGenerationOutputFinishReason } from "@huggingface/tasks"; -import { InferenceOutputError } from "../lib/InferenceOutputError.js"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { @@ -24,6 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; +import { HfInferenceProviderOutputError } from "../error.js"; const TOGETHER_API_BASE_URL = "https://api.together.xyz"; @@ -74,7 +74,7 @@ export class TogetherTextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new InferenceOutputError("Expected Together text generation response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Together text generation API"); } } @@ -113,6 +113,6 @@ export class TogetherTextToImageTask extends TaskProviderHelper implements TextT return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new InferenceOutputError("Expected Together text-to-image response format"); + throw new HfInferenceProviderOutputError("Received malformed response from Together text-to-image API"); } } diff --git a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts index 6f169a20f1..8d9e7228b2 100644 --- a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts +++ b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts @@ -1,10 +1,10 @@ import type { AutomaticSpeechRecognitionInput, AutomaticSpeechRecognitionOutput } from "@huggingface/tasks"; import { resolveProvider } from "../../lib/getInferenceProviderMapping.js"; import { getProviderHelper } from "../../lib/getProviderHelper.js"; -import { InferenceOutputError } from "../../lib/InferenceOutputError.js"; import type { BaseArgs, Options } from "../../types.js"; import { innerRequest } from "../../utils/request.js"; import type { LegacyAudioInput } from "./utils.js"; +import { HfInferenceProviderOutputError } from "../../error.js"; export type AutomaticSpeechRecognitionArgs = BaseArgs & (AutomaticSpeechRecognitionInput | LegacyAudioInput); /** @@ -24,7 +24,7 @@ export async function automaticSpeechRecognition( }); const isValidOutput = typeof res?.text === "string"; if (!isValidOutput) { - throw new InferenceOutputError("Expected {text: string}"); + throw new HfInferenceProviderOutputError("Received malformed response from automatic-speech-recognition API"); } return providerHelper.getResponse(res); } From 92d3421995703e4b70625a4888343ee0c7f27dfb Mon Sep 17 00:00:00 2001 From: Simon Brandeis <33657802+SBrandeis@users.noreply.github.com> Date: Thu, 29 May 2025 15:58:14 +0200 Subject: [PATCH 03/10] Update packages/inference/src/providers/hf-inference.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/inference/src/providers/hf-inference.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index e387a839cd..db4026b151 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -195,7 +195,7 @@ export class HFInferenceTextGenerationTask extends HFInferenceTask implements Te if (Array.isArray(res) && res.every((x) => "generated_text" in x && typeof x?.generated_text === "string")) { return (res as TextGenerationOutput[])?.[0]; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-speech API: expected Array<{generated_text: string}>"); + throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text generation API: expected Array<{generated_text: string}>"); } } From 47e7e42a3bddff91fd7c3885abbf0759f2119ced Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Thu, 29 May 2025 15:59:23 +0200 Subject: [PATCH 04/10] lint + format --- packages/inference/src/error.ts | 21 +-- packages/inference/src/index.ts | 2 +- .../src/lib/getInferenceProviderMapping.ts | 9 +- .../inference/src/lib/getProviderHelper.ts | 8 +- .../inference/src/lib/makeRequestOptions.ts | 38 +++--- .../src/providers/black-forest-labs.ts | 7 +- packages/inference/src/providers/fal-ai.ts | 56 ++++++-- .../inference/src/providers/featherless-ai.ts | 6 +- .../inference/src/providers/hf-inference.ts | 84 +++++++++--- .../inference/src/providers/hyperbolic.ts | 6 +- packages/inference/src/providers/novita.ts | 29 +++- packages/inference/src/providers/ovhcloud.ts | 6 +- .../inference/src/providers/providerHelper.ts | 2 +- packages/inference/src/utils/request.ts | 128 +++++++++++++----- 14 files changed, 278 insertions(+), 124 deletions(-) diff --git a/packages/inference/src/error.ts b/packages/inference/src/error.ts index 53c2d05b7f..8f96008fc1 100644 --- a/packages/inference/src/error.ts +++ b/packages/inference/src/error.ts @@ -1,6 +1,6 @@ import type { JsonObject } from "./vendor/type-fest/basic.js"; -/** +/** * Base class for all inference-related errors. */ export abstract class HfInferenceError extends Error { @@ -37,13 +37,15 @@ abstract class HfInferenceHttpRequestError extends HfInferenceError { super(message); this.httpRequest = { ...httpRequest, - ...(httpRequest.headers ? { - headers: { - ...httpRequest.headers, - ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), - /// redact authentication in the request headers - }, - } : undefined) + ...(httpRequest.headers + ? { + headers: { + ...httpRequest.headers, + ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), + /// redact authentication in the request headers + }, + } + : undefined), }; this.httpResponse = httpResponse; } @@ -70,7 +72,7 @@ export class HfInferenceHubApiError extends HfInferenceHttpRequestError { } /** - * Thrown when the inference output returned by the provider is invalid / does not match the expectations + * Thrown when the inference output returned by the provider is invalid / does not match the expectations */ export class HfInferenceProviderOutputError extends HfInferenceError { constructor(message: string) { @@ -78,4 +80,3 @@ export class HfInferenceProviderOutputError extends HfInferenceError { this.name = "ProviderOutputError"; } } - diff --git a/packages/inference/src/index.ts b/packages/inference/src/index.ts index c898cb680f..ac05fc6a64 100644 --- a/packages/inference/src/index.ts +++ b/packages/inference/src/index.ts @@ -1,5 +1,5 @@ export { InferenceClient, InferenceClientEndpoint, HfInference } from "./InferenceClient.js"; -export * from "./error.js" +export * from "./error.js"; export * from "./types.js"; export * from "./tasks/index.js"; import * as snippets from "./snippets/index.js"; diff --git a/packages/inference/src/lib/getInferenceProviderMapping.ts b/packages/inference/src/lib/getInferenceProviderMapping.ts index f962c90e2b..d1646b267c 100644 --- a/packages/inference/src/lib/getInferenceProviderMapping.ts +++ b/packages/inference/src/lib/getInferenceProviderMapping.ts @@ -34,12 +34,9 @@ export async function fetchInferenceProviderMappingForModel( inferenceProviderMapping = inferenceProviderMappingCache.get(modelId)!; } else { const url = `${HF_HUB_URL}/api/models/${modelId}?expand[]=inferenceProviderMapping`; - const resp = await (options?.fetch ?? fetch)( - url, - { - headers: accessToken?.startsWith("hf_") ? { Authorization: `Bearer ${accessToken}` } : {}, - } - ); + const resp = await (options?.fetch ?? fetch)(url, { + headers: accessToken?.startsWith("hf_") ? { Authorization: `Bearer ${accessToken}` } : {}, + }); if (!resp.ok) { if (resp.headers.get("Content-Type")?.startsWith("application/json")) { const error = await resp.json(); diff --git a/packages/inference/src/lib/getProviderHelper.ts b/packages/inference/src/lib/getProviderHelper.ts index dbc5cc5da7..a0ee7262ae 100644 --- a/packages/inference/src/lib/getProviderHelper.ts +++ b/packages/inference/src/lib/getProviderHelper.ts @@ -282,10 +282,14 @@ export function getProviderHelper( return new HFInference.HFInferenceTask(); } if (!task) { - throw new HfInferenceInputError("you need to provide a task name when using an external provider, e.g. 'text-to-image'"); + throw new HfInferenceInputError( + "you need to provide a task name when using an external provider, e.g. 'text-to-image'" + ); } if (!(provider in PROVIDERS)) { - throw new HfInferenceInputError(`Provider '${provider}' not supported. Available providers: ${Object.keys(PROVIDERS)}`); + throw new HfInferenceInputError( + `Provider '${provider}' not supported. Available providers: ${Object.keys(PROVIDERS)}` + ); } const providerTasks = PROVIDERS[provider]; if (!providerTasks || !(task in providerTasks)) { diff --git a/packages/inference/src/lib/makeRequestOptions.ts b/packages/inference/src/lib/makeRequestOptions.ts index 0c435d34d8..fcb0f9f71a 100644 --- a/packages/inference/src/lib/makeRequestOptions.ts +++ b/packages/inference/src/lib/makeRequestOptions.ts @@ -64,26 +64,28 @@ export async function makeRequestOptions( const inferenceProviderMapping = providerHelper.clientSideRoutingOnly ? ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - providerId: removeProviderPrefix(maybeModel!, provider), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - hfModelId: maybeModel!, - status: "live", - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - task: task!, - } satisfies InferenceProviderModelMapping) - : await getInferenceProviderMapping( - { - modelId: hfModel, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + providerId: removeProviderPrefix(maybeModel!, provider), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + hfModelId: maybeModel!, + status: "live", // eslint-disable-next-line @typescript-eslint/no-non-null-assertion task: task!, - provider, - accessToken: args.accessToken, - }, - { fetch: options?.fetch } - ); + } satisfies InferenceProviderModelMapping) + : await getInferenceProviderMapping( + { + modelId: hfModel, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + task: task!, + provider, + accessToken: args.accessToken, + }, + { fetch: options?.fetch } + ); if (!inferenceProviderMapping) { - throw new HfInferenceInputError(`We have not been able to find inference provider information for model ${hfModel}.`); + throw new HfInferenceInputError( + `We have not been able to find inference provider information for model ${hfModel}.` + ); } // Use the sync version with the resolved model @@ -210,7 +212,7 @@ async function loadTaskInfo(): Promise { if (!response) { - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-image API: response is undefined"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference text-to-image API: response is undefined" + ); } if (typeof response == "object") { if ("data" in response && Array.isArray(response.data) && response.data[0].b64_json) { @@ -154,7 +156,9 @@ export class HFInferenceTextToImageTask extends HFInferenceTask implements TextT } return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-to-image API: expected a Blob"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference text-to-image API: expected a Blob" + ); } } @@ -210,7 +214,9 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen ) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-classification API: expected Array<{label: string, score: number}> but received different format"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference audio-classification API: expected Array<{label: string, score: number}> but received different format" + ); } } @@ -234,7 +240,9 @@ export class HFInferenceAutomaticSpeechRecognitionTask export class HFInferenceAudioToAudioTask extends HFInferenceTask implements AudioToAudioTaskHelper { override async getResponse(response: AudioToAudioOutput[]): Promise { if (!Array.isArray(response)) { - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-to-audio API: expected Array"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference audio-to-audio API: expected Array" + ); } if ( !response.every((elem): elem is AudioToAudioOutput => { @@ -250,7 +258,9 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi ); }) ) { - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference audio-to-audio API: expected Array<{label: string, audio: Blob}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference audio-to-audio API: expected Array<{label: string, audio: Blob}>" + ); } return response; } @@ -276,7 +286,9 @@ export class HFInferenceDocumentQuestionAnsweringTask ) { return response[0]; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference document-question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference document-question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>" + ); } } @@ -293,7 +305,9 @@ export class HFInferenceFeatureExtractionTask extends HFInferenceTask implements if (Array.isArray(response) && isNumArrayRec(response, 3, 0)) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference feature-extraction API: expected Array"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference feature-extraction API: expected Array" + ); } } @@ -302,7 +316,9 @@ export class HFInferenceImageClassificationTask extends HFInferenceTask implemen if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-classification API: expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference image-classification API: expected Array<{label: string, score: number}>" + ); } } @@ -319,14 +335,18 @@ export class HFInferenceImageSegmentationTask extends HFInferenceTask implements ) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-segmentation API: expected Array<{label: string, mask: string, score: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference image-segmentation API: expected Array<{label: string, mask: string, score: number}>" + ); } } export class HFInferenceImageToTextTask extends HFInferenceTask implements ImageToTextTaskHelper { override async getResponse(response: ImageToTextOutput): Promise { if (typeof response?.generated_text !== "string") { - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-to-text API: expected {generated_text: string}"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference image-to-text API: expected {generated_text: string}" + ); } return response; } @@ -354,7 +374,9 @@ export class HFInferenceImageToImageTask extends HFInferenceTask implements Imag if (response instanceof Blob) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference image-to-image API: expected Blob"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference image-to-image API: expected Blob" + ); } } @@ -387,7 +409,9 @@ export class HFInferenceZeroShotImageClassificationTask if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference zero-shot-image-classification API: expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference zero-shot-image-classification API: expected Array<{label: string, score: number}>" + ); } } @@ -397,7 +421,9 @@ export class HFInferenceTextClassificationTask extends HFInferenceTask implement if (Array.isArray(output) && output.every((x) => typeof x?.label === "string" && typeof x.score === "number")) { return output; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text-classification API: expected Array<{label: string, score: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference text-classification API: expected Array<{label: string, score: number}>" + ); } } @@ -425,7 +451,9 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements ) { return Array.isArray(response) ? response[0] : response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>" + ); } } @@ -464,7 +492,9 @@ export class HFInferenceZeroShotClassificationTask extends HFInferenceTask imple ) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference zero-shot-classification API: expected Array<{labels: string[], scores: number[], sequence: string}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference zero-shot-classification API: expected Array<{labels: string[], scores: number[], sequence: string}>" + ); } } @@ -473,7 +503,9 @@ export class HFInferenceSentenceSimilarityTask extends HFInferenceTask implement if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference sentence-similarity API: expected Array"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference sentence-similarity API: expected Array" + ); } } @@ -536,7 +568,9 @@ export class HFInferenceTranslationTask extends HFInferenceTask implements Trans if (Array.isArray(response) && response.every((x) => typeof x?.translation_text === "string")) { return response?.length === 1 ? response?.[0] : response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference translation API: expected Array<{translation_text: string}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference translation API: expected Array<{translation_text: string}>" + ); } } @@ -545,7 +579,9 @@ export class HFInferenceSummarizationTask extends HFInferenceTask implements Sum if (Array.isArray(response) && response.every((x) => typeof x?.summary_text === "string")) { return response?.[0]; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference summarization API: expected Array<{summary_text: string}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference summarization API: expected Array<{summary_text: string}>" + ); } } @@ -560,7 +596,9 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference tabular-classification API: expected Array"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference tabular-classification API: expected Array" + ); } } @@ -577,7 +615,9 @@ export class HFInferenceVisualQuestionAnsweringTask ) { return response[0]; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference visual-question-answering API: expected Array<{answer: string, score: number}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference visual-question-answering API: expected Array<{answer: string, score: number}>" + ); } } @@ -586,7 +626,9 @@ export class HFInferenceTabularRegressionTask extends HFInferenceTask implements if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference tabular-regression API: expected Array"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference tabular-regression API: expected Array" + ); } } diff --git a/packages/inference/src/providers/hyperbolic.ts b/packages/inference/src/providers/hyperbolic.ts index 1d38e86647..5d9ea6949f 100644 --- a/packages/inference/src/providers/hyperbolic.ts +++ b/packages/inference/src/providers/hyperbolic.ts @@ -56,9 +56,9 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { messages: [{ content: params.args.inputs, role: "user" }], ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), ...omit(params.args, ["inputs", "parameters"]), model: params.model, diff --git a/packages/inference/src/providers/novita.ts b/packages/inference/src/providers/novita.ts index bb12dca00d..f5b32b60fe 100644 --- a/packages/inference/src/providers/novita.ts +++ b/packages/inference/src/providers/novita.ts @@ -82,12 +82,15 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV } const taskId = response.task_id; if (!taskId) { - throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: no task ID found in the response"); + throw new HfInferenceProviderOutputError( + "Received malformed response from Novita text-to-video API: no task ID found in the response" + ); } const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/novita" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ + parsedUrl.host === "router.huggingface.co" ? "/novita" : "" + }`; const resultUrl = `${baseUrl}/v3/async/task-result?task_id=${taskId}`; let status = ""; @@ -100,7 +103,11 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV throw new HfInferenceProviderApiError( "Failed to fetch task result", { url: resultUrl, method: "GET", headers }, - { requestId: resultResponse.headers.get("x-request-id") ?? "", status: resultResponse.status, body: await resultResponse.text() } + { + requestId: resultResponse.headers.get("x-request-id") ?? "", + status: resultResponse.status, + body: await resultResponse.text(), + } ); } try { @@ -116,10 +123,14 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV ) { status = taskResult.task.status; } else { - throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: failed to get task status"); + throw new HfInferenceProviderOutputError( + "Received malformed response from Novita text-to-video API: failed to get task status" + ); } } catch (error) { - throw new HfInferenceProviderOutputError("Received malformed response from Novita text-to-video API: failed to parse task result"); + throw new HfInferenceProviderOutputError( + "Received malformed response from Novita text-to-video API: failed to parse task result" + ); } } @@ -142,7 +153,11 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV const urlResponse = await fetch(taskResult.videos[0].video_url); return await urlResponse.blob(); } else { - throw new HfInferenceProviderOutputError(`Received malformed response from Novita text-to-video API: expected { videos: [{ video_url: string }] } format, got instead: ${JSON.stringify(taskResult)}`); + throw new HfInferenceProviderOutputError( + `Received malformed response from Novita text-to-video API: expected { videos: [{ video_url: string }] } format, got instead: ${JSON.stringify( + taskResult + )}` + ); } } } diff --git a/packages/inference/src/providers/ovhcloud.ts b/packages/inference/src/providers/ovhcloud.ts index e8a8f48f23..ff7880cd4c 100644 --- a/packages/inference/src/providers/ovhcloud.ts +++ b/packages/inference/src/providers/ovhcloud.ts @@ -50,9 +50,9 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; diff --git a/packages/inference/src/providers/providerHelper.ts b/packages/inference/src/providers/providerHelper.ts index 2bbfdbb329..ad5274f25f 100644 --- a/packages/inference/src/providers/providerHelper.ts +++ b/packages/inference/src/providers/providerHelper.ts @@ -61,7 +61,7 @@ export abstract class TaskProviderHelper { readonly provider: InferenceProvider, private baseUrl: string, readonly clientSideRoutingOnly: boolean = false - ) { } + ) {} /** * Return the response in the expected format. diff --git a/packages/inference/src/utils/request.ts b/packages/inference/src/utils/request.ts index 08ba930590..96977c5522 100644 --- a/packages/inference/src/utils/request.ts +++ b/packages/inference/src/utils/request.ts @@ -17,10 +17,12 @@ export interface ResponseWrapper { function requestArgsToJson(args: RequestArgs): JsonObject { // Convert the entire args object to a JSON-serializable format const argsWithData = args as RequestArgs & { data?: Blob | ArrayBuffer }; - return JSON.parse(JSON.stringify({ - ...argsWithData, - data: argsWithData.data ? "[Blob or ArrayBuffer]" : null - })) as JsonObject; + return JSON.parse( + JSON.stringify({ + ...argsWithData, + data: argsWithData.data ? "[Blob or ArrayBuffer]" : null, + }) + ) as JsonObject; } /** @@ -51,29 +53,51 @@ export async function innerRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new HfInferenceProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${args.model} . Error: ${JSON.stringify(output.error)}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + `Provider ${args.provider} does not seem to support chat completion for model ${ + args.model + } . Error: ${JSON.stringify(output.error)}`, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } if (typeof output.error === "string" || typeof output.detail === "string") { throw new HfInferenceProviderApiError( `Failed to perform inference: ${output.error ?? output.detail}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } else { throw new HfInferenceProviderApiError( `Failed to perform inference: an HTTP error occurred when requesting the provider.`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } } const message = contentType?.startsWith("text/plain;") ? await response.text() : undefined; throw new HfInferenceProviderApiError( `Failed to perform inference: ${message ?? "an HTTP error occurred when requesting the provider"}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: message ?? "" } ); } @@ -111,46 +135,79 @@ export async function* innerStreamingRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new HfInferenceProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${args.model} . Error: ${JSON.stringify(output.error)}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + `Provider ${args.provider} does not seem to support chat completion for model ${ + args.model + } . Error: ${JSON.stringify(output.error)}`, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } if (typeof output.error === "string") { throw new HfInferenceProviderApiError( `Failed to perform inference: ${output.error}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } if (output.error && "message" in output.error && typeof output.error.message === "string") { /// OpenAI errors throw new HfInferenceProviderApiError( `Failed to perform inference: ${output.error.message}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } // Sambanova errors if (typeof output.message === "string") { throw new HfInferenceProviderApiError( `Failed to perform inference: ${output.message}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } } throw new HfInferenceProviderApiError( `Failed to perform inference: an HTTP error occurred when requesting the provider.`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: "" } ); } if (!response.headers.get("content-type")?.startsWith("text/event-stream")) { throw new HfInferenceProviderApiError( - `Failed to perform inference: server does not support event stream content type, it returned ` + response.headers.get("content-type"), - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, + `Failed to perform inference: server does not support event stream content type, it returned ` + + response.headers.get("content-type"), + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: "" } ); } @@ -169,8 +226,8 @@ export async function* innerStreamingRequest( const onChunk = getLines( getMessages( - () => { }, - () => { }, + () => {}, + () => {}, onEvent ) ); @@ -193,15 +250,20 @@ export async function* innerStreamingRequest( typeof data.error === "string" ? data.error : typeof data.error === "object" && - data.error && - "message" in data.error && - typeof data.error.message === "string" - ? data.error.message - : JSON.stringify(data.error); + data.error && + "message" in data.error && + typeof data.error.message === "string" + ? data.error.message + : JSON.stringify(data.error); throw new HfInferenceProviderApiError( `Failed to perform inference: an occurred while streaming the response: ${errorStr}`, - { url, method: info.method ?? "GET", headers: info.headers as Record, body: requestArgsToJson(args) }, - { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: data }, + { + url, + method: info.method ?? "GET", + headers: info.headers as Record, + body: requestArgsToJson(args), + }, + { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: data } ); } yield data as T; From ed20d689687ff2b9dc0900624692fad0ab3c2182 Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Thu, 29 May 2025 16:03:46 +0200 Subject: [PATCH 05/10] format+lint --- .../inference/src/providers/hf-inference.ts | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index c297e2dc2f..fb95b76a0e 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -199,7 +199,9 @@ export class HFInferenceTextGenerationTask extends HFInferenceTask implements Te if (Array.isArray(res) && res.every((x) => "generated_text" in x && typeof x?.generated_text === "string")) { return (res as TextGenerationOutput[])?.[0]; } - throw new HfInferenceProviderOutputError("Received malformed response from HF-Inference text generation API: expected Array<{generated_text: string}>"); + throw new HfInferenceProviderOutputError( + "Received malformed response from HF-Inference text generation API: expected Array<{generated_text: string}>" + ); } } @@ -222,7 +224,8 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen export class HFInferenceAutomaticSpeechRecognitionTask extends HFInferenceTask - implements AutomaticSpeechRecognitionTaskHelper { + implements AutomaticSpeechRecognitionTaskHelper +{ override async getResponse(response: AutomaticSpeechRecognitionOutput): Promise { return response; } @@ -231,9 +234,9 @@ export class HFInferenceAutomaticSpeechRecognitionTask return "data" in args ? args : { - ...omit(args, "inputs"), - data: args.inputs, - }; + ...omit(args, "inputs"), + data: args.inputs, + }; } } @@ -268,7 +271,8 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi export class HFInferenceDocumentQuestionAnsweringTask extends HFInferenceTask - implements DocumentQuestionAnsweringTaskHelper { + implements DocumentQuestionAnsweringTaskHelper +{ override async getResponse( response: DocumentQuestionAnsweringOutput ): Promise { @@ -404,7 +408,8 @@ export class HFInferenceObjectDetectionTask extends HFInferenceTask implements O export class HFInferenceZeroShotImageClassificationTask extends HFInferenceTask - implements ZeroShotImageClassificationTaskHelper { + implements ZeroShotImageClassificationTaskHelper +{ override async getResponse(response: ZeroShotImageClassificationOutput): Promise { if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; @@ -434,20 +439,20 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements if ( Array.isArray(response) ? response.every( - (elem) => - typeof elem === "object" && - !!elem && - typeof elem.answer === "string" && - typeof elem.end === "number" && - typeof elem.score === "number" && - typeof elem.start === "number" - ) + (elem) => + typeof elem === "object" && + !!elem && + typeof elem.answer === "string" && + typeof elem.end === "number" && + typeof elem.score === "number" && + typeof elem.start === "number" + ) : typeof response === "object" && - !!response && - typeof response.answer === "string" && - typeof response.end === "number" && - typeof response.score === "number" && - typeof response.start === "number" + !!response && + typeof response.answer === "string" && + typeof response.end === "number" && + typeof response.score === "number" && + typeof response.start === "number" ) { return Array.isArray(response) ? response[0] : response; } @@ -604,7 +609,8 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem export class HFInferenceVisualQuestionAnsweringTask extends HFInferenceTask - implements VisualQuestionAnsweringTaskHelper { + implements VisualQuestionAnsweringTaskHelper +{ override async getResponse(response: VisualQuestionAnsweringOutput): Promise { if ( Array.isArray(response) && From 5f1ee42538baaba26f6ebc00428a01d598277f23 Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Thu, 29 May 2025 16:17:11 +0200 Subject: [PATCH 06/10] add some docs about errors in the README --- packages/inference/README.md | 102 +++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/packages/inference/README.md b/packages/inference/README.md index 21e46625be..22e4a98eb7 100644 --- a/packages/inference/README.md +++ b/packages/inference/README.md @@ -120,6 +120,108 @@ await textGeneration({ This will enable tree-shaking by your bundler. +### Error handling + +The inference package provides specific error types to help you handle different error scenarios effectively. + +#### Error Types + +The package defines several error types that extend the base `Error` class: + +- `HfInferenceError`: Base error class for all Hugging Face Inference errors +- `HfInferenceInputError`: Thrown when there are issues with input parameters +- `HfInferenceProviderApiError`: Thrown when there are API-level errors from providers +- `HfInferenceHubApiError`: Thrown when there are API-levels errors from the Hugging Face Hub +- `HfInferenceProviderOutputError`: Thrown when there are issues with providers' API responses format + +### Example Usage + +```typescript +import { InferenceClient } from "@huggingface/inference"; +import { + HfInferenceError, + HfInferenceProviderApiError, + HfInferenceProviderOutputError, + HfInferenceHubApiError, +} from "@huggingface/inference"; + +const hf = new HfInference(); + +try { + const result = await hf.textGeneration({ + model: "gpt2", + inputs: "Hello, I'm a language model", + }); +} catch (error) { + if (error instanceof HfInferenceProviderApiError) { + // Handle API errors (e.g., rate limits, authentication issues) + console.error("Provider API Error:", error.message); + console.error("HTTP Request details:", error.request); + console.error("HTTP Response details:", error.response); + if (error instanceof HfInferenceHubApiError) { + // Handle API errors (e.g., rate limits, authentication issues) + console.error("Hub API Error:", error.message); + console.error("HTTP Request details:", error.request); + console.error("HTTP Response details:", error.response); + } else if (error instanceof HfInferenceProviderOutputError) { + // Handle malformed responses from providers + console.error("Provider Output Error:", error.message); + } else if (error instanceof HfInferenceInputError) { + // Handle invalid input parameters + console.error("Input Error:", error.message); + } else { + // Handle unexpected errors + console.error("Unexpected error:", error); + } +} + +/// Catch all errors from @huggingface/inference +try { + const result = await hf.textGeneration({ + model: "gpt2", + inputs: "Hello, I'm a language model", + }); +} catch (error) { + if (error instanceof HfInferenceError) { + // Handle errors from @huggingface/inference + console.error("Error from InferenceClient:", error); + } else { + // Handle unexpected errors + console.error("Unexpected error:", error); + } +} +``` + +### Error Details + +#### HfInferenceProviderApiError + +This error occurs when there are issues with the API request when performing inference at the selected provider. + +It has several properties: +- `message`: A descriptive error message +- `request`: Details about the failed request (URL, method, headers) +- `response`: Response details including status code and body + +#### HfInferenceHubApiError + +This error occurs when there are issues with the API request when requesting the Hugging Face Hub API. + +It has several properties: +- `message`: A descriptive error message +- `request`: Details about the failed request (URL, method, headers) +- `response`: Response details including status code and body + + +#### HfInferenceProviderOutputError + +This error occurs when a provider returns a response in an unexpected format. + +#### HfInferenceInputError + +This error occurs when input parameters are invalid or missing. The error message describes what's wrong with the input. + + ### Natural Language Processing #### Text Generation From df0c580636e93ffc5315fc6b4473eaf7996b002a Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Mon, 2 Jun 2025 11:24:20 +0200 Subject: [PATCH 07/10] names --- packages/inference/README.md | 36 ++++++------- packages/inference/src/error.ts | 14 ++--- .../src/lib/getInferenceProviderMapping.ts | 18 +++---- .../inference/src/lib/getProviderHelper.ts | 8 +-- .../inference/src/lib/makeRequestOptions.ts | 22 ++++---- .../src/providers/black-forest-labs.ts | 12 +++-- packages/inference/src/providers/fal-ai.ts | 32 ++++++----- .../inference/src/providers/featherless-ai.ts | 4 +- .../inference/src/providers/hf-inference.ts | 54 +++++++++---------- .../inference/src/providers/hyperbolic.ts | 6 +-- packages/inference/src/providers/nebius.ts | 4 +- packages/inference/src/providers/novita.ts | 20 ++++--- packages/inference/src/providers/nscale.ts | 4 +- packages/inference/src/providers/ovhcloud.ts | 4 +- .../inference/src/providers/providerHelper.ts | 6 +-- packages/inference/src/providers/replicate.ts | 8 +-- packages/inference/src/providers/sambanova.ts | 4 +- packages/inference/src/providers/together.ts | 6 +-- .../tasks/audio/automaticSpeechRecognition.ts | 4 +- packages/inference/src/utils/request.ts | 24 ++++----- 20 files changed, 152 insertions(+), 138 deletions(-) diff --git a/packages/inference/README.md b/packages/inference/README.md index 22e4a98eb7..9bb3bdae8a 100644 --- a/packages/inference/README.md +++ b/packages/inference/README.md @@ -128,21 +128,21 @@ The inference package provides specific error types to help you handle different The package defines several error types that extend the base `Error` class: -- `HfInferenceError`: Base error class for all Hugging Face Inference errors -- `HfInferenceInputError`: Thrown when there are issues with input parameters -- `HfInferenceProviderApiError`: Thrown when there are API-level errors from providers -- `HfInferenceHubApiError`: Thrown when there are API-levels errors from the Hugging Face Hub -- `HfInferenceProviderOutputError`: Thrown when there are issues with providers' API responses format +- `InferenceClientError`: Base error class for all Hugging Face Inference errors +- `InferenceClientInputError`: Thrown when there are issues with input parameters +- `InferenceClientProviderApiError`: Thrown when there are API-level errors from providers +- `InferenceClientHubApiError`: Thrown when there are API-levels errors from the Hugging Face Hub +- `InferenceClientProviderOutputError`: Thrown when there are issues with providers' API responses format ### Example Usage ```typescript import { InferenceClient } from "@huggingface/inference"; import { - HfInferenceError, - HfInferenceProviderApiError, - HfInferenceProviderOutputError, - HfInferenceHubApiError, + InferenceClientError, + InferenceClientProviderApiError, + InferenceClientProviderOutputError, + InferenceClientHubApiError, } from "@huggingface/inference"; const hf = new HfInference(); @@ -153,20 +153,20 @@ try { inputs: "Hello, I'm a language model", }); } catch (error) { - if (error instanceof HfInferenceProviderApiError) { + if (error instanceof InferenceClientProviderApiError) { // Handle API errors (e.g., rate limits, authentication issues) console.error("Provider API Error:", error.message); console.error("HTTP Request details:", error.request); console.error("HTTP Response details:", error.response); - if (error instanceof HfInferenceHubApiError) { + if (error instanceof InferenceClientHubApiError) { // Handle API errors (e.g., rate limits, authentication issues) console.error("Hub API Error:", error.message); console.error("HTTP Request details:", error.request); console.error("HTTP Response details:", error.response); - } else if (error instanceof HfInferenceProviderOutputError) { + } else if (error instanceof InferenceClientProviderOutputError) { // Handle malformed responses from providers console.error("Provider Output Error:", error.message); - } else if (error instanceof HfInferenceInputError) { + } else if (error instanceof InferenceClientInputError) { // Handle invalid input parameters console.error("Input Error:", error.message); } else { @@ -182,7 +182,7 @@ try { inputs: "Hello, I'm a language model", }); } catch (error) { - if (error instanceof HfInferenceError) { + if (error instanceof InferenceClientError) { // Handle errors from @huggingface/inference console.error("Error from InferenceClient:", error); } else { @@ -194,7 +194,7 @@ try { ### Error Details -#### HfInferenceProviderApiError +#### InferenceClientProviderApiError This error occurs when there are issues with the API request when performing inference at the selected provider. @@ -203,7 +203,7 @@ It has several properties: - `request`: Details about the failed request (URL, method, headers) - `response`: Response details including status code and body -#### HfInferenceHubApiError +#### InferenceClientHubApiError This error occurs when there are issues with the API request when requesting the Hugging Face Hub API. @@ -213,11 +213,11 @@ It has several properties: - `response`: Response details including status code and body -#### HfInferenceProviderOutputError +#### InferenceClientProviderOutputError This error occurs when a provider returns a response in an unexpected format. -#### HfInferenceInputError +#### InferenceClientInputError This error occurs when input parameters are invalid or missing. The error message describes what's wrong with the input. diff --git a/packages/inference/src/error.ts b/packages/inference/src/error.ts index 8f96008fc1..01c23d976e 100644 --- a/packages/inference/src/error.ts +++ b/packages/inference/src/error.ts @@ -3,14 +3,14 @@ import type { JsonObject } from "./vendor/type-fest/basic.js"; /** * Base class for all inference-related errors. */ -export abstract class HfInferenceError extends Error { +export abstract class InferenceClientError extends Error { constructor(message: string) { super(message); - this.name = "HfInferenceError"; + this.name = "InferenceClientError"; } } -export class HfInferenceInputError extends HfInferenceError { +export class InferenceClientInputError extends InferenceClientError { constructor(message: string) { super(message); this.name = "InputError"; @@ -30,7 +30,7 @@ interface HttpResponse { body: JsonObject | string; } -abstract class HfInferenceHttpRequestError extends HfInferenceError { +abstract class InferenceClientHttpRequestError extends InferenceClientError { httpRequest: HttpRequest; httpResponse: HttpResponse; constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { @@ -54,7 +54,7 @@ abstract class HfInferenceHttpRequestError extends HfInferenceError { /** * Thrown when the HTTP request to the provider fails, e.g. due to API issues or server errors. */ -export class HfInferenceProviderApiError extends HfInferenceHttpRequestError { +export class InferenceClientProviderApiError extends InferenceClientHttpRequestError { constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { super(message, httpRequest, httpResponse); this.name = "ProviderApiError"; @@ -64,7 +64,7 @@ export class HfInferenceProviderApiError extends HfInferenceHttpRequestError { /** * Thrown when the HTTP request to the hub fails, e.g. due to API issues or server errors. */ -export class HfInferenceHubApiError extends HfInferenceHttpRequestError { +export class InferenceClientHubApiError extends InferenceClientHttpRequestError { constructor(message: string, httpRequest: HttpRequest, httpResponse: HttpResponse) { super(message, httpRequest, httpResponse); this.name = "HubApiError"; @@ -74,7 +74,7 @@ export class HfInferenceHubApiError extends HfInferenceHttpRequestError { /** * Thrown when the inference output returned by the provider is invalid / does not match the expectations */ -export class HfInferenceProviderOutputError extends HfInferenceError { +export class InferenceClientProviderOutputError extends InferenceClientError { constructor(message: string) { super(message); this.name = "ProviderOutputError"; diff --git a/packages/inference/src/lib/getInferenceProviderMapping.ts b/packages/inference/src/lib/getInferenceProviderMapping.ts index d1646b267c..1e6248ec40 100644 --- a/packages/inference/src/lib/getInferenceProviderMapping.ts +++ b/packages/inference/src/lib/getInferenceProviderMapping.ts @@ -4,7 +4,7 @@ import { HARDCODED_MODEL_INFERENCE_MAPPING } from "../providers/consts.js"; import { EQUIVALENT_SENTENCE_TRANSFORMERS_TASKS } from "../providers/hf-inference.js"; import type { InferenceProvider, InferenceProviderOrPolicy, ModelId } from "../types.js"; import { typedInclude } from "../utils/typedInclude.js"; -import { HfInferenceHubApiError, HfInferenceInputError } from "../error.js"; +import { InferenceClientHubApiError, InferenceClientInputError } from "../error.js"; export const inferenceProviderMappingCache = new Map(); @@ -41,14 +41,14 @@ export async function fetchInferenceProviderMappingForModel( if (resp.headers.get("Content-Type")?.startsWith("application/json")) { const error = await resp.json(); if ("error" in error && typeof error.error === "string") { - throw new HfInferenceHubApiError( + throw new InferenceClientHubApiError( `Failed to fetch inference provider mapping for model ${modelId}: ${error.error}`, { url, method: "GET" }, { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: error } ); } } else { - throw new HfInferenceHubApiError( + throw new InferenceClientHubApiError( `Failed to fetch inference provider mapping for model ${modelId}`, { url, method: "GET" }, { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } @@ -59,14 +59,14 @@ export async function fetchInferenceProviderMappingForModel( try { payload = await resp.json(); } catch { - throw new HfInferenceHubApiError( + throw new InferenceClientHubApiError( `Failed to fetch inference provider mapping for model ${modelId}: malformed API response, invalid JSON`, { url, method: "GET" }, { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } ); } if (!payload?.inferenceProviderMapping) { - throw new HfInferenceHubApiError( + throw new InferenceClientHubApiError( `We have not been able to find inference provider information for model ${modelId}.`, { url, method: "GET" }, { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() } @@ -103,7 +103,7 @@ export async function getInferenceProviderMapping( ? EQUIVALENT_SENTENCE_TRANSFORMERS_TASKS : [params.task]; if (!typedInclude(equivalentTasks, providerMapping.task)) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `Model ${params.modelId} is not supported for task ${params.task} and provider ${params.provider}. Supported task: ${providerMapping.task}.` ); } @@ -124,7 +124,7 @@ export async function resolveProvider( ): Promise { if (endpointUrl) { if (provider) { - throw new HfInferenceInputError("Specifying both endpointUrl and provider is not supported."); + throw new InferenceClientInputError("Specifying both endpointUrl and provider is not supported."); } /// Defaulting to hf-inference helpers / API return "hf-inference"; @@ -137,13 +137,13 @@ export async function resolveProvider( } if (provider === "auto") { if (!modelId) { - throw new HfInferenceInputError("Specifying a model is required when provider is 'auto'"); + throw new InferenceClientInputError("Specifying a model is required when provider is 'auto'"); } const inferenceProviderMapping = await fetchInferenceProviderMappingForModel(modelId); provider = Object.keys(inferenceProviderMapping)[0] as InferenceProvider | undefined; } if (!provider) { - throw new HfInferenceInputError(`No Inference Provider available for model ${modelId}.`); + throw new InferenceClientInputError(`No Inference Provider available for model ${modelId}.`); } return provider; } diff --git a/packages/inference/src/lib/getProviderHelper.ts b/packages/inference/src/lib/getProviderHelper.ts index a0ee7262ae..11696f8140 100644 --- a/packages/inference/src/lib/getProviderHelper.ts +++ b/packages/inference/src/lib/getProviderHelper.ts @@ -48,7 +48,7 @@ import * as Replicate from "../providers/replicate.js"; import * as Sambanova from "../providers/sambanova.js"; import * as Together from "../providers/together.js"; import type { InferenceProvider, InferenceProviderOrPolicy, InferenceTask } from "../types.js"; -import { HfInferenceInputError } from "../error.js"; +import { InferenceClientInputError } from "../error.js"; export const PROVIDERS: Record>> = { "black-forest-labs": { @@ -282,18 +282,18 @@ export function getProviderHelper( return new HFInference.HFInferenceTask(); } if (!task) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( "you need to provide a task name when using an external provider, e.g. 'text-to-image'" ); } if (!(provider in PROVIDERS)) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `Provider '${provider}' not supported. Available providers: ${Object.keys(PROVIDERS)}` ); } const providerTasks = PROVIDERS[provider]; if (!providerTasks || !(task in providerTasks)) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `Task '${task}' not supported for provider '${provider}'. Available tasks: ${Object.keys(providerTasks ?? {})}` ); } diff --git a/packages/inference/src/lib/makeRequestOptions.ts b/packages/inference/src/lib/makeRequestOptions.ts index fcb0f9f71a..53115e6c4f 100644 --- a/packages/inference/src/lib/makeRequestOptions.ts +++ b/packages/inference/src/lib/makeRequestOptions.ts @@ -5,7 +5,7 @@ import type { InferenceProviderModelMapping } from "./getInferenceProviderMappin import { getInferenceProviderMapping } from "./getInferenceProviderMapping.js"; import type { getProviderHelper } from "./getProviderHelper.js"; import { isUrl } from "./isUrl.js"; -import { HfInferenceHubApiError, HfInferenceInputError } from "../error.js"; +import { InferenceClientHubApiError, InferenceClientInputError } from "../error.js"; /** * Lazy-loaded from huggingface.co/api/tasks when needed @@ -34,10 +34,10 @@ export async function makeRequestOptions( // Validate inputs if (args.endpointUrl && provider !== "hf-inference") { - throw new HfInferenceInputError(`Cannot use endpointUrl with a third-party provider.`); + throw new InferenceClientInputError(`Cannot use endpointUrl with a third-party provider.`); } if (maybeModel && isUrl(maybeModel)) { - throw new HfInferenceInputError(`Model URLs are no longer supported. Use endpointUrl instead.`); + throw new InferenceClientInputError(`Model URLs are no longer supported. Use endpointUrl instead.`); } if (args.endpointUrl) { @@ -52,14 +52,14 @@ export async function makeRequestOptions( } if (!maybeModel && !task) { - throw new HfInferenceInputError("No model provided, and no task has been specified."); + throw new InferenceClientInputError("No model provided, and no task has been specified."); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const hfModel = maybeModel ?? (await loadDefaultModel(task!)); if (providerHelper.clientSideRoutingOnly && !maybeModel) { - throw new HfInferenceInputError(`Provider ${provider} requires a model ID to be passed directly.`); + throw new InferenceClientInputError(`Provider ${provider} requires a model ID to be passed directly.`); } const inferenceProviderMapping = providerHelper.clientSideRoutingOnly @@ -83,7 +83,7 @@ export async function makeRequestOptions( { fetch: options?.fetch } ); if (!inferenceProviderMapping) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `We have not been able to find inference provider information for model ${hfModel}.` ); } @@ -125,7 +125,7 @@ export function makeRequestOptionsFromResolvedModel( if (providerHelper.clientSideRoutingOnly) { // Closed-source providers require an accessToken (cannot be routed). if (accessToken && accessToken.startsWith("hf_")) { - throw new HfInferenceInputError(`Provider ${provider} is closed-source and does not support HF tokens.`); + throw new InferenceClientInputError(`Provider ${provider} is closed-source and does not support HF tokens.`); } } if (accessToken) { @@ -199,7 +199,9 @@ async function loadDefaultModel(task: InferenceTask): Promise { } const taskInfo = tasks[task]; if ((taskInfo?.models.length ?? 0) <= 0) { - throw new HfInferenceInputError(`No default model defined for task ${task}, please define the model explicitly.`); + throw new InferenceClientInputError( + `No default model defined for task ${task}, please define the model explicitly.` + ); } return taskInfo.models[0].id; } @@ -209,7 +211,7 @@ async function loadTaskInfo(): Promise ): Promise { if (!url || !headers) { - throw new HfInferenceInputError("URL and headers are required for text-to-video task"); + throw new InferenceClientInputError("URL and headers are required for text-to-video task"); } const requestId = response.request_id; if (!requestId) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from Fal.ai text-to-video API: no request ID found in the response" ); } @@ -178,7 +182,7 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe const statusResponse = await fetch(statusUrl, { headers }); if (!statusResponse.ok) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( "Failed to fetch response status from fal-ai API", { url: statusUrl, method: "GET" }, { @@ -191,7 +195,7 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe try { status = (await statusResponse.json()).status; } catch (error) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Failed to parse status response from fal-ai API: received malformed response" ); } @@ -202,7 +206,7 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe try { result = await resultResponse.json(); } catch (error) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Failed to parse result response from fal-ai API: received malformed response" ); } @@ -219,7 +223,7 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe const urlResponse = await fetch(result.video.url); return await urlResponse.blob(); } else { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( `Received malformed response from Fal.ai text-to-video API: expected { video: { url: string } } result format, got instead: ${JSON.stringify( result )}` @@ -237,7 +241,7 @@ export class FalAIAutomaticSpeechRecognitionTask extends FalAITask implements Au override async getResponse(response: unknown): Promise { const res = response as FalAIAutomaticSpeechRecognitionOutput; if (typeof res?.text !== "string") { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( `Received malformed response from Fal.ai Automatic Speech Recognition API: expected { text: string } format, got instead: ${JSON.stringify( response )}` @@ -250,12 +254,12 @@ export class FalAIAutomaticSpeechRecognitionTask extends FalAITask implements Au const blob = "data" in args && args.data instanceof Blob ? args.data : "inputs" in args ? args.inputs : undefined; const contentType = blob?.type; if (!contentType) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `Unable to determine the input's content-type. Make sure your are passing a Blob when using provider fal-ai.` ); } if (!FAL_AI_SUPPORTED_BLOB_TYPES.includes(contentType)) { - throw new HfInferenceInputError( + throw new InferenceClientInputError( `Provider fal-ai does not support blob type ${contentType} - supported content types are: ${FAL_AI_SUPPORTED_BLOB_TYPES.join( ", " )}` @@ -281,7 +285,7 @@ export class FalAITextToSpeechTask extends FalAITask { override async getResponse(response: unknown): Promise { const res = response as FalAITextToSpeechOutput; if (typeof res?.audio?.url !== "string") { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( `Received malformed response from Fal.ai Text-to-Speech API: expected { audio: { url: string } } format, got instead: ${JSON.stringify( response )}` @@ -289,7 +293,7 @@ export class FalAITextToSpeechTask extends FalAITask { } const urlResponse = await fetch(res.audio.url); if (!urlResponse.ok) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to fetch audio from ${res.audio.url}: ${urlResponse.statusText}`, { url: res.audio.url, method: "GET", headers: { "Content-Type": "application/json" } }, { @@ -302,7 +306,7 @@ export class FalAITextToSpeechTask extends FalAITask { try { return await urlResponse.blob(); } catch (error) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to fetch audio from ${res.audio.url}: ${error instanceof Error ? error.message : String(error)}`, { url: res.audio.url, method: "GET", headers: { "Content-Type": "application/json" } }, { diff --git a/packages/inference/src/providers/featherless-ai.ts b/packages/inference/src/providers/featherless-ai.ts index 459876e0ab..31e6a52bd8 100644 --- a/packages/inference/src/providers/featherless-ai.ts +++ b/packages/inference/src/providers/featherless-ai.ts @@ -7,7 +7,7 @@ import type { import type { BodyParams } from "../types.js"; import { BaseConversationalTask, BaseTextGenerationTask } from "./providerHelper.js"; import { omit } from "../utils/omit.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; interface FeatherlessAITextCompletionOutput extends Omit { choices: Array<{ @@ -58,6 +58,6 @@ export class FeatherlessAITextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new HfInferenceProviderOutputError("Received malformed response from Featherless AI text generation API"); + throw new InferenceClientProviderOutputError("Received malformed response from Featherless AI text generation API"); } } diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index fb95b76a0e..f765b62355 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -34,7 +34,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; import type { TabularClassificationOutput } from "../tasks/tabular/tabularClassification.js"; import type { BodyParams, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -127,7 +127,7 @@ export class HFInferenceTextToImageTask extends HFInferenceTask implements TextT outputType?: "url" | "blob" ): Promise { if (!response) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference text-to-image API: response is undefined" ); } @@ -156,7 +156,7 @@ export class HFInferenceTextToImageTask extends HFInferenceTask implements TextT } return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference text-to-image API: expected a Blob" ); } @@ -199,7 +199,7 @@ export class HFInferenceTextGenerationTask extends HFInferenceTask implements Te if (Array.isArray(res) && res.every((x) => "generated_text" in x && typeof x?.generated_text === "string")) { return (res as TextGenerationOutput[])?.[0]; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference text generation API: expected Array<{generated_text: string}>" ); } @@ -216,7 +216,7 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference audio-classification API: expected Array<{label: string, score: number}> but received different format" ); } @@ -243,7 +243,7 @@ export class HFInferenceAutomaticSpeechRecognitionTask export class HFInferenceAudioToAudioTask extends HFInferenceTask implements AudioToAudioTaskHelper { override async getResponse(response: AudioToAudioOutput[]): Promise { if (!Array.isArray(response)) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference audio-to-audio API: expected Array" ); } @@ -261,7 +261,7 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi ); }) ) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference audio-to-audio API: expected Array<{label: string, audio: Blob}>" ); } @@ -290,7 +290,7 @@ export class HFInferenceDocumentQuestionAnsweringTask ) { return response[0]; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference document-question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>" ); } @@ -309,7 +309,7 @@ export class HFInferenceFeatureExtractionTask extends HFInferenceTask implements if (Array.isArray(response) && isNumArrayRec(response, 3, 0)) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference feature-extraction API: expected Array" ); } @@ -320,7 +320,7 @@ export class HFInferenceImageClassificationTask extends HFInferenceTask implemen if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference image-classification API: expected Array<{label: string, score: number}>" ); } @@ -339,7 +339,7 @@ export class HFInferenceImageSegmentationTask extends HFInferenceTask implements ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference image-segmentation API: expected Array<{label: string, mask: string, score: number}>" ); } @@ -348,7 +348,7 @@ export class HFInferenceImageSegmentationTask extends HFInferenceTask implements export class HFInferenceImageToTextTask extends HFInferenceTask implements ImageToTextTaskHelper { override async getResponse(response: ImageToTextOutput): Promise { if (typeof response?.generated_text !== "string") { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference image-to-text API: expected {generated_text: string}" ); } @@ -378,7 +378,7 @@ export class HFInferenceImageToImageTask extends HFInferenceTask implements Imag if (response instanceof Blob) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference image-to-image API: expected Blob" ); } @@ -400,7 +400,7 @@ export class HFInferenceObjectDetectionTask extends HFInferenceTask implements O ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference object-detection API: expected Array<{label: string, score: number, box: {xmin: number, ymin: number, xmax: number, ymax: number}}>" ); } @@ -414,7 +414,7 @@ export class HFInferenceZeroShotImageClassificationTask if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference zero-shot-image-classification API: expected Array<{label: string, score: number}>" ); } @@ -426,7 +426,7 @@ export class HFInferenceTextClassificationTask extends HFInferenceTask implement if (Array.isArray(output) && output.every((x) => typeof x?.label === "string" && typeof x.score === "number")) { return output; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference text-classification API: expected Array<{label: string, score: number}>" ); } @@ -456,7 +456,7 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements ) { return Array.isArray(response) ? response[0] : response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference question-answering API: expected Array<{answer: string, end: number, score: number, start: number}>" ); } @@ -476,7 +476,7 @@ export class HFInferenceFillMaskTask extends HFInferenceTask implements FillMask ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference fill-mask API: expected Array<{score: number, sequence: string, token: number, token_str: string}>" ); } @@ -497,7 +497,7 @@ export class HFInferenceZeroShotClassificationTask extends HFInferenceTask imple ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference zero-shot-classification API: expected Array<{labels: string[], scores: number[], sequence: string}>" ); } @@ -508,7 +508,7 @@ export class HFInferenceSentenceSimilarityTask extends HFInferenceTask implement if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference sentence-similarity API: expected Array" ); } @@ -541,7 +541,7 @@ export class HFInferenceTableQuestionAnsweringTask extends HFInferenceTask imple ) { return Array.isArray(response) ? response[0] : response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference table-question-answering API: expected {aggregator: string, answer: string, cells: string[], coordinates: number[][]}" ); } @@ -562,7 +562,7 @@ export class HFInferenceTokenClassificationTask extends HFInferenceTask implemen ) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference token-classification API: expected Array<{end: number, entity_group: string, score: number, start: number, word: string}>" ); } @@ -573,7 +573,7 @@ export class HFInferenceTranslationTask extends HFInferenceTask implements Trans if (Array.isArray(response) && response.every((x) => typeof x?.translation_text === "string")) { return response?.length === 1 ? response?.[0] : response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference translation API: expected Array<{translation_text: string}>" ); } @@ -584,7 +584,7 @@ export class HFInferenceSummarizationTask extends HFInferenceTask implements Sum if (Array.isArray(response) && response.every((x) => typeof x?.summary_text === "string")) { return response?.[0]; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference summarization API: expected Array<{summary_text: string}>" ); } @@ -601,7 +601,7 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference tabular-classification API: expected Array" ); } @@ -621,7 +621,7 @@ export class HFInferenceVisualQuestionAnsweringTask ) { return response[0]; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference visual-question-answering API: expected Array<{answer: string, score: number}>" ); } @@ -632,7 +632,7 @@ export class HFInferenceTabularRegressionTask extends HFInferenceTask implements if (Array.isArray(response) && response.every((x) => typeof x === "number")) { return response; } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from HF-Inference tabular-regression API: expected Array" ); } diff --git a/packages/inference/src/providers/hyperbolic.ts b/packages/inference/src/providers/hyperbolic.ts index 5d9ea6949f..91e26b8f41 100644 --- a/packages/inference/src/providers/hyperbolic.ts +++ b/packages/inference/src/providers/hyperbolic.ts @@ -23,7 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; const HYPERBOLIC_API_BASE_URL = "https://api.hyperbolic.xyz"; export interface HyperbolicTextCompletionOutput extends Omit { @@ -78,7 +78,7 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { }; } - throw new HfInferenceProviderOutputError("Received malformed response from Hyperbolic text generation API"); + throw new InferenceClientProviderOutputError("Received malformed response from Hyperbolic text generation API"); } } @@ -120,6 +120,6 @@ export class HyperbolicTextToImageTask extends TaskProviderHelper implements Tex return fetch(`data:image/jpeg;base64,${response.images[0].image}`).then((res) => res.blob()); } - throw new HfInferenceProviderOutputError("Received malformed response from Hyperbolic text-to-image API"); + throw new InferenceClientProviderOutputError("Received malformed response from Hyperbolic text-to-image API"); } } diff --git a/packages/inference/src/providers/nebius.ts b/packages/inference/src/providers/nebius.ts index a30423e6d4..4e5bb1f384 100644 --- a/packages/inference/src/providers/nebius.ts +++ b/packages/inference/src/providers/nebius.ts @@ -24,7 +24,7 @@ import { type FeatureExtractionTaskHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; const NEBIUS_API_BASE_URL = "https://api.studio.nebius.ai"; @@ -92,7 +92,7 @@ export class NebiusTextToImageTask extends TaskProviderHelper implements TextToI return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new HfInferenceProviderOutputError("Received malformed response from Nebius text-to-image API"); + throw new InferenceClientProviderOutputError("Received malformed response from Nebius text-to-image API"); } } diff --git a/packages/inference/src/providers/novita.ts b/packages/inference/src/providers/novita.ts index f5b32b60fe..747f4f0e0c 100644 --- a/packages/inference/src/providers/novita.ts +++ b/packages/inference/src/providers/novita.ts @@ -25,7 +25,11 @@ import { TaskProviderHelper, type TextToVideoTaskHelper, } from "./providerHelper.js"; -import { HfInferenceInputError, HfInferenceProviderApiError, HfInferenceProviderOutputError } from "../error.js"; +import { + InferenceClientInputError, + InferenceClientProviderApiError, + InferenceClientProviderOutputError, +} from "../error.js"; const NOVITA_API_BASE_URL = "https://api.novita.ai"; @@ -78,11 +82,11 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV headers?: Record ): Promise { if (!url || !headers) { - throw new HfInferenceInputError("URL and headers are required for text-to-video task"); + throw new InferenceClientInputError("URL and headers are required for text-to-video task"); } const taskId = response.task_id; if (!taskId) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from Novita text-to-video API: no task ID found in the response" ); } @@ -100,7 +104,7 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV await delay(500); const resultResponse = await fetch(resultUrl, { headers }); if (!resultResponse.ok) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( "Failed to fetch task result", { url: resultUrl, method: "GET", headers }, { @@ -123,19 +127,19 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV ) { status = taskResult.task.status; } else { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from Novita text-to-video API: failed to get task status" ); } } catch (error) { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from Novita text-to-video API: failed to parse task result" ); } } if (status === "TASK_STATUS_FAILED") { - throw new HfInferenceProviderOutputError("Novita text-to-video task failed"); + throw new InferenceClientProviderOutputError("Novita text-to-video task failed"); } if ( @@ -153,7 +157,7 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV const urlResponse = await fetch(taskResult.videos[0].video_url); return await urlResponse.blob(); } else { - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( `Received malformed response from Novita text-to-video API: expected { videos: [{ video_url: string }] } format, got instead: ${JSON.stringify( taskResult )}` diff --git a/packages/inference/src/providers/nscale.ts b/packages/inference/src/providers/nscale.ts index 7c686b1b05..3444cfdca3 100644 --- a/packages/inference/src/providers/nscale.ts +++ b/packages/inference/src/providers/nscale.ts @@ -18,7 +18,7 @@ import type { TextToImageInput } from "@huggingface/tasks"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { BaseConversationalTask, TaskProviderHelper, type TextToImageTaskHelper } from "./providerHelper.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; const NSCALE_API_BASE_URL = "https://inference.api.nscale.com"; @@ -74,6 +74,6 @@ export class NscaleTextToImageTask extends TaskProviderHelper implements TextToI return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new HfInferenceProviderOutputError("Received malformed response from Nscale text-to-image API"); + throw new InferenceClientProviderOutputError("Received malformed response from Nscale text-to-image API"); } } diff --git a/packages/inference/src/providers/ovhcloud.ts b/packages/inference/src/providers/ovhcloud.ts index ff7880cd4c..92c911571d 100644 --- a/packages/inference/src/providers/ovhcloud.ts +++ b/packages/inference/src/providers/ovhcloud.ts @@ -20,7 +20,7 @@ import type { ChatCompletionOutput, TextGenerationOutput, TextGenerationOutputFi import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import type { TextGenerationInput } from "@huggingface/tasks"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; const OVHCLOUD_API_BASE_URL = "https://oai.endpoints.kepler.ai.cloud.ovh.net"; @@ -70,6 +70,6 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new HfInferenceProviderOutputError("Received malformed response from OVHcloud text generation API"); + throw new InferenceClientProviderOutputError("Received malformed response from OVHcloud text generation API"); } } diff --git a/packages/inference/src/providers/providerHelper.ts b/packages/inference/src/providers/providerHelper.ts index ad5274f25f..7b23702a2c 100644 --- a/packages/inference/src/providers/providerHelper.ts +++ b/packages/inference/src/providers/providerHelper.ts @@ -46,7 +46,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; import type { AudioToAudioOutput } from "../tasks/audio/audioToAudio.js"; import type { BaseArgs, BodyParams, HeaderParams, InferenceProvider, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -320,7 +320,7 @@ export class BaseConversationalTask extends TaskProviderHelper implements Conver return response; } - throw new HfInferenceProviderOutputError("Expected ChatCompletionOutput"); + throw new InferenceClientProviderOutputError("Expected ChatCompletionOutput"); } } @@ -353,6 +353,6 @@ export class BaseTextGenerationTask extends TaskProviderHelper implements TextGe return res[0]; } - throw new HfInferenceProviderOutputError("Expected Array<{generated_text: string}>"); + throw new InferenceClientProviderOutputError("Expected Array<{generated_text: string}>"); } } diff --git a/packages/inference/src/providers/replicate.ts b/packages/inference/src/providers/replicate.ts index e4810afefa..6156a82be4 100644 --- a/packages/inference/src/providers/replicate.ts +++ b/packages/inference/src/providers/replicate.ts @@ -14,7 +14,7 @@ * * Thanks! */ -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; import { isUrl } from "../lib/isUrl.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { omit } from "../utils/omit.js"; @@ -99,7 +99,7 @@ export class ReplicateTextToImageTask extends ReplicateTask implements TextToIma return await urlResponse.blob(); } - throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-image API"); + throw new InferenceClientProviderOutputError("Received malformed response from Replicate text-to-image API"); } } @@ -132,7 +132,7 @@ export class ReplicateTextToSpeechTask extends ReplicateTask { } } } - throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-speech API"); + throw new InferenceClientProviderOutputError("Received malformed response from Replicate text-to-speech API"); } } @@ -149,6 +149,6 @@ export class ReplicateTextToVideoTask extends ReplicateTask implements TextToVid return await urlResponse.blob(); } - throw new HfInferenceProviderOutputError("Received malformed response from Replicate text-to-video API"); + throw new InferenceClientProviderOutputError("Received malformed response from Replicate text-to-video API"); } } diff --git a/packages/inference/src/providers/sambanova.ts b/packages/inference/src/providers/sambanova.ts index 789fabf9ff..3e0bad1d63 100644 --- a/packages/inference/src/providers/sambanova.ts +++ b/packages/inference/src/providers/sambanova.ts @@ -18,7 +18,7 @@ import type { FeatureExtractionOutput } from "@huggingface/tasks"; import type { BodyParams } from "../types.js"; import type { FeatureExtractionTaskHelper } from "./providerHelper.js"; import { BaseConversationalTask, TaskProviderHelper } from "./providerHelper.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; export class SambanovaConversationalTask extends BaseConversationalTask { constructor() { @@ -39,7 +39,7 @@ export class SambanovaFeatureExtractionTask extends TaskProviderHelper implement if (typeof response === "object" && "data" in response && Array.isArray(response.data)) { return response.data.map((item) => item.embedding); } - throw new HfInferenceProviderOutputError( + throw new InferenceClientProviderOutputError( "Received malformed response from Sambanova feature-extraction (embeddings) API" ); } diff --git a/packages/inference/src/providers/together.ts b/packages/inference/src/providers/together.ts index 2520b9027c..c311c594f9 100644 --- a/packages/inference/src/providers/together.ts +++ b/packages/inference/src/providers/together.ts @@ -23,7 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { HfInferenceProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../error.js"; const TOGETHER_API_BASE_URL = "https://api.together.xyz"; @@ -74,7 +74,7 @@ export class TogetherTextGenerationTask extends BaseTextGenerationTask { generated_text: completion.text, }; } - throw new HfInferenceProviderOutputError("Received malformed response from Together text generation API"); + throw new InferenceClientProviderOutputError("Received malformed response from Together text generation API"); } } @@ -113,6 +113,6 @@ export class TogetherTextToImageTask extends TaskProviderHelper implements TextT return fetch(`data:image/jpeg;base64,${base64Data}`).then((res) => res.blob()); } - throw new HfInferenceProviderOutputError("Received malformed response from Together text-to-image API"); + throw new InferenceClientProviderOutputError("Received malformed response from Together text-to-image API"); } } diff --git a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts index 8d9e7228b2..295f193f90 100644 --- a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts +++ b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts @@ -4,7 +4,7 @@ import { getProviderHelper } from "../../lib/getProviderHelper.js"; import type { BaseArgs, Options } from "../../types.js"; import { innerRequest } from "../../utils/request.js"; import type { LegacyAudioInput } from "./utils.js"; -import { HfInferenceProviderOutputError } from "../../error.js"; +import { InferenceClientProviderOutputError } from "../../error.js"; export type AutomaticSpeechRecognitionArgs = BaseArgs & (AutomaticSpeechRecognitionInput | LegacyAudioInput); /** @@ -24,7 +24,7 @@ export async function automaticSpeechRecognition( }); const isValidOutput = typeof res?.text === "string"; if (!isValidOutput) { - throw new HfInferenceProviderOutputError("Received malformed response from automatic-speech-recognition API"); + throw new InferenceClientProviderOutputError("Received malformed response from automatic-speech-recognition API"); } return providerHelper.getResponse(res); } diff --git a/packages/inference/src/utils/request.ts b/packages/inference/src/utils/request.ts index 96977c5522..1390aa75c7 100644 --- a/packages/inference/src/utils/request.ts +++ b/packages/inference/src/utils/request.ts @@ -3,7 +3,7 @@ import { makeRequestOptions } from "../lib/makeRequestOptions.js"; import type { InferenceTask, Options, RequestArgs } from "../types.js"; import type { EventSourceMessage } from "../vendor/fetch-event-source/parse.js"; import { getLines, getMessages } from "../vendor/fetch-event-source/parse.js"; -import { HfInferenceProviderApiError } from "../error.js"; +import { InferenceClientProviderApiError } from "../error.js"; import type { JsonObject } from "../vendor/type-fest/basic.js"; export interface ResponseWrapper { @@ -52,7 +52,7 @@ export async function innerRequest( if (["application/json", "application/problem+json"].some((ct) => contentType?.startsWith(ct))) { const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Provider ${args.provider} does not seem to support chat completion for model ${ args.model } . Error: ${JSON.stringify(output.error)}`, @@ -66,7 +66,7 @@ export async function innerRequest( ); } if (typeof output.error === "string" || typeof output.detail === "string") { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: ${output.error ?? output.detail}`, { url, @@ -77,7 +77,7 @@ export async function innerRequest( { requestId: response.headers.get("x-request-id") ?? "", status: response.status, body: output } ); } else { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: an HTTP error occurred when requesting the provider.`, { url, @@ -90,7 +90,7 @@ export async function innerRequest( } } const message = contentType?.startsWith("text/plain;") ? await response.text() : undefined; - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: ${message ?? "an HTTP error occurred when requesting the provider"}`, { url, @@ -134,7 +134,7 @@ export async function* innerStreamingRequest( if (response.headers.get("Content-Type")?.startsWith("application/json")) { const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Provider ${args.provider} does not seem to support chat completion for model ${ args.model } . Error: ${JSON.stringify(output.error)}`, @@ -148,7 +148,7 @@ export async function* innerStreamingRequest( ); } if (typeof output.error === "string") { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: ${output.error}`, { url, @@ -161,7 +161,7 @@ export async function* innerStreamingRequest( } if (output.error && "message" in output.error && typeof output.error.message === "string") { /// OpenAI errors - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: ${output.error.message}`, { url, @@ -174,7 +174,7 @@ export async function* innerStreamingRequest( } // Sambanova errors if (typeof output.message === "string") { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: ${output.message}`, { url, @@ -187,7 +187,7 @@ export async function* innerStreamingRequest( } } - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: an HTTP error occurred when requesting the provider.`, { url, @@ -199,7 +199,7 @@ export async function* innerStreamingRequest( ); } if (!response.headers.get("content-type")?.startsWith("text/event-stream")) { - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: server does not support event stream content type, it returned ` + response.headers.get("content-type"), { @@ -255,7 +255,7 @@ export async function* innerStreamingRequest( typeof data.error.message === "string" ? data.error.message : JSON.stringify(data.error); - throw new HfInferenceProviderApiError( + throw new InferenceClientProviderApiError( `Failed to perform inference: an occurred while streaming the response: ${errorStr}`, { url, From 9a3240d37ad96bd08d1d5b7f9c16ed311c7854d5 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Mon, 2 Jun 2025 11:27:06 +0200 Subject: [PATCH 08/10] =?UTF-8?q?one=20more,=20will=20we=20ever=20get=20ri?= =?UTF-8?q?d=20of=20that=20old=20name=3F=20=F0=9F=94=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/inference/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/inference/README.md b/packages/inference/README.md index 9bb3bdae8a..5425204d05 100644 --- a/packages/inference/README.md +++ b/packages/inference/README.md @@ -145,10 +145,10 @@ import { InferenceClientHubApiError, } from "@huggingface/inference"; -const hf = new HfInference(); +const client = new InferenceClient(); try { - const result = await hf.textGeneration({ + const result = await client.textGeneration({ model: "gpt2", inputs: "Hello, I'm a language model", }); @@ -177,7 +177,7 @@ try { /// Catch all errors from @huggingface/inference try { - const result = await hf.textGeneration({ + const result = await client.textGeneration({ model: "gpt2", inputs: "Hello, I'm a language model", }); From 665f449ef12bfdf2ccfafdad7cab9fb10df2d600 Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Mon, 2 Jun 2025 12:23:33 +0200 Subject: [PATCH 09/10] rename error -> errors --- .../inference/src/{error.ts => errors.ts} | 12 ++--- packages/inference/src/index.ts | 2 +- .../src/lib/getInferenceProviderMapping.ts | 2 +- .../inference/src/lib/getProviderHelper.ts | 2 +- .../inference/src/lib/makeRequestOptions.ts | 34 +++++++------- .../src/providers/black-forest-labs.ts | 2 +- packages/inference/src/providers/fal-ai.ts | 7 ++- .../inference/src/providers/featherless-ai.ts | 8 ++-- .../inference/src/providers/hf-inference.ts | 46 +++++++++---------- .../inference/src/providers/hyperbolic.ts | 8 ++-- packages/inference/src/providers/nebius.ts | 2 +- packages/inference/src/providers/novita.ts | 7 ++- packages/inference/src/providers/nscale.ts | 2 +- packages/inference/src/providers/ovhcloud.ts | 8 ++-- .../inference/src/providers/providerHelper.ts | 4 +- packages/inference/src/providers/replicate.ts | 2 +- packages/inference/src/providers/sambanova.ts | 2 +- packages/inference/src/providers/together.ts | 2 +- .../tasks/audio/automaticSpeechRecognition.ts | 2 +- packages/inference/src/utils/request.ts | 24 +++++----- 20 files changed, 85 insertions(+), 93 deletions(-) rename packages/inference/src/{error.ts => errors.ts} (89%) diff --git a/packages/inference/src/error.ts b/packages/inference/src/errors.ts similarity index 89% rename from packages/inference/src/error.ts rename to packages/inference/src/errors.ts index 01c23d976e..1caa4ce7c6 100644 --- a/packages/inference/src/error.ts +++ b/packages/inference/src/errors.ts @@ -39,12 +39,12 @@ abstract class InferenceClientHttpRequestError extends InferenceClientError { ...httpRequest, ...(httpRequest.headers ? { - headers: { - ...httpRequest.headers, - ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), - /// redact authentication in the request headers - }, - } + headers: { + ...httpRequest.headers, + ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), + /// redact authentication in the request headers + }, + } : undefined), }; this.httpResponse = httpResponse; diff --git a/packages/inference/src/index.ts b/packages/inference/src/index.ts index ac05fc6a64..de23f5b6d2 100644 --- a/packages/inference/src/index.ts +++ b/packages/inference/src/index.ts @@ -1,5 +1,5 @@ export { InferenceClient, InferenceClientEndpoint, HfInference } from "./InferenceClient.js"; -export * from "./error.js"; +export * from "./errors.js"; export * from "./types.js"; export * from "./tasks/index.js"; import * as snippets from "./snippets/index.js"; diff --git a/packages/inference/src/lib/getInferenceProviderMapping.ts b/packages/inference/src/lib/getInferenceProviderMapping.ts index 1e6248ec40..29f779742d 100644 --- a/packages/inference/src/lib/getInferenceProviderMapping.ts +++ b/packages/inference/src/lib/getInferenceProviderMapping.ts @@ -4,7 +4,7 @@ import { HARDCODED_MODEL_INFERENCE_MAPPING } from "../providers/consts.js"; import { EQUIVALENT_SENTENCE_TRANSFORMERS_TASKS } from "../providers/hf-inference.js"; import type { InferenceProvider, InferenceProviderOrPolicy, ModelId } from "../types.js"; import { typedInclude } from "../utils/typedInclude.js"; -import { InferenceClientHubApiError, InferenceClientInputError } from "../error.js"; +import { InferenceClientHubApiError, InferenceClientInputError } from "../errors.js"; export const inferenceProviderMappingCache = new Map(); diff --git a/packages/inference/src/lib/getProviderHelper.ts b/packages/inference/src/lib/getProviderHelper.ts index 11696f8140..ad0eb89a6d 100644 --- a/packages/inference/src/lib/getProviderHelper.ts +++ b/packages/inference/src/lib/getProviderHelper.ts @@ -48,7 +48,7 @@ import * as Replicate from "../providers/replicate.js"; import * as Sambanova from "../providers/sambanova.js"; import * as Together from "../providers/together.js"; import type { InferenceProvider, InferenceProviderOrPolicy, InferenceTask } from "../types.js"; -import { InferenceClientInputError } from "../error.js"; +import { InferenceClientInputError } from "../errors.js"; export const PROVIDERS: Record>> = { "black-forest-labs": { diff --git a/packages/inference/src/lib/makeRequestOptions.ts b/packages/inference/src/lib/makeRequestOptions.ts index 53115e6c4f..5f87d8efc4 100644 --- a/packages/inference/src/lib/makeRequestOptions.ts +++ b/packages/inference/src/lib/makeRequestOptions.ts @@ -5,7 +5,7 @@ import type { InferenceProviderModelMapping } from "./getInferenceProviderMappin import { getInferenceProviderMapping } from "./getInferenceProviderMapping.js"; import type { getProviderHelper } from "./getProviderHelper.js"; import { isUrl } from "./isUrl.js"; -import { InferenceClientHubApiError, InferenceClientInputError } from "../error.js"; +import { InferenceClientHubApiError, InferenceClientInputError } from "../errors.js"; /** * Lazy-loaded from huggingface.co/api/tasks when needed @@ -64,24 +64,24 @@ export async function makeRequestOptions( const inferenceProviderMapping = providerHelper.clientSideRoutingOnly ? ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - providerId: removeProviderPrefix(maybeModel!, provider), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - hfModelId: maybeModel!, - status: "live", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + providerId: removeProviderPrefix(maybeModel!, provider), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + hfModelId: maybeModel!, + status: "live", + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + task: task!, + } satisfies InferenceProviderModelMapping) + : await getInferenceProviderMapping( + { + modelId: hfModel, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion task: task!, - } satisfies InferenceProviderModelMapping) - : await getInferenceProviderMapping( - { - modelId: hfModel, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - task: task!, - provider, - accessToken: args.accessToken, - }, - { fetch: options?.fetch } - ); + provider, + accessToken: args.accessToken, + }, + { fetch: options?.fetch } + ); if (!inferenceProviderMapping) { throw new InferenceClientInputError( `We have not been able to find inference provider information for model ${hfModel}.` diff --git a/packages/inference/src/providers/black-forest-labs.ts b/packages/inference/src/providers/black-forest-labs.ts index 273d71860c..82f275bea9 100644 --- a/packages/inference/src/providers/black-forest-labs.ts +++ b/packages/inference/src/providers/black-forest-labs.ts @@ -18,7 +18,7 @@ import { InferenceClientInputError, InferenceClientProviderApiError, InferenceClientProviderOutputError, -} from "../error.js"; +} from "../errors.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { delay } from "../utils/delay.js"; import { omit } from "../utils/omit.js"; diff --git a/packages/inference/src/providers/fal-ai.ts b/packages/inference/src/providers/fal-ai.ts index 53ca9bb211..8b72bc5e1d 100644 --- a/packages/inference/src/providers/fal-ai.ts +++ b/packages/inference/src/providers/fal-ai.ts @@ -33,7 +33,7 @@ import { InferenceClientInputError, InferenceClientProviderApiError, InferenceClientProviderOutputError, -} from "../error.js"; +} from "../errors.js"; export interface FalAiQueueOutput { request_id: string; @@ -165,9 +165,8 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe let status = response.status; const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ - parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" + }`; // extracting the provider model id for status and result urls // from the response as it might be different from the mapped model in `url` diff --git a/packages/inference/src/providers/featherless-ai.ts b/packages/inference/src/providers/featherless-ai.ts index 31e6a52bd8..4bd0239ccd 100644 --- a/packages/inference/src/providers/featherless-ai.ts +++ b/packages/inference/src/providers/featherless-ai.ts @@ -7,7 +7,7 @@ import type { import type { BodyParams } from "../types.js"; import { BaseConversationalTask, BaseTextGenerationTask } from "./providerHelper.js"; import { omit } from "../utils/omit.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; interface FeatherlessAITextCompletionOutput extends Omit { choices: Array<{ @@ -38,9 +38,9 @@ export class FeatherlessAITextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: params.args.parameters.max_new_tokens, - ...omit(params.args.parameters, "max_new_tokens"), - } + max_tokens: params.args.parameters.max_new_tokens, + ...omit(params.args.parameters, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index f765b62355..0ea55f8bd4 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -34,7 +34,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; import type { TabularClassificationOutput } from "../tasks/tabular/tabularClassification.js"; import type { BodyParams, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -224,8 +224,7 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen export class HFInferenceAutomaticSpeechRecognitionTask extends HFInferenceTask - implements AutomaticSpeechRecognitionTaskHelper -{ + implements AutomaticSpeechRecognitionTaskHelper { override async getResponse(response: AutomaticSpeechRecognitionOutput): Promise { return response; } @@ -234,9 +233,9 @@ export class HFInferenceAutomaticSpeechRecognitionTask return "data" in args ? args : { - ...omit(args, "inputs"), - data: args.inputs, - }; + ...omit(args, "inputs"), + data: args.inputs, + }; } } @@ -271,8 +270,7 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi export class HFInferenceDocumentQuestionAnsweringTask extends HFInferenceTask - implements DocumentQuestionAnsweringTaskHelper -{ + implements DocumentQuestionAnsweringTaskHelper { override async getResponse( response: DocumentQuestionAnsweringOutput ): Promise { @@ -408,8 +406,7 @@ export class HFInferenceObjectDetectionTask extends HFInferenceTask implements O export class HFInferenceZeroShotImageClassificationTask extends HFInferenceTask - implements ZeroShotImageClassificationTaskHelper -{ + implements ZeroShotImageClassificationTaskHelper { override async getResponse(response: ZeroShotImageClassificationOutput): Promise { if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; @@ -439,20 +436,20 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements if ( Array.isArray(response) ? response.every( - (elem) => - typeof elem === "object" && - !!elem && - typeof elem.answer === "string" && - typeof elem.end === "number" && - typeof elem.score === "number" && - typeof elem.start === "number" - ) + (elem) => + typeof elem === "object" && + !!elem && + typeof elem.answer === "string" && + typeof elem.end === "number" && + typeof elem.score === "number" && + typeof elem.start === "number" + ) : typeof response === "object" && - !!response && - typeof response.answer === "string" && - typeof response.end === "number" && - typeof response.score === "number" && - typeof response.start === "number" + !!response && + typeof response.answer === "string" && + typeof response.end === "number" && + typeof response.score === "number" && + typeof response.start === "number" ) { return Array.isArray(response) ? response[0] : response; } @@ -609,8 +606,7 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem export class HFInferenceVisualQuestionAnsweringTask extends HFInferenceTask - implements VisualQuestionAnsweringTaskHelper -{ + implements VisualQuestionAnsweringTaskHelper { override async getResponse(response: VisualQuestionAnsweringOutput): Promise { if ( Array.isArray(response) && diff --git a/packages/inference/src/providers/hyperbolic.ts b/packages/inference/src/providers/hyperbolic.ts index 91e26b8f41..3e399a33f1 100644 --- a/packages/inference/src/providers/hyperbolic.ts +++ b/packages/inference/src/providers/hyperbolic.ts @@ -23,7 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; const HYPERBOLIC_API_BASE_URL = "https://api.hyperbolic.xyz"; export interface HyperbolicTextCompletionOutput extends Omit { @@ -56,9 +56,9 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { messages: [{ content: params.args.inputs, role: "user" }], ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), ...omit(params.args, ["inputs", "parameters"]), model: params.model, diff --git a/packages/inference/src/providers/nebius.ts b/packages/inference/src/providers/nebius.ts index 4e5bb1f384..a5b31c6cf6 100644 --- a/packages/inference/src/providers/nebius.ts +++ b/packages/inference/src/providers/nebius.ts @@ -24,7 +24,7 @@ import { type FeatureExtractionTaskHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; const NEBIUS_API_BASE_URL = "https://api.studio.nebius.ai"; diff --git a/packages/inference/src/providers/novita.ts b/packages/inference/src/providers/novita.ts index 747f4f0e0c..df09e21ac9 100644 --- a/packages/inference/src/providers/novita.ts +++ b/packages/inference/src/providers/novita.ts @@ -29,7 +29,7 @@ import { InferenceClientInputError, InferenceClientProviderApiError, InferenceClientProviderOutputError, -} from "../error.js"; +} from "../errors.js"; const NOVITA_API_BASE_URL = "https://api.novita.ai"; @@ -92,9 +92,8 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV } const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ - parsedUrl.host === "router.huggingface.co" ? "/novita" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/novita" : "" + }`; const resultUrl = `${baseUrl}/v3/async/task-result?task_id=${taskId}`; let status = ""; diff --git a/packages/inference/src/providers/nscale.ts b/packages/inference/src/providers/nscale.ts index 3444cfdca3..85d1a8d0ff 100644 --- a/packages/inference/src/providers/nscale.ts +++ b/packages/inference/src/providers/nscale.ts @@ -18,7 +18,7 @@ import type { TextToImageInput } from "@huggingface/tasks"; import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import { BaseConversationalTask, TaskProviderHelper, type TextToImageTaskHelper } from "./providerHelper.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; const NSCALE_API_BASE_URL = "https://inference.api.nscale.com"; diff --git a/packages/inference/src/providers/ovhcloud.ts b/packages/inference/src/providers/ovhcloud.ts index 92c911571d..6673919184 100644 --- a/packages/inference/src/providers/ovhcloud.ts +++ b/packages/inference/src/providers/ovhcloud.ts @@ -20,7 +20,7 @@ import type { ChatCompletionOutput, TextGenerationOutput, TextGenerationOutputFi import type { BodyParams } from "../types.js"; import { omit } from "../utils/omit.js"; import type { TextGenerationInput } from "@huggingface/tasks"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; const OVHCLOUD_API_BASE_URL = "https://oai.endpoints.kepler.ai.cloud.ovh.net"; @@ -50,9 +50,9 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; diff --git a/packages/inference/src/providers/providerHelper.ts b/packages/inference/src/providers/providerHelper.ts index 7b23702a2c..dd359b108e 100644 --- a/packages/inference/src/providers/providerHelper.ts +++ b/packages/inference/src/providers/providerHelper.ts @@ -46,7 +46,7 @@ import type { ZeroShotImageClassificationOutput, } from "@huggingface/tasks"; import { HF_ROUTER_URL } from "../config.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; import type { AudioToAudioOutput } from "../tasks/audio/audioToAudio.js"; import type { BaseArgs, BodyParams, HeaderParams, InferenceProvider, RequestArgs, UrlParams } from "../types.js"; import { toArray } from "../utils/toArray.js"; @@ -61,7 +61,7 @@ export abstract class TaskProviderHelper { readonly provider: InferenceProvider, private baseUrl: string, readonly clientSideRoutingOnly: boolean = false - ) {} + ) { } /** * Return the response in the expected format. diff --git a/packages/inference/src/providers/replicate.ts b/packages/inference/src/providers/replicate.ts index 6156a82be4..6e704d93b2 100644 --- a/packages/inference/src/providers/replicate.ts +++ b/packages/inference/src/providers/replicate.ts @@ -14,7 +14,7 @@ * * Thanks! */ -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; import { isUrl } from "../lib/isUrl.js"; import type { BodyParams, HeaderParams, UrlParams } from "../types.js"; import { omit } from "../utils/omit.js"; diff --git a/packages/inference/src/providers/sambanova.ts b/packages/inference/src/providers/sambanova.ts index 3e0bad1d63..d198ff53c6 100644 --- a/packages/inference/src/providers/sambanova.ts +++ b/packages/inference/src/providers/sambanova.ts @@ -18,7 +18,7 @@ import type { FeatureExtractionOutput } from "@huggingface/tasks"; import type { BodyParams } from "../types.js"; import type { FeatureExtractionTaskHelper } from "./providerHelper.js"; import { BaseConversationalTask, TaskProviderHelper } from "./providerHelper.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; export class SambanovaConversationalTask extends BaseConversationalTask { constructor() { diff --git a/packages/inference/src/providers/together.ts b/packages/inference/src/providers/together.ts index c311c594f9..6777fdb169 100644 --- a/packages/inference/src/providers/together.ts +++ b/packages/inference/src/providers/together.ts @@ -23,7 +23,7 @@ import { TaskProviderHelper, type TextToImageTaskHelper, } from "./providerHelper.js"; -import { InferenceClientProviderOutputError } from "../error.js"; +import { InferenceClientProviderOutputError } from "../errors.js"; const TOGETHER_API_BASE_URL = "https://api.together.xyz"; diff --git a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts index 295f193f90..a8ce6ebed6 100644 --- a/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts +++ b/packages/inference/src/tasks/audio/automaticSpeechRecognition.ts @@ -4,7 +4,7 @@ import { getProviderHelper } from "../../lib/getProviderHelper.js"; import type { BaseArgs, Options } from "../../types.js"; import { innerRequest } from "../../utils/request.js"; import type { LegacyAudioInput } from "./utils.js"; -import { InferenceClientProviderOutputError } from "../../error.js"; +import { InferenceClientProviderOutputError } from "../../errors.js"; export type AutomaticSpeechRecognitionArgs = BaseArgs & (AutomaticSpeechRecognitionInput | LegacyAudioInput); /** diff --git a/packages/inference/src/utils/request.ts b/packages/inference/src/utils/request.ts index 1390aa75c7..adfd87be9d 100644 --- a/packages/inference/src/utils/request.ts +++ b/packages/inference/src/utils/request.ts @@ -3,7 +3,7 @@ import { makeRequestOptions } from "../lib/makeRequestOptions.js"; import type { InferenceTask, Options, RequestArgs } from "../types.js"; import type { EventSourceMessage } from "../vendor/fetch-event-source/parse.js"; import { getLines, getMessages } from "../vendor/fetch-event-source/parse.js"; -import { InferenceClientProviderApiError } from "../error.js"; +import { InferenceClientProviderApiError } from "../errors.js"; import type { JsonObject } from "../vendor/type-fest/basic.js"; export interface ResponseWrapper { @@ -53,8 +53,7 @@ export async function innerRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new InferenceClientProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${ - args.model + `Provider ${args.provider} does not seem to support chat completion for model ${args.model } . Error: ${JSON.stringify(output.error)}`, { url, @@ -135,8 +134,7 @@ export async function* innerStreamingRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new InferenceClientProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${ - args.model + `Provider ${args.provider} does not seem to support chat completion for model ${args.model } . Error: ${JSON.stringify(output.error)}`, { url, @@ -201,7 +199,7 @@ export async function* innerStreamingRequest( if (!response.headers.get("content-type")?.startsWith("text/event-stream")) { throw new InferenceClientProviderApiError( `Failed to perform inference: server does not support event stream content type, it returned ` + - response.headers.get("content-type"), + response.headers.get("content-type"), { url, method: info.method ?? "GET", @@ -226,8 +224,8 @@ export async function* innerStreamingRequest( const onChunk = getLines( getMessages( - () => {}, - () => {}, + () => { }, + () => { }, onEvent ) ); @@ -250,11 +248,11 @@ export async function* innerStreamingRequest( typeof data.error === "string" ? data.error : typeof data.error === "object" && - data.error && - "message" in data.error && - typeof data.error.message === "string" - ? data.error.message - : JSON.stringify(data.error); + data.error && + "message" in data.error && + typeof data.error.message === "string" + ? data.error.message + : JSON.stringify(data.error); throw new InferenceClientProviderApiError( `Failed to perform inference: an occurred while streaming the response: ${errorStr}`, { From 43b3b9209ef44dc733a5f954e2c49df9c22364fe Mon Sep 17 00:00:00 2001 From: SBrandeis Date: Mon, 2 Jun 2025 14:09:50 +0200 Subject: [PATCH 10/10] format+lint --- packages/inference/src/errors.ts | 12 ++--- .../inference/src/lib/makeRequestOptions.ts | 32 +++++++------- packages/inference/src/providers/fal-ai.ts | 5 ++- .../inference/src/providers/featherless-ai.ts | 6 +-- .../inference/src/providers/hf-inference.ts | 44 ++++++++++--------- .../inference/src/providers/hyperbolic.ts | 6 +-- packages/inference/src/providers/novita.ts | 5 ++- packages/inference/src/providers/ovhcloud.ts | 6 +-- .../inference/src/providers/providerHelper.ts | 2 +- packages/inference/src/utils/request.ts | 22 +++++----- 10 files changed, 74 insertions(+), 66 deletions(-) diff --git a/packages/inference/src/errors.ts b/packages/inference/src/errors.ts index 1caa4ce7c6..01c23d976e 100644 --- a/packages/inference/src/errors.ts +++ b/packages/inference/src/errors.ts @@ -39,12 +39,12 @@ abstract class InferenceClientHttpRequestError extends InferenceClientError { ...httpRequest, ...(httpRequest.headers ? { - headers: { - ...httpRequest.headers, - ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), - /// redact authentication in the request headers - }, - } + headers: { + ...httpRequest.headers, + ...("Authorization" in httpRequest.headers ? { Authorization: `Bearer [redacted]` } : undefined), + /// redact authentication in the request headers + }, + } : undefined), }; this.httpResponse = httpResponse; diff --git a/packages/inference/src/lib/makeRequestOptions.ts b/packages/inference/src/lib/makeRequestOptions.ts index 5f87d8efc4..8ce29d285f 100644 --- a/packages/inference/src/lib/makeRequestOptions.ts +++ b/packages/inference/src/lib/makeRequestOptions.ts @@ -64,24 +64,24 @@ export async function makeRequestOptions( const inferenceProviderMapping = providerHelper.clientSideRoutingOnly ? ({ - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - providerId: removeProviderPrefix(maybeModel!, provider), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - hfModelId: maybeModel!, - status: "live", - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - task: task!, - } satisfies InferenceProviderModelMapping) - : await getInferenceProviderMapping( - { - modelId: hfModel, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + providerId: removeProviderPrefix(maybeModel!, provider), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + hfModelId: maybeModel!, + status: "live", // eslint-disable-next-line @typescript-eslint/no-non-null-assertion task: task!, - provider, - accessToken: args.accessToken, - }, - { fetch: options?.fetch } - ); + } satisfies InferenceProviderModelMapping) + : await getInferenceProviderMapping( + { + modelId: hfModel, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + task: task!, + provider, + accessToken: args.accessToken, + }, + { fetch: options?.fetch } + ); if (!inferenceProviderMapping) { throw new InferenceClientInputError( `We have not been able to find inference provider information for model ${hfModel}.` diff --git a/packages/inference/src/providers/fal-ai.ts b/packages/inference/src/providers/fal-ai.ts index 8b72bc5e1d..3420a922ec 100644 --- a/packages/inference/src/providers/fal-ai.ts +++ b/packages/inference/src/providers/fal-ai.ts @@ -165,8 +165,9 @@ export class FalAITextToVideoTask extends FalAITask implements TextToVideoTaskHe let status = response.status; const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ + parsedUrl.host === "router.huggingface.co" ? "/fal-ai" : "" + }`; // extracting the provider model id for status and result urls // from the response as it might be different from the mapped model in `url` diff --git a/packages/inference/src/providers/featherless-ai.ts b/packages/inference/src/providers/featherless-ai.ts index 4bd0239ccd..249ee28aa9 100644 --- a/packages/inference/src/providers/featherless-ai.ts +++ b/packages/inference/src/providers/featherless-ai.ts @@ -38,9 +38,9 @@ export class FeatherlessAITextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: params.args.parameters.max_new_tokens, - ...omit(params.args.parameters, "max_new_tokens"), - } + max_tokens: params.args.parameters.max_new_tokens, + ...omit(params.args.parameters, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; diff --git a/packages/inference/src/providers/hf-inference.ts b/packages/inference/src/providers/hf-inference.ts index 0ea55f8bd4..406592bb1a 100644 --- a/packages/inference/src/providers/hf-inference.ts +++ b/packages/inference/src/providers/hf-inference.ts @@ -224,7 +224,8 @@ export class HFInferenceAudioClassificationTask extends HFInferenceTask implemen export class HFInferenceAutomaticSpeechRecognitionTask extends HFInferenceTask - implements AutomaticSpeechRecognitionTaskHelper { + implements AutomaticSpeechRecognitionTaskHelper +{ override async getResponse(response: AutomaticSpeechRecognitionOutput): Promise { return response; } @@ -233,9 +234,9 @@ export class HFInferenceAutomaticSpeechRecognitionTask return "data" in args ? args : { - ...omit(args, "inputs"), - data: args.inputs, - }; + ...omit(args, "inputs"), + data: args.inputs, + }; } } @@ -270,7 +271,8 @@ export class HFInferenceAudioToAudioTask extends HFInferenceTask implements Audi export class HFInferenceDocumentQuestionAnsweringTask extends HFInferenceTask - implements DocumentQuestionAnsweringTaskHelper { + implements DocumentQuestionAnsweringTaskHelper +{ override async getResponse( response: DocumentQuestionAnsweringOutput ): Promise { @@ -406,7 +408,8 @@ export class HFInferenceObjectDetectionTask extends HFInferenceTask implements O export class HFInferenceZeroShotImageClassificationTask extends HFInferenceTask - implements ZeroShotImageClassificationTaskHelper { + implements ZeroShotImageClassificationTaskHelper +{ override async getResponse(response: ZeroShotImageClassificationOutput): Promise { if (Array.isArray(response) && response.every((x) => typeof x.label === "string" && typeof x.score === "number")) { return response; @@ -436,20 +439,20 @@ export class HFInferenceQuestionAnsweringTask extends HFInferenceTask implements if ( Array.isArray(response) ? response.every( - (elem) => - typeof elem === "object" && - !!elem && - typeof elem.answer === "string" && - typeof elem.end === "number" && - typeof elem.score === "number" && - typeof elem.start === "number" - ) + (elem) => + typeof elem === "object" && + !!elem && + typeof elem.answer === "string" && + typeof elem.end === "number" && + typeof elem.score === "number" && + typeof elem.start === "number" + ) : typeof response === "object" && - !!response && - typeof response.answer === "string" && - typeof response.end === "number" && - typeof response.score === "number" && - typeof response.start === "number" + !!response && + typeof response.answer === "string" && + typeof response.end === "number" && + typeof response.score === "number" && + typeof response.start === "number" ) { return Array.isArray(response) ? response[0] : response; } @@ -606,7 +609,8 @@ export class HFInferenceTabularClassificationTask extends HFInferenceTask implem export class HFInferenceVisualQuestionAnsweringTask extends HFInferenceTask - implements VisualQuestionAnsweringTaskHelper { + implements VisualQuestionAnsweringTaskHelper +{ override async getResponse(response: VisualQuestionAnsweringOutput): Promise { if ( Array.isArray(response) && diff --git a/packages/inference/src/providers/hyperbolic.ts b/packages/inference/src/providers/hyperbolic.ts index 3e399a33f1..cfddb59a58 100644 --- a/packages/inference/src/providers/hyperbolic.ts +++ b/packages/inference/src/providers/hyperbolic.ts @@ -56,9 +56,9 @@ export class HyperbolicTextGenerationTask extends BaseTextGenerationTask { messages: [{ content: params.args.inputs, role: "user" }], ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), ...omit(params.args, ["inputs", "parameters"]), model: params.model, diff --git a/packages/inference/src/providers/novita.ts b/packages/inference/src/providers/novita.ts index df09e21ac9..13c8ee2832 100644 --- a/packages/inference/src/providers/novita.ts +++ b/packages/inference/src/providers/novita.ts @@ -92,8 +92,9 @@ export class NovitaTextToVideoTask extends TaskProviderHelper implements TextToV } const parsedUrl = new URL(url); - const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/novita" : "" - }`; + const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${ + parsedUrl.host === "router.huggingface.co" ? "/novita" : "" + }`; const resultUrl = `${baseUrl}/v3/async/task-result?task_id=${taskId}`; let status = ""; diff --git a/packages/inference/src/providers/ovhcloud.ts b/packages/inference/src/providers/ovhcloud.ts index 6673919184..ff9c52f961 100644 --- a/packages/inference/src/providers/ovhcloud.ts +++ b/packages/inference/src/providers/ovhcloud.ts @@ -50,9 +50,9 @@ export class OvhCloudTextGenerationTask extends BaseTextGenerationTask { ...omit(params.args, ["inputs", "parameters"]), ...(params.args.parameters ? { - max_tokens: (params.args.parameters as Record).max_new_tokens, - ...omit(params.args.parameters as Record, "max_new_tokens"), - } + max_tokens: (params.args.parameters as Record).max_new_tokens, + ...omit(params.args.parameters as Record, "max_new_tokens"), + } : undefined), prompt: params.args.inputs, }; diff --git a/packages/inference/src/providers/providerHelper.ts b/packages/inference/src/providers/providerHelper.ts index dd359b108e..19388ee41f 100644 --- a/packages/inference/src/providers/providerHelper.ts +++ b/packages/inference/src/providers/providerHelper.ts @@ -61,7 +61,7 @@ export abstract class TaskProviderHelper { readonly provider: InferenceProvider, private baseUrl: string, readonly clientSideRoutingOnly: boolean = false - ) { } + ) {} /** * Return the response in the expected format. diff --git a/packages/inference/src/utils/request.ts b/packages/inference/src/utils/request.ts index adfd87be9d..9bc0333434 100644 --- a/packages/inference/src/utils/request.ts +++ b/packages/inference/src/utils/request.ts @@ -53,7 +53,8 @@ export async function innerRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new InferenceClientProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${args.model + `Provider ${args.provider} does not seem to support chat completion for model ${ + args.model } . Error: ${JSON.stringify(output.error)}`, { url, @@ -134,7 +135,8 @@ export async function* innerStreamingRequest( const output = await response.json(); if ([400, 422, 404, 500].includes(response.status) && options?.chatCompletion) { throw new InferenceClientProviderApiError( - `Provider ${args.provider} does not seem to support chat completion for model ${args.model + `Provider ${args.provider} does not seem to support chat completion for model ${ + args.model } . Error: ${JSON.stringify(output.error)}`, { url, @@ -199,7 +201,7 @@ export async function* innerStreamingRequest( if (!response.headers.get("content-type")?.startsWith("text/event-stream")) { throw new InferenceClientProviderApiError( `Failed to perform inference: server does not support event stream content type, it returned ` + - response.headers.get("content-type"), + response.headers.get("content-type"), { url, method: info.method ?? "GET", @@ -224,8 +226,8 @@ export async function* innerStreamingRequest( const onChunk = getLines( getMessages( - () => { }, - () => { }, + () => {}, + () => {}, onEvent ) ); @@ -248,11 +250,11 @@ export async function* innerStreamingRequest( typeof data.error === "string" ? data.error : typeof data.error === "object" && - data.error && - "message" in data.error && - typeof data.error.message === "string" - ? data.error.message - : JSON.stringify(data.error); + data.error && + "message" in data.error && + typeof data.error.message === "string" + ? data.error.message + : JSON.stringify(data.error); throw new InferenceClientProviderApiError( `Failed to perform inference: an occurred while streaming the response: ${errorStr}`, {