@charivo/realtime-client-remote 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -32,6 +32,7 @@ declare class RemoteRealtimeClient implements RealtimeTransportClient {
32
32
  disconnect(): Promise<void>;
33
33
  sendText(text: string): Promise<void>;
34
34
  sendAudio(audio: ArrayBuffer): Promise<void>;
35
+ sendToolResult(callId: string, output: Record<string, unknown>): Promise<void>;
35
36
  interrupt(): Promise<void>;
36
37
  onEvent(callback: (event: RealtimeTransportEvent) => void): void;
37
38
  private getActiveTransportClient;
package/dist/index.js CHANGED
@@ -59,6 +59,9 @@ var RemoteRealtimeClient = class {
59
59
  async sendAudio(audio) {
60
60
  await this.getActiveTransportClient().sendAudio(audio);
61
61
  }
62
+ async sendToolResult(callId, output) {
63
+ await this.getActiveTransportClient().sendToolResult(callId, output);
64
+ }
62
65
  async interrupt() {
63
66
  await this.getActiveTransportClient().interrupt();
64
67
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n RealtimeSessionBootstrap,\n RealtimeSessionConfig,\n RealtimeSessionRequest,\n} from \"@charivo/core\";\nimport { OPENAI_REALTIME_ADAPTER } from \"@charivo/core\";\nimport { createOpenAIRealtimeClient } from \"@charivo/realtime-client-openai\";\nimport type {\n RealtimeTransportClient,\n RealtimeTransportEvent,\n} from \"@charivo/realtime-core\";\nimport {\n DEFAULT_REQUEST_TIMEOUT_MS,\n fetchWithTimeout,\n isRealtimeSessionBootstrap,\n} from \"@charivo/shared\";\n\nexport interface RemoteRealtimeAdapterFactoryOptions {\n debug?: boolean;\n requestBootstrap: (\n request: RealtimeSessionRequest,\n ) => Promise<RealtimeSessionBootstrap>;\n}\n\nexport type RemoteRealtimeAdapterFactory = (\n options: RemoteRealtimeAdapterFactoryOptions,\n) => RealtimeTransportClient;\n\nexport const DEFAULT_REMOTE_REALTIME_ADAPTERS = {\n [OPENAI_REALTIME_ADAPTER]: (options) =>\n createOpenAIRealtimeClient({\n debug: options.debug,\n sessionBootstrap: options.requestBootstrap,\n }),\n} satisfies Record<string, RemoteRealtimeAdapterFactory>;\n\nexport interface RemoteRealtimeClientConfig {\n apiEndpoint?: string;\n debug?: boolean;\n adapters?: Record<string, RemoteRealtimeAdapterFactory>;\n resolveAdapterId?: (config?: RealtimeSessionConfig) => string;\n}\n\n/**\n * Production browser client for server-mediated realtime sessions.\n *\n * It resolves a browser-side transport adapter, forwards bootstrap requests to\n * your server route, and validates that the returned bootstrap matches the\n * selected adapter.\n */\nexport class RemoteRealtimeClient implements RealtimeTransportClient {\n private transportClient: RealtimeTransportClient | null = null;\n private readonly adapters: Map<string, RemoteRealtimeAdapterFactory>;\n private readonly eventCallbacks = new Set<\n (event: RealtimeTransportEvent) => void\n >();\n\n constructor(private config: RemoteRealtimeClientConfig = {}) {\n this.adapters = new Map(\n Object.entries({\n ...DEFAULT_REMOTE_REALTIME_ADAPTERS,\n ...config.adapters,\n }),\n );\n }\n\n async connect(config?: RealtimeSessionConfig): Promise<void> {\n const adapterId = this.resolveAdapterId(config);\n const factory = this.adapters.get(adapterId);\n\n if (!factory) {\n throw new Error(\n `No realtime adapter registered for \"${adapterId}\". Registered adapters: ${Array.from(this.adapters.keys()).join(\", \") || \"(none)\"}`,\n );\n }\n\n const transportClient = factory({\n debug: this.config.debug,\n requestBootstrap: (request) =>\n this.requestBootstrap(request, { expectedAdapterId: adapterId }),\n });\n\n for (const callback of this.eventCallbacks) {\n transportClient.onEvent(callback);\n }\n\n this.transportClient = transportClient;\n\n try {\n await transportClient.connect(config);\n } catch (error) {\n this.transportClient = null;\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.transportClient) {\n return;\n }\n\n const transportClient = this.transportClient;\n this.transportClient = null;\n await transportClient.disconnect();\n }\n\n async sendText(text: string): Promise<void> {\n await this.getActiveTransportClient().sendText(text);\n }\n\n async sendAudio(audio: ArrayBuffer): Promise<void> {\n await this.getActiveTransportClient().sendAudio(audio);\n }\n\n async interrupt(): Promise<void> {\n await this.getActiveTransportClient().interrupt();\n }\n\n onEvent(callback: (event: RealtimeTransportEvent) => void): void {\n this.eventCallbacks.add(callback);\n this.transportClient?.onEvent(callback);\n }\n\n private getActiveTransportClient(): RealtimeTransportClient {\n if (!this.transportClient) {\n throw new Error(\"Realtime session not active\");\n }\n\n return this.transportClient;\n }\n\n private resolveAdapterId(config?: RealtimeSessionConfig): string {\n if (this.config.resolveAdapterId) {\n return this.config.resolveAdapterId(config);\n }\n\n const transport = config?.transport ?? \"webrtc\";\n if (config?.provider === \"openai\" && transport === \"webrtc\") {\n return OPENAI_REALTIME_ADAPTER;\n }\n\n throw new Error(\n `No remote realtime adapter resolver for provider \"${config?.provider ?? \"(unspecified)\"}\" and transport \"${transport}\"`,\n );\n }\n\n private async requestBootstrap(\n request: RealtimeSessionRequest,\n options: { expectedAdapterId: string },\n ): Promise<RealtimeSessionBootstrap> {\n const response = await fetchWithTimeout(\n this.config.apiEndpoint || \"/api/realtime\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n },\n `Realtime session request timed out after ${DEFAULT_REQUEST_TIMEOUT_MS}ms`,\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create Realtime session: ${errorText}`);\n }\n\n const bootstrap = (await response.json()) as unknown;\n if (!isRealtimeSessionBootstrap(bootstrap)) {\n throw new Error(\"Invalid realtime session bootstrap response\");\n }\n\n if (bootstrap.adapter !== options.expectedAdapterId) {\n throw new Error(\n `Realtime session bootstrap adapter mismatch: expected ${options.expectedAdapterId}, received ${bootstrap.adapter}`,\n );\n }\n\n return bootstrap as RealtimeSessionBootstrap;\n }\n}\n\nexport function createRemoteRealtimeClient(\n config?: RemoteRealtimeClientConfig,\n): RealtimeTransportClient {\n return new RemoteRealtimeClient(config);\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n RealtimeSessionBootstrap,\n RealtimeSessionConfig,\n RealtimeSessionRequest,\n} from \"@charivo/core\";\nimport { OPENAI_REALTIME_ADAPTER } from \"@charivo/core\";\nimport { createOpenAIRealtimeClient } from \"@charivo/realtime-client-openai\";\nimport type {\n RealtimeTransportClient,\n RealtimeTransportEvent,\n} from \"@charivo/realtime-core\";\nimport {\n DEFAULT_REQUEST_TIMEOUT_MS,\n fetchWithTimeout,\n isRealtimeSessionBootstrap,\n} from \"@charivo/shared\";\n\nexport interface RemoteRealtimeAdapterFactoryOptions {\n debug?: boolean;\n requestBootstrap: (\n request: RealtimeSessionRequest,\n ) => Promise<RealtimeSessionBootstrap>;\n}\n\nexport type RemoteRealtimeAdapterFactory = (\n options: RemoteRealtimeAdapterFactoryOptions,\n) => RealtimeTransportClient;\n\nexport const DEFAULT_REMOTE_REALTIME_ADAPTERS = {\n [OPENAI_REALTIME_ADAPTER]: (options) =>\n createOpenAIRealtimeClient({\n debug: options.debug,\n sessionBootstrap: options.requestBootstrap,\n }),\n} satisfies Record<string, RemoteRealtimeAdapterFactory>;\n\nexport interface RemoteRealtimeClientConfig {\n apiEndpoint?: string;\n debug?: boolean;\n adapters?: Record<string, RemoteRealtimeAdapterFactory>;\n resolveAdapterId?: (config?: RealtimeSessionConfig) => string;\n}\n\n/**\n * Production browser client for server-mediated realtime sessions.\n *\n * It resolves a browser-side transport adapter, forwards bootstrap requests to\n * your server route, and validates that the returned bootstrap matches the\n * selected adapter.\n */\nexport class RemoteRealtimeClient implements RealtimeTransportClient {\n private transportClient: RealtimeTransportClient | null = null;\n private readonly adapters: Map<string, RemoteRealtimeAdapterFactory>;\n private readonly eventCallbacks = new Set<\n (event: RealtimeTransportEvent) => void\n >();\n\n constructor(private config: RemoteRealtimeClientConfig = {}) {\n this.adapters = new Map(\n Object.entries({\n ...DEFAULT_REMOTE_REALTIME_ADAPTERS,\n ...config.adapters,\n }),\n );\n }\n\n async connect(config?: RealtimeSessionConfig): Promise<void> {\n const adapterId = this.resolveAdapterId(config);\n const factory = this.adapters.get(adapterId);\n\n if (!factory) {\n throw new Error(\n `No realtime adapter registered for \"${adapterId}\". Registered adapters: ${Array.from(this.adapters.keys()).join(\", \") || \"(none)\"}`,\n );\n }\n\n const transportClient = factory({\n debug: this.config.debug,\n requestBootstrap: (request) =>\n this.requestBootstrap(request, { expectedAdapterId: adapterId }),\n });\n\n for (const callback of this.eventCallbacks) {\n transportClient.onEvent(callback);\n }\n\n this.transportClient = transportClient;\n\n try {\n await transportClient.connect(config);\n } catch (error) {\n this.transportClient = null;\n throw error;\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.transportClient) {\n return;\n }\n\n const transportClient = this.transportClient;\n this.transportClient = null;\n await transportClient.disconnect();\n }\n\n async sendText(text: string): Promise<void> {\n await this.getActiveTransportClient().sendText(text);\n }\n\n async sendAudio(audio: ArrayBuffer): Promise<void> {\n await this.getActiveTransportClient().sendAudio(audio);\n }\n\n async sendToolResult(\n callId: string,\n output: Record<string, unknown>,\n ): Promise<void> {\n await this.getActiveTransportClient().sendToolResult(callId, output);\n }\n\n async interrupt(): Promise<void> {\n await this.getActiveTransportClient().interrupt();\n }\n\n onEvent(callback: (event: RealtimeTransportEvent) => void): void {\n this.eventCallbacks.add(callback);\n this.transportClient?.onEvent(callback);\n }\n\n private getActiveTransportClient(): RealtimeTransportClient {\n if (!this.transportClient) {\n throw new Error(\"Realtime session not active\");\n }\n\n return this.transportClient;\n }\n\n private resolveAdapterId(config?: RealtimeSessionConfig): string {\n if (this.config.resolveAdapterId) {\n return this.config.resolveAdapterId(config);\n }\n\n const transport = config?.transport ?? \"webrtc\";\n if (config?.provider === \"openai\" && transport === \"webrtc\") {\n return OPENAI_REALTIME_ADAPTER;\n }\n\n throw new Error(\n `No remote realtime adapter resolver for provider \"${config?.provider ?? \"(unspecified)\"}\" and transport \"${transport}\"`,\n );\n }\n\n private async requestBootstrap(\n request: RealtimeSessionRequest,\n options: { expectedAdapterId: string },\n ): Promise<RealtimeSessionBootstrap> {\n const response = await fetchWithTimeout(\n this.config.apiEndpoint || \"/api/realtime\",\n {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n },\n `Realtime session request timed out after ${DEFAULT_REQUEST_TIMEOUT_MS}ms`,\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to create Realtime session: ${errorText}`);\n }\n\n const bootstrap = (await response.json()) as unknown;\n if (!isRealtimeSessionBootstrap(bootstrap)) {\n throw new Error(\"Invalid realtime session bootstrap response\");\n }\n\n if (bootstrap.adapter !== options.expectedAdapterId) {\n throw new Error(\n `Realtime session bootstrap adapter mismatch: expected ${options.expectedAdapterId}, received ${bootstrap.adapter}`,\n );\n }\n\n return bootstrap as RealtimeSessionBootstrap;\n }\n}\n\nexport function createRemoteRealtimeClient(\n config?: RemoteRealtimeClientConfig,\n): RealtimeTransportClient {\n return new RemoteRealtimeClient(config);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@charivo/realtime-client-remote",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Remote realtime client for Charivo (calls server API)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,10 +17,10 @@
17
17
  "dist"
18
18
  ],
19
19
  "dependencies": {
20
- "@charivo/core": "0.3.0",
21
- "@charivo/realtime-client-openai": "0.1.0",
22
- "@charivo/shared": "0.1.0",
23
- "@charivo/realtime-core": "0.2.0"
20
+ "@charivo/core": "0.4.0",
21
+ "@charivo/realtime-client-openai": "0.2.0",
22
+ "@charivo/realtime-core": "0.3.0",
23
+ "@charivo/shared": "0.1.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "tsup": "^8.5.0",