diff --git a/.changeset/honest-apes-sparkle.md b/.changeset/honest-apes-sparkle.md new file mode 100644 index 00000000..32303dd3 --- /dev/null +++ b/.changeset/honest-apes-sparkle.md @@ -0,0 +1,5 @@ +--- +"livekit-server-sdk": patch +--- + +Add optional timeouts to clients in server APIs diff --git a/packages/livekit-server-sdk/src/AgentDispatchClient.ts b/packages/livekit-server-sdk/src/AgentDispatchClient.ts index 4ee507d9..91dbabb5 100644 --- a/packages/livekit-server-sdk/src/AgentDispatchClient.ts +++ b/packages/livekit-server-sdk/src/AgentDispatchClient.ts @@ -8,6 +8,7 @@ import { ListAgentDispatchRequest, ListAgentDispatchResponse, } from '@livekit/protocol'; +import type { ClientOptions } from './ClientOptions.js'; import { ServiceBase } from './ServiceBase.js'; import { type Rpc, TwirpRpc, livekitPackage } from './TwirpRPC.js'; @@ -29,10 +30,14 @@ export class AgentDispatchClient extends ServiceBase { * @param host - hostname including protocol. i.e. 'https://.livekit.cloud' * @param apiKey - API Key, can be set in env var LIVEKIT_API_KEY * @param secret - API Secret, can be set in env var LIVEKIT_API_SECRET + * @param options - client options */ - constructor(host: string, apiKey?: string, secret?: string) { + constructor(host: string, apiKey?: string, secret?: string, options?: ClientOptions) { super(apiKey, secret); - this.rpc = new TwirpRpc(host, livekitPackage); + const rpcOptions = options?.requestTimeout + ? { requestTimeout: options.requestTimeout } + : undefined; + this.rpc = new TwirpRpc(host, livekitPackage, rpcOptions); } /** diff --git a/packages/livekit-server-sdk/src/ClientOptions.ts b/packages/livekit-server-sdk/src/ClientOptions.ts new file mode 100644 index 00000000..b2ef093f --- /dev/null +++ b/packages/livekit-server-sdk/src/ClientOptions.ts @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: 2024 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 + +/** + * Options common to all clients + */ +export type ClientOptions = { + /** + * Optional timeout, in seconds, for all server requests + */ + requestTimeout?: number; +}; diff --git a/packages/livekit-server-sdk/src/EgressClient.ts b/packages/livekit-server-sdk/src/EgressClient.ts index 0c500ecd..5d6f97aa 100644 --- a/packages/livekit-server-sdk/src/EgressClient.ts +++ b/packages/livekit-server-sdk/src/EgressClient.ts @@ -25,6 +25,7 @@ import { UpdateStreamRequest, WebEgressRequest, } from '@livekit/protocol'; +import type { ClientOptions } from './ClientOptions.js'; import { ServiceBase } from './ServiceBase.js'; import type { Rpc } from './TwirpRPC.js'; import { TwirpRpc, livekitPackage } from './TwirpRPC.js'; @@ -137,10 +138,14 @@ export class EgressClient extends ServiceBase { * @param host - hostname including protocol. i.e. 'https://.livekit.cloud' * @param apiKey - API Key, can be set in env var LIVEKIT_API_KEY * @param secret - API Secret, can be set in env var LIVEKIT_API_SECRET + * @param options - client options */ - constructor(host: string, apiKey?: string, secret?: string) { + constructor(host: string, apiKey?: string, secret?: string, options?: ClientOptions) { super(apiKey, secret); - this.rpc = new TwirpRpc(host, livekitPackage); + const rpcOptions = options?.requestTimeout + ? { requestTimeout: options.requestTimeout } + : undefined; + this.rpc = new TwirpRpc(host, livekitPackage, rpcOptions); } /** diff --git a/packages/livekit-server-sdk/src/IngressClient.ts b/packages/livekit-server-sdk/src/IngressClient.ts index ef4b8b99..f0198720 100644 --- a/packages/livekit-server-sdk/src/IngressClient.ts +++ b/packages/livekit-server-sdk/src/IngressClient.ts @@ -10,6 +10,7 @@ import { ListIngressResponse, UpdateIngressRequest, } from '@livekit/protocol'; +import type { ClientOptions } from './ClientOptions.js'; import { ServiceBase } from './ServiceBase.js'; import type { Rpc } from './TwirpRPC.js'; import { TwirpRpc, livekitPackage } from './TwirpRPC.js'; @@ -124,10 +125,14 @@ export class IngressClient extends ServiceBase { * @param host - hostname including protocol. i.e. 'https://.livekit.cloud' * @param apiKey - API Key, can be set in env var LIVEKIT_API_KEY * @param secret - API Secret, can be set in env var LIVEKIT_API_SECRET + * @param options - client options */ - constructor(host: string, apiKey?: string, secret?: string) { + constructor(host: string, apiKey?: string, secret?: string, options?: ClientOptions) { super(apiKey, secret); - this.rpc = new TwirpRpc(host, livekitPackage); + const rpcOptions = options?.requestTimeout + ? { requestTimeout: options.requestTimeout } + : undefined; + this.rpc = new TwirpRpc(host, livekitPackage, rpcOptions); } /** diff --git a/packages/livekit-server-sdk/src/RoomServiceClient.ts b/packages/livekit-server-sdk/src/RoomServiceClient.ts index fd7ad3f6..39097cf4 100644 --- a/packages/livekit-server-sdk/src/RoomServiceClient.ts +++ b/packages/livekit-server-sdk/src/RoomServiceClient.ts @@ -22,6 +22,7 @@ import { UpdateRoomMetadataRequest, UpdateSubscriptionsRequest, } from '@livekit/protocol'; +import type { ClientOptions } from './ClientOptions.js'; import { ServiceBase } from './ServiceBase.js'; import type { Rpc } from './TwirpRPC.js'; import { TwirpRpc, livekitPackage } from './TwirpRPC.js'; @@ -120,10 +121,14 @@ export class RoomServiceClient extends ServiceBase { * @param host - hostname including protocol. i.e. 'https://.livekit.cloud' * @param apiKey - API Key, can be set in env var LIVEKIT_API_KEY * @param secret - API Secret, can be set in env var LIVEKIT_API_SECRET + * @param options - client options */ - constructor(host: string, apiKey?: string, secret?: string) { + constructor(host: string, apiKey?: string, secret?: string, options?: ClientOptions) { super(apiKey, secret); - this.rpc = new TwirpRpc(host, livekitPackage); + const rpcOptions = options?.requestTimeout + ? { requestTimeout: options.requestTimeout } + : undefined; + this.rpc = new TwirpRpc(host, livekitPackage, rpcOptions); } /** diff --git a/packages/livekit-server-sdk/src/SipClient.ts b/packages/livekit-server-sdk/src/SipClient.ts index 7e369c4f..1312725b 100644 --- a/packages/livekit-server-sdk/src/SipClient.ts +++ b/packages/livekit-server-sdk/src/SipClient.ts @@ -39,6 +39,7 @@ import { UpdateSIPInboundTrunkRequest, UpdateSIPOutboundTrunkRequest, } from '@livekit/protocol'; +import type { ClientOptions } from './ClientOptions.js'; import { ServiceBase } from './ServiceBase.js'; import type { Rpc } from './TwirpRPC.js'; import { TwirpRpc, livekitPackage } from './TwirpRPC.js'; @@ -216,10 +217,14 @@ export class SipClient extends ServiceBase { * @param host - hostname including protocol. i.e. 'https://.livekit.cloud' * @param apiKey - API Key, can be set in env var LIVEKIT_API_KEY * @param secret - API Secret, can be set in env var LIVEKIT_API_SECRET + * @param options - client options */ - constructor(host: string, apiKey?: string, secret?: string) { + constructor(host: string, apiKey?: string, secret?: string, options?: ClientOptions) { super(apiKey, secret); - this.rpc = new TwirpRpc(host, livekitPackage); + const rpcOptions = options?.requestTimeout + ? { requestTimeout: options.requestTimeout } + : undefined; + this.rpc = new TwirpRpc(host, livekitPackage, rpcOptions); } /** diff --git a/packages/livekit-server-sdk/src/TwirpRPC.ts b/packages/livekit-server-sdk/src/TwirpRPC.ts index 11f725dc..f7f63d30 100644 --- a/packages/livekit-server-sdk/src/TwirpRPC.ts +++ b/packages/livekit-server-sdk/src/TwirpRPC.ts @@ -5,7 +5,15 @@ import type { JsonValue } from '@bufbuild/protobuf'; // twirp RPC adapter for client implementation +type Options = { + /** Prefix for the RPC requests */ + prefix?: string; + /** Timeout for fetch requests, in seconds. Must be within the valid range for abort signal timeouts. */ + requestTimeout?: number; +}; + const defaultPrefix = '/twirp'; +const defaultTimeoutSeconds = 60; export const livekitPackage = 'livekit'; export interface Rpc { @@ -48,13 +56,16 @@ export class TwirpRpc { prefix: string; - constructor(host: string, pkg: string, prefix?: string) { + requestTimeout: number; + + constructor(host: string, pkg: string, options?: Options) { if (host.startsWith('ws')) { host = host.replace('ws', 'http'); } this.host = host; this.pkg = pkg; - this.prefix = prefix || defaultPrefix; + this.requestTimeout = options?.requestTimeout ?? defaultTimeoutSeconds; + this.prefix = options?.prefix || defaultPrefix; } async request( @@ -62,7 +73,7 @@ export class TwirpRpc { method: string, data: any, // eslint-disable-line @typescript-eslint/no-explicit-any headers: any, // eslint-disable-line @typescript-eslint/no-explicit-any - timeout = 60, + timeout = this.requestTimeout, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): Promise { const path = `${this.prefix}/${this.pkg}.${service}/${method}`;