UNPKG

commandbase

Version:

The AI SDK for building declarative and composable AI-powered LLM products.

1 lines 81.2 kB
{"version":3,"sources":["../src/lib/utils/doc-to-formdata.ts","../src/data/constants.ts","../src/common/errors.ts","../src/common/stream.ts","../src/common/request.ts","../src/langbase/langbase.ts","../src/pipes/pipes.ts","../src/lib/helpers/index.ts"],"sourcesContent":["\n/**\n * Converts various document formats to FormData.\n *\n * @param options - The conversion options\n * @param options.document - The document to convert. Can be Buffer, File, FormData or ReadableStream\n * @param options.documentName - The name of the document\n * @param options.contentType - The MIME type of the document\n *\n * @returns A Promise that resolves to FormData containing the document\n *\n * @example\n * ```ts\n * const buffer = Buffer.from('Hello World');\n * const formData = await convertDocToFormData({\n * document: buffer,\n * documentName: 'hello.txt',\n * contentType: 'text/plain'\n * });\n * ```\n */\nexport async function convertDocToFormData(options: {\n\tdocument: Buffer | File | FormData | ReadableStream;\n\tdocumentName: string;\n\tcontentType: string;\n}) {\n\tlet formData = new FormData();\n\n\tif (options.document instanceof Buffer) {\n\t\tconst documentBlob = new Blob([options.document], {\n\t\t\ttype: options.contentType,\n\t\t});\n\t\tformData.append('document', documentBlob, options.documentName);\n\t} else if (options.document instanceof File) {\n\t\tformData.append('document', options.document, options.documentName);\n\t} else if (options.document instanceof FormData) {\n\t\tformData = options.document;\n\t} else if (options.document instanceof ReadableStream) {\n\t\tconst chunks: Uint8Array[] = [];\n\t\tconst reader = options.document.getReader();\n\n\t\twhile (true) {\n\t\t\tconst {done, value} = await reader.read();\n\t\t\tif (done) break;\n\t\t\tchunks.push(value);\n\t\t}\n\n\t\tconst documentBlob = new Blob(chunks, {type: options.contentType});\n\t\tformData.append('document', documentBlob, options.documentName);\n\t}\n\n\tformData.append('documentName', options.documentName);\n\n\treturn formData;\n}\n","export const GENERATION_ENDPOINTS = [\n\t'/v1/pipes/run',\n\t'/beta/chat',\n\t'/beta/generate',\n]\n","import {Headers} from './../../types'; // Ensure this import is correct\n\nexport class APIError extends Error {\n\treadonly status: number | undefined;\n\treadonly headers: Headers | undefined;\n\treadonly error: Object | undefined;\n\n\treadonly code: string | null | undefined;\n\treadonly param: string | null | undefined;\n\treadonly type: string | undefined;\n\n\treadonly request_id: string | null | undefined;\n\n\tconstructor(\n\t\tstatus: number | undefined,\n\t\terror: Object | undefined,\n\t\tmessage: string | undefined,\n\t\theaders: Headers | undefined,\n\t) {\n\t\tsuper(APIError.makeMessage(status, error, message));\n\t\tthis.status = status;\n\t\tthis.headers = headers;\n\t\tthis.request_id = headers?.['lb-request-id'];\n\n\t\tconst data = error as Record<string, any>;\n\t\tthis.error = data;\n\t\tthis.code = data?.['code'];\n\t\tthis.status = data?.['status'];\n\t\t// this.param = data?.['param'];\n\t\t// this.type = data?.['type'];\n\t}\n\n\tprivate static makeMessage(\n\t\tstatus: number | undefined,\n\t\terror: any,\n\t\tmessage: string | undefined,\n\t): string {\n\t\tconst msg = error?.message\n\t\t\t? typeof error.message === 'string'\n\t\t\t\t? error.message\n\t\t\t\t: JSON.stringify(error.message)\n\t\t\t: error\n\t\t\t\t? JSON.stringify(error)\n\t\t\t\t: message;\n\n\t\tif (status && msg) {\n\t\t\treturn `${status} ${msg}`;\n\t\t}\n\t\tif (status) {\n\t\t\treturn `${status} status code (no body)`;\n\t\t}\n\t\tif (msg) {\n\t\t\treturn msg;\n\t\t}\n\t\treturn '(no status code or body)';\n\t}\n\n\tstatic generate(\n\t\tstatus: number | undefined,\n\t\terrorResponse: Object | undefined,\n\t\tmessage: string | undefined,\n\t\theaders: Headers | undefined,\n\t): APIError {\n\t\tif (!status) {\n\t\t\treturn new APIConnectionError({\n\t\t\t\tcause:\n\t\t\t\t\terrorResponse instanceof Error ? errorResponse : undefined,\n\t\t\t});\n\t\t}\n\n\t\tconst error = (errorResponse as Record<string, any>)?.['error'];\n\n\t\tswitch (status) {\n\t\t\tcase 400:\n\t\t\t\treturn new BadRequestError(status, error, message, headers);\n\t\t\tcase 401:\n\t\t\t\treturn new AuthenticationError(status, error, message, headers);\n\t\t\tcase 403:\n\t\t\t\treturn new PermissionDeniedError(\n\t\t\t\t\tstatus,\n\t\t\t\t\terror,\n\t\t\t\t\tmessage,\n\t\t\t\t\theaders,\n\t\t\t\t);\n\t\t\tcase 404:\n\t\t\t\treturn new NotFoundError(status, error, message, headers);\n\t\t\tcase 409:\n\t\t\t\treturn new ConflictError(status, error, message, headers);\n\t\t\tcase 422:\n\t\t\t\treturn new UnprocessableEntityError(\n\t\t\t\t\tstatus,\n\t\t\t\t\terror,\n\t\t\t\t\tmessage,\n\t\t\t\t\theaders,\n\t\t\t\t);\n\t\t\tcase 429:\n\t\t\t\treturn new RateLimitError(status, error, message, headers);\n\t\t\tdefault:\n\t\t\t\treturn status >= 500\n\t\t\t\t\t? new InternalServerError(status, error, message, headers)\n\t\t\t\t\t: new APIError(status, error, message, headers);\n\t\t}\n\t}\n}\n\nexport class APIConnectionError extends APIError {\n\toverride readonly status: undefined = undefined;\n\n\tconstructor({message, cause}: {message?: string; cause?: Error}) {\n\t\tsuper(undefined, undefined, message || 'Connection error.', undefined);\n\t\tif (cause) (this as Error).cause = cause;\n\t}\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n\tconstructor({message}: {message?: string} = {}) {\n\t\tsuper({message: message ?? 'Request timed out.'});\n\t}\n}\n\nexport class BadRequestError extends APIError {\n\toverride readonly status: 400 = 400;\n}\n\nexport class AuthenticationError extends APIError {\n\toverride readonly status: 401 = 401;\n}\n\nexport class PermissionDeniedError extends APIError {\n\toverride readonly status: 403 = 403;\n}\n\nexport class NotFoundError extends APIError {\n\toverride readonly status: 404 = 404;\n}\n\nexport class ConflictError extends APIError {\n\toverride readonly status: 409 = 409;\n}\n\nexport class UnprocessableEntityError extends APIError {\n\toverride readonly status: 422 = 422;\n}\n\nexport class RateLimitError extends APIError {\n\toverride readonly status: 429 = 429;\n}\n\nexport class InternalServerError extends APIError {}\n","type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;\n\nexport type ServerSentEvent = {\n\tevent: string | null;\n\tdata: string;\n\traw: string[];\n};\n\nexport class Stream<Item> implements AsyncIterable<Item> {\n\tcontroller: AbortController;\n\n\tconstructor(\n\t\tprivate iterator: () => AsyncIterator<Item>,\n\t\tcontroller: AbortController,\n\t) {\n\t\tthis.controller = controller;\n\t}\n\n\t/**\n\t * Creates a stream of AsyncIterator from a Server-Sent Events (SSE) response.\n\t *\n\t * @template Item - The type of items in the stream.\n\t * @param {Response} response - The SSE response object.\n\t * @param {AbortController} controller - The abort controller used to cancel the ongoing request.\n\t * @returns {Stream<AsyncIterator<Item, any, undefined>>} - The stream created from the SSE response.\n\t * @throws {Error} - If the stream has already been consumed.\n\t */\n\tstatic fromSSEResponse<Item>(\n\t\tresponse: Response,\n\t\tcontroller: AbortController,\n\t) {\n\t\tlet consumed = false;\n\n\t\tasync function* iterator(): AsyncIterator<Item, any, undefined> {\n\t\t\tif (consumed) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Cannot iterate over a consumed stream, use `.tee()` to split the stream.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconsumed = true;\n\t\t\tlet done = false;\n\t\t\ttry {\n\t\t\t\tfor await (const sse of _iterSSEMessages(\n\t\t\t\t\tresponse,\n\t\t\t\t\tcontroller,\n\t\t\t\t)) {\n\t\t\t\t\tif (done) continue;\n\n\t\t\t\t\tif (sse.data.startsWith('[DONE]')) {\n\t\t\t\t\t\tdone = true;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (sse.event === null) {\n\t\t\t\t\t\tlet data;\n\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tdata = JSON.parse(sse.data);\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t`Could not parse message into JSON:`,\n\t\t\t\t\t\t\t\tsse.data,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.error(`From chunk:`, sse.raw);\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (data && data.error) {\n\t\t\t\t\t\t\tthrow new Error(data.error);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tyield data;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlet data;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tdata = JSON.parse(sse.data);\n\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t`Could not parse message into JSON:`,\n\t\t\t\t\t\t\t\tsse.data,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tconsole.error(`From chunk:`, sse.raw);\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// TODO: Is this where the error should be thrown?\n\t\t\t\t\t\tif (sse.event == 'error') {\n\t\t\t\t\t\t\tthrow new Error(data.message);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tyield {event: sse.event, data: data} as any;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tdone = true;\n\t\t\t} catch (e) {\n\t\t\t\t// If the user calls `stream.controller.abort()`, we should exit without throwing.\n\t\t\t\tif (e instanceof Error && e.name === 'AbortError') return;\n\t\t\t\tthrow e;\n\t\t\t} finally {\n\t\t\t\t// If the user `break`s, abort the ongoing request.\n\t\t\t\tif (!done) controller.abort();\n\t\t\t}\n\t\t}\n\n\t\treturn new Stream(iterator, controller);\n\t}\n\n\t/**\n\t * Generates a Stream from a newline-separated ReadableStream\n\t * where each item is a JSON value.\n\t *\n\t * @template Item - The type of items in the stream.\n\t * @param {ReadableStream} readableStream - The readable stream to create the stream from.\n\t * @param {AbortController} controller - The abort controller to control the stream.\n\t * @returns {Stream<Item>} - The created stream.\n\t */\n\tstatic fromReadableStream<Item>(\n\t\treadableStream: ReadableStream,\n\t\tcontroller: AbortController,\n\t) {\n\t\tlet consumed = false;\n\n\t\tasync function* iterLines(): AsyncGenerator<string, void, unknown> {\n\t\t\tconst lineDecoder = new LineDecoder();\n\n\t\t\tconst iter = readableStreamAsyncIterable<Bytes>(readableStream);\n\t\t\tfor await (const chunk of iter) {\n\t\t\t\tfor (const line of lineDecoder.decode(chunk)) {\n\t\t\t\t\tyield line;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const line of lineDecoder.flush()) {\n\t\t\t\tyield line;\n\t\t\t}\n\t\t}\n\n\t\tasync function* iterator(): AsyncIterator<Item, any, undefined> {\n\t\t\tif (consumed) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Cannot iterate over a consumed stream, use `.tee()` to split the stream.',\n\t\t\t\t);\n\t\t\t}\n\t\t\tconsumed = true;\n\t\t\tlet done = false;\n\t\t\ttry {\n\t\t\t\tfor await (const line of iterLines()) {\n\t\t\t\t\tif (done) continue;\n\t\t\t\t\tif (line) yield JSON.parse(line);\n\t\t\t\t}\n\t\t\t\tdone = true;\n\t\t\t} catch (e) {\n\t\t\t\t// If the user calls `stream.controller.abort()`, we should exit without throwing.\n\t\t\t\tif (e instanceof Error && e.name === 'AbortError') return;\n\t\t\t\tthrow e;\n\t\t\t} finally {\n\t\t\t\t// If the user `break`s, abort the ongoing request.\n\t\t\t\tif (!done) controller.abort();\n\t\t\t}\n\t\t}\n\n\t\treturn new Stream(iterator, controller);\n\t}\n\n\t[Symbol.asyncIterator](): AsyncIterator<Item> {\n\t\treturn this.iterator();\n\t}\n\n\t/**\n\t * Splits the stream into two streams which can be\n\t * independently read from at different speeds.\n\t */\n\ttee(): [Stream<Item>, Stream<Item>] {\n\t\tconst left: Array<Promise<IteratorResult<Item>>> = [];\n\t\tconst right: Array<Promise<IteratorResult<Item>>> = [];\n\t\tconst iterator = this.iterator();\n\n\t\tconst teeIterator = (\n\t\t\tqueue: Array<Promise<IteratorResult<Item>>>,\n\t\t): AsyncIterator<Item> => {\n\t\t\treturn {\n\t\t\t\tnext: () => {\n\t\t\t\t\tif (queue.length === 0) {\n\t\t\t\t\t\tconst result = iterator.next();\n\t\t\t\t\t\tleft.push(result);\n\t\t\t\t\t\tright.push(result);\n\t\t\t\t\t}\n\t\t\t\t\treturn queue.shift()!;\n\t\t\t\t},\n\t\t\t};\n\t\t};\n\n\t\treturn [\n\t\t\tnew Stream(() => teeIterator(left), this.controller),\n\t\t\tnew Stream(() => teeIterator(right), this.controller),\n\t\t];\n\t}\n\n\t/**\n\t * Converts this stream to a newline-separated ReadableStream of\n\t * JSON stringified values in the stream which can be turned back into a Stream with `Stream.fromReadableStream()`.\n\t */\n\ttoReadableStream(): ReadableStream {\n\t\tconst self = this;\n\t\tlet iter: AsyncIterator<Item>;\n\t\tconst encoder = new TextEncoder();\n\n\t\treturn new ReadableStream({\n\t\t\tasync start() {\n\t\t\t\titer = self[Symbol.asyncIterator]();\n\t\t\t},\n\t\t\tasync pull(ctrl: any) {\n\t\t\t\ttry {\n\t\t\t\t\tconst {value, done} = await iter.next();\n\t\t\t\t\tif (done) return ctrl.close();\n\n\t\t\t\t\tconst bytes = encoder.encode(JSON.stringify(value) + '\\n');\n\n\t\t\t\t\tctrl.enqueue(bytes);\n\t\t\t\t} catch (err) {\n\t\t\t\t\tctrl.error(err);\n\t\t\t\t}\n\t\t\t},\n\t\t\tasync cancel() {\n\t\t\t\tawait iter.return?.();\n\t\t\t},\n\t\t});\n\t}\n}\n\n/**\n * Asynchronously iterates over Server-Sent Event (SSE) messages from a response body.\n *\n * @param response - The response object containing the SSE messages.\n * @param controller - The AbortController used to abort the iteration if needed.\n * @returns An async generator that yields ServerSentEvent objects.\n * @throws Error if the response has no body.\n */\nexport async function* _iterSSEMessages(\n\tresponse: Response,\n\tcontroller: AbortController,\n): AsyncGenerator<ServerSentEvent, void, unknown> {\n\tif (!response.body) {\n\t\tcontroller.abort();\n\t\tthrow new Error(`Attempted to iterate over a response with no body`);\n\t}\n\n\tconst sseDecoder = new SSEDecoder();\n\tconst lineDecoder = new LineDecoder();\n\n\tconst iter = readableStreamAsyncIterable<Bytes>(response.body);\n\tfor await (const sseChunk of iterSSEChunks(iter)) {\n\t\tfor (const line of lineDecoder.decode(sseChunk)) {\n\t\t\tconst sse = sseDecoder.decode(line);\n\t\t\tif (sse) yield sse;\n\t\t}\n\t}\n\n\tfor (const line of lineDecoder.flush()) {\n\t\tconst sse = sseDecoder.decode(line);\n\t\tif (sse) yield sse;\n\t}\n}\n\n/**\n * Asynchronously iterates over SSE (Server-Sent Events) chunks and yields the data as Uint8Array.\n *\n * Given an async iterable iterator, iterates over it and yields full\n * SSE chunks, i.e. yields when a double new-line is encountered.\n *\n * @param iterator - The async iterator that provides the SSE chunks.\n * @returns An async generator that yields Uint8Array chunks.\n */\nasync function* iterSSEChunks(\n\titerator: AsyncIterableIterator<Bytes>,\n): AsyncGenerator<Uint8Array> {\n\tlet data = new Uint8Array();\n\n\tfor await (const chunk of iterator) {\n\t\tif (chunk == null) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst binaryChunk =\n\t\t\tchunk instanceof ArrayBuffer\n\t\t\t\t? new Uint8Array(chunk)\n\t\t\t\t: typeof chunk === 'string'\n\t\t\t\t\t? new TextEncoder().encode(chunk)\n\t\t\t\t\t: chunk;\n\n\t\tlet newData = new Uint8Array(data.length + binaryChunk.length);\n\t\tnewData.set(data);\n\t\tnewData.set(binaryChunk, data.length);\n\t\tdata = newData;\n\n\t\tlet patternIndex;\n\t\twhile ((patternIndex = findDoubleNewlineIndex(data)) !== -1) {\n\t\t\tyield data.slice(0, patternIndex);\n\t\t\tdata = data.slice(patternIndex);\n\t\t}\n\t}\n\n\tif (data.length > 0) {\n\t\tyield data;\n\t}\n}\n\n/**\n * Finds the index of the first occurrence of a double newline pattern (\\r\\r, \\n\\n, \\r\\n\\r\\n) in the given buffer.\n *\n * @param buffer - The buffer to search for the double newline pattern.\n * @returns The index right after the first occurrence of the double newline pattern, or -1 if not found.\n */\nfunction findDoubleNewlineIndex(buffer: Uint8Array): number {\n\t// This function searches the buffer for the end patterns (\\r\\r, \\n\\n, \\r\\n\\r\\n)\n\t// and returns the index right after the first occurrence of any pattern,\n\t// or -1 if none of the patterns are found.\n\tconst newline = 0x0a; // \\n\n\tconst carriage = 0x0d; // \\r\n\n\tfor (let i = 0; i < buffer.length - 2; i++) {\n\t\tif (buffer[i] === newline && buffer[i + 1] === newline) {\n\t\t\t// \\n\\n\n\t\t\treturn i + 2;\n\t\t}\n\t\tif (buffer[i] === carriage && buffer[i + 1] === carriage) {\n\t\t\t// \\r\\r\n\t\t\treturn i + 2;\n\t\t}\n\t\tif (\n\t\t\tbuffer[i] === carriage &&\n\t\t\tbuffer[i + 1] === newline &&\n\t\t\ti + 3 < buffer.length &&\n\t\t\tbuffer[i + 2] === carriage &&\n\t\t\tbuffer[i + 3] === newline\n\t\t) {\n\t\t\t// \\r\\n\\r\\n\n\t\t\treturn i + 4;\n\t\t}\n\t}\n\n\treturn -1;\n}\n\n/**\n * Represents a Server-Sent Event (SSE) decoder.\n */\nclass SSEDecoder {\n\tprivate data: string[];\n\tprivate event: string | null;\n\tprivate chunks: string[];\n\n\tconstructor() {\n\t\tthis.event = null;\n\t\tthis.data = [];\n\t\tthis.chunks = [];\n\t}\n\n\t/**\n\t * Decodes a line of text and returns a ServerSentEvent object if a complete event is found.\n\t * @param line - The line of text to decode.\n\t * @returns A ServerSentEvent object if a complete event is found, otherwise null.\n\t */\n\tdecode(line: string) {\n\t\tif (line.endsWith('\\r')) {\n\t\t\tline = line.substring(0, line.length - 1);\n\t\t}\n\n\t\tif (!line) {\n\t\t\t// empty line and we didn't previously encounter any messages\n\t\t\tif (!this.event && !this.data.length) return null;\n\n\t\t\tconst sse: ServerSentEvent = {\n\t\t\t\tevent: this.event,\n\t\t\t\tdata: this.data.join('\\n'),\n\t\t\t\traw: this.chunks,\n\t\t\t};\n\n\t\t\tthis.event = null;\n\t\t\tthis.data = [];\n\t\t\tthis.chunks = [];\n\n\t\t\treturn sse;\n\t\t}\n\n\t\tthis.chunks.push(line);\n\n\t\tif (line.startsWith(':')) {\n\t\t\treturn null;\n\t\t}\n\n\t\tlet [fieldname, _, value] = partition(line, ':');\n\n\t\tif (value.startsWith(' ')) {\n\t\t\tvalue = value.substring(1);\n\t\t}\n\n\t\tif (fieldname === 'event') {\n\t\t\tthis.event = value;\n\t\t} else if (fieldname === 'data') {\n\t\t\tthis.data.push(value);\n\t\t}\n\n\t\treturn null;\n\t}\n}\n\n/**\n * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally\n * reading lines from text.\n *\n * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258\n */\nclass LineDecoder {\n\t// prettier-ignore\n\tstatic NEWLINE_CHARS = new Set(['\\n', '\\r']);\n\tstatic NEWLINE_REGEXP = /\\r\\n|[\\n\\r]/g;\n\n\tbuffer: string[];\n\ttrailingCR: boolean;\n\ttextDecoder: any; // TextDecoder found in browsers; not typed to avoid pulling in either \"dom\" or \"node\" types.\n\n\tconstructor() {\n\t\tthis.buffer = [];\n\t\tthis.trailingCR = false;\n\t}\n\n\tdecode(chunk: Bytes): string[] {\n\t\tlet text = this.decodeText(chunk);\n\n\t\tif (this.trailingCR) {\n\t\t\ttext = '\\r' + text;\n\t\t\tthis.trailingCR = false;\n\t\t}\n\t\tif (text.endsWith('\\r')) {\n\t\t\tthis.trailingCR = true;\n\t\t\ttext = text.slice(0, -1);\n\t\t}\n\n\t\tif (!text) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst trailingNewline = LineDecoder.NEWLINE_CHARS.has(\n\t\t\ttext[text.length - 1] || '',\n\t\t);\n\t\tlet lines = text.split(LineDecoder.NEWLINE_REGEXP);\n\n\t\t// if there is a trailing new line then the last entry will be an empty\n\t\t// string which we don't care about\n\t\tif (trailingNewline) {\n\t\t\tlines.pop();\n\t\t}\n\n\t\tif (lines.length === 1 && !trailingNewline) {\n\t\t\tthis.buffer.push(lines[0]!);\n\t\t\treturn [];\n\t\t}\n\n\t\tif (this.buffer.length > 0) {\n\t\t\tlines = [this.buffer.join('') + lines[0], ...lines.slice(1)];\n\t\t\tthis.buffer = [];\n\t\t}\n\n\t\tif (!trailingNewline) {\n\t\t\tthis.buffer = [lines.pop() || ''];\n\t\t}\n\n\t\treturn lines;\n\t}\n\n\tdecodeText(bytes: Bytes): string {\n\t\tif (bytes == null) return '';\n\t\tif (typeof bytes === 'string') return bytes;\n\n\t\t// Node:\n\t\tif (typeof Buffer !== 'undefined') {\n\t\t\tif (bytes instanceof Buffer) {\n\t\t\t\treturn bytes.toString();\n\t\t\t}\n\t\t\tif (bytes instanceof Uint8Array) {\n\t\t\t\treturn Buffer.from(bytes).toString();\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global \"Buffer\" defined, which this library assumes to be Node. Please report this error.`,\n\t\t\t);\n\t\t}\n\n\t\t// Browser\n\t\tif (typeof TextDecoder !== 'undefined') {\n\t\t\tif (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {\n\t\t\t\tthis.textDecoder ??= new TextDecoder('utf8');\n\t\t\t\treturn this.textDecoder.decode(bytes);\n\t\t\t}\n\n\t\t\tthrow new Error(\n\t\t\t\t`Unexpected: received non-Uint8Array/ArrayBuffer (${\n\t\t\t\t\t(bytes as any).constructor.name\n\t\t\t\t}) in a web platform. Please report this error.`,\n\t\t\t);\n\t\t}\n\n\t\tthrow new Error(\n\t\t\t`Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,\n\t\t);\n\t}\n\n\tflush(): string[] {\n\t\tif (!this.buffer.length && !this.trailingCR) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst lines = [this.buffer.join('')];\n\t\tthis.buffer = [];\n\t\tthis.trailingCR = false;\n\t\treturn lines;\n\t}\n}\n\n/**\n * Decodes an array of chunks into an array of lines.\n *\n * This is an internal helper function that's just used for testing\n *\n * @param chunks - An array of chunks to decode.\n * @returns An array of decoded lines.\n */\nexport function _decodeChunks(chunks: string[]): string[] {\n\tconst decoder = new LineDecoder();\n\tconst lines: string[] = [];\n\tfor (const chunk of chunks) {\n\t\tlines.push(...decoder.decode(chunk));\n\t}\n\n\treturn lines;\n}\n\nfunction partition(str: string, delimiter: string): [string, string, string] {\n\tconst index = str.indexOf(delimiter);\n\tif (index !== -1) {\n\t\treturn [\n\t\t\tstr.substring(0, index),\n\t\t\tdelimiter,\n\t\t\tstr.substring(index + delimiter.length),\n\t\t];\n\t}\n\n\treturn [str, '', ''];\n}\n\n/**\n * Most browsers don't yet have async iterable support for ReadableStream,\n * and Node has a very different way of reading bytes from its \"ReadableStream\".\n *\n * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490\n */\nexport function readableStreamAsyncIterable<T>(\n\tstream: any,\n): AsyncIterableIterator<T> {\n\tif (stream[Symbol.asyncIterator]) return stream;\n\n\tconst reader = stream.getReader();\n\treturn {\n\t\tasync next() {\n\t\t\ttry {\n\t\t\t\tconst result = await reader.read();\n\t\t\t\tif (result?.done) reader.releaseLock(); // release lock when stream becomes closed\n\t\t\t\treturn result;\n\t\t\t} catch (e) {\n\t\t\t\treader.releaseLock(); // release lock when stream becomes errored\n\t\t\t\tthrow e;\n\t\t\t}\n\t\t},\n\t\tasync return() {\n\t\t\tconst cancelPromise = reader.cancel();\n\t\t\treader.releaseLock();\n\t\t\tawait cancelPromise;\n\t\t\treturn {done: true, value: undefined};\n\t\t},\n\t\t[Symbol.asyncIterator]() {\n\t\t\treturn this;\n\t\t},\n\t};\n}\n","import {GENERATION_ENDPOINTS} from '@/data/constants';\nimport {Headers} from './../../types'; // Ensure this import is correct\nimport {APIConnectionError, APIError} from './errors';\nimport {Stream} from './stream';\n\ninterface RequestOptions {\n\tendpoint: string;\n\tmethod: string;\n\theaders?: Headers;\n\tbody?: any;\n\tstream?: boolean;\n}\n\ninterface RequestConfig {\n\tapiKey: string;\n\tbaseUrl: string;\n\ttimeout?: number;\n}\n\ninterface SendOptions extends RequestOptions {\n\tendpoint: string;\n}\n\ninterface MakeRequestParams {\n\turl: string;\n\toptions: RequestOptions;\n\theaders: Record<string, string>;\n}\n\ninterface HandleGenerateResponseParams {\n\tresponse: Response;\n\tthreadId: string | null;\n\trawResponse: boolean;\n}\n\nexport class Request {\n\tprivate config: RequestConfig;\n\n\tconstructor(config: RequestConfig) {\n\t\tthis.config = config;\n\t}\n\n\t// Main send function\n\tprivate async send<T>({endpoint, ...options}: SendOptions): Promise<T> {\n\t\tconst url = this.buildUrl({endpoint});\n\t\tconst headers = this.buildHeaders({headers: options.headers});\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await this.makeRequest({url, options, headers});\n\t\t} catch (error) {\n\t\t\tthrow new APIConnectionError({\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tawait this.handleErrorResponse({response});\n\t\t}\n\n\t\tconst isLllmGenerationEndpoint =\n\t\t\tGENERATION_ENDPOINTS.includes(endpoint);\n\n\t\tif (isLllmGenerationEndpoint) {\n\t\t\tconst threadId = response.headers.get('lb-thread-id');\n\n\t\t\tif (!options.body) {\n\t\t\t\treturn this.handleRunResponse({\n\t\t\t\t\tresponse,\n\t\t\t\t\tthreadId: null,\n\t\t\t\t\trawResponse: options.body?.rawResponse ?? false,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (options.body?.stream && url.includes('run')) {\n\t\t\t\treturn this.handleRunResponseStream({\n\t\t\t\t\tresponse,\n\t\t\t\t\trawResponse: options.body.rawResponse,\n\t\t\t\t}) as T;\n\t\t\t}\n\n\t\t\tif (options.body.stream) {\n\t\t\t\treturn this.handleStreamResponse({response}) as T;\n\t\t\t}\n\n\t\t\treturn this.handleRunResponse({\n\t\t\t\tresponse,\n\t\t\t\tthreadId,\n\t\t\t\trawResponse: options.body?.rawResponse ?? false,\n\t\t\t});\n\t\t} else {\n\t\t\tconst res = response.json();\n\t\t\treturn res as T;\n\t\t}\n\t}\n\n\tprivate buildUrl({endpoint}: {endpoint: string}): string {\n\t\treturn `${this.config.baseUrl}${endpoint}`;\n\t}\n\n\tprivate buildHeaders({\n\t\theaders,\n\t}: {\n\t\theaders?: Record<string, string>;\n\t}): Record<string, string> {\n\t\treturn {\n\t\t\t'Content-Type': 'application/json',\n\t\t\tAuthorization: `Bearer ${this.config.apiKey}`,\n\t\t\t...headers,\n\t\t};\n\t}\n\n\tprivate async makeRequest({\n\t\turl,\n\t\toptions,\n\t\theaders,\n\t}: MakeRequestParams): Promise<Response> {\n\t\treturn fetch(url, {\n\t\t\tmethod: options.method,\n\t\t\theaders,\n\t\t\tbody: JSON.stringify(options.body),\n\t\t\t// signal: AbortSignal.timeout(this.config.timeout || 30000),\n\t\t});\n\t}\n\n\tprivate async handleErrorResponse({\n\t\tresponse,\n\t}: {\n\t\tresponse: Response;\n\t}): Promise<never> {\n\t\tlet errorBody;\n\t\ttry {\n\t\t\terrorBody = await response.json();\n\t\t} catch {\n\t\t\terrorBody = await response.text();\n\t\t}\n\t\tthrow APIError.generate(\n\t\t\tresponse.status,\n\t\t\terrorBody,\n\t\t\tresponse.statusText,\n\t\t\tresponse.headers as unknown as Headers,\n\t\t);\n\t}\n\n\tprivate handleStreamResponse({response}: {response: Response}): {\n\t\tstream: any;\n\t\tthreadId: string | null;\n\t} {\n\t\tconst controller = new AbortController();\n\t\tconst stream = Stream.fromSSEResponse(response, controller);\n\t\treturn {stream, threadId: response.headers.get('lb-thread-id')};\n\t}\n\n\tprivate handleRunResponseStream({\n\t\tresponse,\n\t\trawResponse,\n\t}: {\n\t\tresponse: Response;\n\t\trawResponse?: boolean;\n\t}): {\n\t\tstream: any;\n\t\tthreadId: string | null;\n\t\trawResponse?: {\n\t\t\theaders: Record<string, string>;\n\t\t};\n\t} {\n\t\tconst controller = new AbortController();\n\t\tconst streamSSE = Stream.fromSSEResponse(response, controller);\n\t\tconst stream = streamSSE.toReadableStream();\n\n\t\tconst result: {\n\t\t\tstream: ReadableStream<any>;\n\t\t\tthreadId: string | null;\n\t\t\trawResponse?: {\n\t\t\t\theaders: Record<string, string>;\n\t\t\t};\n\t\t} = {\n\t\t\tstream,\n\t\t\tthreadId: response.headers.get('lb-thread-id'),\n\t\t};\n\t\tif (rawResponse) {\n\t\t\tresult.rawResponse = {\n\t\t\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\t\t};\n\t\t}\n\t\treturn result;\n\t}\n\n\tprivate async handleRunResponse({\n\t\tresponse,\n\t\tthreadId,\n\t\trawResponse,\n\t}: HandleGenerateResponseParams): Promise<any> {\n\t\tconst generateResponse = await response.json();\n\n\t\tconst buildResponse = generateResponse.raw\n\t\t\t? {\n\t\t\t\t\tcompletion: generateResponse.completion,\n\t\t\t\t\t...generateResponse.raw,\n\t\t\t\t}\n\t\t\t: generateResponse;\n\n\t\tconst result: any = {\n\t\t\t...buildResponse,\n\t\t};\n\n\t\tif (threadId) {\n\t\t\tresult.threadId = threadId;\n\t\t}\n\n\t\tif (rawResponse) {\n\t\t\tresult.rawResponse = {\n\t\t\t\theaders: Object.fromEntries(response.headers.entries()),\n\t\t\t};\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tasync post<T>(options: Omit<RequestOptions, 'method'>): Promise<T> {\n\t\treturn this.send<T>({...options, method: 'POST'});\n\t}\n\n\tasync get<T>(options: Omit<RequestOptions, 'method' | 'body'>): Promise<T> {\n\t\treturn this.send<T>({...options, method: 'GET'});\n\t}\n\n\tasync put<T>(options: Omit<RequestOptions, 'method'>): Promise<T> {\n\t\treturn this.send<T>({...options, method: 'PUT'});\n\t}\n\n\tasync delete<T>(\n\t\toptions: Omit<RequestOptions, 'method' | 'body'>,\n\t): Promise<T> {\n\t\treturn this.send<T>({...options, method: 'DELETE'});\n\t}\n}\n","import {convertDocToFormData} from '@/lib/utils/doc-to-formdata';\nimport {Request} from '../common/request';\n\nexport type Role = 'user' | 'assistant' | 'system' | 'tool';\n\nexport interface RunOptionsBase {\n\tmessages?: Message[];\n\tvariables?: Variable[];\n\tthreadId?: string;\n\trawResponse?: boolean;\n\trunTools?: boolean;\n\ttools?: Tools[];\n\tname?: string; // Pipe name for SDK,\n\tapiKey?: string; // pipe level key for SDK\n\tllmKey?: string; // LLM API key\n\tjson?: boolean;\n}\n\nexport interface RunOptionsT extends RunOptionsBase {\n\tstream?: false;\n}\n\nexport interface RunOptionsStreamT extends RunOptionsBase {\n\tstream: true;\n}\n\ninterface ChoiceGenerate {\n\tindex: number;\n\tmessage: Message;\n\tlogprobs: boolean | null;\n\tfinish_reason: string;\n}\n\nexport interface Usage {\n\tprompt_tokens: number;\n\tcompletion_tokens: number;\n\ttotal_tokens: number;\n}\n\nexport interface RunResponse {\n\tcompletion: string;\n\tthreadId?: string;\n\tid: string;\n\tobject: string;\n\tcreated: number;\n\tmodel: string;\n\tchoices: ChoiceGenerate[];\n\tusage: Usage;\n\tsystem_fingerprint: string | null;\n\trawResponse?: {\n\t\theaders: Record<string, string>;\n\t};\n\tmessages: Message[];\n\tllmKey?: string;\n\tname?: string;\n}\n\nexport interface RunResponseStream {\n\tstream: ReadableStream<any>;\n\tthreadId: string | null;\n\trawResponse?: {\n\t\theaders: Record<string, string>;\n\t};\n}\n\n// Union type for RunOptions\nexport type RunOptions =\n\t| (RunOptionsT & {name: string; apiKey?: never})\n\t| (RunOptionsT & {name?: never; apiKey: string});\n\nexport type RunOptionsStream =\n\t| (RunOptionsStreamT & {name: string; apiKey?: never})\n\t| (RunOptionsStreamT & {name?: never; apiKey: string});\n\nexport interface Function {\n\tname: string;\n\targuments: string;\n}\n\nexport interface ToolCall {\n\tid: string;\n\ttype: 'function';\n\tfunction: Function;\n}\n\nexport interface Message {\n\trole: Role;\n\tcontent: string | null;\n\tname?: string;\n\ttool_call_id?: string;\n\ttool_calls?: ToolCall[];\n}\n\nexport interface Variable {\n\tname: string;\n\tvalue: string;\n}\n\ninterface ToolChoice {\n\ttype: 'function';\n\tfunction: {name: string};\n}\n\ninterface Tools {\n\ttype: 'function';\n\tfunction: {\n\t\tname: string;\n\t\tdescription?: string;\n\t\tparameters?: Record<string, any>;\n\t};\n}\n\ninterface PipeBaseOptions {\n\tname: string;\n\tdescription?: string;\n\tstatus?: 'public' | 'private';\n\tupsert?: boolean;\n\tmodel?: string;\n\tstream?: boolean;\n\tjson?: boolean;\n\tstore?: boolean;\n\tmoderate?: boolean;\n\ttop_p?: number;\n\tmax_tokens?: number;\n\ttemperature?: number;\n\tpresence_penalty?: number;\n\tfrequency_penalty?: number;\n\tstop?: string[];\n\ttools?: Tools[];\n\ttool_choice?: 'auto' | 'required' | ToolChoice;\n\tparallel_tool_calls?: boolean;\n\tmessages?: Message[];\n\tvariables?: Variable[];\n\tmemory?: {\n\t\tname: string;\n\t}[];\n}\n\nexport interface PipeListResponse {\n\tname: string;\n\tdescription: string;\n\tstatus: 'public' | 'private';\n\towner_login: string;\n\turl: string;\n\tmodel: string;\n\tstream: boolean;\n\tjson: boolean;\n\tstore: boolean;\n\tmoderate: boolean;\n\ttop_p: number;\n\tmax_tokens: number;\n\ttemperature: number;\n\tpresence_penalty: number;\n\tfrequency_penalty: number;\n\tstop: string[];\n\ttool_choice: 'auto' | 'required' | ToolChoice;\n\tparallel_tool_calls: boolean;\n\tmessages: Message[];\n\tvariables: Variable[] | [];\n\ttools: Tools[] | [];\n\tmemory:\n\t\t| {\n\t\t\t\tname: string;\n\t\t }[]\n\t\t| [];\n}\n\ninterface PipeBaseResponse {\n\tname: string;\n\tdescription: string;\n\tstatus: 'public' | 'private';\n\towner_login: string;\n\turl: string;\n\ttype: 'chat' | 'generate' | 'run';\n\tapi_key: string;\n}\n\nexport interface PipeCreateOptions extends PipeBaseOptions {}\nexport interface PipeUpdateOptions extends PipeBaseOptions {}\nexport interface PipeCreateResponse extends PipeBaseResponse {}\nexport interface PipeUpdateResponse extends PipeBaseResponse {}\n\ninterface MemoryBaseResponse {\n\tname: string;\n\tdescription: string;\n\towner_login: string;\n\turl: string;\n}\n\nexport type EmbeddingModels =\n\t| 'openai:text-embedding-3-large'\n\t| 'cohere:embed-multilingual-v3.0'\n\t| 'cohere:embed-multilingual-light-v3.0'\n\t| 'google:text-embedding-004';\n\nexport type ContentType =\n\t| 'application/pdf'\n\t| 'text/plain'\n\t| 'text/markdown'\n\t| 'text/csv'\n\t| 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n\t| 'application/vnd.ms-excel';\n\nexport interface MemoryCreateOptions {\n\tname: string;\n\tdescription?: string;\n\tembedding_model?: EmbeddingModels;\n}\n\nexport interface MemoryDeleteOptions {\n\tname: string;\n}\n\ntype FilterOperator = 'Eq' | 'NotEq' | 'In' | 'NotIn' | 'And' | 'Or';\ntype FilterConnective = 'And' | 'Or';\ntype FilterValue = string | string[];\ntype MemoryFilters = [\n\tFilterOperator | FilterConnective,\n\tFilterValue | MemoryFilters,\n][];\n\nexport interface MemoryRetrieveOptions {\n\tquery: string;\n\tmemory: {\n\t\tname: string;\n\t\tfilters?: MemoryFilters;\n\t}[];\n\ttopK?: number;\n}\n\nexport interface MemoryListDocOptions {\n\tmemoryName: string;\n}\n\nexport interface MemoryDeleteDocOptions {\n\tmemoryName: string;\n\tdocumentName: string;\n}\n\nexport interface MemoryUploadDocOptions {\n\tmemoryName: string;\n\tdocumentName: string;\n\tmeta?: Record<string, string>;\n\tdocument: Buffer | File | FormData | ReadableStream;\n\tcontentType: ContentType;\n}\n\nexport interface MemoryRetryDocEmbedOptions {\n\tmemoryName: string;\n\tdocumentName: string;\n}\n\nexport interface MemoryCreateResponse extends MemoryBaseResponse {\n\tembedding_model: EmbeddingModels;\n}\nexport interface MemoryListResponse extends MemoryBaseResponse {\n\tembedding_model: EmbeddingModels;\n}\nexport interface BaseDeleteResponse {\n\tsuccess: boolean;\n}\n\nexport interface MemoryDeleteResponse extends BaseDeleteResponse {}\nexport interface MemoryDeleteDocResponse extends BaseDeleteResponse {}\nexport interface MemoryRetryDocEmbedResponse extends BaseDeleteResponse {}\n\nexport interface MemoryRetrieveResponse {\n\ttext: string;\n\tsimilarity: number;\n\tmeta: Record<string, string>;\n}\n\nexport interface MemoryListDocResponse {\n\tname: string;\n\tstatus: 'queued' | 'in_progress' | 'completed' | 'failed';\n\tstatus_message: string | null;\n\tmetadata: {\n\t\tsize: number;\n\t\ttype: ContentType;\n\t};\n\tenabled: boolean;\n\tchunk_size: number;\n\tchunk_overlap: number;\n\towner_login: string;\n}\n\nexport interface LangbaseOptions {\n\tapiKey: string;\n\tbaseUrl?: string;\n}\n\nexport interface ToolWebSearchOptions {\n\tquery: string;\n\tservice: 'exa';\n\ttotalResults?: number;\n\tdomains?: string[];\n\tapiKey?: string;\n}\n\nexport interface ToolWebSearchResponse {\n\turl: string;\n\tcontent: string;\n}\n\nexport interface ToolCrawlOptions {\n\turl: string[];\n\tmaxPages?: number;\n\tapiKey?: string;\n}\n\nexport interface ToolCrawlResponse {\n\turl: string;\n\tcontent: string;\n}\n\nexport interface EmbedOptions {\n\tchunks: string[];\n\tembeddingModel?: EmbeddingModels;\n}\n\nexport type EmbedResponse = number[][];\n\nexport interface ChunkOptions {\n\tdocument: Buffer | File | FormData | ReadableStream;\n\tdocumentName: string;\n\tcontentType: ContentType;\n\tchunkMaxLength?: string;\n\tchunkOverlap?: string;\n\tseparator?: string;\n}\n\nexport type ChunkResponse = string[];\n\nexport type ParseOptions = {\n\tdocument: Buffer | File | FormData | ReadableStream;\n\tdocumentName: string;\n\tcontentType: ContentType;\n};\n\nexport type ParseResponse = {\n\tdocumentName: string;\n\tcontent: string;\n};\n\nexport interface AddMessageOptions {\n\tthreadId: string;\n\tmessages: Message[];\n}\n\nexport interface ListMessagesOptions {\n\tthreadId: string;\n}\n\nexport interface DeleteThreadOptions {\n\tthreadId: string;\n}\n\nexport class Langbase {\n\tprivate request: Request;\n\tprivate apiKey: string;\n\tprivate baseUrl: string;\n\tpublic pipe: {\n\t\tlist: () => Promise<PipeListResponse[]>;\n\t\tcreate: (options: PipeCreateOptions) => Promise<PipeCreateResponse>;\n\t\tupdate: (options: PipeUpdateOptions) => Promise<PipeUpdateResponse>;\n\t\trun: {\n\t\t\t(options: RunOptionsStream): Promise<RunResponseStream>;\n\t\t\t(options: RunOptions): Promise<RunResponse>;\n\t\t};\n\t};\n\tpublic memory: {\n\t\tcreate: (options: MemoryCreateOptions) => Promise<MemoryCreateResponse>;\n\t\tdelete: (options: MemoryDeleteOptions) => Promise<MemoryDeleteResponse>;\n\t\tretrieve: (\n\t\t\toptions: MemoryRetrieveOptions,\n\t\t) => Promise<MemoryRetrieveResponse[]>;\n\t\tlist: () => Promise<MemoryListResponse[]>;\n\t\tdocuments: {\n\t\t\tlist: (\n\t\t\t\toptions: MemoryListDocOptions,\n\t\t\t) => Promise<MemoryListDocResponse[]>;\n\t\t\tdelete: (\n\t\t\t\toptions: MemoryDeleteDocOptions,\n\t\t\t) => Promise<MemoryDeleteDocResponse>;\n\t\t\tupload: (options: MemoryUploadDocOptions) => Promise<Response>;\n\t\t\tembedding: {\n\t\t\t\tretry: (\n\t\t\t\t\toptions: MemoryRetryDocEmbedOptions,\n\t\t\t\t) => Promise<MemoryRetryDocEmbedResponse>;\n\t\t\t};\n\t\t};\n\t};\n\n\tpublic thread: {\n\t\tmessages: {\n\t\t\tadd: (options: AddMessageOptions) => Promise<Message[]>;\n\t\t\tlist: (options: ListMessagesOptions) => Promise<Message[]>;\n\t\t};\n\t\tdelete: (options: DeleteThreadOptions) => Promise<boolean>;\n\t};\n\n\tpublic tool: {\n\t\tcrawl: (options: ToolCrawlOptions) => Promise<ToolCrawlResponse[]>;\n\t\twebSearch: (\n\t\t\toptions: ToolWebSearchOptions,\n\t\t) => Promise<ToolWebSearchResponse[]>;\n\t};\n\n\tpublic embed: (options: EmbedOptions) => Promise<EmbedResponse>;\n\tpublic chunk: (options: ChunkOptions) => Promise<ChunkResponse>;\n\tpublic parse: (options: ParseOptions) => Promise<ParseResponse>;\n\n\tconstructor(options?: LangbaseOptions) {\n\t\tthis.baseUrl = options?.baseUrl ?? 'https://api.langbase.com';\n\t\tthis.apiKey = options?.apiKey ?? '';\n\t\tthis.request = new Request({\n\t\t\tapiKey: this.apiKey,\n\t\t\tbaseUrl: this.baseUrl,\n\t\t});\n\n\t\t// Initialize pipe property with method bindings\n\t\tthis.pipe = {\n\t\t\tlist: this.listPipe.bind(this),\n\t\t\tcreate: this.createPipe.bind(this),\n\t\t\tupdate: this.updatePipe.bind(this),\n\t\t\trun: this.runPipe.bind(this),\n\t\t};\n\n\t\t// Initialize memory property with method bindings\n\t\tthis.memory = {\n\t\t\tcreate: this.createMemory.bind(this),\n\t\t\tdelete: this.deleteMemory.bind(this),\n\t\t\tretrieve: this.retrieveMemory.bind(this),\n\t\t\tlist: this.listMemory.bind(this),\n\t\t\tdocuments: {\n\t\t\t\tlist: this.listDocs.bind(this),\n\t\t\t\tdelete: this.deleteDoc.bind(this),\n\t\t\t\tupload: this.uploadDocs.bind(this),\n\t\t\t\tembedding: {\n\t\t\t\t\tretry: this.retryDocEmbed.bind(this),\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tthis.tool = {\n\t\t\tcrawl: this.webCrawl.bind(this),\n\t\t\twebSearch: this.webSearch.bind(this),\n\t\t};\n\n\t\tthis.embed = this.generateEmbeddings.bind(this);\n\t\tthis.chunk = this.chunkDocument.bind(this);\n\t\tthis.parse = this.parseDocument.bind(this);\n\t\tthis.thread = {\n\t\t\tmessages: {\n\t\t\t\tadd: this.addMessages.bind(this),\n\t\t\t\tlist: this.listMessages.bind(this),\n\t\t\t},\n\t\t\tdelete: this.deleteThread.bind(this),\n\t\t};\n\t}\n\n\tprivate async runPipe(\n\t\toptions: RunOptionsStream,\n\t): Promise<RunResponseStream>;\n\tprivate async runPipe(options: RunOptions): Promise<RunResponse>;\n\tprivate async runPipe(\n\t\toptions: RunOptions | RunOptionsStream,\n\t): Promise<RunResponse | RunResponseStream> {\n\t\tif (!options.name?.trim() && !options.apiKey) {\n\t\t\tthrow new Error(\n\t\t\t\t'Pipe name or Pipe API key is required to run the pipe.',\n\t\t\t);\n\t\t}\n\n\t\t// Remove stream property if it's not set to true\n\t\tif (typeof options.stream === 'undefined') {\n\t\t\tdelete options.stream;\n\t\t}\n\n\t\t// if apikey is provided, create a new request instance\n\t\tif (options.apiKey) {\n\t\t\tthis.request = new Request({\n\t\t\t\tapiKey: options.apiKey,\n\t\t\t\tbaseUrl: this.baseUrl,\n\t\t\t});\n\t\t}\n\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/pipes/run',\n\t\t\tbody: options,\n\t\t\theaders: {\n\t\t\t\t...(options.llmKey && {\n\t\t\t\t\t'LB-LLM-KEY': options.llmKey,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Creates a new pipe on Langbase.\n\t *\n\t * @param {PipeCreateOptions} options - The options for creating the pipe.\n\t * @returns {Promise<PipeCreateResponse>} A promise that resolves to the response of the pipe creation.\n\t */\n\tprivate async createPipe(\n\t\toptions: PipeCreateOptions,\n\t): Promise<PipeCreateResponse> {\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/pipes',\n\t\t\tbody: options,\n\t\t});\n\t}\n\n\t/**\n\t * Updates a pipe on Langbase.\n\t *\n\t * @param {PipeUpdateOptions} options - The options for updating the pipe.\n\t * @returns {Promise<PipeUpdateResponse>} A promise that resolves to the response of the update operation.\n\t */\n\tprivate async updatePipe(\n\t\toptions: PipeUpdateOptions,\n\t): Promise<PipeUpdateResponse> {\n\t\treturn this.request.post({\n\t\t\tendpoint: `/v1/pipes/${options.name}`,\n\t\t\tbody: options,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves a list of pipes.\n\t *\n\t * @returns {Promise<PipeListResponse[]>} A promise that resolves to an array of PipeListResponse objects.\n\t */\n\tprivate async listPipe(): Promise<PipeListResponse[]> {\n\t\treturn this.request.get({\n\t\t\tendpoint: '/v1/pipes',\n\t\t});\n\t}\n\n\t/**\n\t * Creates a new memory on Langbase.\n\t *\n\t * @param {MemoryCreateOptions} options - The options to create the memory instance.\n\t * @param {string} options.name - The name of the memory.\n\t * @param {string} options.description - The description of the memory.\n\t * @returns {Promise<MemoryCreateResponse>} A promise that resolves to the response of the memory creation.\n\t */\n\tprivate async createMemory(\n\t\toptions: MemoryCreateOptions,\n\t): Promise<MemoryCreateResponse> {\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/memory',\n\t\t\tbody: options,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves a list of all memories on Langbase.\n\t *\n\t * @returns {Promise<MemoryListResponse[]>} A promise that resolves to an array of memory list responses.\n\t */\n\tprivate async listMemory(): Promise<MemoryListResponse[]> {\n\t\treturn this.request.get({\n\t\t\tendpoint: '/v1/memory',\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a memory on Langbase.\n\t *\n\t * @param {MemoryDeleteOptions} options - The options for deleting the memory resource.\n\t * @param {string} options.name - The name of the memory to delete.\n\t * @returns {Promise<MemoryDeleteResponse>} A promise that resolves to the response of the delete operation.\n\t */\n\tprivate async deleteMemory(\n\t\toptions: MemoryDeleteOptions,\n\t): Promise<MemoryDeleteResponse> {\n\t\treturn this.request.delete({\n\t\t\tendpoint: `/v1/memory/${options.name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves similar text from the memory.\n\t *\n\t * @param {MemoryRetrieveOptions} options - The options to use for retrieving memory data.\n\t * @param {string} options.query - The query text to search for.\n\t * @param {object[]} options.memory - The memory to search in.\n\t * @param {number} [options.topK] - The number of similar texts to retrieve.\n\t * @returns A promise that resolves to an array of `MemoryRetrieveResponse` objects.\n\t */\n\tprivate async retrieveMemory(\n\t\toptions: MemoryRetrieveOptions,\n\t): Promise<MemoryRetrieveResponse[]> {\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/memory/retrieve',\n\t\t\tbody: options,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves a list of documents inside a memory.\n\t *\n\t * @param {MemoryListDocOptions} options - The options for listing documents, including the memory name.\n\t * @param {string} options.memoryName - The name of the memory to list documents from.\n\t * @returns A promise that resolves to an array of `MemoryListDocResponse` objects.\n\t */\n\tprivate async listDocs(\n\t\toptions: MemoryListDocOptions,\n\t): Promise<MemoryListDocResponse[]> {\n\t\treturn this.request.get({\n\t\t\tendpoint: `/v1/memory/${options.memoryName}/documents`,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a document from a memory.\n\t *\n\t * @param {MemoryDeleteDocOptions} options - The options for deleting the document.\n\t * @param {string} options.memoryName - The name of the memory to delete the document from.\n\t * @param {string} options.documentName - The name of the document to delete.\n\t * @returns A promise that resolves to a `MemoryDeleteDocResponse` indicating the result of the delete operation.\n\t */\n\tprivate async deleteDoc(\n\t\toptions: MemoryDeleteDocOptions,\n\t): Promise<MemoryDeleteDocResponse> {\n\t\treturn this.request.delete({\n\t\t\tendpoint: `/v1/memory/${options.memoryName}/documents/${options.documentName}`,\n\t\t});\n\t}\n\n\t/**\n\t * Uploads a document to the memory.\n\t *\n\t * @param {MemoryUploadDocOptions} options - The options for uploading the document.\n\t * @param {string} options.memoryName - The name of the memory to upload the document to.\n\t * @param {string} options.fileName - The name of the file being uploaded.\n\t * @param {object} [options.meta] - Optional metadata associated with the document.\n\t * @param {string} options.contentType - The MIME type of the file being uploaded.\n\t * @param {Blob | Buffer} options.file - The file content to be uploaded.\n\t * @returns {Promise<Response>} The response from the upload request.\n\t * @throws Will throw an error if the upload fails.\n\t */\n\tprivate async uploadDocs(\n\t\toptions: MemoryUploadDocOptions,\n\t): Promise<Response> {\n\t\ttry {\n\t\t\tconst response = (await this.request.post({\n\t\t\t\tendpoint: `/v1/memory/documents`,\n\t\t\t\tbody: {\n\t\t\t\t\tmemoryName: options.memoryName,\n\t\t\t\t\tfileName: options.documentName,\n\t\t\t\t\tmeta: options.meta,\n\t\t\t\t},\n\t\t\t})) as unknown as {signedUrl: string};\n\n\t\t\tconst uploadUrl = response.signedUrl;\n\n\t\t\treturn await fetch(uploadUrl, {\n\t\t\t\tmethod: 'PUT',\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t\t'Content-Type': options.contentType,\n\t\t\t\t},\n\t\t\t\tbody: options.document,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Retries the embedding process for a specific document in memory.\n\t *\n\t * @param options - The options required to retry the document embedding.\n\t * @param options.memoryName - The name of the memory containing the document.\n\t * @param options.documentName - The name of the document to retry embedding for.\n\t * @returns A promise that resolves to the response of the retry operation.\n\t */\n\tprivate async retryDocEmbed(\n\t\toptions: MemoryRetryDocEmbedOptions,\n\t): Promise<MemoryRetryDocEmbedResponse> {\n\t\treturn this.request.get({\n\t\t\tendpoint: `/v1/memory/${options.memoryName}/documents/${options.documentName}/embeddings/retry`,\n\t\t});\n\t}\n\n\t/**\n\t * Performs a web search using the Langbase API.\n\t *\n\t * @param options - Web search configuration options\n\t * @param options.apiKey - Optional API key for web search authentication\n\t * @returns Promise that resolves to an array of web search results\n\t */\n\tprivate async webSearch(\n\t\toptions: ToolWebSearchOptions,\n\t): Promise<ToolWebSearchResponse[]> {\n\t\tconst apiKey = options.apiKey ? options.apiKey : null;\n\t\tapiKey && delete options.apiKey;\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/tools/web-search',\n\t\t\tbody: options,\n\t\t\theaders: {\n\t\t\t\t...(apiKey && {\n\t\t\t\t\t'LB-WEB-SEARCH-KEY': apiKey,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Performs a web crawls on target websites using the Langbase API.\n\t *\n\t * @param options - Crawl configuration options\n\t * @returns An array of responses containing data from the crawl operation.\n\t */\n\tprivate async webCrawl(\n\t\toptions: ToolCrawlOptions,\n\t): Promise<ToolCrawlResponse[]> {\n\t\tconst apiKey = options.apiKey ? options.apiKey : null;\n\t\tapiKey && delete options.apiKey;\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/tools/crawl',\n\t\t\tbody: options,\n\t\t\theaders: {\n\t\t\t\t...(apiKey && {\n\t\t\t\t\t'LB-CRAWL-KEY': apiKey,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Generates embeddings for the given input using the LangBase API.\n\t *\n\t * @param options - Embed options\n\t * @returns Promise that resolves to the embedding response containing vector representations\n\t */\n\tprivate async generateEmbeddings(\n\t\toptions: EmbedOptions,\n\t): Promise<EmbedResponse> {\n\t\treturn this.request.post({\n\t\t\tendpoint: '/v1/embed',\n\t\t\tbody: options,\n\t\t});\n\t}\n\n\t/**\n\t * Splits a given document into multiple chunks using the Langbase API.\n\t *\n\t * @param options - The chunking options.\n\t * @param options.document - The document to be chunked.\n\t * @param options.chunk_max_length - An optional maximum length for each chunk.\n\t * @param options.chunk_overlap - An optional number of overlapping characters between chunks.\n\t * @param options.separator - An optional separator used to split the document.\n\t * @returns A prom