UNPKG

@upstash/qstash

Version:

Official Typescript client for QStash

1,745 lines (1,725 loc) 78 kB
import { Ok, Err } from 'neverthrow'; /** * Necessary to verify the signature of a request. */ type ReceiverConfig = { /** * The current signing key. Get it from `https://console.upstash.com/qstash * * If not provided, value will be inferred from environment variables based on QSTASH_REGION * and UPSTASH_REGION header. */ currentSigningKey?: string; /** * The next signing key. Get it from `https://console.upstash.com/qstash * * If not provided, value will be inferred from environment variables based on QSTASH_REGION * and UPSTASH_REGION header. */ nextSigningKey?: string; /** * Controls the local dev server signing keys. * - `true`: use dev server signing keys * - `false`: never use dev server signing keys (ignores QSTASH_DEV env var) * - `undefined`: check QSTASH_DEV env var * * @default undefined */ devMode?: boolean; }; type VerifyRequest = { /** * The signature from the `upstash-signature` header. */ signature: string; /** * The raw request body. */ body: string; /** * URL of the endpoint where the request was sent to. * * Omit empty to disable checking the url. */ url?: string; /** * Number of seconds to tolerate when checking `nbf` and `exp` claims, to deal with small clock differences among different servers * * @default 0 */ clockTolerance?: number; /** * The region from the `upstash-region` header. * * Used to infer which signing keys to use for verification in multi-region setups. */ upstashRegion?: string; }; declare class SignatureError extends Error { constructor(message: string); } /** * Receiver offers a simple way to verify the signature of a request. */ declare class Receiver { private readonly currentSigningKey?; private readonly nextSigningKey?; private readonly devMode?; constructor(config?: ReceiverConfig); /** * Verify the signature of a request. * * Tries to verify the signature with the current signing key. * If that fails, maybe because you have rotated the keys recently, it will * try to verify the signature with the next signing key. * * If that fails, the signature is invalid and a `SignatureError` is thrown. */ verify(request: VerifyRequest): Promise<boolean>; /** * Verify signature with a specific signing key */ private verifyWithKey; private verifyBodyAndUrl; } type Unit = "s" | "m" | "h" | "d"; type Duration = `${bigint}${Unit}`; type State = "CREATED" | "ACTIVE" | "DELIVERED" | "ERROR" | "RETRY" | "FAILED" | "CANCELED" | "IN_PROGRESS"; type HTTPMethods = "GET" | "POST" | "PUT" | "DELETE" | "PATCH"; type Log = { time: number; state: State; messageId: string; nextDeliveryTime?: number; error?: string; url: string; urlGroup?: string; topicName?: string; endpointName?: string; header?: Record<string, string>; body?: string; label?: string; }; /** * Deprecated. Use the `Log` type instead. * * @deprecated */ type Event = Log; type LogPayload = Omit<Log, "urlGroup"> & { topicName: string; }; /** * Deprecated. Use the `LogPayload` type instead. * * @deprecated */ type EventPayload = LogPayload; type GetLogsPayload = { cursor?: string; events: LogPayload[]; }; /** * Deprecated. use the `GetLogsPayload` type instead. * * @deprecated */ type GetEventsPayload = GetLogsPayload; type WithCursor<T> = T & { cursor?: number; }; type BodyInit = Blob | FormData | URLSearchParams | ReadableStream<Uint8Array> | string; type HeadersInit = Headers | Record<string, string> | [string, string][] | IterableIterator<[string, string]>; type RequestOptions = RequestInit & { backend?: string; }; type ChatRateLimit = { "limit-requests": string | null; "limit-tokens": string | null; "remaining-requests": string | null; "remaining-tokens": string | null; "reset-requests": string | null; "reset-tokens": string | null; }; type RateLimit = { limit: string | null; remaining: string | null; reset: string | null; }; type FlowControl = { /** * flow control key */ key: string; } & ({ /** * number of requests which can be active with the same flow control key */ parallelism: number; /** * number of requests to activate per second with the same flow control key * * @deprecated use rate instead */ ratePerSecond?: number; /** * number of requests to activate within the period with the same flow control key. * * Default period is a second. */ rate?: number; /** * The time interval for the `rate` limit. * * For example, if `rate` is 10 and `period` is "1s" (or 1), then 10 requests can be activated per second. * If `rate` is 5 and `period` is "1m" (or 60), then 5 requests can be activated per minute. * * Defaults to "1s" (one second) if not specified. * * Can be specified as a number (in seconds) or a duration string (e.g., "10s", "5m", "1h", "2d"). */ period?: Duration | number; } | { /** * number of requests which can be active with the same flow control key */ parallelism?: number; /** * number of requests to activate per second with the same flow control key * * @deprecated use rate instead */ ratePerSecond: number; /** * number of requests to activate within the period with the same flow control key. * Default period is a second. */ rate?: number; /** * The time interval for the `rate` limit. * * For example, if `rate` is 10 and `period` is "1s" (or 1), then 10 requests can be activated per second. * If `rate` is 5 and `period` is "1m" (or 60), then 5 requests can be activated per minute. * * Defaults to "1s" (one second) if not specified. * * Can be specified as a number (in seconds) or a duration string (e.g., "10s", "5m", "1h", "2d"). */ period?: Duration | number; } | { /** * number of requests which can be active with the same flow control key */ parallelism?: number; /** * number of requests to activate per second with the same flow control key * * @deprecated use rate instead */ ratePerSecond?: number; /** * number of requests to activate within the period with the same flow control key. * Default period is a second. */ rate: number; /** * The time interval for the `rate` limit. * * For example, if `rate` is 10 and `period` is "1s" (or 1), then 10 requests can be activated per second. * If `rate` is 5 and `period` is "1m" (or 60), then 5 requests can be activated per minute. * * Defaults to "1s" (one second) if not specified. * * Can be specified as a number (in seconds) or a duration string (e.g., "10s", "5m", "1h", "2d"). */ period?: Duration | number; }); type ProviderInfo = { /** * full url used for request */ url: string; /** * base url of the request */ baseUrl: string; /** * route elements which will follow the baseUrl */ route: string[]; /** * headers to include in the request */ appendHeaders: Record<string, string>; /** * provider owner */ owner: Owner; /** * method to use in the request */ method: HTTPMethods; }; type ApiKind = "llm" | "email"; type Owner = EmailOwner | LLMOwner; type PublishApi<TName extends ApiKind, TProvider extends BaseProvider<TName>> = { name: TName; provider?: TProvider; }; /** * Email */ type EmailOwner = "resend"; type PublishEmailApi = Required<PublishApi<"email", BaseProvider<"email", EmailOwner>>>; /** * LLM */ type LLMOwner = "upstash" | "openai" | "anthropic" | "custom"; type LLMOptions = { analytics?: { name: "helicone"; token: string; }; }; type PublishLLMApi = PublishApi<"llm", BaseProvider<"llm", LLMOwner>> & LLMOptions; declare abstract class BaseProvider<TName extends ApiKind, TOwner = Owner> { abstract readonly apiKind: TName; abstract readonly method: HTTPMethods; readonly baseUrl: string; token: string; readonly owner: TOwner; constructor(baseUrl: string, token: string, owner: TOwner); /** * called before returning the final request * * @param request */ abstract onFinish(request: ProviderInfo, options: unknown): ProviderInfo; abstract getRoute(): string[]; abstract getHeaders(options: unknown): Record<string, string>; getUrl(): string; } declare class LLMProvider<TOwner extends LLMOwner> extends BaseProvider<"llm", LLMOwner> { readonly apiKind = "llm"; readonly organization?: string; readonly method = "POST"; constructor(baseUrl: string, token: string, owner: TOwner, organization?: string); getRoute(): string[]; getHeaders(options: LLMOptions): Record<string, string>; /** * Checks if callback exists and adds analytics in place if it's set. * * @param request * @param options */ onFinish(providerInfo: ProviderInfo, options: LLMOptions): ProviderInfo; } /** * @deprecated as of version 2.7.17. Will be removed in qstash-js 3.0.0. * * Please use an alternative LLM provider. * * openai: https://upstash.com/docs/qstash/integrations/llm * anthropic: https://upstash.com/docs/qstash/integrations/anthropic */ declare const upstash: () => LLMProvider<"upstash">; declare const openai: ({ token, organization, }: { token: string; organization?: string; }) => LLMProvider<"openai">; declare const anthropic: ({ token }: { token: string; }) => LLMProvider<"anthropic">; declare const custom: ({ baseUrl, token, }: { baseUrl: string; token: string; }) => LLMProvider<"custom">; type ChatCompletionMessage = { role: "system" | "assistant" | "user"; content: string; }; type ChatModel = "meta-llama/Meta-Llama-3-8B-Instruct" | "mistralai/Mistral-7B-Instruct-v0.2"; type ChatResponseFormat = { type: "text" | "json_object"; }; type TopLogprob = { token: string; bytes: number[]; logprob: number; }; type ChatCompletionTokenLogprob = { token: string; bytes: number[]; logprob: number; top_logprobs: TopLogprob[]; }; type ChoiceLogprobs = { content: ChatCompletionTokenLogprob[]; }; type Choice = { finish_reason: "stop" | "length"; index: number; logprobs: ChoiceLogprobs; message: ChatCompletionMessage; }; type CompletionUsage = { completion_tokens: number; prompt_tokens: number; total_tokens: number; }; type ChatCompletion = { id: string; choices: Choice[]; created: number; model: string; object: "chat.completion"; system_fingerprint: string; usage: CompletionUsage; }; type ChunkChoice = { delta: ChatCompletionMessage; finish_reason: "stop" | "length"; index: number; logprobs: ChoiceLogprobs; }; type ChatCompletionChunk = { id: string; choices: ChunkChoice[]; created: number; model: string; object: "chat.completion.chunk"; system_fingerprint: string; usage: CompletionUsage; }; type StreamEnabled = { stream: true; }; type StreamDisabled = { stream: false; } | object; type StreamParameter = StreamEnabled | StreamDisabled; type OpenAIChatModel = "gpt-4-turbo" | "gpt-4-turbo-2024-04-09" | "gpt-4-0125-preview" | "gpt-4-turbo-preview" | "gpt-4-1106-preview" | "gpt-4-vision-preview" | "gpt-4" | "gpt-4-0314" | "gpt-4-0613" | "gpt-4-32k" | "gpt-4-32k-0314" | "gpt-4-32k-0613" | "gpt-3.5-turbo" | "gpt-3.5-turbo-16k" | "gpt-3.5-turbo-0301" | "gpt-3.5-turbo-0613" | "gpt-3.5-turbo-1106" | "gpt-3.5-turbo-0125" | "gpt-3.5-turbo-16k-0613"; type ChatRequestCommonFields = { frequency_penalty?: number; logit_bias?: Record<string, number>; logprobs?: boolean; top_logprobs?: number; max_tokens?: number; n?: number; presence_penalty?: number; response_format?: ChatResponseFormat; seed?: number; stop?: string | string[]; temperature?: number; top_p?: number; }; type PromptChatRequestFields = ChatRequestCommonFields & { system: string; user: string; }; type ChatRequestFields = ChatRequestCommonFields & { messages: ChatCompletionMessage[]; }; type ChatRequestProviders = { provider: LLMProvider<"openai">; model: OpenAIChatModel; analytics?: { name: "helicone"; token: string; }; } | { provider: LLMProvider<"custom">; model: string; analytics?: { name: "helicone"; token: string; }; } | { provider: LLMProvider<"upstash">; model: ChatModel; analytics?: { name: "helicone"; token: string; }; }; type PromptChatRequest<TStream extends StreamParameter> = ChatRequestProviders & PromptChatRequestFields & TStream; type ChatRequest<TStream extends StreamParameter> = ChatRequestProviders & ChatRequestFields & TStream; type UpstashRequest = { /** * The path to the resource. */ path: string[]; /** * A BodyInit object or null to set request's body. */ body?: BodyInit | null; /** * A Headers object, an object literal, or an array of two-item arrays to set * request's headers. */ headers?: HeadersInit; /** * A boolean to set request's keepalive. */ keepalive?: boolean; /** * A string to set request's method. */ method?: HTTPMethods; query?: Record<string, string | number | boolean | Date | string[] | undefined>; /** * if enabled, call `res.json()` * * @default true */ parseResponseAsJson?: boolean; /** * optionally overwrite the baseUrl of the http. * * default value of the http is base qstash url. */ baseUrl?: string; }; type UpstashResponse<TResult> = TResult & { error?: string; }; type Requester = { request: <TResult = unknown>(request: UpstashRequest) => Promise<UpstashResponse<TResult>>; requestStream: (request: UpstashRequest) => AsyncIterable<ChatCompletionChunk>; headers?: Headers; telemetryHeaders?: Headers; }; type RetryConfig = false | { /** * The number of retries to attempt before giving up. * * @default 5 */ retries?: number; /** * A backoff function receives the current retry cound and returns a number in milliseconds to wait before retrying. * * @default * ```ts * Math.exp(retryCount) * 50 * ``` */ backoff?: (retryCount: number) => number; }; type RequireAtLeastOne<T> = { [K in keyof T]-?: Required<Pick<T, K>>; }[keyof T]; type NeverKeys<T> = { [K in keyof T]?: never; }; /** Two-branch exclusive union: A or B, never both. */ type Exclusive<A, B> = (A & NeverKeys<B>) | (B & NeverKeys<A>); /** Shared filter fields accepted by every qstash & workflow endpoint. */ type UniversalFilterFields = { fromDate?: Date | number; toDate?: Date | number; callerIp?: string; label?: string; flowControlKey?: string; }; /** QStash-specific identity filters (DLQ + message endpoints). */ type QStashIdentityFields = { messageId?: string; url?: string; urlGroup?: string; scheduleId?: string; queueName?: string; }; /** DLQ-specific response filter. */ type DLQResponseFields = { responseStatus?: number; }; /** Logs-specific filter fields exclusive to log endpoints. */ type LogsFilterFields = { state?: State; }; type DLQFilterFields = UniversalFilterFields & QStashIdentityFields & DLQResponseFields & { /** * @deprecated `api` filter has been removed from the API and will be ignored */ api?: string; }; type MessageCancelFilterFields = UniversalFilterFields & Omit<QStashIdentityFields, "messageId">; /** * Doesn't allow a single messageId because this is a bulk action. * Cancel does not support cursor. */ type MessageCancelFilters = { messageIds: string[]; filter?: never; all?: never; count?: never; } | ({ filter: RequireAtLeastOne<MessageCancelFilterFields>; messageIds?: never; all?: never; } & MessageCancelCount) | ({ all: true; messageIds?: never; filter?: never; } & MessageCancelCount); type MessageCancelCount = { /** * Maximum number of messages to cancel per call. * * @default 100 */ count?: number; }; /** * DLQ bulk actions support three modes: * - By dlqIds (no cursor) * - By filter fields (with optional cursor) * - All (with optional cursor) */ type DLQBulkActionFilters = { dlqIds: string | string[]; filter?: never; all?: never; count?: never; cursor?: never; } | ({ filter: RequireAtLeastOne<DLQFilterFields>; dlqIds?: never; all?: never; } & DLQBulkActionCount) | ({ all: true; dlqIds?: never; filter?: never; } & DLQBulkActionCount); type DLQBulkActionCount = { cursor?: string; /** * Maximum number of messages to process per call. * * @default 100 */ count?: number; }; type DLQListRequest = Exclusive<{ dlqIds: string | string[]; }, { filter?: DLQFilterFields; cursor?: string; }>; type LogsListRequest = Exclusive<{ messageIds: string[]; }, { filter?: LogsListFilters; /** * Passing `number` may silently break due to JavaScript integer precision * limits — cursor values can exceed `Number.MAX_SAFE_INTEGER`. Use `string` instead. */ cursor?: string | number; }>; type LogsListFilters = UniversalFilterFields & Omit<QStashIdentityFields, "messageId"> & LogsFilterFields & { /** * @deprecated `api` filter has been removed from the API and will be ignored */ api?: string; /** * @deprecated use `messageIds` in the root instead of `messageId` in the `filter` object * * Example: * ```ts * await client.logs({ messageIds: ["id1", "id2"] }) * ``` */ messageId?: string; /** * @deprecated use `urlGroup` instead */ topicName?: string; /** * @deprecated use `count` option in the root instead of the `filter` object * * Example: * ```ts * await client.logs({ count: 50 }) * ``` */ count?: number; }; type Message = { /** * A unique identifier for this message. */ messageId: string; /** * The url group name if this message was sent to a urlGroup. */ urlGroup?: string; /** * Deprecated. The topic name if this message was sent to a urlGroup. Use urlGroup instead */ topicName?: string; /** * The url where this message is sent to. */ url: string; /** * The endpoint name of the message if the endpoint is given a * name within the url group. */ endpointName?: string; /** * The api name if this message was sent to an api */ api?: string; /** * The http method used to deliver the message */ method?: HTTPMethods; /** * The http headers sent along with the message to your API. */ header?: Record<string, string[]>; /** * The http body sent to your API */ body?: string; /** * The base64 encoded body if the body contains non-UTF-8 characters, * `None` otherwise. */ bodyBase64?: string; /** * Maxmimum number of retries. */ maxRetries?: number; /** * The retry delay expression for this message, * if retry_delay was set when publishing the message. */ retryDelayExpression?: PublishRequest["retryDelay"]; /** * A unix timestamp (milliseconds) after which this message may get delivered. */ notBefore?: number; /** * A unix timestamp (milliseconds) when this messages was created. */ createdAt: number; /** * The callback url if configured. */ callback?: string; /** * The failure callback url if configured. */ failureCallback?: string; /** * The queue name if this message was sent to a queue. */ queueName?: string; /** * The scheduleId of the message if the message is triggered by a schedule */ scheduleId?: string; /** * IP address of the publisher of this message */ callerIp?: string; /** * flow control key */ flowControlKey: string; /** * number of requests which can be active with the same flow control key */ parallelism?: number; /** * number of requests to activate per second with the same flow control key * * @deprecated use rate instead */ ratePerSecond?: number; /** * number of requests to activate within the period with the same flow control key. * Default period is a second. */ rate?: number; /** * The time interval during which the specified `rate` of requests can be activated * using the same flow control key. * * In seconds. */ period?: number; /** * The label assigned to the message for filtering purposes. */ label?: string; }; type MessagePayload = Omit<Message, "urlGroup"> & { topicName: string; }; declare class Messages { private readonly http; constructor(http: Requester); /** * Get a message */ get(messageId: string): Promise<Message>; /** * Cancel messages. * * Can be called with: * - A single messageId: `cancel("id")` * - An array of messageIds: `cancel(["id1", "id2"])` * - A filter object: `cancel({ filter: { flowControlKey: "key", label: "label" } })` * - All messages: `cancel({ all: true })` * * Pass `count` to limit the number of messages processed per call (defaults to 100). * Call in a loop until `cancelled` is 0: * * ```ts * let cancelled: number; * do { * const result = await messages.cancel({ all: true, count: 100 }); * cancelled = result.cancelled; * } while (cancelled > 0); * ``` */ cancel(request: string | string[] | MessageCancelFilters): Promise<{ cancelled: number; }>; /** * Delete a message. * * @deprecated Use `cancel(messageId: string)` instead */ delete(messageId: string): Promise<void>; /** * Cancel multiple messages by their messageIds. * * @deprecated Use `cancel(messageIds: string[])` instead */ deleteMany(messageIds: string[]): Promise<number>; /** * Cancel all messages * @deprecated Use `cancel({all: true})` to cancel all */ deleteAll(): Promise<number>; } type DlqMessage = Message & { /** * The unique id within the DLQ */ dlqId: string; /** * The HTTP status code of the last failed delivery attempt */ responseStatus?: number; /** * The response headers of the last failed delivery attempt */ responseHeader?: Record<string, string[]>; /** * The response body of the last failed delivery attempt if it is * composed of UTF-8 characters only, `None` otherwise. */ responseBody?: string; /** * The base64 encoded response body of the last failed delivery attempt * if the response body contains non-UTF-8 characters, `None` otherwise. */ responseBodyBase64?: string; }; declare class DLQ { private readonly http; constructor(http: Requester); /** * List messages in the dlq * * Can be called with: * - Filters: `listMessages({ filter: { url: "https://example.com" } })` * - DLQ IDs: `listMessages({ dlqIds: ["id1", "id2"] })` * - No filter (list all): `listMessages()` */ listMessages(options?: { count?: number; } & DLQListRequest): Promise<{ messages: DlqMessage[]; cursor?: string; }>; /** * Remove messages from the dlq. * * Can be called with: * - A single dlqId: `delete("id")` * - An array of dlqIds: `delete(["id1", "id2"])` * - An object with dlqIds: `delete({ dlqIds: ["id1", "id2"] })` * - A filter object: `delete({ filter: { url: "https://example.com", label: "label" } })` * - All messages: `delete({ all: true })` * * Pass `count` to limit the number of messages processed per call (defaults to 100). * Call in a loop until cursor is undefined: * * ```ts * let cursor: string | undefined; * do { * const result = await dlq.delete({ all: true, count: 100, cursor }); * cursor = result.cursor; * } while (cursor); * ``` */ delete(request: string | string[] | DLQBulkActionFilters): Promise<{ deleted: number; cursor?: string; }>; /** * Remove multiple messages from the dlq using their `dlqId`s * * @deprecated Use `delete` instead */ deleteMany(request: { dlqIds: string[]; }): Promise<{ deleted: number; cursor?: string; }>; /** * Retry messages from the dlq. * * Can be called with: * - A single dlqId: `retry("id")` * - An array of dlqIds: `retry(["id1", "id2"])` * - An object with dlqIds: `retry({ dlqIds: ["id1", "id2"] })` * - A filter object: `retry({ filter: { url: "https://example.com", label: "label" } })` * - All messages: `retry({ all: true })` * * Pass `count` to limit the number of messages processed per call (defaults to 100). * Call in a loop until cursor is undefined: * * ```ts * let cursor: string | undefined; * do { * const result = await dlq.retry({ all: true, count: 100, cursor }); * cursor = result.cursor; * } while (cursor); * ``` */ retry(request: string | string[] | DLQBulkActionFilters): Promise<{ cursor?: string; responses: { messageId: string; }[]; }>; } type FlowControlInfo = { /** * The flow control key. */ flowControlKey: string; /** * The number of messages waiting in the wait list. */ waitListSize: number; /** * The maximum parallelism configured for this flow control key. */ parallelismMax: number; /** * The current number of active requests for this flow control key. */ parallelismCount: number; /** * The maximum rate configured for this flow control key. */ rateMax: number; /** * The current number of requests consumed in the current period. */ rateCount: number; /** * The rate period in seconds. */ ratePeriod: number; /** * The start time of the current rate period as a unix timestamp. */ ratePeriodStart: number; /** * Whether message delivery is paused for this flow control key. */ isPaused: boolean; /** * Whether the parallelism configuration is pinned. */ isPinnedParallelism: boolean; /** * Whether the rate configuration is pinned. */ isPinnedRate: boolean; }; type GlobalParallelismInfo = { /** * The maximum global parallelism. */ parallelismMax: number; /** * The current number of active requests globally. */ parallelismCount: number; }; type PinFlowControlOptions = { /** * The parallelism value to apply to the flow-control key. */ parallelism?: number; /** * The rate value to apply to the flow-control key. */ rate?: number; /** * The period value to apply to the flow-control key, in seconds. */ period?: number; }; type UnpinFlowControlOptions = { /** * Whether to unpin the parallelism configuration. */ parallelism?: boolean; /** * Whether to unpin the rate configuration. */ rate?: boolean; }; declare class FlowControlApi { private readonly http; constructor(http: Requester); /** * Get a single flow control by key. */ get(flowControlKey: string): Promise<FlowControlInfo>; /** * Get the global parallelism info. */ getGlobalParallelism(): Promise<GlobalParallelismInfo>; /** * Pause message delivery for a flow-control key. * * Messages already in the waitlist will remain there. * New incoming messages will be added directly to the waitlist. */ pause(flowControlKey: string): Promise<void>; /** * Resume message delivery for a flow-control key. */ resume(flowControlKey: string): Promise<void>; /** * Pin a processing configuration for a flow-control key. * * While pinned, the system ignores configurations provided by incoming * messages and uses the pinned configuration instead. */ pin(flowControlKey: string, options: PinFlowControlOptions): Promise<void>; /** * Remove the pinned configuration for a flow-control key. * * After unpinning, the system resumes updating the configuration * based on incoming messages. */ unpin(flowControlKey: string, options: UnpinFlowControlOptions): Promise<void>; /** * Reset the rate configuration state for a flow-control key. * * Clears the current rate count and immediately ends the current period. * The current timestamp becomes the start of the new rate period. */ resetRate(flowControlKey: string): Promise<void>; } declare class Chat { private http; private token; constructor(http: Requester, token: string); private static toChatRequest; /** * Calls the Upstash completions api given a ChatRequest. * * Returns a ChatCompletion or a stream of ChatCompletionChunks * if stream is enabled. * * @param request ChatRequest with messages * @returns Chat completion or stream */ create: <TStream extends StreamParameter>(request: ChatRequest<TStream>) => Promise<TStream extends StreamEnabled ? AsyncIterable<ChatCompletionChunk> : ChatCompletion>; /** * Calls the Upstash completions api given a ChatRequest. * * Returns a ChatCompletion or a stream of ChatCompletionChunks * if stream is enabled. * * @param request ChatRequest with messages * @returns Chat completion or stream */ private createThirdParty; private getAuthorizationToken; /** * Calls the Upstash completions api given a PromptRequest. * * Returns a ChatCompletion or a stream of ChatCompletionChunks * if stream is enabled. * * @param request PromptRequest with system and user messages. * Note that system parameter shouldn't be passed in the case of * mistralai/Mistral-7B-Instruct-v0.2 model. * @returns Chat completion or stream */ prompt: <TStream extends StreamParameter>(request: PromptChatRequest<TStream>) => Promise<TStream extends StreamEnabled ? AsyncIterable<ChatCompletionChunk> : ChatCompletion>; } type QueueResponse = { createdAt: number; updatedAt: number; name: string; parallelism: number; lag: number; paused: boolean; }; type UpsertQueueRequest = { /** * The number of parallel consumers consuming from the queue. * * @default 1 */ parallelism?: number; /** * Whether to pause the queue or not. A paused queue will not * deliver new messages until it is resumed. * * @default false */ paused?: boolean; }; declare class Queue { private readonly http; private readonly queueName; constructor(http: Requester, queueName?: string); /** * Create or update the queue */ upsert(request: UpsertQueueRequest): Promise<void>; /** * Get the queue details */ get(): Promise<QueueResponse>; /** * List queues */ list(): Promise<QueueResponse[]>; /** * Delete the queue */ delete(): Promise<void>; /** * Enqueue a message to a queue. */ enqueue<TRequest extends PublishRequest>(request: TRequest): Promise<PublishResponse<TRequest>>; /** * Enqueue a message to a queue, serializing the body to JSON. */ enqueueJSON<TBody = unknown, TRequest extends PublishRequest<TBody> = PublishRequest<TBody>>(request: TRequest): Promise<PublishResponse<TRequest>>; /** * Pauses the queue. * * A paused queue will not deliver messages until * it is resumed. */ pause(): Promise<void>; /** * Resumes the queue. */ resume(): Promise<void>; } type Schedule = { scheduleId: string; cron: string; createdAt: number; destination: string; method: string; header?: Record<string, string[]>; body?: string; bodyBase64?: string; retries: number; delay?: number; callback?: string; failureCallback?: string; callerIp?: string; isPaused: boolean; queueName?: string; flowControlKey?: string; parallelism?: number; rate?: number; /** * @deprecated use rate instead */ ratePerSecond?: number; /** * The time interval during which the specified `rate` of requests can be activated * using the same flow control key. * * In seconds. */ period?: number; /** * The retry delay expression for this schedule, * if retry_delay was set when creating the schedule. */ retryDelayExpression?: PublishRequest["retryDelay"]; /** * The label assigned to the schedule for filtering purposes. */ label?: string; /** * The timestamp of the last scheduled execution. */ lastScheduleTime?: number; /** * The timestamp of the next scheduled execution. */ nextScheduleTime?: number; /** * The states of the last scheduled messages. * * Maps message id to state */ lastScheduleStates?: Record<string, "IN_PROGRESS" | "SUCCESS" | "FAIL">; /** * The IP address of the caller who created the schedule. */ callerIP?: string; }; type CreateScheduleRequest = { /** * Either a URL or urlGroup name */ destination: string; /** * The message to send. * * This can be anything, but please set the `Content-Type` header accordingly. * * You can leave this empty if you want to send a message with no body. */ body?: BodyInit; /** * Optionally send along headers with the message. * These headers will be sent to your destination. * * We highly recommend sending a `Content-Type` header along, as this will help your destination * server to understand the content of the message. */ headers?: HeadersInit; /** * Optionally delay the delivery of this message. * * In seconds. * * @default undefined */ delay?: Duration | number; /** * In case your destination server is unavailable or returns a status code outside of the 200-299 * range, we will retry the request after a certain amount of time. * * Configure how many times you would like the delivery to be retried * * @default The maximum retry quota associated with your account. */ retries?: number; /** * Use a callback url to forward the response of your destination server to your callback url. * * The callback url must be publicly accessible * * @default undefined */ callback?: string; /** * Use a failure callback url to handle messages that could not be delivered. * * The failure callback url must be publicly accessible * * @default undefined */ failureCallback?: string; /** * The method to use when sending a request to your API * * @default `POST` */ method?: HTTPMethods; /** * Specify a cron expression to repeatedly send this message to the destination. */ cron: string; /** * The HTTP timeout value to use while calling the destination URL. * When a timeout is specified, it will be used instead of the maximum timeout * value permitted by the QStash plan. It is useful in scenarios, where a message * should be delivered with a shorter timeout. * * In seconds. * * @default undefined */ timeout?: Duration | number; /** * Schedule id to use. * * Can be used to update the settings of an existing schedule. * * @default undefined */ scheduleId?: string; /** * Queue name to schedule the message over. */ queueName?: string; /** * Settings for controlling the number of active requests * and number of requests per second with the same key. */ flowControl?: FlowControl; /** * Assign a label to the schedule to filter logs later. * * @default undefined */ label?: string; /** * Configure which fields should be redacted in logs. * * - `body: true` redacts the request body * - `header: true` redacts all headers * - `header: ["X"]` redacts specific headers (e.g., ["Authorization"]) * * @default undefined */ redact?: { body?: true; header?: true | string[]; }; } & Pick<PublishRequest, "retryDelay">; declare class Schedules { private readonly http; constructor(http: Requester); /** * Create a schedule */ create(request: CreateScheduleRequest): Promise<{ scheduleId: string; }>; /** * Get a schedule */ get(scheduleId: string): Promise<Schedule>; /** * List your schedules */ list(): Promise<Schedule[]>; /** * Delete a schedule */ delete(scheduleId: string): Promise<void>; /** * Pauses the schedule. * * A paused schedule will not deliver messages until * it is resumed. */ pause({ schedule }: { schedule: string; }): Promise<void>; /** * Resumes the schedule. */ resume({ schedule }: { schedule: string; }): Promise<void>; } type Endpoint = { /** * The name of the endpoint (optional) */ name?: string; /** * The url of the endpoint */ url: string; }; type AddEndpointsRequest = { /** * The name of the url group. * Must be unique and only contain alphanumeric, hyphen, underscore and periods. */ name: string; endpoints: Endpoint[]; }; type RemoveEndpointsRequest = { /** * The name of the url group. * Must be unique and only contain alphanumeric, hyphen, underscore and periods. */ name: string; endpoints: ({ name: string; url?: string; } | { name?: string; url: string; })[]; }; type UrlGroup = { /** * A unix timestamp (milliseconds) */ createdAt: number; /** * A unix timestamp (milliseconds) */ updatedAt: number; /** * The name of this url group. */ name: string; /** * A list of all subscribed endpoints */ endpoints: Endpoint[]; }; declare class UrlGroups { private readonly http; constructor(http: Requester); /** * Create a new url group with the given name and endpoints */ addEndpoints(request: AddEndpointsRequest): Promise<void>; /** * Remove endpoints from a url group. */ removeEndpoints(request: RemoveEndpointsRequest): Promise<void>; /** * Get a list of all url groups. */ list(): Promise<UrlGroup[]>; /** * Get a single url group */ get(name: string): Promise<UrlGroup>; /** * Delete a url group */ delete(name: string): Promise<void>; } /** * Base class outlining steps. Basically, each step kind (run/sleep/sleepUntil) * should have two methods: getPlanStep & getResultStep. * * getPlanStep works the same way for all so it's implemented here. * The different step types will implement their own getResultStep method. */ declare abstract class BaseLazyStep<TResult = unknown> { readonly stepName: string; abstract readonly stepType: StepType; constructor(stepName: string); /** * plan step to submit when step will run parallel with other * steps (parallel call state `first`) * * @param concurrent number of steps running parallel * @param targetStep target step id corresponding to this step * @returns */ abstract getPlanStep(concurrent: number, targetStep: number): Step<undefined>; /** * result step to submit after the step executes. Used in single step executions * and when a plan step executes in parallel executions (parallel call state `partial`). * * @param concurrent * @param stepId */ abstract getResultStep(concurrent: number, stepId: number): Promise<Step<TResult>>; } declare const LOG_LEVELS: readonly ["DEBUG", "INFO", "SUBMIT", "WARN", "ERROR"]; type LogLevel = (typeof LOG_LEVELS)[number]; type ChatLogEntry = { timestamp: number; workflowRunId: string; logLevel: LogLevel; eventType: "ENDPOINT_START" | "SUBMIT_THIRD_PARTY_RESULT" | "CREATE_CONTEXT" | "SUBMIT_FIRST_INVOCATION" | "RUN_SINGLE" | "RUN_PARALLEL" | "SUBMIT_STEP" | "SUBMIT_CLEANUP" | "RESPONSE_WORKFLOW" | "RESPONSE_DEFAULT" | "ERROR"; details: unknown; }; type WorkflowLoggerOptions = { logLevel: LogLevel; logOutput: "console"; }; declare class WorkflowLogger { private logs; private options; private workflowRunId?; constructor(options: WorkflowLoggerOptions); log(level: LogLevel, eventType: ChatLogEntry["eventType"], details?: unknown): Promise<void>; setWorkflowRunId(workflowRunId: string): void; private writeToConsole; private shouldLog; static getLogger(verbose?: boolean | WorkflowLogger): WorkflowLogger | undefined; } declare class AutoExecutor { private context; private promises; private activeLazyStepList?; private debug?; private readonly nonPlanStepCount; private readonly steps; private indexInCurrentList; stepCount: number; planStepCount: number; protected executingStep: string | false; constructor(context: WorkflowContext, steps: Step[], debug?: WorkflowLogger); /** * Adds the step function to the list of step functions to run in * parallel. After adding the function, defers the execution, so * that if there is another step function to be added, it's also * added. * * After all functions are added, list of functions are executed. * If there is a single function, it's executed by itself. If there * are multiple, they are run in parallel. * * If a function is already executing (this.executingStep), this * means that there is a nested step which is not allowed. In this * case, addStep throws QStashWorkflowError. * * @param stepInfo step plan to add * @returns result of the step function */ addStep<TResult>(stepInfo: BaseLazyStep<TResult>): Promise<TResult>; /** * Wraps a step function to set this.executingStep to step name * before running and set this.executingStep to False after execution * ends. * * this.executingStep allows us to detect nested steps which are not * allowed. * * @param stepName name of the step being wrapped * @param stepFunction step function to wrap * @returns wrapped step function */ wrapStep<TResult = unknown>(stepName: string, stepFunction: StepFunction<TResult>): TResult | Promise<TResult>; /** * Executes a step: * - If the step result is available in the steps, returns the result * - If the result is not avaiable, runs the function * - Sends the result to QStash * * @param lazyStep lazy step to execute * @returns step result */ protected runSingle<TResult>(lazyStep: BaseLazyStep<TResult>): Promise<TResult>; /** * Runs steps in parallel. * * @param stepName parallel step name * @param stepFunctions list of async functions to run in parallel * @returns results of the functions run in parallel */ protected runParallel<TResults extends unknown[]>(parallelSteps: { [K in keyof TResults]: BaseLazyStep<TResults[K]>; }): Promise<TResults>; /** * Determines the parallel call state * * First filters the steps to get the steps which are after `initialStepCount` parameter. * * Depending on the remaining steps, decides the parallel state: * - "first": If there are no steps * - "last" If there are equal to or more than `2 * parallelStepCount`. We multiply by two * because each step in a parallel execution will have 2 steps: a plan step and a result * step. * - "partial": If the last step is a plan step * - "discard": If the last step is not a plan step. This means that the parallel execution * is in progress (there are still steps to run) and one step has finished and submitted * its result to QStash * * @param parallelStepCount number of steps to run in parallel * @param initialStepCount steps after the parallel invocation * @returns parallel call state */ protected getParallelCallState(parallelStepCount: number, initialStepCount: number): ParallelCallState; /** * sends the steps to QStash as batch * * @param steps steps to send */ private submitStepsToQStash; /** * Get the promise by executing the lazt steps list. If there is a single * step, we call `runSingle`. Otherwise `runParallel` is called. * * @param lazyStepList steps list to execute * @returns promise corresponding to the execution */ private getExecutionPromise; /** * @param lazyStepList steps we executed * @param result result of the promise from `getExecutionPromise` * @param index index of the current step * @returns result[index] if lazyStepList > 1, otherwise result */ private static getResult; private deferExecution; } /** * Upstash Workflow context * * See the docs for fields and methods https://upstash.com/docs/qstash/workflows/basics/context */ declare class WorkflowContext<TInitialPayload = unknown> { protected readonly executor: AutoExecutor; protected readonly steps: Step[]; /** * QStash client of the workflow * * Can be overwritten by passing `qstashClient` parameter in `serve`: * * ```ts * import { Client } from "@upstash/qstash" * * export const POST = serve( * async (context) => { * ... * }, * { * qstashClient: new Client({...}) * } * ) * ``` */ readonly qstashClient: WorkflowClient; /** * Run id of the workflow */ readonly workflowRunId: string; /** * URL of the workflow * * Can be overwritten by passing a `url` parameter in `serve`: * * ```ts * export const POST = serve( * async (context) => { * ... * }, * { * url: "new-url-value" * } * ) * ``` */ readonly url: string; /** * URL to call in case of workflow failure with QStash failure callback * * https://upstash.com/docs/qstash/features/callbacks#what-is-a-failure-callback * * Can be overwritten by passing a `failureUrl` parameter in `serve`: * * ```ts * export const POST = serve( * async (context) => { * ... * }, * { * failureUrl: "new-url-value" * } * ) * ``` */ readonly failureUrl?: string; /** * Payload of the request which started the workflow. * * To specify its type, you can define `serve` as follows: * * ```ts * // set requestPayload type to MyPayload: * export const POST = serve<MyPayload>( * async (context) => { * ... * } * ) * ``` * * By default, `serve` tries to apply `JSON.parse` to the request payload. * If your payload is encoded in a format other than JSON, you can utilize * the `initialPayloadParser` parameter: * * ```ts * export const POST = serve<MyPayload>( * async (context) => { * ... * }, * { * initialPayloadParser: (initialPayload) => {return doSomething(initialPayload)} * } * ) * ``` */ readonly requestPayload: TInitialPayload; /** * headers of the initial request */ readonly headers: Headers; /** * initial payload as a raw string */ readonly rawInitialPayload: string; /** * Map of environment variables and their values. * * Can be set using the `env` option of serve: * * ```ts * export const POST = serve<MyPayload>( * async (context) => { * const key = context.env["API_KEY"]; * }, * { * env: { * "API_KEY": "*****"; * } * } * ) * ``` * * Default value is set to `process.env`. */ readonly env: Record<string, string | undefined>; /** * Number of retries */ readonly retries: number; constructor({ qstashClient, workflowRunId, headers, steps, url, failureUrl, debug, initialPayload, rawInitialPayload, env, retries, }: { qstashClient: WorkflowClient; workflowRunId: string; headers: Headers; steps: Step[]; url: string; failureUrl?: string; debug?: WorkflowLogger; initialPayload: TInitialPayload; rawInitialPayload?: string; env?: Record<string, string | undefined>; retries?: number; }); /** * Exec