UNPKG

@huggingface/inference

Version:

Typescript client for the Hugging Face Inference Providers and Inference Endpoints

147 lines (146 loc) 6.57 kB
import { InferenceClientInputError, InferenceClientProviderApiError, InferenceClientProviderOutputError, } from "../errors.js"; import { isUrl } from "../lib/isUrl.js"; import { base64FromBytes } from "../utils/base64FromBytes.js"; import { dataUrlFromBlob } from "../utils/dataUrlFromBlob.js"; import { delay } from "../utils/delay.js"; import { omit } from "../utils/omit.js"; import { BaseConversationalTask, TaskProviderHelper, } from "./providerHelper.js"; const ZAI_API_BASE_URL = "https://api.z.ai"; class ZaiTask extends TaskProviderHelper { constructor() { super("zai-org", ZAI_API_BASE_URL); } prepareHeaders(params, binary) { const headers = super.prepareHeaders(params, binary); headers["x-source-channel"] = "hugging_face"; headers["accept-language"] = "en-US,en"; return headers; } } export class ZaiConversationalTask extends BaseConversationalTask { constructor() { super("zai-org", ZAI_API_BASE_URL); } prepareHeaders(params, binary) { const headers = super.prepareHeaders(params, binary); headers["x-source-channel"] = "hugging_face"; headers["accept-language"] = "en-US,en"; return headers; } makeRoute() { return "/api/paas/v4/chat/completions"; } } const MAX_POLL_ATTEMPTS = 60; const POLL_INTERVAL_MS = 5000; export class ZaiTextToImageTask extends ZaiTask { makeRoute() { return "/api/paas/v4/async/images/generations"; } preparePayload(params) { return { ...omit(params.args, ["inputs", "parameters"]), ...params.args.parameters, model: params.model, prompt: params.args.inputs, }; } async getResponse(response, url, headers, outputType, signal) { if (!url || !headers) { throw new InferenceClientInputError(`URL and headers are required for 'text-to-image' task`); } if (typeof response !== "object" || !response || !("task_status" in response) || !("id" in response) || typeof response.id !== "string") { throw new InferenceClientProviderOutputError(`Received malformed response from ZAI text-to-image API: expected { id: string, task_status: string }, got: ${JSON.stringify(response)}`); } if (response.task_status === "FAIL") { throw new InferenceClientProviderOutputError("ZAI API returned task status: FAIL"); } const taskId = response.id; const parsedUrl = new URL(url); const baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}${parsedUrl.host === "router.huggingface.co" ? "/zai-org" : ""}`; const pollUrl = `${baseUrl}/api/paas/v4/async-result/${taskId}`; const pollHeaders = { ...headers, "x-source-channel": "hugging_face", "accept-language": "en-US,en", }; for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) { await delay(POLL_INTERVAL_MS, signal); const resp = await fetch(pollUrl, { method: "GET", headers: pollHeaders, signal, }); if (!resp.ok) { throw new InferenceClientProviderApiError(`Failed to fetch result from ZAI text-to-image API: ${resp.status}`, { url: pollUrl, method: "GET" }, { requestId: resp.headers.get("x-request-id") ?? "", status: resp.status, body: await resp.text() }); } const result = await resp.json(); if (result.task_status === "FAIL") { throw new InferenceClientProviderOutputError("ZAI text-to-image API task failed"); } if (result.task_status === "SUCCESS") { if (!result.image_result || !Array.isArray(result.image_result) || result.image_result.length === 0 || typeof result.image_result[0]?.url !== "string" || !isUrl(result.image_result[0].url)) { throw new InferenceClientProviderOutputError(`Received malformed response from ZAI text-to-image API: expected { image_result: Array<{ url: string }> }, got: ${JSON.stringify(result)}`); } const imageUrl = result.image_result[0].url; if (outputType === "json") { return { ...result }; } if (outputType === "url") { return imageUrl; } const imageResponse = await fetch(imageUrl, { signal }); const blob = await imageResponse.blob(); return outputType === "dataUrl" ? dataUrlFromBlob(blob) : blob; } } throw new InferenceClientProviderOutputError(`Timed out while waiting for the result from ZAI API - aborting after ${MAX_POLL_ATTEMPTS} attempts`); } } export class ZaiImageToTextTask extends ZaiTask { makeRoute() { return "/api/paas/v4/layout_parsing"; } async preparePayloadAsync(args, signal) { const blob = "data" in args && args.data instanceof Blob ? args.data : "inputs" in args ? typeof args.inputs === "string" && isUrl(args.inputs) ? await fetch(args.inputs, { signal }).then((r) => r.blob()) : args.inputs instanceof Blob ? args.inputs : undefined : undefined; if (!blob || !(blob instanceof Blob)) { throw new InferenceClientInputError("ZAI image-to-text requires a URL string or Blob as inputs"); } const mimeType = blob.type || "image/png"; const b64 = base64FromBytes(new Uint8Array(await blob.arrayBuffer())); const file = `data:${mimeType};base64,${b64}`; return { ...("data" in args ? omit(args, "data") : omit(args, "inputs")), inputs: file, }; } preparePayload(params) { return { model: params.model, file: params.args.inputs, }; } async getResponse(response) { const mdResults = response?.md_results; if (typeof mdResults !== "string") { throw new InferenceClientProviderOutputError(`Received malformed response from ZAI layout_parsing API: expected { md_results: string }, got: ${JSON.stringify(response)}`); } return { generated_text: mdResults, generatedText: mdResults }; } }