UNPKG

@millisai/web-sdk

Version:

Millis AI's web sdk to build voice agents on web and browser extension.

1 lines 34.7 kB
{"version":3,"sources":["../src/client/index.ts","../src/client/client.ts","../src/audio/service.ts","../src/audio/worker/AudioWorklet.ts","../src/client/types.ts","../src/index.ts"],"sourcesContent":["import { Client } from \"./client\";\nimport { Config } from \"./types\";\n\nexport function createClient(config: Config) {\n return new Client(config);\n}\n\nexport async function createMicrophoneAudioTrack(sampleRate: number, config?: Config): Promise<MediaStream> {\n const audioConstraints = {\n sampleRate: sampleRate,\n echoCancellation: true,\n noiseSuppression: true,\n channelCount: 1,\n autoGainControl: true,\n latency: 0,\n ...(config?.audioSource?.constraints || {}),\n };\n\n if (config?.audioSource?.deviceId) {\n audioConstraints.deviceId = config.audioSource.deviceId;\n }\n\n return (navigator.mediaDevices as any).getUserMedia({\n audio: audioConstraints,\n });\n}","import { EventEmitter } from \"eventemitter3\";\nimport { AudioService } from \"../audio/service\";\nimport { LatencyEstimator } from \"../audio/latency_estimator\";\nimport { AgentConfig, AgentState, ClientEvents, ClientInterface, Config } from \"./types\";\n\n// const HOST = \"wss://gateway.millis.ai:8080/\";\n// const HOST = \"ws://54.177.98.36:8080/\";\n// const HOST = \"ws://betaapi.tmatehq.com:8080/\";\nconst HOST = \"wss://api-west.millis.ai:8080/millis\";\n// const HOST = \"ws://localhost:8080/millis\";\n\nexport interface IMessage {\n method: string;\n data?: string;\n payload?: any;\n}\n\nexport interface IStartPayload {\n // call_id?: string,\n agent: {\n agent_id?: string,\n agent_config?: AgentConfig,\n },\n metadata?: object,\n include_metadata_in_prompt?: boolean,\n session_continuation?: {\n session_id?: string\n }\n}\n\nenum State {\n IDLE,\n READY,\n GOT_READY_EVENT,\n GOT_FIRST_AUDIO\n}\n\nexport class Client extends EventEmitter<ClientEvents> implements ClientInterface {\n private config: Config;\n private ws: WebSocket | null;\n private audioService: AudioService | null;\n private startAnswering: number = 0;\n private count: number = 0;\n private latencyEstimator: LatencyEstimator | null;\n private agentState: AgentState = AgentState.IDLE;\n private state: State = State.IDLE;\n private muted: boolean = false;\n private readyPayload: object | undefined = undefined;\n\n constructor(config: Config) {\n super();\n this.ws = null;\n this.audioService = null;\n this.latencyEstimator = null;\n const defaultConfig = {\n debug: false\n }\n this.config = {...defaultConfig, ...config};\n this.print('init');\n }\n\n private reset() {\n this.state = State.IDLE;\n this.agentState = AgentState.IDLE;\n this.count = 0;\n this.startAnswering = 0;\n this.muted = false;\n }\n\n private print(message: string) {\n if (this.config.debug) {\n console.log(\"[millis]\", message);\n }\n }\n\n private connect(payload: IStartPayload) {\n this.print('starting websocket');\n this.ws = new WebSocket(this.config.endPoint || HOST);\n this.ws.binaryType = \"arraybuffer\";\n\n this.ws.onopen = () => {\n this.print('websocket connected, sending initiate message');\n this.emit(\"onopen\");\n this.send(JSON.stringify({\n \"method\": \"initiate\",\n \"data\": {\n public_key: this.config.publicKey,\n agent: payload.agent,\n metadata: payload.metadata,\n include_metadata_in_prompt: payload.include_metadata_in_prompt,\n session_continuation: payload.session_continuation\n }}));\n };\n\n this.ws.onmessage = (event) => {\n if (event.data instanceof ArrayBuffer) {\n this.print('audio data received');\n // this.config.isTest && this.latencyEstimator?.onAudio();\n if (this.startAnswering > 0) {\n this.emit(\"onlatency\", Date.now() - this.startAnswering);\n this.print('latency: ' + (Date.now() - this.startAnswering));\n this.startAnswering = 0;\n this.switchState(AgentState.ANSWER);\n }\n if (!this.config.isBenchmark) {\n const audio = new Uint8Array(event.data);\n this.audioService?.play(audio);\n this.emit(\"onaudio\", audio);\n }\n } else {\n const message: IMessage = JSON.parse(event.data);\n if (message.method === \"onready\") {\n this.onready(message.payload);\n return;\n }\n if (!this.handle(message)) {\n this.emit(message.method as keyof ClientEvents, message.data as any, message.payload);\n }\n this.print(`received ${message.method}`);\n }\n };\n this.ws.onclose = (event: CloseEvent) => {\n this.stop();\n this.emit(\"onclose\", event);\n this.print('websocket disconnected');\n this.switchState(AgentState.IDLE);\n };\n this.ws.onerror = (event: Event) => {\n this.stop();\n this.emit(\"onerror\", event);\n this.print('websocket error: ' + event);\n };\n }\n\n public send(data: Uint8Array | string) {\n if (this.ws?.readyState === 1) {\n this.ws.send(data);\n }\n }\n\n private handle(message: IMessage) {\n switch(message.method) {\n case \"start_answering\":\n this.startAnswering = Date.now();\n this.switchState(AgentState.PREPARE_ANSWER);\n return true;\n case \"clear\":\n this.audioService?.reset();\n this.switchState(AgentState.IDLE);\n return true;\n case \"pause\":\n this.audioService?.setpause(true);\n this.switchState(AgentState.PAUSE);\n return true;\n case \"unpause\":\n this.audioService?.setpause(false);\n this.switchState(AgentState.ANSWER);\n return true;\n case \"pong\":\n this.print(\"Ping rtt \" + (Date.now() - Number(message.data)))\n return true;\n }\n return false;\n }\n\n private onready(payload?: object) {\n // if (this.config.isTest) {\n // this.latencyEstimator = new LatencyEstimator(this);\n // this.latencyEstimator.start();\n // }\n this.readyPayload = payload;\n this.switchState(AgentState.IDLE);\n this.switchConnectionState(State.GOT_READY_EVENT);\n }\n\n public async start(payload: IStartPayload | AgentConfig | string, metadata?: object, includeMetadataInPrompt?: boolean) {\n this.print('starting conversation');\n this.audioService = new AudioService(this.config);\n this.audioService.on(\"data\", (audio: Uint8Array) => {\n // if (this.config.isTest && this.latencyEstimator?.isTesting()) {\n // return;\n // }\n if (this.muted) {\n audio.fill(0);\n } else {\n this.print('sending audio data');\n }\n this.send(audio);\n this.count++;\n if (this.count % 1000 == 0) {\n this.print('sending ping');\n this.send(JSON.stringify({\"method\": \"ping\", \"data\": Date.now().toString()}));\n }\n this.switchConnectionState(State.GOT_FIRST_AUDIO);\n });\n this.audioService.on(\"analyzer\", (analyzer) => {\n this.emit(\"analyzer\", analyzer);\n });\n this.audioService.on(\"useraudioready\", (analyzer) => {\n this.print('user audio ready');\n this.emit(\"useraudioready\", analyzer);\n });\n this.audioService.on(\"playback_finished\", () => {\n if (this.agentState === AgentState.ANSWER) {\n this.switchState(AgentState.IDLE);\n }\n })\n\n this.print('starting audio service');\n await this.audioService.start();\n this.print('audio service started');\n let finalPayload: IStartPayload;\n if (typeof payload === \"object\" && ('agent' in payload || 'call_id' in payload)) {\n finalPayload = payload as IStartPayload;\n } else {\n finalPayload = {agent: typeof payload === \"string\" ? {agent_id: payload} : {agent_config: payload as AgentConfig}};\n if (metadata) {\n finalPayload.metadata = metadata;\n }\n if (includeMetadataInPrompt) {\n finalPayload.include_metadata_in_prompt = includeMetadataInPrompt;\n }\n }\n this.connect(finalPayload);\n }\n\n public async stop() {\n if (this.audioService) {\n this.print('stopping audio service');\n this.audioService.stop();\n this.audioService = null;\n this.print('audio service stopped');\n }\n this.ws?.close();\n this.reset();\n }\n\n public mute() {\n this.muted = true;\n }\n\n public unmute() {\n this.muted = false;\n }\n\n private switchState(state: AgentState) {\n this.agentState = state;\n this.emit(\"onagentstate\", state);\n }\n\n private switchConnectionState(newState: State) {\n if (this.state === State.IDLE) {\n this.state = newState;\n } else if ((this.state === State.GOT_FIRST_AUDIO && newState === State.GOT_READY_EVENT)\n || (this.state === State.GOT_READY_EVENT && newState === State.GOT_FIRST_AUDIO)) {\n this.state = State.READY;\n this.emit(\"onready\", this.readyPayload);\n }\n }\n}\n","import EventEmitter from \"eventemitter3\";\nimport { createMicrophoneAudioTrack } from \"../client\";\nimport { workletCode } from \"./worker/AudioWorklet\";\nimport { create, ConverterType } from '@alexanderolsen/libsamplerate-js';\nimport { Config } from \"../client/types\";\n\nconst SAMPLE_RATE = 16000;\n\nexport class AudioService extends EventEmitter {\n private config: Config;\n private audioContext: AudioContext | null = null;\n private stream: MediaStream | null = null;\n\n // Chrome\n private audioNode: AudioWorkletNode | null = null;\n\n // Others\n private captureNode: ScriptProcessorNode | null = null;\n private audioData: Float32Array[] = [];\n private audioDataIndex: number = 0;\n private pause: boolean = false;\n private inputResampler: any | null;\n private outputResampler: any | null;\n private needResample: boolean = false;\n private resamplerCreated: boolean = false;\n\n constructor(config: Config) {\n super();\n this.config = config;\n }\n\n private print(message: string) {\n if (this.config.debug) {\n console.log(\"[millis audio service]\", message);\n }\n }\n\n public async start() {\n const sampleRate = SAMPLE_RATE;\n if (this.isFirefox()) {\n // TODO assume firefox doesn't use audioworklet\n this.audioContext = new AudioContext({ latencyHint: \"interactive\" });\n this.needResample = true;\n } else {\n this.needResample = false;\n this.audioContext = new AudioContext({ latencyHint: \"interactive\", sampleRate: sampleRate });\n }\n this.print(\"starting audio service, firefox: \" + this.isFirefox() + \", need resample: \" + this.needResample);\n\n try {\n this.print(\"requesting microphone permission\");\n this.stream = await createMicrophoneAudioTrack(sampleRate, this.config);\n this.print(\"microphone permission granted\");\n } catch (error) {\n this.print(\"microphone permission denied\");\n throw new Error(\"User didn't give microphone permission\");\n }\n\n this.print(\"starting audio capture and playback processor, worklet: \" + this.isAudioWorkletSupported());\n if (this.isAudioWorkletSupported()) {\n this.print(\"Starting audio worklet\");\n this.audioContext.resume();\n const blob = new Blob([workletCode], { type: \"application/javascript\" });\n const blobURL = URL.createObjectURL(blob);\n await this.audioContext.audioWorklet.addModule(blobURL);\n this.print(\"Audio worklet loaded\");\n this.audioNode = new AudioWorkletNode(\n this.audioContext,\n \"capture-and-playback-processor\",\n );\n this.print(\"Audio worklet setup\");\n\n this.audioNode.port.onmessage = (e) => {\n if (e.data instanceof Uint8Array) {\n this.emit(\"data\", e.data);\n } else if (e.data === \"playback_finished\") {\n this.onPlaybackFinished();\n }\n };\n\n const source = this.audioContext.createMediaStreamSource(this.stream);\n source.connect(this.audioNode);\n this.audioNode.connect(this.audioContext.destination);\n const userAnalyser = this.audioContext.createAnalyser();\n source.connect(userAnalyser);\n this.emit(\"useraudioready\", {\n analyser: userAnalyser,\n stream: this.stream\n });\n\n const analyser = this.audioContext.createAnalyser();\n this.audioNode.connect(analyser);\n analyser.connect(this.audioContext.destination);\n this.emit(\"analyzer\", analyser);\n } else {\n this.print(\"Starting audio capture node\");\n const source = this.audioContext.createMediaStreamSource(this.stream);\n this.captureNode = this.audioContext.createScriptProcessor(2048, 1, 1);\n this.captureNode.onaudioprocess = (audioProcessingEvent: AudioProcessingEvent) => {\n\n if (this.captureNode && this.audioContext) {\n // Resample inputData\n if (this.needResample && !this.resamplerCreated) {\n let converterType = ConverterType.SRC_SINC_FASTEST;\n let nChannels = 1;\n this.resamplerCreated = true;\n create(nChannels, this.audioContext.sampleRate, sampleRate, {\n converterType: converterType, // default SRC_SINC_FASTEST. see API for more\n }).then((src: any) => {\n this.inputResampler = src;\n });\n create(nChannels, sampleRate, this.audioContext.sampleRate, {\n converterType: converterType, // default SRC_SINC_FASTEST. see API for more\n }).then((src: any) => {\n this.outputResampler = src;\n });\n }\n var inputData = audioProcessingEvent.inputBuffer.getChannelData(0);\n var resampledAudio = null;\n if (this.inputResampler != null) {\n resampledAudio = this.inputResampler.full(inputData);\n } else {\n resampledAudio = inputData;\n }\n const pcmData = convertFloat32ToUint8(resampledAudio);\n this.emit(\"data\", pcmData);\n\n // Playback here\n const outputBuffer = audioProcessingEvent.outputBuffer;\n const outputChannel = outputBuffer.getChannelData(0);\n for (let i = 0; i < outputChannel.length; ++i) {\n if (this.audioData.length > 0 && !this.pause) {\n outputChannel[i] = this.audioData[0][this.audioDataIndex++];\n if (this.audioDataIndex === this.audioData[0].length) {\n this.audioData.shift();\n this.audioDataIndex = 0;\n if (this.audioData.length == 0) {\n this.onPlaybackFinished();\n }\n }\n } else {\n outputChannel[i] = 0;\n }\n }\n }\n };\n source.connect(this.captureNode);\n this.captureNode.connect(this.audioContext.destination);\n this.print(\"Audio capture node setup\");\n const userAnalyser = this.audioContext.createAnalyser();\n source.connect(userAnalyser);\n this.emit(\"useraudioready\", {\n analyser: userAnalyser,\n stream: this.stream\n });\n const analyser = this.audioContext.createAnalyser();\n this.captureNode.connect(analyser);\n analyser.connect(this.audioContext.destination);\n this.emit(\"analyzer\", analyser);\n }\n }\n\n public async stop() {\n this.audioContext?.suspend();\n this.audioContext?.close();\n\n if (this.isAudioWorkletSupported()) {\n this.audioNode?.disconnect();\n this.audioNode = null;\n } else {\n if (this.captureNode) {\n this.captureNode.disconnect();\n this.captureNode.onaudioprocess = null;\n this.captureNode = null;\n this.audioData = [];\n this.audioDataIndex = 0;\n }\n }\n this.stream?.getTracks().forEach((track) => track.stop());\n this.audioContext = null;\n this.stream = null;\n }\n\n public play(audio: Uint8Array) {\n if (this.isAudioWorkletSupported()) {\n this.audioNode?.port.postMessage(audio);\n } else {\n const float32Data = convertUint8ToFloat32(audio);\n var resampledAudio = null;\n if (this.outputResampler != null) {\n // Resample output samples\n resampledAudio = this.outputResampler.full(float32Data);\n } else {\n resampledAudio = float32Data;\n }\n this.audioData.push(resampledAudio);\n }\n }\n\n public setpause(pause: boolean) {\n if (this.isAudioWorkletSupported()) {\n this.audioNode?.port.postMessage(pause ? \"pause\" : \"unpause\");\n } else {\n this.pause = pause;\n }\n }\n\n public reset() {\n if (this.isAudioWorkletSupported()) {\n this.audioNode?.port.postMessage(\"clear\");\n } else {\n this.audioData = [];\n this.audioDataIndex = 0;\n }\n }\n\n private onPlaybackFinished() {\n this.emit(\"playback_finished\");\n }\n\n private isAudioWorkletSupported(): boolean {\n return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);\n }\n private isFirefox(): boolean {\n return /Firefox/.test(navigator.userAgent);\n }\n}\n\nexport function convertUint8ToFloat32(array: Uint8Array): Float32Array {\n const targetArray = new Float32Array(array.byteLength / 2);\n\n // A DataView is used to read our 16-bit little-endian samples out of the Uint8Array buffer\n const sourceDataView = new DataView(array.buffer);\n\n // Loop through, get values, and divide by 32,768\n for (let i = 0; i < targetArray.length; i++) {\n targetArray[i] = sourceDataView.getInt16(i * 2, true) / Math.pow(2, 16 - 1);\n }\n return targetArray;\n}\n\nexport function convertFloat32ToUint8(array: Float32Array): Uint8Array {\n const buffer = new ArrayBuffer(array.length * 2);\n const view = new DataView(buffer);\n\n for (let i = 0; i < array.length; i++) {\n const value = (array[i] as number) * 32768;\n view.setInt16(i * 2, value, true); // true for little-endian\n }\n\n return new Uint8Array(buffer);\n}","export const workletCode = `\nclass captureAndPlaybackProcessor extends AudioWorkletProcessor {\n audioData = [];\n index = 0;\n pause = false;\n\n constructor() {\n super();\n //set listener to receive audio data, data is float32 array.\n this.port.onmessage = (e) => {\n if (e.data === \"clear\") {\n // Clear all buffer.\n this.audioData = [];\n this.index = 0;\n } else if (e.data === \"pause\") {\n this.pause = true;\n } else if (e.data === \"unpause\") {\n this.pause = false;\n } else if (e.data.length > 0) {\n this.audioData.push(this.convertUint8ToFloat32(e.data));\n }\n };\n }\n\n convertUint8ToFloat32(array) {\n const targetArray = new Float32Array(array.byteLength / 2);\n\n // A DataView is used to read our 16-bit little-endian samples out of the Uint8Array buffer\n const sourceDataView = new DataView(array.buffer);\n\n // Loop through, get values, and divide by 32,768\n for (let i = 0; i < targetArray.length; i++) {\n targetArray[i] = sourceDataView.getInt16(i * 2, true) / Math.pow(2, 16 - 1);\n }\n return targetArray;\n }\n\n convertFloat32ToUint8(array) {\n const buffer = new ArrayBuffer(array.length * 2);\n const view = new DataView(buffer);\n\n for (let i = 0; i < array.length; i++) {\n const value = array[i] * 32768;\n view.setInt16(i * 2, value, true); // true for little-endian\n }\n\n return new Uint8Array(buffer);\n }\n\n process(inputs, outputs, parameters) {\n // Capture\n const input = inputs[0];\n const inputChannel1 = input[0];\n const inputChannel2 = input[1];\n this.port.postMessage(this.convertFloat32ToUint8(inputChannel1));\n\n // Playback\n const output = outputs[0];\n const outputChannel1 = output[0];\n const outputChannel2 = output[1];\n // start playback.\n for (let i = 0; i < outputChannel1.length; ++i) {\n if (this.audioData.length > 0 && !this.pause) {\n outputChannel1[i] = this.audioData[0][this.index];\n outputChannel2[i] = this.audioData[0][this.index];\n this.index++;\n if (this.index == this.audioData[0].length) {\n this.audioData.shift();\n this.index = 0;\n if (this.audioData.length == 0) {\n this.port.postMessage(\"playback_finished\");\n }\n }\n } else {\n outputChannel1[i] = 0;\n outputChannel2[i] = 0;\n }\n }\n\n return true;\n }\n }\n\n registerProcessor(\n \"capture-and-playback-processor\",\n captureAndPlaybackProcessor,\n );\n`;","import { IStartPayload } from \"./client\";\n\nexport interface Config {\n publicKey: string;\n endPoint?: string;\n debug?: boolean;\n audioSource?: {\n deviceId?: string;\n constraints?: MediaTrackConstraints;\n };\n\n [key: string]: any;\n}\n\nexport type VoiceProvider = 'elevenlabs' | 'rime' | 'openai' | 'cartesia' | 'playht' | 'neets' | 'deepgram' | 'dubverse';\n\ninterface Tool { // Main structure for defining a function.\n name: string; // Function name, formatted as a valid identifier (no spaces, begins with a letter)\n description: string; // Detailed description to help the agent understand when to use the function\n webhook: string; // The URL of the webhook to which the request will be sent\n header: object; // Any necessary headers for the webhook request. Ex: {\"Content-Type\": \"application/json\"}\n params: Param[]; // Array of `Param` objects, defining the parameters needed by the function\n}\n\ninterface Param {\n name: string; // Valid parameter name, no spaces, starts with a letter, can use underscores or camelCase\n type: \"string\" | \"number\" | \"boolean\"; // The data type of the parameter (e.g., string, number, boolean)\n description: string; // Detailed description of the parameter\n required: boolean; // Whether this parameter is mandatory\n}\n\nexport interface Voice {\n provider: VoiceProvider;\n voice_id: string;\n}\n\ninterface AgentSetting {\n first_message?: string;\n vad_threshold?: number;\n privacy_settings?: {\n opt_out_data_collection: boolean;\n };\n session_timeout?: {\n max_duration: number;\n max_idle: number;\n message?: string;\n },\n custom_vocabulary?: {\n keywords?: object;\n };\n flow?: {\n user_start_first?: boolean;\n interruption?: {\n allowed: boolean;\n keep_interruption_message: boolean;\n };\n response_delay?: number;\n }\n}\n\ninterface WebhookSetting {\n session_data_webhook?: string;\n extra_prompt_webhook?: string;\n}\n\ntype AllSettings = AgentSetting & WebhookSetting;\n\nexport interface AgentConfig extends AllSettings {\n prompt?: string;\n voice?: Voice;\n tools?: Tool[];\n language?: string;\n custom_llm_websocket?: string;\n llm?: {model: string};\n [key: string]: any;\n}\n\nexport interface ClientEvents {\n 'onopen': () => void;\n 'onready': (payload?: { session_id?: string }) => void;\n 'onsessionended': () => void;\n 'onaudio': (audio: Uint8Array) => void;\n 'onresponsetext': (text: string, payload: { is_final?: boolean }) => void;\n 'ontranscript': (text: string, payload: { is_final?: boolean }) => void;\n 'analyzer': (analyzer: AnalyserNode) => void;\n 'useraudioready': (data: { analyser: AnalyserNode, stream: MediaStream }) => void;\n 'onlatency': (latency: number) => void;\n 'onclose': (event: CloseEvent) => void;\n 'onerror': (error: Event) => void;\n 'onagentstate': (state: AgentState) => void;\n 'ai_action': (action: any) => void;\n [key: string]: any;\n}\n\nexport interface ClientInterface {\n start(payload: IStartPayload | AgentConfig | string, metadata?: object, includeMetadataInPrompt?: boolean): Promise<void>;\n stop(): Promise<void>;\n mute(): void;\n unmute(): void;\n}\n\nexport enum AgentState {\n IDLE = \"idle\",\n PREPARE_ANSWER = \"prepare_answer\",\n ANSWER = \"answer\",\n PAUSE = \"pause\",\n}","import * as Millis from \"./client\";\nexport * from \"./client/types\";\n\nexport default Millis;\n"],"mappings":"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","names":["client_exports","__export","createClient","createMicrophoneAudioTrack","EventEmitter","EventEmitter","workletCode","create","ConverterType","SAMPLE_RATE","AudioService","EventEmitter","config","message","__async","sampleRate","createMicrophoneAudioTrack","error","blob","workletCode","blobURL","e","source","userAnalyser","analyser","audioProcessingEvent","converterType","ConverterType","nChannels","create","src","inputData","resampledAudio","pcmData","convertFloat32ToUint8","outputChannel","i","_a","_b","_c","_d","track","audio","float32Data","convertUint8ToFloat32","pause","array","targetArray","sourceDataView","buffer","view","value","AgentState","HOST","Client","EventEmitter","config","defaultConfig","__spreadValues","message","payload","HOST","event","_a","audio","data","_b","_c","metadata","includeMetadataInPrompt","__async","AudioService","analyzer","finalPayload","state","newState","createClient","config","Client","createMicrophoneAudioTrack","sampleRate","__async","_a","_b","audioConstraints","__spreadValues","src_default","client_exports"]}