@axflow/models/shared
A set of shared utilities for working with streams, HTTP, and more.
import {
IterableToStream,
StreamToIterable,
NdJsonStream,
StreamingJsonResponse,
POST,
HttpError,
isHttpError,
} from '@axflow/models/shared';
import type { NdJsonValueType, JSONValueType, MessageType } from '@axflow/models/shared';
import {
IterableToStream,
StreamToIterable,
NdJsonStream,
StreamingJsonResponse,
POST,
HttpError,
isHttpError,
} from '@axflow/models/shared';
import type { NdJsonValueType, JSONValueType, MessageType } from '@axflow/models/shared';
IterableToStream
/**
* Converts an AsyncIterable<T> to a ReadableStream<T>.
*
* ReadableStreams implement this natively as `ReadableStream.from` but this is
* hardly available in any environments as of the time this was written.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/from_static
*
* @param iterable Any async iterable object.
* @returns A ReadableStream over the iterable contents.
*/
declare function IterableToStream<T>(iterable: AsyncIterable<T>): ReadableStream<T>;
/**
* Converts an AsyncIterable<T> to a ReadableStream<T>.
*
* ReadableStreams implement this natively as `ReadableStream.from` but this is
* hardly available in any environments as of the time this was written.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/from_static
*
* @param iterable Any async iterable object.
* @returns A ReadableStream over the iterable contents.
*/
declare function IterableToStream<T>(iterable: AsyncIterable<T>): ReadableStream<T>;
StreamToIterable
/**
* Convert a ReadableStream<T> to an AsyncIterable<T>.
*
* ReadableStreams implement this natively in recent node versions. Unfortunately, older
* node versions, most browsers, and the TypeScript type system do not support it yet.
*
* Example:
*
* for await (const chunk of StreamToIterable(stream)) {
* // Do stuff with chunk
* }
*
* @param stream A ReadableStream.
* @returns An AsyncIterable over the stream contents.
*/
declare function StreamToIterable<T>(stream: ReadableStream<T>): AsyncIterable<T>;
/**
* Convert a ReadableStream<T> to an AsyncIterable<T>.
*
* ReadableStreams implement this natively in recent node versions. Unfortunately, older
* node versions, most browsers, and the TypeScript type system do not support it yet.
*
* Example:
*
* for await (const chunk of StreamToIterable(stream)) {
* // Do stuff with chunk
* }
*
* @param stream A ReadableStream.
* @returns An AsyncIterable over the stream contents.
*/
declare function StreamToIterable<T>(stream: ReadableStream<T>): AsyncIterable<T>;
NdJsonStream
The streaming guide for more information.
/**
* An object that can encode and decode newline-delimited JSON in the following format:
*
* { type: 'data' | 'chunk', value: <any valid JSON value> }
*
* When `type` is `chunk`, `value` represents a chunk of the source stream. When `type`
* is `data`, `value` represents any additional data sent along with the source stream.
*
* @see http://ndjson.org
*/
declare class NdJsonStream {
/**
* These are the proper headers for newline-delimited JSON streams.
*
* @see http://ndjson.org
*/
static headers: Readonly<{
'content-type': 'application/x-ndjson; charset=utf-8';
}>;
/**
* Transforms a stream of JSON-serializable objects to stream of newline-delimited JSON.
*
* Each object is wrapped with an object that specifies the `type` and references
* the `value`. The `type` is one of `chunk` or `data`. A type of `chunk` means that
* the `value` corresponds to chunks from the input stream. A type of `data` means
* that the `value` corresponds to the additional data provided as the second argument
* to this function.
*
*
* Example WITHOUT additional data:
*
* const chunk = { key: 'value' };
* const stream = new ReadableStream({start(con) { con.enqueue(chunk); con.close() }});
* const ndJsonStream = NdJsonStream.encode(stream);
* const entries = [];
* for await (const chunk of stream) {
* entry.push(new TextDecoder().decode(chunk));
* }
* console.log(entries); // [ "{\"type\":\"chunk\",\"value\":{\"key\":\"value\"}}\n" ]
*
*
* Example WITH additional data:
*
* const chunk = { key: 'value' };
* const stream = new ReadableStream({start(con) { con.enqueue(chunk); con.close() }});
* const ndJsonStream = NdJsonStream.encode(stream, { data: [{ extra: 'data' }] });
* const entries = [];
* for await (const chunk of stream) {
* entry.push(new TextDecoder().decode(chunk));
* }
* console.log(entries); // [ "{\"type\":\"data\",\"value\":{\"extra\":\"data\"}}\n", "{\"type\":\"chunk\",\"value\":{\"key\":\"value\"}}\n" ]
*
* @see http://ndjson.org
*
* @param stream A readable stream of chunks to encode as newline-delimited JSON.
* @param options
* @param options.data Additional data to enqueue to the output stream. If data is a `Promise`, the stream will wait for it to resolve and enqueue its resolved values before closing.
* @returns A readable stream of newline-delimited JSON.
*/
static encode<T = any>(
stream: ReadableStream<T>,
options?: {
data?: JSONValueType[] | Promise<JSONValueType[]>;
}
): ReadableStream<Uint8Array>;
/**
* Transforms a stream of newline-delimited JSON to a stream of objects.
*
* @see http://ndjson.org
*
* @param stream A readable stream of newline-delimited JSON objects.
* @returns A readable stream of objects.
*/
static decode(stream: ReadableStream<Uint8Array>): ReadableStream<NdJsonValueType>;
}
/**
* An object that can encode and decode newline-delimited JSON in the following format:
*
* { type: 'data' | 'chunk', value: <any valid JSON value> }
*
* When `type` is `chunk`, `value` represents a chunk of the source stream. When `type`
* is `data`, `value` represents any additional data sent along with the source stream.
*
* @see http://ndjson.org
*/
declare class NdJsonStream {
/**
* These are the proper headers for newline-delimited JSON streams.
*
* @see http://ndjson.org
*/
static headers: Readonly<{
'content-type': 'application/x-ndjson; charset=utf-8';
}>;
/**
* Transforms a stream of JSON-serializable objects to stream of newline-delimited JSON.
*
* Each object is wrapped with an object that specifies the `type` and references
* the `value`. The `type` is one of `chunk` or `data`. A type of `chunk` means that
* the `value` corresponds to chunks from the input stream. A type of `data` means
* that the `value` corresponds to the additional data provided as the second argument
* to this function.
*
*
* Example WITHOUT additional data:
*
* const chunk = { key: 'value' };
* const stream = new ReadableStream({start(con) { con.enqueue(chunk); con.close() }});
* const ndJsonStream = NdJsonStream.encode(stream);
* const entries = [];
* for await (const chunk of stream) {
* entry.push(new TextDecoder().decode(chunk));
* }
* console.log(entries); // [ "{\"type\":\"chunk\",\"value\":{\"key\":\"value\"}}\n" ]
*
*
* Example WITH additional data:
*
* const chunk = { key: 'value' };
* const stream = new ReadableStream({start(con) { con.enqueue(chunk); con.close() }});
* const ndJsonStream = NdJsonStream.encode(stream, { data: [{ extra: 'data' }] });
* const entries = [];
* for await (const chunk of stream) {
* entry.push(new TextDecoder().decode(chunk));
* }
* console.log(entries); // [ "{\"type\":\"data\",\"value\":{\"extra\":\"data\"}}\n", "{\"type\":\"chunk\",\"value\":{\"key\":\"value\"}}\n" ]
*
* @see http://ndjson.org
*
* @param stream A readable stream of chunks to encode as newline-delimited JSON.
* @param options
* @param options.data Additional data to enqueue to the output stream. If data is a `Promise`, the stream will wait for it to resolve and enqueue its resolved values before closing.
* @returns A readable stream of newline-delimited JSON.
*/
static encode<T = any>(
stream: ReadableStream<T>,
options?: {
data?: JSONValueType[] | Promise<JSONValueType[]>;
}
): ReadableStream<Uint8Array>;
/**
* Transforms a stream of newline-delimited JSON to a stream of objects.
*
* @see http://ndjson.org
*
* @param stream A readable stream of newline-delimited JSON objects.
* @returns A readable stream of objects.
*/
static decode(stream: ReadableStream<Uint8Array>): ReadableStream<NdJsonValueType>;
}
StreamingJsonResponse
The streaming guide for more information.
/**
* A subclass of `Response` that streams newline-delimited JSON.
*/
declare class StreamingJsonResponse<T> extends Response {
/**
* Create a `Response` object that streams newline-delimited JSON objects.
*
* Example
*
* export async function POST(request: Request) {
* const req = await request.json();
* const stream = await OpenAIChat.stream(req, { apiKey: OPENAI_API_KEY });
* return new StreamingJsonResponse(stream, {
* map: (chunk) => chunk.choices[0].delta.content ?? ''
* data: [{ stream: "additional" }, { data: "here" }]
* });
* }
*
* @see http://ndjson.org
*
* @param stream A readable stream of chunks to encode as newline-delimited JSON.
* @param options
* @param options.status HTTP response status.
* @param options.statusText HTTP response status text.
* @param options.headers HTTP response headers.
* @param options.data Additional data to enqueue to the output stream. If data is a `Promise`, the stream will wait for it to resolve and enqueue its resolved values before closing.
*/
constructor(
stream: ReadableStream<T>,
options?: ResponseInit & {
data?: JSONValueType[] | Promise<JSONValueType[]>;
}
);
}
/**
* A subclass of `Response` that streams newline-delimited JSON.
*/
declare class StreamingJsonResponse<T> extends Response {
/**
* Create a `Response` object that streams newline-delimited JSON objects.
*
* Example
*
* export async function POST(request: Request) {
* const req = await request.json();
* const stream = await OpenAIChat.stream(req, { apiKey: OPENAI_API_KEY });
* return new StreamingJsonResponse(stream, {
* map: (chunk) => chunk.choices[0].delta.content ?? ''
* data: [{ stream: "additional" }, { data: "here" }]
* });
* }
*
* @see http://ndjson.org
*
* @param stream A readable stream of chunks to encode as newline-delimited JSON.
* @param options
* @param options.status HTTP response status.
* @param options.statusText HTTP response status text.
* @param options.headers HTTP response headers.
* @param options.data Additional data to enqueue to the output stream. If data is a `Promise`, the stream will wait for it to resolve and enqueue its resolved values before closing.
*/
constructor(
stream: ReadableStream<T>,
options?: ResponseInit & {
data?: JSONValueType[] | Promise<JSONValueType[]>;
}
);
}
POST
Wrapper around fetch.
declare function POST(url: string, ptions?: HttpOptionsType): Promise<Response>;
declare function POST(url: string, ptions?: HttpOptionsType): Promise<Response>;
HttpError
An error class raised by POST
.
declare class HttpError extends Error {
readonly code: number;
readonly response: Response;
constructor(message: string, response: Response);
}
declare class HttpError extends Error {
readonly code: number;
readonly response: Response;
constructor(message: string, response: Response);
}
isHttpError
Utility method to check if an error is an HttpError
.
declare function isHttpError(e: unknown): e is HttpError;
declare function isHttpError(e: unknown): e is HttpError;
NdJsonValueType
The type of each JSON object in a newline-delimited JSON event stream.
type NdJsonValueType = {
type: 'chunk' | 'data';
value: JSONValueType;
};
type NdJsonValueType = {
type: 'chunk' | 'data';
value: JSONValueType;
};
JSONValueType
A type representing any value that can be serialized to JSON.
type JSONValueType =
| null
| string
| number
| boolean
| {
[x: string]: JSONValueType;
}
| Array<JSONValueType>;
type JSONValueType =
| null
| string
| number
| boolean
| {
[x: string]: JSONValueType;
}
| Array<JSONValueType>;
FunctionType
If using OpenAI functions, this type corresponds to the functions passed to OpenAI as part of a request.
See https://platform.openai.com/docs/api-reference/chat/create for more information.
type FunctionType = {
name: string;
description?: string;
parameters: JSONValueType;
};
type FunctionType = {
name: string;
description?: string;
parameters: JSONValueType;
};
ToolType
Tools have replaced functions in openAI's nomenclature. Functions are now a type of tools, and each LLM response can contain multiple tool calls.
type ToolType = {
type: 'function';
function: FunctionType;
};
type ToolType = {
type: 'function';
function: FunctionType;
};
ToolCallType
When the LLM decides to call a tool (previously named a function), this is the type that we send down to the client.
type ToolCallType = {
index: number;
id: string;
type: 'function';
function: { name: string; arguments: string };
};
type ToolCallType = {
index: number;
id: string;
type: 'function';
function: { name: string; arguments: string };
};
MessageType
This is the type of each message in a chat, mostly used by the useChat
hook.
type MessageType = {
/**
* Can be any unique string.
*
* For example, the `useChat` hook uses UUIDs because of their native availability in both Node and browsers.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
*/
id: string;
/**
* Specifies who this message is from.
*/
role: 'user' | 'assistant' | 'system';
/**
* The content of the message. If the message was a function call from the assistant,
* then this field will be an empty string and the `functionCall` field will be populated.
*/
content: string;
/**
* The time this message was created, expressed as milliseconds since Epoch.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
*/
created: number;
/**
* Any additional data to associate with the message.
*/
data?: JSONValueType[];
/**
* If using OpenAI functions, the functions available to the assistant can be defined here.
*
* @see https://platform.openai.com/docs/api-reference/chat/create
*/
functions?: FunctionType[];
/**
* If using OpenAI functions and the assistant responds with a function call,
* this field will be populated with the function invocation information.
*
* @see https://platform.openai.com/docs/api-reference/chat/object
*/
functionCall?: {
name: string;
arguments: string;
};
/**
* If using openAI tools, the tools available to the assistant can be defined here.
*
* @see https://platform.openai.com/docs/guides/function-calling
*/
tools?: ToolType[];
/**
* If using OpenAI tools and the assistant responds with one or more tool calls,
* this field will be populated with the tool invocation information.
*
*
* @see https://platform.openai.com/docs/guides/function-calling
*/
toolCalls?: ToolCallType[];
};
type MessageType = {
/**
* Can be any unique string.
*
* For example, the `useChat` hook uses UUIDs because of their native availability in both Node and browsers.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
*/
id: string;
/**
* Specifies who this message is from.
*/
role: 'user' | 'assistant' | 'system';
/**
* The content of the message. If the message was a function call from the assistant,
* then this field will be an empty string and the `functionCall` field will be populated.
*/
content: string;
/**
* The time this message was created, expressed as milliseconds since Epoch.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now
*/
created: number;
/**
* Any additional data to associate with the message.
*/
data?: JSONValueType[];
/**
* If using OpenAI functions, the functions available to the assistant can be defined here.
*
* @see https://platform.openai.com/docs/api-reference/chat/create
*/
functions?: FunctionType[];
/**
* If using OpenAI functions and the assistant responds with a function call,
* this field will be populated with the function invocation information.
*
* @see https://platform.openai.com/docs/api-reference/chat/object
*/
functionCall?: {
name: string;
arguments: string;
};
/**
* If using openAI tools, the tools available to the assistant can be defined here.
*
* @see https://platform.openai.com/docs/guides/function-calling
*/
tools?: ToolType[];
/**
* If using OpenAI tools and the assistant responds with one or more tool calls,
* this field will be populated with the tool invocation information.
*
*
* @see https://platform.openai.com/docs/guides/function-calling
*/
toolCalls?: ToolCallType[];
};
createMessage
/*
* Create a new MessageType object and assign default values.
* This is particularly useful if the user doesn't want to bother
* creating an Id and/or a created timestamp, and would like a default
* behavior for these.
*
* @param message - The message object to create, a Partial<MessageType> object.
* @returns A MessageType object, with all required values assigned.
*/
declare function createMessage = (message: Partial<MessageType>): MessageType;
/*
* Create a new MessageType object and assign default values.
* This is particularly useful if the user doesn't want to bother
* creating an Id and/or a created timestamp, and would like a default
* behavior for these.
*
* @param message - The message object to create, a Partial<MessageType> object.
* @returns A MessageType object, with all required values assigned.
*/
declare function createMessage = (message: Partial<MessageType>): MessageType;