UNPKG

@fal-ai/serverless-client

Version:

Deprecation note: this library has been deprecated in favor of @fal-ai/client

269 lines 12.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FalStream = void 0; exports.stream = stream; const eventsource_parser_1 = require("eventsource-parser"); const auth_1 = require("./auth"); const config_1 = require("./config"); const function_1 = require("./function"); const request_1 = require("./request"); const response_1 = require("./response"); const storage_1 = require("./storage"); const CONTENT_TYPE_EVENT_STREAM = "text/event-stream"; const EVENT_STREAM_TIMEOUT = 15 * 1000; /** * The class representing a streaming response. With t */ class FalStream { constructor(endpointId, options) { var _a; // support for event listeners this.listeners = new Map(); this.buffer = []; // local state this.currentData = undefined; this.lastEventTimestamp = 0; this.streamClosed = false; this.abortController = new AbortController(); this.start = () => __awaiter(this, void 0, void 0, function* () { var _a, _b; const { endpointId, options } = this; const { input, method = "post", connectionMode = "server" } = options; try { if (connectionMode === "client") { // if we are in the browser, we need to get a temporary token // to authenticate the request const token = yield (0, auth_1.getTemporaryAuthToken)(endpointId); const { fetch } = (0, config_1.getConfig)(); const parsedUrl = new URL(this.url); parsedUrl.searchParams.set("fal_jwt_token", token); const response = yield fetch(parsedUrl.toString(), { method: method.toUpperCase(), headers: { accept: (_a = options.accept) !== null && _a !== void 0 ? _a : CONTENT_TYPE_EVENT_STREAM, "content-type": "application/json", }, body: input && method !== "get" ? JSON.stringify(input) : undefined, signal: this.abortController.signal, }); return yield this.handleResponse(response); } return yield (0, request_1.dispatchRequest)(method.toUpperCase(), this.url, input, { headers: { accept: (_b = options.accept) !== null && _b !== void 0 ? _b : CONTENT_TYPE_EVENT_STREAM, }, responseHandler: this.handleResponse, signal: this.abortController.signal, }); } catch (error) { this.handleError(error); } }); this.handleResponse = (response) => __awaiter(this, void 0, void 0, function* () { var _a; if (!response.ok) { try { // we know the response failed, call the response handler // so the exception gets converted to ApiError correctly yield (0, response_1.defaultResponseHandler)(response); } catch (error) { this.emit("error", error); } return; } const body = response.body; if (!body) { this.emit("error", new response_1.ApiError({ message: "Response body is empty.", status: 400, body: undefined, })); return; } const isEventStream = response.headers .get("content-type") .startsWith(CONTENT_TYPE_EVENT_STREAM); // any response that is not a text/event-stream will be handled as a binary stream if (!isEventStream) { const reader = body.getReader(); const emitRawChunk = () => { reader.read().then(({ done, value }) => { if (done) { this.emit("done", this.currentData); return; } this.currentData = value; this.emit("data", value); emitRawChunk(); }); }; emitRawChunk(); return; } const decoder = new TextDecoder("utf-8"); const reader = response.body.getReader(); const parser = (0, eventsource_parser_1.createParser)((event) => { if (event.type === "event") { const data = event.data; try { const parsedData = JSON.parse(data); this.buffer.push(parsedData); this.currentData = parsedData; this.emit("data", parsedData); // also emit 'message'for backwards compatibility this.emit("message", parsedData); } catch (e) { this.emit("error", e); } } }); const timeout = (_a = this.options.timeout) !== null && _a !== void 0 ? _a : EVENT_STREAM_TIMEOUT; const readPartialResponse = () => __awaiter(this, void 0, void 0, function* () { const { value, done } = yield reader.read(); this.lastEventTimestamp = Date.now(); parser.feed(decoder.decode(value)); if (Date.now() - this.lastEventTimestamp > timeout) { this.emit("error", new response_1.ApiError({ message: `Event stream timed out after ${(timeout / 1000).toFixed(0)} seconds with no messages.`, status: 408, })); } if (!done) { readPartialResponse().catch(this.handleError); } else { this.emit("done", this.currentData); } }); readPartialResponse().catch(this.handleError); return; }); this.handleError = (error) => { var _a; const apiError = error instanceof response_1.ApiError ? error : new response_1.ApiError({ message: (_a = error.message) !== null && _a !== void 0 ? _a : "An unknown error occurred", status: 500, }); this.emit("error", apiError); return; }; this.on = (type, listener) => { var _a; if (!this.listeners.has(type)) { this.listeners.set(type, []); } (_a = this.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.push(listener); }; this.emit = (type, event) => { const listeners = this.listeners.get(type) || []; for (const listener of listeners) { listener(event); } }; /** * Gets a reference to the `Promise` that indicates whether the streaming * is done or not. Developers should always call this in their apps to ensure * the request is over. * * An alternative to this, is to use `on('done')` in case your application * architecture works best with event listeners. * * @returns the promise that resolves when the request is done. */ this.done = () => __awaiter(this, void 0, void 0, function* () { return this.donePromise; }); /** * Aborts the streaming request. */ this.abort = () => { this.abortController.abort(); }; this.endpointId = endpointId; this.url = (_a = options.url) !== null && _a !== void 0 ? _a : (0, function_1.buildUrl)(endpointId, { path: "/stream", query: options.queryParams, }); this.options = options; this.donePromise = new Promise((resolve, reject) => { if (this.streamClosed) { reject(new response_1.ApiError({ message: "Streaming connection is already closed.", status: 400, body: undefined, })); } this.on("done", (data) => { this.streamClosed = true; resolve(data); }); this.on("error", (error) => { this.streamClosed = true; reject(error); }); }); this.start().catch(this.handleError); } [Symbol.asyncIterator]() { return __asyncGenerator(this, arguments, function* _a() { let running = true; const stopAsyncIterator = () => (running = false); this.on("error", stopAsyncIterator); this.on("done", stopAsyncIterator); while (running) { const data = this.buffer.shift(); if (data) { yield yield __await(data); } // the short timeout ensures the while loop doesn't block other // frames getting executed concurrently yield __await(new Promise((resolve) => setTimeout(resolve, 16))); } }); } } exports.FalStream = FalStream; /** * Calls a fal app that supports streaming and provides a streaming-capable * object as a result, that can be used to get partial results through either * `AsyncIterator` or through an event listener. * * @param endpointId the endpoint id, e.g. `fal-ai/llavav15-13b`. * @param options the request options, including the input payload. * @returns the `FalStream` instance. */ function stream(endpointId, options) { return __awaiter(this, void 0, void 0, function* () { const input = options.input && options.autoUpload !== false ? yield storage_1.storageImpl.transformInput(options.input) : options.input; return new FalStream(endpointId, Object.assign(Object.assign({}, options), { input: input })); }); } //# sourceMappingURL=streaming.js.map