UNPKG

@deepgram/sdk

Version:

Isomorphic Javascript client for Deepgram

160 lines (145 loc) 5.39 kB
import { DEFAULT_AGENT_URL } from "../lib/constants"; import { AgentEvents } from "../lib/enums/AgentEvents"; import type { AgentLiveSchema, DeepgramClientOptions, FunctionCallResponse } from "../lib/types"; import { AbstractLiveClient } from "./AbstractLiveClient"; export class AgentLiveClient extends AbstractLiveClient { public namespace: string = "agent"; constructor(options: DeepgramClientOptions, endpoint: string = "/:version/agent/converse") { super(options); this.baseUrl = options.agent?.websocket?.options?.url ?? DEFAULT_AGENT_URL; this.connect({}, endpoint); } /** * Sets up the connection event handlers. * This method is responsible for handling the various events that can occur on the WebSocket connection, such as opening, closing, and receiving messages. * - When the connection is opened, it emits the `AgentEvents.Open` event. * - When the connection is closed, it emits the `AgentEvents.Close` event. * - When an error occurs on the connection, it emits the `AgentEvents.Error` event. * - When a message is received, it parses the message and emits the appropriate event based on the message type. */ public setupConnection(): void { // Set up standard connection events (open, close, error) using abstracted method this.setupConnectionEvents({ Open: AgentEvents.Open, Close: AgentEvents.Close, Error: AgentEvents.Error, }); // Set up message handling specific to agent conversations if (this.conn) { this.conn.onmessage = (event: MessageEvent) => { this.handleMessage(event); }; } } /** * Handles incoming messages from the WebSocket connection. * @param event - The MessageEvent object representing the received message. */ protected handleMessage(event: MessageEvent): void { if (typeof event.data === "string") { try { const data = JSON.parse(event.data); this.handleTextMessage(data); } catch (error) { this.emit(AgentEvents.Error, { event, data: event.data?.toString().substring(0, 200) + (event.data?.toString().length > 200 ? "..." : ""), message: "Unable to parse `data` as JSON.", error, url: this.conn?.url, readyState: this.conn?.readyState, }); } } else if (event.data instanceof Blob) { event.data.arrayBuffer().then((buffer) => { this.handleBinaryMessage(Buffer.from(buffer)); }); } else if (event.data instanceof ArrayBuffer) { this.handleBinaryMessage(Buffer.from(event.data)); } else if (Buffer.isBuffer(event.data)) { this.handleBinaryMessage(event.data); } else { console.log("Received unknown data type", event.data); this.emit(AgentEvents.Error, { event, message: "Received unknown data type.", url: this.conn?.url, readyState: this.conn?.readyState, dataType: typeof event.data, }); } } /** * Handles binary messages received from the WebSocket connection. * @param data - The binary data. */ protected handleBinaryMessage(data: Buffer): void { this.emit(AgentEvents.Audio, data); } /** * Handles text messages received from the WebSocket connection. * @param data - The parsed JSON data. */ protected handleTextMessage(data: any): void { if (data.type in AgentEvents) { this.emit(data.type, data); } else { this.emit(AgentEvents.Unhandled, data); } } /** * To be called with your model configuration BEFORE sending * any audio data. * @param options - The SettingsConfiguration object. */ public configure(options: AgentLiveSchema): void { const string = JSON.stringify({ type: "Settings", ...options, }); this.send(string); } /** * Provide new system prompt to the LLM. * @param prompt - The system prompt to provide. */ public updatePrompt(prompt: string): void { this.send(JSON.stringify({ type: "UpdatePrompt", prompt })); } /** * Change the speak model. * @param model - The new model to use. */ public updateSpeak(speakConfig: Exclude<AgentLiveSchema["agent"]["speak"], undefined>): void { this.send(JSON.stringify({ type: "UpdateSpeak", speak: speakConfig })); } /** * Immediately trigger an agent message. If this message * is sent while the user is speaking, or while the server is in the * middle of sending audio, then the request will be ignored and an InjectionRefused * event will be emitted. * @example "Hold on while I look that up for you." * @example "Are you still on the line?" * @param content - The message to speak. */ public injectAgentMessage(content: string): void { this.send(JSON.stringify({ type: "InjectAgentMessage", content })); } /** * Respond to a function call request. * @param response - The response to the function call request. */ public functionCallResponse(response: FunctionCallResponse): void { this.send(JSON.stringify({ type: "FunctionCallResponse", ...response })); } /** * Send a keepalive to avoid closing the websocket while you * are not transmitting audio. This should be sent at least * every 8 seconds. */ public keepAlive(): void { this.send(JSON.stringify({ type: "KeepAlive" })); } }