UNPKG

@ibm-cloud/watsonx-ai

Version:
360 lines 14.7 kB
/** * (C) Copyright IBM Corp. 2024-2026. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ 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 __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; 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"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } 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]); } }; import os from 'os'; import { addAbortSignal, pipeline, Readable as Rdb, Transform } from 'stream'; import { VERSION } from "../version.mjs"; /** * Generates SDK headers with User-Agent information including package name, version, operating * system details, and Node.js version. * * @returns {SdkHeaders} Headers object containing User-Agent string */ export function getSdkHeaders() { const packageName = 'ibm-cloud-watsonx-ai'; const sdkVersion = VERSION; const osName = os.platform(); const osVersion = os.release(); const nodeVersion = process.version; const headers = { 'User-Agent': `${packageName}/${sdkVersion} (lang=node.js; os.name=${osName} os.version=${osVersion} node.version=${nodeVersion})`, }; return headers; } /** * Converts an array of Server-Sent Events (SSE) formatted strings into a JavaScript object. Parses * lines with 'key: value' format and handles special fields like 'id', 'event', and 'data'. * * @example * ```typescript * const lines = ['id: 1', 'event: message', 'data: {"text":"hello"}']; * const obj = stringToObj(lines); * // Returns: { id: 1, event: 'message', data: { text: 'hello' } } * ```; * * @param {string[]} chunk - Array of SSE formatted strings to parse * @returns {Record<string, any> | null} Parsed object or null if no valid data found */ const stringToObj = (chunk) => { const obj = {}; chunk.forEach((line) => { const index = line.indexOf(': '); if (index === -1) return; const key = line.substring(0, index); const value = line.substring(index + 2); switch (key) { case 'id': obj[key] = Number(value); break; case 'event': obj[key] = String(value); break; case 'data': if (value === '[DONE]') return; try { obj[key] = JSON.parse(value); } catch (e) { console.error("There is invalid JSON in your stream under 'data' field and it was not parsed into an object.", `${key}: ${value}`); console.error(e); } break; default: break; } }); return Object.keys(obj).length > 0 ? obj : null; }; /** * Base transform stream class that maintains a buffer for processing streaming data. Extends * Node.js Transform stream with object mode for readable output. */ export class StreamTransform extends Transform { constructor() { super({ readableObjectMode: true, writableObjectMode: false }); this.buffer = ''; } } /** * Transform stream that converts Server-Sent Events (SSE) formatted text into JavaScript objects. * Buffers incoming chunks, splits by double newlines, and parses each event into an object. * * @example * ```typescript * const transformStream = new ObjectTransformStream(); * stream.pipe(transformStream).on('data', (obj) => { * console.log(obj); // Parsed SSE object * }); * ```; * * @extends StreamTransform */ export class ObjectTransformStream extends StreamTransform { /** * Transforms incoming chunks by buffering and parsing SSE formatted data. Splits events by double * newlines and converts each to an object. * * @param {any} chunk - Incoming data chunk * @param {string} _encoding - Character encoding (unused) * @param {TransformCallback} callback - Callback to signal completion */ _transform(chunk, _encoding, callback) { var _a; this.buffer += chunk.toString(); const events = this.buffer.split('\n\n'); this.buffer = (_a = events.pop()) !== null && _a !== void 0 ? _a : ''; for (const event of events) { const lines = event.split('\n'); const obj = stringToObj(lines); if (obj) this.push(obj); } callback(); } /** * Flushes any remaining buffered data when the stream ends. * * @param {TransformCallback} callback - Callback to signal completion */ _flush(callback) { if (this.buffer) { const parts = this.buffer.split('\n'); const obj = stringToObj(parts); this.push(obj); } callback(); } } /** * Generic async iterable stream wrapper that provides abort control functionality. Allows for * cancellation of streaming operations via AbortController. * * @example * ```typescript * const controller = new AbortController(); * const stream = await Stream.createStream<MyType>(transformStream, controller); * for await (const item of stream) { * console.log(item); * } * // Later: controller.abort() to cancel * ```; * * @template T - Type of items in the stream */ export class Stream { /** * Creates a new Stream instance. * * @param {() => AsyncIterator<T>} iterator - Function that returns an async iterator * @param {AbortController} controller - Controller for aborting the stream */ constructor(iterator, controller) { this.iterator = iterator; this.controller = controller; } /** * Creates a Stream instance from a Transform stream. * * @template T - Type of items in the stream * @param {Transform} stream - Node.js Transform stream to wrap * @param {AbortController} controller - Controller for aborting the stream * @returns {Promise<Stream<T>>} Promise resolving to a Stream instance */ static createStream(stream, controller) { return __awaiter(this, void 0, void 0, function* () { function iterator() { return __asyncGenerator(this, arguments, function* iterator_1() { var _a, e_1, _b, _c; try { for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield __await(stream_1.next()), _a = stream_1_1.done, !_a;) { _c = stream_1_1.value; _d = false; try { const chunk = _c; yield yield __await(chunk); } finally { _d = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_d && !_a && (_b = stream_1.return)) yield __await(_b.call(stream_1)); } finally { if (e_1) throw e_1.error; } } }); } return new Stream(iterator, controller); }); } /** * Implements the async iterator protocol for the stream. * * @returns {AsyncIterator<T>} Async iterator for the stream */ [Symbol.asyncIterator]() { return this.iterator(); } } /** * Handles errors that occur in stream pipelines. Logs warnings for abort errors and errors for * other pipeline failures. * * @param {any} e - Error object from the pipeline */ const handlePipelineError = (e) => { if ((e === null || e === void 0 ? void 0 : e.name) === 'AbortError') { console.warn('Stream pipeline aborted'); } else if (e) { console.error('Stream pipeline error'); } }; /** * Creates a pipeline that transforms an API response stream using a custom transform stream. Sets * up abort control and error handling for the pipeline. * * @template T - Type of items in the resulting stream * @param {Record<'result', Iterable<any> | AsyncIterable<any>>} apiResponse - API response * containing iterable result * @param {StreamTransform} transformStream - Transform stream to process the data * @returns {Promise<Stream<T>>} Promise resolving to an abortable Stream instance */ function transformStreamWithPipeline(apiResponse, transformStream) { const readableStream = Rdb.from(apiResponse.result); const controller = new AbortController(); const combinedStream = pipeline(readableStream, transformStream, handlePipelineError); const abortableStream = addAbortSignal(controller.signal, combinedStream); const res = Stream.createStream(abortableStream, controller); return res; } /** * Transforms an API response stream into a stream of parsed JavaScript objects. Uses * ObjectTransformStream to parse Server-Sent Events (SSE) formatted data. * * @example * ```typescript * const objectStream = await transformStreamToObjectStream<MyType>(apiResponse); * for await (const obj of objectStream) { * console.log(obj); // Parsed object from SSE stream * } * ```; * * @template T - Type of objects in the resulting stream * @param {any} apiResponse - API response containing a stream result * @returns {Promise<Stream<T>>} Promise resolving to a Stream of parsed objects */ export function transformStreamToObjectStream(apiResponse) { return __awaiter(this, void 0, void 0, function* () { const transformStream = new ObjectTransformStream(); return transformStreamWithPipeline(apiResponse, transformStream); }); } /** * Transform stream that splits incoming data into individual lines. Buffers partial lines and emits * complete lines as they arrive. * * @example * ```typescript * const lineStream = new LineTransformStream(); * stream.pipe(lineStream).on('data', (line) => { * console.log(line); // Individual line of text * }); * ```; * * @extends StreamTransform */ export class LineTransformStream extends StreamTransform { /** * Transforms incoming chunks by splitting on newlines and emitting complete lines. * * @param {any} chunk - Incoming data chunk * @param {string} _encoding - Character encoding (unused) * @param {TransformCallback} callback - Callback to signal completion */ _transform(chunk, _encoding, callback) { var _a; this.buffer += chunk.toString(); const lines = this.buffer.split('\n'); this.buffer = (_a = lines.pop()) !== null && _a !== void 0 ? _a : ''; lines.forEach((line) => this.push(line)); callback(); } /** * Flushes any remaining buffered line when the stream ends. * * @param {TransformCallback} callback - Callback to signal completion */ _flush(callback) { if (this.buffer) { this.push(this.buffer); } callback(); } } /** * Transforms an API response stream into a stream of text lines. Uses LineTransformStream to split * data by newlines. * * @example * ```typescript * const lineStream = await transformStreamToStringStream<string>(apiResponse); * for await (const line of lineStream) { * console.log(line); // Individual line of text * } * ```; * * @template T - Type of items in the resulting stream (typically string) * @param {any} apiResponse - API response containing a stream result * @returns {Promise<Stream<T>>} Promise resolving to a Stream of text lines */ export function transformStreamToStringStream(apiResponse) { return __awaiter(this, void 0, void 0, function* () { const transformStream = new LineTransformStream(); return transformStreamWithPipeline(apiResponse, transformStream); }); } //# sourceMappingURL=common.mjs.map