import { Assistant, AssistantGraph, CancelAction, Config, DefaultValues, GraphSchema, Metadata, Run, RunStatus, Thread, ThreadState, Cron, AssistantVersion, Subgraphs, Checkpoint, SearchItemsResponse, ListNamespaceResponse, Item, ThreadStatus, CronCreateResponse, CronCreateForThreadResponse, } from "./schema.js"; import { AsyncCaller, AsyncCallerParams } from "./utils/async_caller.js"; import { EventSourceParser, createParser, } from "./utils/eventsource-parser/index.js"; import { IterableReadableStream } from "./utils/stream.js"; import { RunsCreatePayload, RunsStreamPayload, RunsWaitPayload, StreamEvent, CronsCreatePayload, OnConflictBehavior, } from "./types.js"; import { mergeSignals } from "./utils/signals.js"; import { getEnvironmentVariable } from "./utils/env.js"; import { _getFetchImplementation } from "./singletons/fetch.js"; /** * Get the API key from the environment. * Precedence: * 1. explicit argument * 2. LANGGRAPH_API_KEY * 3. LANGSMITH_API_KEY * 4. LANGCHAIN_API_KEY * * @param apiKey - Optional API key provided as an argument * @returns The API key if found, otherwise undefined */ export function getApiKey(apiKey?: string): string | undefined { if (apiKey) { return apiKey; } const prefixes = ["LANGGRAPH", "LANGSMITH", "LANGCHAIN"]; for (const prefix of prefixes) { const envKey = getEnvironmentVariable(`${prefix}_API_KEY`); if (envKey) { // Remove surrounding quotes return envKey.trim().replace(/^["']|["']$/g, ""); } } return undefined; } interface ClientConfig { apiUrl?: string; apiKey?: string; callerOptions?: AsyncCallerParams; timeoutMs?: number; defaultHeaders?: Record; } class BaseClient { protected asyncCaller: AsyncCaller; protected timeoutMs: number; protected apiUrl: string; protected defaultHeaders: Record; constructor(config?: ClientConfig) { this.asyncCaller = new AsyncCaller({ maxRetries: 4, maxConcurrency: 4, ...config?.callerOptions, }); this.timeoutMs = config?.timeoutMs || 12_000; // default limit being capped by Chrome // https://github.com/nodejs/undici/issues/1373 // Regex to remove trailing slash, if present this.apiUrl = config?.apiUrl?.replace(/\/$/, "") || "http://localhost:8123"; this.defaultHeaders = config?.defaultHeaders || {}; const apiKey = getApiKey(config?.apiKey); if (apiKey) { this.defaultHeaders["X-Api-Key"] = apiKey; } } protected prepareFetchOptions( path: string, options?: RequestInit & { json?: unknown; params?: Record; timeoutMs?: number | null; }, ): [url: URL, init: RequestInit] { const mutatedOptions = { ...options, headers: { ...this.defaultHeaders, ...options?.headers }, }; if (mutatedOptions.json) { mutatedOptions.body = JSON.stringify(mutatedOptions.json); mutatedOptions.headers = { ...mutatedOptions.headers, "Content-Type": "application/json", }; delete mutatedOptions.json; } let timeoutSignal: AbortSignal | null = null; if (typeof options?.timeoutMs !== "undefined") { if (options.timeoutMs != null) { timeoutSignal = AbortSignal.timeout(options.timeoutMs); } } else { timeoutSignal = AbortSignal.timeout(this.timeoutMs); } mutatedOptions.signal = mergeSignals(timeoutSignal, mutatedOptions.signal); const targetUrl = new URL(`${this.apiUrl}${path}`); if (mutatedOptions.params) { for (const [key, value] of Object.entries(mutatedOptions.params)) { if (value == null) continue; let strValue = typeof value === "string" || typeof value === "number" ? value.toString() : JSON.stringify(value); targetUrl.searchParams.append(key, strValue); } delete mutatedOptions.params; } return [targetUrl, mutatedOptions]; } protected async fetch( path: string, options?: RequestInit & { json?: unknown; params?: Record; timeoutMs?: number | null; signal?: AbortSignal; }, ): Promise { const response = await this.asyncCaller.call( _getFetchImplementation(), ...this.prepareFetchOptions(path, options), ); if (response.status === 202 || response.status === 204) { return undefined as T; } return response.json() as T; } } export class CronsClient extends BaseClient { /** * * @param threadId The ID of the thread. * @param assistantId Assistant ID to use for this cron job. * @param payload Payload for creating a cron job. * @returns The created background run. */ async createForThread( threadId: string, assistantId: string, payload?: CronsCreatePayload, ): Promise { const json: Record = { schedule: payload?.schedule, input: payload?.input, config: payload?.config, metadata: payload?.metadata, assistant_id: assistantId, interrupt_before: payload?.interruptBefore, interrupt_after: payload?.interruptAfter, webhook: payload?.webhook, multitask_strategy: payload?.multitaskStrategy, if_not_exists: payload?.ifNotExists, }; return this.fetch( `/threads/${threadId}/runs/crons`, { method: "POST", json, }, ); } /** * * @param assistantId Assistant ID to use for this cron job. * @param payload Payload for creating a cron job. * @returns */ async create( assistantId: string, payload?: CronsCreatePayload, ): Promise { const json: Record = { schedule: payload?.schedule, input: payload?.input, config: payload?.config, metadata: payload?.metadata, assistant_id: assistantId, interrupt_before: payload?.interruptBefore, interrupt_after: payload?.interruptAfter, webhook: payload?.webhook, multitask_strategy: payload?.multitaskStrategy, if_not_exists: payload?.ifNotExists, }; return this.fetch(`/runs/crons`, { method: "POST", json, }); } /** * * @param cronId Cron ID of Cron job to delete. */ async delete(cronId: string): Promise { await this.fetch(`/runs/crons/${cronId}`, { method: "DELETE", }); } /** * * @param query Query options. * @returns List of crons. */ async search(query?: { assistantId?: string; threadId?: string; limit?: number; offset?: number; }): Promise { return this.fetch("/runs/crons/search", { method: "POST", json: { assistant_id: query?.assistantId ?? undefined, thread_id: query?.threadId ?? undefined, limit: query?.limit ?? 10, offset: query?.offset ?? 0, }, }); } } export class AssistantsClient extends BaseClient { /** * Get an assistant by ID. * * @param assistantId The ID of the assistant. * @returns Assistant */ async get(assistantId: string): Promise { return this.fetch(`/assistants/${assistantId}`); } /** * Get the JSON representation of the graph assigned to a runnable * @param assistantId The ID of the assistant. * @param options.xray Whether to include subgraphs in the serialized graph representation. If an integer value is provided, only subgraphs with a depth less than or equal to the value will be included. * @returns Serialized graph */ async getGraph( assistantId: string, options?: { xray?: boolean | number }, ): Promise { return this.fetch(`/assistants/${assistantId}/graph`, { params: { xray: options?.xray }, }); } /** * Get the state and config schema of the graph assigned to a runnable * @param assistantId The ID of the assistant. * @returns Graph schema */ async getSchemas(assistantId: string): Promise { return this.fetch(`/assistants/${assistantId}/schemas`); } /** * Get the schemas of an assistant by ID. * * @param assistantId The ID of the assistant to get the schema of. * @param options Additional options for getting subgraphs, such as namespace or recursion extraction. * @returns The subgraphs of the assistant. */ async getSubgraphs( assistantId: string, options?: { namespace?: string; recurse?: boolean; }, ): Promise { if (options?.namespace) { return this.fetch( `/assistants/${assistantId}/subgraphs/${options.namespace}`, { params: { recurse: options?.recurse } }, ); } return this.fetch(`/assistants/${assistantId}/subgraphs`, { params: { recurse: options?.recurse }, }); } /** * Create a new assistant. * @param payload Payload for creating an assistant. * @returns The created assistant. */ async create(payload: { graphId: string; config?: Config; metadata?: Metadata; assistantId?: string; ifExists?: OnConflictBehavior; name?: string; }): Promise { return this.fetch("/assistants", { method: "POST", json: { graph_id: payload.graphId, config: payload.config, metadata: payload.metadata, assistant_id: payload.assistantId, if_exists: payload.ifExists, name: payload.name, }, }); } /** * Update an assistant. * @param assistantId ID of the assistant. * @param payload Payload for updating the assistant. * @returns The updated assistant. */ async update( assistantId: string, payload: { graphId?: string; config?: Config; metadata?: Metadata; name?: string; }, ): Promise { return this.fetch(`/assistants/${assistantId}`, { method: "PATCH", json: { graph_id: payload.graphId, config: payload.config, metadata: payload.metadata, name: payload.name, }, }); } /** * Delete an assistant. * * @param assistantId ID of the assistant. */ async delete(assistantId: string): Promise { return this.fetch(`/assistants/${assistantId}`, { method: "DELETE", }); } /** * List assistants. * @param query Query options. * @returns List of assistants. */ async search(query?: { graphId?: string; metadata?: Metadata; limit?: number; offset?: number; }): Promise { return this.fetch("/assistants/search", { method: "POST", json: { graph_id: query?.graphId ?? undefined, metadata: query?.metadata ?? undefined, limit: query?.limit ?? 10, offset: query?.offset ?? 0, }, }); } /** * List all versions of an assistant. * * @param assistantId ID of the assistant. * @returns List of assistant versions. */ async getVersions( assistantId: string, payload?: { metadata?: Metadata; limit?: number; offset?: number; }, ): Promise { return this.fetch( `/assistants/${assistantId}/versions`, { method: "POST", json: { metadata: payload?.metadata ?? undefined, limit: payload?.limit ?? 10, offset: payload?.offset ?? 0, }, }, ); } /** * Change the version of an assistant. * * @param assistantId ID of the assistant. * @param version The version to change to. * @returns The updated assistant. */ async setLatest(assistantId: string, version: number): Promise { return this.fetch(`/assistants/${assistantId}/latest`, { method: "POST", json: { version }, }); } } export class ThreadsClient extends BaseClient { /** * Get a thread by ID. * * @param threadId ID of the thread. * @returns The thread. */ async get(threadId: string): Promise { return this.fetch(`/threads/${threadId}`); } /** * Create a new thread. * * @param payload Payload for creating a thread. * @returns The created thread. */ async create(payload?: { /** * Metadata for the thread. */ metadata?: Metadata; threadId?: string; ifExists?: OnConflictBehavior; }): Promise { return this.fetch(`/threads`, { method: "POST", json: { metadata: payload?.metadata, thread_id: payload?.threadId, if_exists: payload?.ifExists, }, }); } /** * Copy an existing thread * @param threadId ID of the thread to be copied * @returns Newly copied thread */ async copy(threadId: string): Promise { return this.fetch(`/threads/${threadId}/copy`, { method: "POST", }); } /** * Update a thread. * * @param threadId ID of the thread. * @param payload Payload for updating the thread. * @returns The updated thread. */ async update( threadId: string, payload?: { /** * Metadata for the thread. */ metadata?: Metadata; }, ): Promise { return this.fetch(`/threads/${threadId}`, { method: "PATCH", json: { metadata: payload?.metadata }, }); } /** * Delete a thread. * * @param threadId ID of the thread. */ async delete(threadId: string): Promise { return this.fetch(`/threads/${threadId}`, { method: "DELETE", }); } /** * List threads * * @param query Query options * @returns List of threads */ async search(query?: { /** * Metadata to filter threads by. */ metadata?: Metadata; /** * Maximum number of threads to return. * Defaults to 10 */ limit?: number; /** * Offset to start from. */ offset?: number; /** * Thread status to filter on. * Must be one of 'idle', 'busy', 'interrupted' or 'error'. */ status?: ThreadStatus; }): Promise { return this.fetch("/threads/search", { method: "POST", json: { metadata: query?.metadata ?? undefined, limit: query?.limit ?? 10, offset: query?.offset ?? 0, status: query?.status, }, }); } /** * Get state for a thread. * * @param threadId ID of the thread. * @returns Thread state. */ async getState( threadId: string, checkpoint?: Checkpoint | string, options?: { subgraphs?: boolean }, ): Promise> { if (checkpoint != null) { if (typeof checkpoint !== "string") { return this.fetch>( `/threads/${threadId}/state/checkpoint`, { method: "POST", json: { checkpoint, subgraphs: options?.subgraphs }, }, ); } // deprecated return this.fetch>( `/threads/${threadId}/state/${checkpoint}`, { params: { subgraphs: options?.subgraphs } }, ); } return this.fetch>(`/threads/${threadId}/state`, { params: { subgraphs: options?.subgraphs }, }); } /** * Add state to a thread. * * @param threadId The ID of the thread. * @returns */ async updateState( threadId: string, options: { values: ValuesType; checkpoint?: Checkpoint; checkpointId?: string; asNode?: string; }, ): Promise> { return this.fetch>( `/threads/${threadId}/state`, { method: "POST", json: { values: options.values, checkpoint_id: options.checkpointId, checkpoint: options.checkpoint, as_node: options?.asNode, }, }, ); } /** * Patch the metadata of a thread. * * @param threadIdOrConfig Thread ID or config to patch the state of. * @param metadata Metadata to patch the state with. */ async patchState( threadIdOrConfig: string | Config, metadata: Metadata, ): Promise { let threadId: string; if (typeof threadIdOrConfig !== "string") { if (typeof threadIdOrConfig.configurable.thread_id !== "string") { throw new Error( "Thread ID is required when updating state with a config.", ); } threadId = threadIdOrConfig.configurable.thread_id; } else { threadId = threadIdOrConfig; } return this.fetch(`/threads/${threadId}/state`, { method: "PATCH", json: { metadata: metadata }, }); } /** * Get all past states for a thread. * * @param threadId ID of the thread. * @param options Additional options. * @returns List of thread states. */ async getHistory( threadId: string, options?: { limit?: number; before?: Config; checkpoint?: Partial>; metadata?: Metadata; }, ): Promise[]> { return this.fetch[]>( `/threads/${threadId}/history`, { method: "POST", json: { limit: options?.limit ?? 10, before: options?.before, metadata: options?.metadata, checkpoint: options?.checkpoint, }, }, ); } } export class RunsClient extends BaseClient { stream( threadId: null, assistantId: string, payload?: Omit, ): AsyncGenerator<{ event: StreamEvent; data: any; }>; stream( threadId: string, assistantId: string, payload?: RunsStreamPayload, ): AsyncGenerator<{ event: StreamEvent; data: any; }>; /** * Create a run and stream the results. * * @param threadId The ID of the thread. * @param assistantId Assistant ID to use for this run. * @param payload Payload for creating a run. */ async *stream( threadId: string | null, assistantId: string, payload?: RunsStreamPayload, ): AsyncGenerator<{ event: StreamEvent; data: any; }> { const json: Record = { input: payload?.input, command: payload?.command, config: payload?.config, metadata: payload?.metadata, stream_mode: payload?.streamMode, stream_subgraphs: payload?.streamSubgraphs, feedback_keys: payload?.feedbackKeys, assistant_id: assistantId, interrupt_before: payload?.interruptBefore, interrupt_after: payload?.interruptAfter, checkpoint: payload?.checkpoint, checkpoint_id: payload?.checkpointId, webhook: payload?.webhook, multitask_strategy: payload?.multitaskStrategy, on_completion: payload?.onCompletion, on_disconnect: payload?.onDisconnect, after_seconds: payload?.afterSeconds, if_not_exists: payload?.ifNotExists, }; const endpoint = threadId == null ? `/runs/stream` : `/threads/${threadId}/runs/stream`; const response = await this.asyncCaller.call( _getFetchImplementation(), ...this.prepareFetchOptions(endpoint, { method: "POST", json, timeoutMs: null, signal: payload?.signal, }), ); let parser: EventSourceParser; let onEndEvent: () => void; const textDecoder = new TextDecoder(); const stream: ReadableStream<{ event: string; data: any }> = ( response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }) ).pipeThrough( new TransformStream({ async start(ctrl) { parser = createParser((event) => { if ( (payload?.signal && payload.signal.aborted) || (event.type === "event" && event.data === "[DONE]") ) { ctrl.terminate(); return; } if ("data" in event) { ctrl.enqueue({ event: event.event ?? "message", data: JSON.parse(event.data), }); } }); onEndEvent = () => { ctrl.enqueue({ event: "end", data: undefined }); }; }, async transform(chunk) { const payload = textDecoder.decode(chunk); parser.feed(payload); // eventsource-parser will ignore events // that are not terminated by a newline if (payload.trim() === "event: end") onEndEvent(); }, }), ); yield* IterableReadableStream.fromReadableStream(stream); } /** * Create a run. * * @param threadId The ID of the thread. * @param assistantId Assistant ID to use for this run. * @param payload Payload for creating a run. * @returns The created run. */ async create( threadId: string, assistantId: string, payload?: RunsCreatePayload, ): Promise { const json: Record = { input: payload?.input, command: payload?.command, config: payload?.config, metadata: payload?.metadata, stream_mode: payload?.streamMode, stream_subgraphs: payload?.streamSubgraphs, assistant_id: assistantId, interrupt_before: payload?.interruptBefore, interrupt_after: payload?.interruptAfter, webhook: payload?.webhook, checkpoint: payload?.checkpoint, checkpoint_id: payload?.checkpointId, multitask_strategy: payload?.multitaskStrategy, after_seconds: payload?.afterSeconds, if_not_exists: payload?.ifNotExists, }; return this.fetch(`/threads/${threadId}/runs`, { method: "POST", json, signal: payload?.signal, }); } /** * Create a batch of stateless background runs. * * @param payloads An array of payloads for creating runs. * @returns An array of created runs. */ async createBatch( payloads: (RunsCreatePayload & { assistantId: string })[], ): Promise { const filteredPayloads = payloads .map((payload) => ({ ...payload, assistant_id: payload.assistantId })) .map((payload) => { return Object.fromEntries( Object.entries(payload).filter(([_, v]) => v !== undefined), ); }); return this.fetch("/runs/batch", { method: "POST", json: filteredPayloads, }); } async wait( threadId: null, assistantId: string, payload?: Omit, ): Promise; async wait( threadId: string, assistantId: string, payload?: RunsWaitPayload, ): Promise; /** * Create a run and wait for it to complete. * * @param threadId The ID of the thread. * @param assistantId Assistant ID to use for this run. * @param payload Payload for creating a run. * @returns The last values chunk of the thread. */ async wait( threadId: string | null, assistantId: string, payload?: RunsWaitPayload, ): Promise { const json: Record = { input: payload?.input, command: payload?.command, config: payload?.config, metadata: payload?.metadata, assistant_id: assistantId, interrupt_before: payload?.interruptBefore, interrupt_after: payload?.interruptAfter, checkpoint: payload?.checkpoint, checkpoint_id: payload?.checkpointId, webhook: payload?.webhook, multitask_strategy: payload?.multitaskStrategy, on_completion: payload?.onCompletion, on_disconnect: payload?.onDisconnect, after_seconds: payload?.afterSeconds, if_not_exists: payload?.ifNotExists, }; const endpoint = threadId == null ? `/runs/wait` : `/threads/${threadId}/runs/wait`; const response = await this.fetch(endpoint, { method: "POST", json, timeoutMs: null, signal: payload?.signal, }); const raiseError = payload?.raiseError !== undefined ? payload.raiseError : true; if ( raiseError && "__error__" in response && typeof response.__error__ === "object" && response.__error__ && "error" in response.__error__ && "message" in response.__error__ ) { throw new Error( `${response.__error__?.error}: ${response.__error__?.message}`, ); } return response; } /** * List all runs for a thread. * * @param threadId The ID of the thread. * @param options Filtering and pagination options. * @returns List of runs. */ async list( threadId: string, options?: { /** * Maximum number of runs to return. * Defaults to 10 */ limit?: number; /** * Offset to start from. * Defaults to 0. */ offset?: number; /** * Status of the run to filter by. */ status?: RunStatus; }, ): Promise { return this.fetch(`/threads/${threadId}/runs`, { params: { limit: options?.limit ?? 10, offset: options?.offset ?? 0, status: options?.status ?? undefined, }, }); } /** * Get a run by ID. * * @param threadId The ID of the thread. * @param runId The ID of the run. * @returns The run. */ async get(threadId: string, runId: string): Promise { return this.fetch(`/threads/${threadId}/runs/${runId}`); } /** * Cancel a run. * * @param threadId The ID of the thread. * @param runId The ID of the run. * @param wait Whether to block when canceling * @param action Action to take when cancelling the run. Possible values are `interrupt` or `rollback`. Default is `interrupt`. * @returns */ async cancel( threadId: string, runId: string, wait: boolean = false, action: CancelAction = "interrupt", ): Promise { return this.fetch(`/threads/${threadId}/runs/${runId}/cancel`, { method: "POST", params: { wait: wait ? "1" : "0", action: action, }, }); } /** * Block until a run is done. * * @param threadId The ID of the thread. * @param runId The ID of the run. * @returns */ async join( threadId: string, runId: string, options?: { signal?: AbortSignal }, ): Promise { return this.fetch(`/threads/${threadId}/runs/${runId}/join`, { timeoutMs: null, signal: options?.signal, }); } /** * Stream output from a run in real-time, until the run is done. * Output is not buffered, so any output produced before this call will * not be received here. * * @param threadId The ID of the thread. * @param runId The ID of the run. * @returns An async generator yielding stream parts. */ async *joinStream( threadId: string, runId: string, options?: | { signal?: AbortSignal; cancelOnDisconnect?: boolean } | AbortSignal, ): AsyncGenerator<{ event: StreamEvent; data: any }> { const opts = typeof options === "object" && options != null && options instanceof AbortSignal ? { signal: options } : options; const response = await this.asyncCaller.call( _getFetchImplementation(), ...this.prepareFetchOptions(`/threads/${threadId}/runs/${runId}/stream`, { method: "GET", timeoutMs: null, signal: opts?.signal, params: { cancel_on_disconnect: opts?.cancelOnDisconnect ? "1" : "0" }, }), ); let parser: EventSourceParser; let onEndEvent: () => void; const textDecoder = new TextDecoder(); const stream: ReadableStream<{ event: string; data: any }> = ( response.body || new ReadableStream({ start: (ctrl) => ctrl.close() }) ).pipeThrough( new TransformStream({ async start(ctrl) { parser = createParser((event) => { if ( (opts?.signal && opts.signal.aborted) || (event.type === "event" && event.data === "[DONE]") ) { ctrl.terminate(); return; } if ("data" in event) { ctrl.enqueue({ event: event.event ?? "message", data: JSON.parse(event.data), }); } }); onEndEvent = () => { ctrl.enqueue({ event: "end", data: undefined }); }; }, async transform(chunk) { const payload = textDecoder.decode(chunk); parser.feed(payload); // eventsource-parser will ignore events // that are not terminated by a newline if (payload.trim() === "event: end") onEndEvent(); }, }), ); yield* IterableReadableStream.fromReadableStream(stream); } /** * Delete a run. * * @param threadId The ID of the thread. * @param runId The ID of the run. * @returns */ async delete(threadId: string, runId: string): Promise { return this.fetch(`/threads/${threadId}/runs/${runId}`, { method: "DELETE", }); } } interface APIItem { namespace: string[]; key: string; value: Record; created_at: string; updated_at: string; } interface APISearchItemsResponse { items: APIItem[]; } export class StoreClient extends BaseClient { /** * Store or update an item. * * @param namespace A list of strings representing the namespace path. * @param key The unique identifier for the item within the namespace. * @param value A dictionary containing the item's data. * @returns Promise */ async putItem( namespace: string[], key: string, value: Record, ): Promise { namespace.forEach((label) => { if (label.includes(".")) { throw new Error( `Invalid namespace label '${label}'. Namespace labels cannot contain periods ('.')`, ); } }); const payload = { namespace, key, value, }; return this.fetch("/store/items", { method: "PUT", json: payload, }); } /** * Retrieve a single item. * * @param namespace A list of strings representing the namespace path. * @param key The unique identifier for the item. * @returns Promise */ async getItem(namespace: string[], key: string): Promise { namespace.forEach((label) => { if (label.includes(".")) { throw new Error( `Invalid namespace label '${label}'. Namespace labels cannot contain periods ('.')`, ); } }); const response = await this.fetch("/store/items", { params: { namespace: namespace.join("."), key }, }); return response ? { ...response, createdAt: response.created_at, updatedAt: response.updated_at, } : null; } /** * Delete an item. * * @param namespace A list of strings representing the namespace path. * @param key The unique identifier for the item. * @returns Promise */ async deleteItem(namespace: string[], key: string): Promise { namespace.forEach((label) => { if (label.includes(".")) { throw new Error( `Invalid namespace label '${label}'. Namespace labels cannot contain periods ('.')`, ); } }); return this.fetch("/store/items", { method: "DELETE", json: { namespace, key }, }); } /** * Search for items within a namespace prefix. * * @param namespacePrefix List of strings representing the namespace prefix. * @param options.filter Optional dictionary of key-value pairs to filter results. * @param options.limit Maximum number of items to return (default is 10). * @param options.offset Number of items to skip before returning results (default is 0). * @param options.query Optional search query. * @returns Promise */ async searchItems( namespacePrefix: string[], options?: { filter?: Record; limit?: number; offset?: number; query?: string; }, ): Promise { const payload = { namespace_prefix: namespacePrefix, filter: options?.filter, limit: options?.limit ?? 10, offset: options?.offset ?? 0, query: options?.query, }; const response = await this.fetch( "/store/items/search", { method: "POST", json: payload, }, ); return { items: response.items.map((item) => ({ ...item, createdAt: item.created_at, updatedAt: item.updated_at, })), }; } /** * List namespaces with optional match conditions. * * @param options.prefix Optional list of strings representing the prefix to filter namespaces. * @param options.suffix Optional list of strings representing the suffix to filter namespaces. * @param options.maxDepth Optional integer specifying the maximum depth of namespaces to return. * @param options.limit Maximum number of namespaces to return (default is 100). * @param options.offset Number of namespaces to skip before returning results (default is 0). * @returns Promise */ async listNamespaces(options?: { prefix?: string[]; suffix?: string[]; maxDepth?: number; limit?: number; offset?: number; }): Promise { const payload = { prefix: options?.prefix, suffix: options?.suffix, max_depth: options?.maxDepth, limit: options?.limit ?? 100, offset: options?.offset ?? 0, }; return this.fetch("/store/namespaces", { method: "POST", json: payload, }); } } export class Client { /** * The client for interacting with assistants. */ public assistants: AssistantsClient; /** * The client for interacting with threads. */ public threads: ThreadsClient; /** * The client for interacting with runs. */ public runs: RunsClient; /** * The client for interacting with cron runs. */ public crons: CronsClient; /** * The client for interacting with the KV store. */ public store: StoreClient; constructor(config?: ClientConfig) { this.assistants = new AssistantsClient(config); this.threads = new ThreadsClient(config); this.runs = new RunsClient(config); this.crons = new CronsClient(config); this.store = new StoreClient(config); } }