GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

azure-ai-voicelive-ts

Azure AI Voice Live SDK for JavaScript/TypeScript. Build real-time voice AI applications with bidirectional WebSocket communication. Use for voice assistants, conversational AI, real-time speech-to-speech, and voice-enabled chatbots in Node.js or browser environments. Triggers: "

Ciza · 0 points · 20 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-ai-voicelive-ts-e58528d.zip · 11 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-ai-voicelive-ts
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git git clone https://github.com/microsoft/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

@azure/ai-voicelive (JavaScript/TypeScript)

Real-time voice AI SDK for building bidirectional voice assistants with Azure AI in Node.js and browser environments.

Installation

npm install @azure/ai-voicelive @azure/identity
# TypeScript users
npm install @types/node

Current Version: 1.0.0-beta.3

Supported Environments:

  • Node.js LTS versions (20+)
  • Modern browsers (Chrome, Firefox, Safari, Edge)

Environment Variables

AZURE_VOICELIVE_ENDPOINT=https://<resource>.cognitiveservices.azure.com
# Optional: API key if not using Entra ID
AZURE_VOICELIVE_API_KEY=<your-api-key>
# Optional: Logging
AZURE_LOG_LEVEL=info
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production

Authentication

Microsoft Entra Token Credential (Recommended)

import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
import { VoiceLiveClient } from "@azure/ai-voicelive";

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();
const endpoint = "https://your-resource.cognitiveservices.azure.com";

const client = new VoiceLiveClient(endpoint, credential);

API Key

import { AzureKeyCredential } from "@azure/core-auth";
import { VoiceLiveClient } from "@azure/ai-voicelive";

const endpoint = "https://your-resource.cognitiveservices.azure.com";
const credential = new AzureKeyCredential("your-api-key");

const client = new VoiceLiveClient(endpoint, credential);

Client Hierarchy

VoiceLiveClient
└── VoiceLiveSession (WebSocket connection)
    ├── updateSession()      → Configure session options
    ├── subscribe()          → Event handlers (Azure SDK pattern)
    ├── sendAudio()          → Stream audio input
    ├── addConversationItem() → Add messages/function outputs
    └── sendEvent()          → Send raw protocol events

Quick Start

import { DefaultAzureCredential } from "@azure/identity";
import { VoiceLiveClient } from "@azure/ai-voicelive";

const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
const endpoint = process.env.AZURE_VOICELIVE_ENDPOINT!;

// Create client and start session
const client = new VoiceLiveClient(endpoint, credential);
const session = await client.startSession("gpt-4o-mini-realtime-preview");

// Configure session
await session.updateSession({
  modalities: ["text", "audio"],
  instructions: "You are a helpful AI assistant. Respond naturally.",
  voice: {
    type: "azure-standard",
    name: "en-US-AvaNeural",
  },
  turnDetection: {
    type: "server_vad",
    threshold: 0.5,
    prefixPaddingMs: 300,
    silenceDurationMs: 500,
  },
  inputAudioFormat: "pcm16",
  outputAudioFormat: "pcm16",
});

// Subscribe to events
const subscription = session.subscribe({
  onResponseAudioDelta: async (event, context) => {
    // Handle streaming audio output
    const audioData = event.delta;
    playAudioChunk(audioData);
  },
  onResponseTextDelta: async (event, context) => {
    // Handle streaming text
    process.stdout.write(event.delta);
  },
  onInputAudioTranscriptionCompleted: async (event, context) => {
    console.log("User said:", event.transcript);
  },
});

// Send audio from microphone
function sendAudioChunk(audioBuffer: ArrayBuffer) {
  session.sendAudio(audioBuffer);
}

Session Configuration

await session.updateSession({
  // Modalities
  modalities: ["audio", "text"],
  
  // System instructions
  instructions: "You are a customer service representative.",
  
  // Voice selection
  voice: {
    type: "azure-standard",  // or "azure-custom", "openai"
    name: "en-US-AvaNeural",
  },
  
  // Turn detection (VAD)
  turnDetection: {
    type: "server_vad",      // or "azure_semantic_vad"
    threshold: 0.5,
    prefixPaddingMs: 300,
    silenceDurationMs: 500,
  },
  
  // Audio formats
  inputAudioFormat: "pcm16",
  outputAudioFormat: "pcm16",
  
  // Tools (function calling)
  tools: [
    {
      type: "function",
      name: "get_weather",
      description: "Get current weather",
      parameters: {
        type: "object",
        properties: {
          location: { type: "string" }
        },
        required: ["location"]
      }
    }
  ],
  toolChoice: "auto",
});

Event Handling (Azure SDK Pattern)

The SDK uses a subscription-based event handling pattern:

const subscription = session.subscribe({
  // Connection lifecycle
  onConnected: async (args, context) => {
    console.log("Connected:", args.connectionId);
  },
  onDisconnected: async (args, context) => {
    console.log("Disconnected:", args.code, args.reason);
  },
  onError: async (args, context) => {
    console.error("Error:", args.error.message);
  },
  
  // Session events
  onSessionCreated: async (event, context) => {
    console.log("Session created:", context.sessionId);
  },
  onSessionUpdated: async (event, context) => {
    console.log("Session updated");
  },
  
  // Audio input events (VAD)
  onInputAudioBufferSpeechStarted: async (event, context) => {
    console.log("Speech started at:", event.audioStartMs);
  },
  onInputAudioBufferSpeechStopped: async (event, context) => {
    console.log("Speech stopped at:", event.audioEndMs);
  },
  
  // Transcription events
  onConversationItemInputAudioTranscriptionCompleted: async (event, context) => {
    console.log("User said:", event.transcript);
  },
  onConversationItemInputAudioTranscriptionDelta: async (event, context) => {
    process.stdout.write(event.delta);
  },
  
  // Response events
  onResponseCreated: async (event, context) => {
    console.log("Response started");
  },
  onResponseDone: async (event, context) => {
    console.log("Response complete");
  },
  
  // Streaming text
  onResponseTextDelta: async (event, context) => {
    process.stdout.write(event.delta);
  },
  onResponseTextDone: async (event, context) => {
    console.log("\n--- Text complete ---");
  },
  
  // Streaming audio
  onResponseAudioDelta: async (event, context) => {
    const audioData = event.delta;
    playAudioChunk(audioData);
  },
  onResponseAudioDone: async (event, context) => {
    console.log("Audio complete");
  },
  
  // Audio transcript (what assistant said)
  onResponseAudioTranscriptDelta: async (event, context) => {
    process.stdout.write(event.delta);
  },
  
  // Function calling
  onResponseFunctionCallArgumentsDone: async (event, context) => {
    if (event.name === "get_weather") {
      const args = JSON.parse(event.arguments);
      const result = await getWeather(args.location);
      
      await session.addConversationItem({
        type: "function_call_output",
        callId: event.callId,
        output: JSON.stringify(result),
      });
      
      await session.sendEvent({ type: "response.create" });
    }
  },
  
  // Catch-all for debugging
  onServerEvent: async (event, context) => {
    console.log("Event:", event.type);
  },
});

// Clean up when done
await subscription.close();

Function Calling

// Define tools in session config
await session.updateSession({
  modalities: ["audio", "text"],
  instructions: "Help users with weather information.",
  tools: [
    {
      type: "function",
      name: "get_weather",
      description: "Get current weather for a location",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "City and state or country",
          },
        },
        required: ["location"],
      },
    },
  ],
  toolChoice: "auto",
});

// Handle function calls
const subscription = session.subscribe({
  onResponseFunctionCallArgumentsDone: async (event, context) => {
    if (event.name === "get_weather") {
      const args = JSON.parse(event.arguments);
      const weatherData = await fetchWeather(args.location);
      
      // Send function result
      await session.addConversationItem({
        type: "function_call_output",
        callId: event.callId,
        output: JSON.stringify(weatherData),
      });
      
      // Trigger response generation
      await session.sendEvent({ type: "response.create" });
    }
  },
});

Voice Options

Voice Type Config Example
Azure Standard { type: "azure-standard", name: "..." } "en-US-AvaNeural"
Azure Custom { type: "azure-custom", name: "...", endpointId: "..." } Custom voice endpoint
Azure Personal { type: "azure-personal", speakerProfileId: "..." } Personal voice clone
OpenAI { type: "openai", name: "..." } "alloy", "echo", "shimmer"

Supported Models

Model Description Use Case
gpt-4o-realtime-preview GPT-4o with real-time audio High-quality conversational AI
gpt-4o-mini-realtime-preview Lightweight GPT-4o Fast, efficient interactions
phi4-mm-realtime Phi multimodal Cost-effective applications

Turn Detection Options

// Server VAD (default)
turnDetection: {
  type: "server_vad",
  threshold: 0.5,
  prefixPaddingMs: 300,
  silenceDurationMs: 500,
}

// Azure Semantic VAD (smarter detection)
turnDetection: {
  type: "azure_semantic_vad",
}

// Azure Semantic VAD (English optimized)
turnDetection: {
  type: "azure_semantic_vad_en",
}

// Azure Semantic VAD (Multilingual)
turnDetection: {
  type: "azure_semantic_vad_multilingual",
}

Audio Formats

Format Sample Rate Use Case
pcm16 24kHz Default, high quality
pcm16-8000hz 8kHz Telephony
pcm16-16000hz 16kHz Voice assistants
g711_ulaw 8kHz Telephony (US)
g711_alaw 8kHz Telephony (EU)

Key Types Reference

Type Purpose
VoiceLiveClient Main client for creating sessions
VoiceLiveSession Active WebSocket session
VoiceLiveSessionHandlers Event handler interface
VoiceLiveSubscription Active event subscription
ConnectionContext Context for connection events
SessionContext Context for session events
ServerEventUnion Union of all server events

Error Handling

import {
  VoiceLiveError,
  VoiceLiveConnectionError,
  VoiceLiveAuthenticationError,
  VoiceLiveProtocolError,
} from "@azure/ai-voicelive";

const subscription = session.subscribe({
  onError: async (args, context) => {
    const { error } = args;
    
    if (error instanceof VoiceLiveConnectionError) {
      console.error("Connection error:", error.message);
    } else if (error instanceof VoiceLiveAuthenticationError) {
      console.error("Auth error:", error.message);
    } else if (error instanceof VoiceLiveProtocolError) {
      console.error("Protocol error:", error.message);
    }
  },
  
  onServerError: async (event, context) => {
    console.error("Server error:", event.error?.message);
  },
});

Logging

import { setLogLevel } from "@azure/logger";

// Enable verbose logging
setLogLevel("info");

// Or via environment variable
// AZURE_LOG_LEVEL=info

Browser Usage

// Browser requires bundler (Vite, webpack, etc.)
import { VoiceLiveClient } from "@azure/ai-voicelive";
import { InteractiveBrowserCredential } from "@azure/identity";

// Use browser-compatible credential
const credential = new InteractiveBrowserCredential({
  clientId: "your-client-id",
  tenantId: "your-tenant-id",
});

const client = new VoiceLiveClient(endpoint, credential);

// Request microphone access
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const audioContext = new AudioContext({ sampleRate: 24000 });

// Process audio and send to session
// ... (see samples for full implementation)

Best Practices

  1. Use DefaultAzureCredential for local development; use ManagedIdentityCredential or WorkloadIdentityCredential for production — Never hardcode API keys
  2. Set both modalities — Include ["text", "audio"] for voice assistants
  3. Use Azure Semantic VAD — Better turn detection than basic server VAD
  4. Handle all error types — Connection, auth, and protocol errors
  5. Clean up subscriptions — Call subscription.close() when done
  6. Use appropriate audio format — PCM16 at 24kHz for best quality

Reference Links

Resource URL
npm Package https://www.npmjs.com/package/@azure/ai-voicelive
GitHub Source https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/ai/ai-voicelive
Samples https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/ai/ai-voicelive/samples
API Reference https://learn.microsoft.com/javascript/api/@azure/ai-voicelive
Files (skills)
  • references
    • audio-streaming.md 12.3 KB
      # Audio Streaming Reference
      
      Real-time audio capture and playback patterns for Azure AI Voice Live using the @azure/ai-voicelive TypeScript SDK.
      
      ## Overview
      
      Voice Live requires bidirectional audio streaming over WebSocket. This reference covers browser microphone capture, PCM format conversion, and audio playback patterns for voice assistants.
      
      ## Audio Format Requirements
      
      | Property | Default Value | Options |
      |----------|---------------|---------|
      | `inputAudioFormat` | `"pcm16"` | `"pcm16"`, `"g711_alaw"`, `"g711_ulaw"` |
      | `outputAudioFormat` | `"pcm16"` | `"pcm16"` |
      | Sample Rate (pcm16) | 24000 Hz | 8000, 16000, 24000 |
      | Channels | Mono (1) | - |
      
      ### Sample Rate Inference
      
      ```typescript
      function inferPcmSampleRateFromOutputFormat(outputAudioFormat: string | undefined): number {
        if (!outputAudioFormat) return 24000;
        const fmt = outputAudioFormat.toLowerCase();
        if (fmt === "pcm16") return 24000;
        if (fmt === "pcm16-16000hz") return 16000;
        if (fmt === "pcm16-8000hz") return 8000;
        return 24000;
      }
      ```
      
      ## Browser Microphone Capture
      
      ### Pattern A: ScriptProcessorNode (Simple)
      
      Simpler implementation but uses deprecated Web Audio API.
      
      ```typescript
      export class SimpleAudioCapture {
        private audioContext?: AudioContext;
        private mediaStream?: MediaStream;
        private scriptProcessor?: ScriptProcessorNode;
        private dataCallback: (data: ArrayBuffer) => void;
        
        private readonly targetSampleRate = 24000;
      
        constructor(onData: (data: ArrayBuffer) => void) {
          this.dataCallback = onData;
        }
      
        async initialize(): Promise<void> {
          // Request microphone access
          this.mediaStream = await navigator.mediaDevices.getUserMedia({
            audio: {
              channelCount: 1,
              sampleRate: this.targetSampleRate,
              echoCancellation: true,
              noiseSuppression: true,
            },
          });
      
          // Create audio context at target sample rate
          this.audioContext = new AudioContext({ sampleRate: this.targetSampleRate });
          
          // Create processing chain
          const source = this.audioContext.createMediaStreamSource(this.mediaStream);
          this.scriptProcessor = this.audioContext.createScriptProcessor(4096, 1, 1);
          
          // Connect nodes
          source.connect(this.scriptProcessor);
          this.scriptProcessor.connect(this.audioContext.destination);
          
          // Process audio data
          this.scriptProcessor.onaudioprocess = (event) => {
            const inputData = event.inputBuffer.getChannelData(0);
            const pcm16Data = this.convertToPCM16(inputData);
            this.dataCallback(pcm16Data.buffer);
          };
        }
      
        private convertToPCM16(floatData: Float32Array): Int16Array {
          const pcm16 = new Int16Array(floatData.length);
          for (let i = 0; i < floatData.length; i++) {
            const sample = Math.max(-1, Math.min(1, floatData[i]));
            pcm16[i] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;
          }
          return pcm16;
        }
      
        stop(): void {
          this.scriptProcessor?.disconnect();
          this.mediaStream?.getTracks().forEach((track) => track.stop());
          this.audioContext?.close();
        }
      }
      ```
      
      ### Pattern B: AudioWorklet (Modern, Recommended)
      
      Uses modern AudioWorklet API with better performance.
      
      ```typescript
      // Main thread code
      export class AudioWorkletCapture {
        private audioContext?: AudioContext;
        private mediaStream?: MediaStream;
        private workletNode?: AudioWorkletNode;
        private onAudioData: (data: Uint8Array) => void;
      
        constructor(onData: (data: Uint8Array) => void) {
          this.onAudioData = onData;
        }
      
        async start(): Promise<void> {
          const ac = new AudioContext();
          this.audioContext = ac;
      
          // Load AudioWorklet module
          const workletUrl = new URL("worklets/pcm16-downsampler.js", document.baseURI).toString();
          await ac.audioWorklet.addModule(workletUrl);
      
          // Get microphone stream
          // Disable browser processing - server handles VAD
          const stream = await navigator.mediaDevices.getUserMedia({
            audio: {
              echoCancellation: false,
              noiseSuppression: false,
              autoGainControl: false,
            },
          });
          this.mediaStream = stream;
      
          // Create processing chain
          const source = ac.createMediaStreamSource(stream);
          const worklet = new AudioWorkletNode(ac, "pcm16-downsampler");
          this.workletNode = worklet;
      
          // Receive processed PCM16 data from worklet
          worklet.port.onmessage = (e) => {
            const buffer = e.data as ArrayBuffer;
            const pcm16Bytes = new Uint8Array(buffer);
            this.onAudioData(pcm16Bytes);
          };
      
          // Connect with muted output to prevent feedback
          const gain = ac.createGain();
          gain.gain.value = 0;
          source.connect(worklet);
          worklet.connect(gain);
          gain.connect(ac.destination);
        }
      
        stop(): void {
          this.workletNode?.disconnect();
          this.mediaStream?.getTracks().forEach((track) => track.stop());
          this.audioContext?.close();
        }
      }
      ```
      
      ### AudioWorklet Processor (pcm16-downsampler.js)
      
      Save this file in `/public/worklets/pcm16-downsampler.js`:
      
      ```javascript
      class Pcm16DownsamplerProcessor extends AudioWorkletProcessor {
        constructor() {
          super();
          this._targetSampleRate = 24000;
          this._inputSampleRate = sampleRate;  // Browser's native rate (e.g., 48000)
          this._ratio = this._inputSampleRate / this._targetSampleRate;
          this._carry = 0;
        }
      
        process(inputs) {
          const input = inputs[0];
          if (!input || input.length === 0) return true;
      
          const channel0 = input[0];
          if (!channel0 || channel0.length === 0) return true;
      
          // Downsample by averaging
          const outLength = Math.max(0, Math.floor((channel0.length - this._carry) / this._ratio));
          if (outLength === 0) return true;
      
          const pcm16 = new Int16Array(outLength);
          let outIndex = 0;
          let i = this._carry;
      
          while (outIndex < outLength) {
            const start = Math.floor(i);
            const end = Math.floor(i + this._ratio);
      
            let sum = 0, count = 0;
            for (let j = start; j < end && j < channel0.length; j++) {
              sum += channel0[j];
              count++;
            }
      
            const sample = count > 0 ? sum / count : 0;
            const clamped = Math.max(-1, Math.min(1, sample));
            pcm16[outIndex] = clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff;
      
            outIndex++;
            i += this._ratio;
          }
      
          this._carry = i - channel0.length;
          if (this._carry < 0) this._carry = 0;
      
          // Transfer buffer to main thread
          this.port.postMessage(pcm16.buffer, [pcm16.buffer]);
          return true;
        }
      }
      
      registerProcessor("pcm16-downsampler", Pcm16DownsamplerProcessor);
      ```
      
      ## Audio Playback
      
      ### Pattern A: AudioBufferSourceNode Queue
      
      Simple queue-based playback using AudioBufferSourceNode.
      
      ```typescript
      export class Pcm16Player {
        private readonly audioContext: AudioContext;
        private nextStartTime = 0;
        private sources: AudioBufferSourceNode[] = [];
        private sampleRate: number;
      
        constructor(sampleRate = 24000) {
          this.audioContext = new AudioContext();
          this.sampleRate = sampleRate;
        }
      
        async resume(): Promise<void> {
          if (this.audioContext.state !== "running") {
            await this.audioContext.resume();
          }
        }
      
        enqueuePcm16(bytes: Uint8Array): void {
          // Convert PCM16 bytes to Float32 for Web Audio API
          const floatData = this.pcm16BytesToFloat32(bytes);
          
          // Create audio buffer
          const buffer = this.audioContext.createBuffer(1, floatData.length, this.sampleRate);
          buffer.getChannelData(0).set(floatData);
      
          // Create and schedule source
          const src = this.audioContext.createBufferSource();
          src.buffer = buffer;
          src.connect(this.audioContext.destination);
      
          // Schedule seamless playback
          const now = this.audioContext.currentTime;
          if (this.nextStartTime < now) {
            this.nextStartTime = now;
          }
          const startAt = this.nextStartTime;
          this.nextStartTime += buffer.duration;
      
          // Track for cleanup
          src.onended = () => {
            this.sources = this.sources.filter((s) => s !== src);
          };
          this.sources.push(src);
          src.start(startAt);
        }
      
        private pcm16BytesToFloat32(bytes: Uint8Array): Float32Array {
          const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
          const sampleCount = Math.floor(bytes.byteLength / 2);
          const out = new Float32Array(sampleCount);
          for (let i = 0; i < sampleCount; i++) {
            const s = view.getInt16(i * 2, true);  // Little-endian
            out[i] = s / 0x8000;
          }
          return out;
        }
      
        stop(): void {
          for (const s of this.sources) {
            try {
              s.stop();
            } catch {
              /* ignore */
            }
          }
          this.sources = [];
          this.nextStartTime = 0;
        }
      }
      ```
      
      ### Pattern B: AudioWorklet Playback
      
      Better for handling barge-in (user interrupts assistant).
      
      ```javascript
      // audio-playback-worklet.js
      class AudioPlaybackWorklet extends AudioWorkletProcessor {
        constructor() {
          super();
          this.port.onmessage = this.handleMessage.bind(this);
          this.buffer = [];
        }
      
        handleMessage(event) {
          if (event.data === null) {
            // Clear buffer on barge-in
            this.buffer = [];
            return;
          }
          // Append Int16 samples
          this.buffer.push(...event.data);
        }
      
        process(inputs, outputs) {
          const output = outputs[0];
          const channel = output[0];
      
          if (this.buffer.length > channel.length) {
            const toProcess = this.buffer.slice(0, channel.length);
            this.buffer = this.buffer.slice(channel.length);
            channel.set(toProcess.map((v) => v / 32768));  // Int16 to Float32
          } else {
            channel.set(this.buffer.map((v) => v / 32768));
            this.buffer = [];
          }
      
          return true;
        }
      }
      
      registerProcessor("audio-playback-worklet", AudioPlaybackWorklet);
      ```
      
      ## PCM16 Conversion Utilities
      
      ```typescript
      // Float32 to PCM16 (for sending to server)
      export function floatTo16BitPCM(input: Float32Array): Int16Array {
        const output = new Int16Array(input.length);
        for (let i = 0; i < input.length; i++) {
          const s = Math.max(-1, Math.min(1, input[i] ?? 0));
          output[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
        }
        return output;
      }
      
      // PCM16 bytes to Float32 (for playback)
      export function pcm16BytesToFloat32LE(bytes: Uint8Array): Float32Array {
        const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
        const sampleCount = Math.floor(bytes.byteLength / 2);
        const out = new Float32Array(sampleCount);
        for (let i = 0; i < sampleCount; i++) {
          const s = view.getInt16(i * 2, true);  // Little-endian
          out[i] = s / 0x8000;
        }
        return out;
      }
      
      // Int16Array to Uint8Array (for sendAudio)
      export function int16ToUint8(int16Array: Int16Array): Uint8Array {
        return new Uint8Array(int16Array.buffer);
      }
      ```
      
      ## Integration with VoiceLiveSession
      
      ```typescript
      import { VoiceLiveClient } from "@azure/ai-voicelive";
      import { DefaultAzureCredential } from "@azure/identity";
      
      const client = new VoiceLiveClient(endpoint, new DefaultAzureCredential());
      const session = await client.startSession("gpt-4o-mini-realtime-preview");
      
      // Configure audio formats
      await session.updateSession({
        modalities: ["audio", "text"],
        inputAudioFormat: "pcm16",
        outputAudioFormat: "pcm16",
        turnDetection: {
          type: "server_vad",
          threshold: 0.5,
          silenceDurationMs: 500,
        },
      });
      
      // Initialize audio capture and playback
      const player = new Pcm16Player(24000);
      const capture = new AudioWorkletCapture((data) => {
        session.sendAudio(data);
      });
      
      // Handle audio output from assistant
      const subscription = session.subscribe({
        onResponseAudioDelta: async (event) => {
          player.enqueuePcm16(event.delta);
        },
        onInputAudioBufferSpeechStarted: async () => {
          // User started speaking - stop playback (barge-in)
          player.stop();
        },
      });
      
      // Start capture
      await capture.start();
      await player.resume();
      
      // Cleanup
      async function cleanup() {
        capture.stop();
        player.stop();
        await subscription.close();
        await session.close();
      }
      ```
      
      ## Best Practices
      
      1. **Use AudioWorklet over ScriptProcessorNode** - Better performance and officially supported
      2. **Disable browser audio processing** - Set `echoCancellation: false` when server handles VAD
      3. **Handle barge-in** - Clear playback buffer when user starts speaking
      4. **Match sample rates** - Ensure capture matches server expectation (usually 24kHz)
      5. **Use transferable objects** - Pass `ArrayBuffer` with transfer list for efficiency
      6. **Resume AudioContext on user gesture** - Browser policy requires user interaction
      
      ## See Also
      
      - [Function Calling Reference](./function-calling.md)
      - [SKILL.md](../SKILL.md) - Main skill documentation
      - [Azure Voice Live Samples](https://github.com/microsoft-foundry/voicelive-samples)
      
    • function-calling.md 13.5 KB
      # Function Calling Reference
      
      Tool definitions and function call handling patterns for Azure AI Voice Live using the @azure/ai-voicelive TypeScript SDK.
      
      ## Overview
      
      Voice Live supports function calling (tools) that allow the voice assistant to execute actions and retrieve information during a conversation. This reference covers tool definitions, handling function call events, and sending function results.
      
      ## Tool Definition Schema
      
      Tools are defined using JSON Schema in the session configuration:
      
      ```typescript
      interface FunctionTool {
        type: "function";
        name: string;
        description?: string;
        parameters?: {
          type: "object";
          properties: Record<string, {
            type: string;
            description?: string;
            enum?: string[];
          }>;
          required?: string[];
        };
      }
      ```
      
      ## Session Configuration with Tools
      
      ```typescript
      import { VoiceLiveClient } from "@azure/ai-voicelive";
      import { DefaultAzureCredential } from "@azure/identity";
      
      const client = new VoiceLiveClient(endpoint, new DefaultAzureCredential());
      const session = await client.startSession("gpt-4o-mini-realtime-preview");
      
      await session.updateSession({
        modalities: ["audio", "text"],
        instructions: "You are a helpful assistant with access to weather data.",
        voice: {
          type: "azure-standard",
          name: "en-US-AvaNeural",
        },
        turnDetection: {
          type: "server_vad",
          threshold: 0.5,
          silenceDurationMs: 500,
        },
        tools: [
          {
            type: "function",
            name: "get_weather",
            description: "Get the current weather for a location",
            parameters: {
              type: "object",
              properties: {
                location: {
                  type: "string",
                  description: "City and state, e.g., San Francisco, CA",
                },
                unit: {
                  type: "string",
                  enum: ["celsius", "fahrenheit"],
                  description: "Temperature unit",
                },
              },
              required: ["location"],
            },
          },
          {
            type: "function",
            name: "search_products",
            description: "Search for products in the catalog",
            parameters: {
              type: "object",
              properties: {
                query: {
                  type: "string",
                  description: "Search query",
                },
                category: {
                  type: "string",
                  enum: ["electronics", "clothing", "home", "sports"],
                },
                max_results: {
                  type: "integer",
                  description: "Maximum number of results to return",
                },
              },
              required: ["query"],
            },
          },
        ],
        toolChoice: "auto",  // "auto", "none", "required", or { type: "function", name: "..." }
      });
      ```
      
      ## Handling Function Calls
      
      ### Basic Pattern
      
      ```typescript
      const subscription = session.subscribe({
        onResponseFunctionCallArgumentsDone: async (event, context) => {
          console.log(`Function called: ${event.name}`);
          console.log(`Call ID: ${event.callId}`);
          console.log(`Arguments: ${event.arguments}`);
      
          // Parse arguments
          const args = JSON.parse(event.arguments);
      
          // Execute function based on name
          let result: string;
          
          switch (event.name) {
            case "get_weather":
              result = await getWeather(args.location, args.unit ?? "celsius");
              break;
            case "search_products":
              result = await searchProducts(args.query, args.category, args.max_results);
              break;
            default:
              result = JSON.stringify({ error: `Unknown function: ${event.name}` });
          }
      
          // Send function result back
          await session.addConversationItem({
            type: "function_call_output",
            callId: event.callId,
            output: result,
          });
      
          // Trigger response generation
          await session.sendEvent({ type: "response.create" });
        },
      });
      ```
      
      ### Complete Example with Multiple Tools
      
      ```typescript
      import { VoiceLiveClient, VoiceLiveSession } from "@azure/ai-voicelive";
      import { DefaultAzureCredential } from "@azure/identity";
      
      // Function implementations
      async function getWeather(location: string, unit: string): Promise<string> {
        // In production, call actual weather API
        const temp = unit === "celsius" ? "22°C" : "72°F";
        return JSON.stringify({
          location,
          temperature: temp,
          conditions: "Partly cloudy",
          humidity: "45%",
        });
      }
      
      async function searchProducts(
        query: string,
        category?: string,
        maxResults = 5
      ): Promise<string> {
        // In production, query product database
        const products = [
          { id: "1", name: `${query} - Premium`, price: 99.99 },
          { id: "2", name: `${query} - Standard`, price: 49.99 },
        ];
        return JSON.stringify({
          query,
          category: category ?? "all",
          results: products.slice(0, maxResults),
        });
      }
      
      async function bookAppointment(
        date: string,
        time: string,
        service: string
      ): Promise<string> {
        // In production, book in calendar system
        return JSON.stringify({
          success: true,
          confirmation: `APT-${Date.now()}`,
          date,
          time,
          service,
        });
      }
      
      // Function dispatcher
      async function handleFunctionCall(
        name: string,
        args: Record<string, unknown>
      ): Promise<string> {
        switch (name) {
          case "get_weather":
            return getWeather(
              args.location as string,
              (args.unit as string) ?? "celsius"
            );
          case "search_products":
            return searchProducts(
              args.query as string,
              args.category as string | undefined,
              args.max_results as number | undefined
            );
          case "book_appointment":
            return bookAppointment(
              args.date as string,
              args.time as string,
              args.service as string
            );
          default:
            return JSON.stringify({ error: `Unknown function: ${name}` });
        }
      }
      
      // Main voice assistant
      async function startAssistant() {
        const client = new VoiceLiveClient(endpoint, new DefaultAzureCredential());
        const session = await client.startSession("gpt-4o-mini-realtime-preview");
      
        await session.updateSession({
          modalities: ["audio", "text"],
          instructions: `You are a helpful assistant that can:
            - Get weather information
            - Search for products
            - Book appointments
            Always confirm details with the user before booking.`,
          tools: [
            {
              type: "function",
              name: "get_weather",
              description: "Get current weather",
              parameters: {
                type: "object",
                properties: {
                  location: { type: "string", description: "City name" },
                  unit: { type: "string", enum: ["celsius", "fahrenheit"] },
                },
                required: ["location"],
              },
            },
            {
              type: "function",
              name: "search_products",
              description: "Search product catalog",
              parameters: {
                type: "object",
                properties: {
                  query: { type: "string" },
                  category: { type: "string", enum: ["electronics", "clothing", "home"] },
                  max_results: { type: "integer" },
                },
                required: ["query"],
              },
            },
            {
              type: "function",
              name: "book_appointment",
              description: "Book an appointment",
              parameters: {
                type: "object",
                properties: {
                  date: { type: "string", description: "Date in YYYY-MM-DD format" },
                  time: { type: "string", description: "Time in HH:MM format" },
                  service: { type: "string", description: "Type of service" },
                },
                required: ["date", "time", "service"],
              },
            },
          ],
          toolChoice: "auto",
        });
      
        const subscription = session.subscribe({
          onResponseFunctionCallArgumentsDone: async (event, context) => {
            console.log(`Executing: ${event.name}`);
            
            try {
              const args = JSON.parse(event.arguments);
              const result = await handleFunctionCall(event.name, args);
      
              await session.addConversationItem({
                type: "function_call_output",
                callId: event.callId,
                output: result,
              });
      
              await session.sendEvent({ type: "response.create" });
            } catch (error) {
              // Send error as function output
              await session.addConversationItem({
                type: "function_call_output",
                callId: event.callId,
                output: JSON.stringify({ error: String(error) }),
              });
              await session.sendEvent({ type: "response.create" });
            }
          },
      
          onResponseTextDelta: async (event) => {
            process.stdout.write(event.delta);
          },
      
          onError: async (args) => {
            console.error("Session error:", args.error);
          },
        });
      
        return { session, subscription };
      }
      ```
      
      ## Streaming Function Arguments
      
      For long-running function calls, you can process arguments as they stream in:
      
      ```typescript
      const pendingCalls = new Map<string, { name: string; arguments: string }>();
      
      const subscription = session.subscribe({
        // Arguments arrive in chunks
        onResponseFunctionCallArgumentsDelta: async (event, context) => {
          const callId = event.callId;
          
          if (!pendingCalls.has(callId)) {
            pendingCalls.set(callId, { name: event.name ?? "", arguments: "" });
          }
          
          const pending = pendingCalls.get(callId)!;
          if (event.name) {
            pending.name = event.name;
          }
          if (event.delta) {
            pending.arguments += event.delta;
          }
        },
      
        // All arguments received
        onResponseFunctionCallArgumentsDone: async (event, context) => {
          const callId = event.callId;
          const pending = pendingCalls.get(callId);
          pendingCalls.delete(callId);
      
          if (!pending) {
            console.error("No pending call found for:", callId);
            return;
          }
      
          // Now process the complete function call
          const args = JSON.parse(pending.arguments || event.arguments);
          const result = await handleFunctionCall(pending.name || event.name, args);
      
          await session.addConversationItem({
            type: "function_call_output",
            callId,
            output: result,
          });
      
          await session.sendEvent({ type: "response.create" });
        },
      });
      ```
      
      ## Parallel Function Calls
      
      Voice Live may invoke multiple functions in parallel. Handle them all before triggering response:
      
      ```typescript
      const pendingCalls = new Map<string, Promise<string>>();
      let pendingCount = 0;
      
      const subscription = session.subscribe({
        onResponseFunctionCallArgumentsDone: async (event, context) => {
          pendingCount++;
          
          // Start function execution (don't await)
          const resultPromise = handleFunctionCall(
            event.name,
            JSON.parse(event.arguments)
          );
          pendingCalls.set(event.callId, resultPromise);
        },
      
        onResponseDone: async (event, context) => {
          // Check if there are pending function calls
          if (pendingCalls.size === 0) return;
      
          // Wait for all functions to complete
          for (const [callId, promise] of pendingCalls) {
            const result = await promise;
            await session.addConversationItem({
              type: "function_call_output",
              callId,
              output: result,
            });
          }
      
          pendingCalls.clear();
          
          // Trigger response generation after all results sent
          await session.sendEvent({ type: "response.create" });
        },
      });
      ```
      
      ## Error Handling
      
      ```typescript
      const subscription = session.subscribe({
        onResponseFunctionCallArgumentsDone: async (event, context) => {
          try {
            const args = JSON.parse(event.arguments);
            const result = await handleFunctionCall(event.name, args);
      
            await session.addConversationItem({
              type: "function_call_output",
              callId: event.callId,
              output: result,
            });
          } catch (error) {
            // Send error message so assistant can respond appropriately
            const errorMessage = error instanceof Error ? error.message : String(error);
            
            await session.addConversationItem({
              type: "function_call_output",
              callId: event.callId,
              output: JSON.stringify({
                error: true,
                message: `Function execution failed: ${errorMessage}`,
              }),
            });
          }
      
          await session.sendEvent({ type: "response.create" });
        },
      });
      ```
      
      ## Tool Choice Options
      
      Control how the model selects tools:
      
      ```typescript
      // Auto - model decides when to use tools
      await session.updateSession({ toolChoice: "auto" });
      
      // None - disable tool usage
      await session.updateSession({ toolChoice: "none" });
      
      // Required - model must use a tool
      await session.updateSession({ toolChoice: "required" });
      
      // Force specific function
      await session.updateSession({
        toolChoice: {
          type: "function",
          name: "get_weather",
        },
      });
      ```
      
      ## Conversation Item Types
      
      ```typescript
      // Add user message
      await session.addConversationItem({
        type: "message",
        role: "user",
        content: [{ type: "text", text: "What's the weather in Seattle?" }],
      });
      
      // Add function call output
      await session.addConversationItem({
        type: "function_call_output",
        callId: "call_abc123",
        output: JSON.stringify({ temperature: "72°F", conditions: "Sunny" }),
      });
      
      // Trigger response
      await session.sendEvent({ type: "response.create" });
      ```
      
      ## Best Practices
      
      1. **Return JSON from functions** - Structured data is easier for the model to interpret
      2. **Include error information** - Return `{ error: true, message: "..." }` on failure
      3. **Handle timeouts** - Set reasonable timeouts for external API calls
      4. **Validate arguments** - Check required fields before executing functions
      5. **Use descriptive names** - Function and parameter names help the model understand usage
      6. **Provide examples in description** - E.g., "City and state, e.g., San Francisco, CA"
      7. **Handle parallel calls** - Don't assume functions are called sequentially
      
      ## See Also
      
      - [Audio Streaming Reference](./audio-streaming.md)
      - [SKILL.md](../SKILL.md) - Main skill documentation
      - [Azure Voice Live API Reference](https://learn.microsoft.com/javascript/api/@azure/ai-voicelive)
      
  • SKILL.md 13.5 KB
    ---
    name: azure-ai-voicelive-ts
    description: |
      Azure AI Voice Live SDK for JavaScript/TypeScript. Build real-time voice AI applications with bidirectional WebSocket communication. Use for voice assistants, conversational AI, real-time speech-to-speech, and voice-enabled chatbots in Node.js or browser environments. Triggers: "voice live", "real-time voice", "VoiceLiveClient", "VoiceLiveSession", "voice assistant TypeScript", "bidirectional audio", "speech-to-speech JavaScript".
    license: MIT
    metadata:
      author: Microsoft
      version: "1.0.0"
      package: '@azure/ai-voicelive'
    ---
    
    # @azure/ai-voicelive (JavaScript/TypeScript)
    
    Real-time voice AI SDK for building bidirectional voice assistants with Azure AI in Node.js and browser environments.
    
    ## Installation
    
    ```bash
    npm install @azure/ai-voicelive @azure/identity
    # TypeScript users
    npm install @types/node
    ```
    
    **Current Version**: 1.0.0-beta.3
    
    **Supported Environments**:
    - Node.js LTS versions (20+)
    - Modern browsers (Chrome, Firefox, Safari, Edge)
    
    ## Environment Variables
    
    ```bash
    AZURE_VOICELIVE_ENDPOINT=https://<resource>.cognitiveservices.azure.com
    # Optional: API key if not using Entra ID
    AZURE_VOICELIVE_API_KEY=<your-api-key>
    # Optional: Logging
    AZURE_LOG_LEVEL=info
    AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
    ```
    
    ## Authentication
    
    ### Microsoft Entra Token Credential (Recommended)
    
    ```typescript
    import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
    import { VoiceLiveClient } from "@azure/ai-voicelive";
    
    // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
    const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
    // Or use a specific credential directly in production:
    // See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
    // const credential = new ManagedIdentityCredential();
    const endpoint = "https://your-resource.cognitiveservices.azure.com";
    
    const client = new VoiceLiveClient(endpoint, credential);
    ```
    
    ### API Key
    
    ```typescript
    import { AzureKeyCredential } from "@azure/core-auth";
    import { VoiceLiveClient } from "@azure/ai-voicelive";
    
    const endpoint = "https://your-resource.cognitiveservices.azure.com";
    const credential = new AzureKeyCredential("your-api-key");
    
    const client = new VoiceLiveClient(endpoint, credential);
    ```
    
    ## Client Hierarchy
    
    ```
    VoiceLiveClient
    └── VoiceLiveSession (WebSocket connection)
        ├── updateSession()      → Configure session options
        ├── subscribe()          → Event handlers (Azure SDK pattern)
        ├── sendAudio()          → Stream audio input
        ├── addConversationItem() → Add messages/function outputs
        └── sendEvent()          → Send raw protocol events
    ```
    
    ## Quick Start
    
    ```typescript
    import { DefaultAzureCredential } from "@azure/identity";
    import { VoiceLiveClient } from "@azure/ai-voicelive";
    
    const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
    const endpoint = process.env.AZURE_VOICELIVE_ENDPOINT!;
    
    // Create client and start session
    const client = new VoiceLiveClient(endpoint, credential);
    const session = await client.startSession("gpt-4o-mini-realtime-preview");
    
    // Configure session
    await session.updateSession({
      modalities: ["text", "audio"],
      instructions: "You are a helpful AI assistant. Respond naturally.",
      voice: {
        type: "azure-standard",
        name: "en-US-AvaNeural",
      },
      turnDetection: {
        type: "server_vad",
        threshold: 0.5,
        prefixPaddingMs: 300,
        silenceDurationMs: 500,
      },
      inputAudioFormat: "pcm16",
      outputAudioFormat: "pcm16",
    });
    
    // Subscribe to events
    const subscription = session.subscribe({
      onResponseAudioDelta: async (event, context) => {
        // Handle streaming audio output
        const audioData = event.delta;
        playAudioChunk(audioData);
      },
      onResponseTextDelta: async (event, context) => {
        // Handle streaming text
        process.stdout.write(event.delta);
      },
      onInputAudioTranscriptionCompleted: async (event, context) => {
        console.log("User said:", event.transcript);
      },
    });
    
    // Send audio from microphone
    function sendAudioChunk(audioBuffer: ArrayBuffer) {
      session.sendAudio(audioBuffer);
    }
    ```
    
    ## Session Configuration
    
    ```typescript
    await session.updateSession({
      // Modalities
      modalities: ["audio", "text"],
      
      // System instructions
      instructions: "You are a customer service representative.",
      
      // Voice selection
      voice: {
        type: "azure-standard",  // or "azure-custom", "openai"
        name: "en-US-AvaNeural",
      },
      
      // Turn detection (VAD)
      turnDetection: {
        type: "server_vad",      // or "azure_semantic_vad"
        threshold: 0.5,
        prefixPaddingMs: 300,
        silenceDurationMs: 500,
      },
      
      // Audio formats
      inputAudioFormat: "pcm16",
      outputAudioFormat: "pcm16",
      
      // Tools (function calling)
      tools: [
        {
          type: "function",
          name: "get_weather",
          description: "Get current weather",
          parameters: {
            type: "object",
            properties: {
              location: { type: "string" }
            },
            required: ["location"]
          }
        }
      ],
      toolChoice: "auto",
    });
    ```
    
    ## Event Handling (Azure SDK Pattern)
    
    The SDK uses a subscription-based event handling pattern:
    
    ```typescript
    const subscription = session.subscribe({
      // Connection lifecycle
      onConnected: async (args, context) => {
        console.log("Connected:", args.connectionId);
      },
      onDisconnected: async (args, context) => {
        console.log("Disconnected:", args.code, args.reason);
      },
      onError: async (args, context) => {
        console.error("Error:", args.error.message);
      },
      
      // Session events
      onSessionCreated: async (event, context) => {
        console.log("Session created:", context.sessionId);
      },
      onSessionUpdated: async (event, context) => {
        console.log("Session updated");
      },
      
      // Audio input events (VAD)
      onInputAudioBufferSpeechStarted: async (event, context) => {
        console.log("Speech started at:", event.audioStartMs);
      },
      onInputAudioBufferSpeechStopped: async (event, context) => {
        console.log("Speech stopped at:", event.audioEndMs);
      },
      
      // Transcription events
      onConversationItemInputAudioTranscriptionCompleted: async (event, context) => {
        console.log("User said:", event.transcript);
      },
      onConversationItemInputAudioTranscriptionDelta: async (event, context) => {
        process.stdout.write(event.delta);
      },
      
      // Response events
      onResponseCreated: async (event, context) => {
        console.log("Response started");
      },
      onResponseDone: async (event, context) => {
        console.log("Response complete");
      },
      
      // Streaming text
      onResponseTextDelta: async (event, context) => {
        process.stdout.write(event.delta);
      },
      onResponseTextDone: async (event, context) => {
        console.log("\n--- Text complete ---");
      },
      
      // Streaming audio
      onResponseAudioDelta: async (event, context) => {
        const audioData = event.delta;
        playAudioChunk(audioData);
      },
      onResponseAudioDone: async (event, context) => {
        console.log("Audio complete");
      },
      
      // Audio transcript (what assistant said)
      onResponseAudioTranscriptDelta: async (event, context) => {
        process.stdout.write(event.delta);
      },
      
      // Function calling
      onResponseFunctionCallArgumentsDone: async (event, context) => {
        if (event.name === "get_weather") {
          const args = JSON.parse(event.arguments);
          const result = await getWeather(args.location);
          
          await session.addConversationItem({
            type: "function_call_output",
            callId: event.callId,
            output: JSON.stringify(result),
          });
          
          await session.sendEvent({ type: "response.create" });
        }
      },
      
      // Catch-all for debugging
      onServerEvent: async (event, context) => {
        console.log("Event:", event.type);
      },
    });
    
    // Clean up when done
    await subscription.close();
    ```
    
    ## Function Calling
    
    ```typescript
    // Define tools in session config
    await session.updateSession({
      modalities: ["audio", "text"],
      instructions: "Help users with weather information.",
      tools: [
        {
          type: "function",
          name: "get_weather",
          description: "Get current weather for a location",
          parameters: {
            type: "object",
            properties: {
              location: {
                type: "string",
                description: "City and state or country",
              },
            },
            required: ["location"],
          },
        },
      ],
      toolChoice: "auto",
    });
    
    // Handle function calls
    const subscription = session.subscribe({
      onResponseFunctionCallArgumentsDone: async (event, context) => {
        if (event.name === "get_weather") {
          const args = JSON.parse(event.arguments);
          const weatherData = await fetchWeather(args.location);
          
          // Send function result
          await session.addConversationItem({
            type: "function_call_output",
            callId: event.callId,
            output: JSON.stringify(weatherData),
          });
          
          // Trigger response generation
          await session.sendEvent({ type: "response.create" });
        }
      },
    });
    ```
    
    ## Voice Options
    
    | Voice Type | Config | Example |
    |------------|--------|---------|
    | Azure Standard | `{ type: "azure-standard", name: "..." }` | `"en-US-AvaNeural"` |
    | Azure Custom | `{ type: "azure-custom", name: "...", endpointId: "..." }` | Custom voice endpoint |
    | Azure Personal | `{ type: "azure-personal", speakerProfileId: "..." }` | Personal voice clone |
    | OpenAI | `{ type: "openai", name: "..." }` | `"alloy"`, `"echo"`, `"shimmer"` |
    
    ## Supported Models
    
    | Model | Description | Use Case |
    |-------|-------------|----------|
    | `gpt-4o-realtime-preview` | GPT-4o with real-time audio | High-quality conversational AI |
    | `gpt-4o-mini-realtime-preview` | Lightweight GPT-4o | Fast, efficient interactions |
    | `phi4-mm-realtime` | Phi multimodal | Cost-effective applications |
    
    ## Turn Detection Options
    
    ```typescript
    // Server VAD (default)
    turnDetection: {
      type: "server_vad",
      threshold: 0.5,
      prefixPaddingMs: 300,
      silenceDurationMs: 500,
    }
    
    // Azure Semantic VAD (smarter detection)
    turnDetection: {
      type: "azure_semantic_vad",
    }
    
    // Azure Semantic VAD (English optimized)
    turnDetection: {
      type: "azure_semantic_vad_en",
    }
    
    // Azure Semantic VAD (Multilingual)
    turnDetection: {
      type: "azure_semantic_vad_multilingual",
    }
    ```
    
    ## Audio Formats
    
    | Format | Sample Rate | Use Case |
    |--------|-------------|----------|
    | `pcm16` | 24kHz | Default, high quality |
    | `pcm16-8000hz` | 8kHz | Telephony |
    | `pcm16-16000hz` | 16kHz | Voice assistants |
    | `g711_ulaw` | 8kHz | Telephony (US) |
    | `g711_alaw` | 8kHz | Telephony (EU) |
    
    ## Key Types Reference
    
    | Type | Purpose |
    |------|---------|
    | `VoiceLiveClient` | Main client for creating sessions |
    | `VoiceLiveSession` | Active WebSocket session |
    | `VoiceLiveSessionHandlers` | Event handler interface |
    | `VoiceLiveSubscription` | Active event subscription |
    | `ConnectionContext` | Context for connection events |
    | `SessionContext` | Context for session events |
    | `ServerEventUnion` | Union of all server events |
    
    ## Error Handling
    
    ```typescript
    import {
      VoiceLiveError,
      VoiceLiveConnectionError,
      VoiceLiveAuthenticationError,
      VoiceLiveProtocolError,
    } from "@azure/ai-voicelive";
    
    const subscription = session.subscribe({
      onError: async (args, context) => {
        const { error } = args;
        
        if (error instanceof VoiceLiveConnectionError) {
          console.error("Connection error:", error.message);
        } else if (error instanceof VoiceLiveAuthenticationError) {
          console.error("Auth error:", error.message);
        } else if (error instanceof VoiceLiveProtocolError) {
          console.error("Protocol error:", error.message);
        }
      },
      
      onServerError: async (event, context) => {
        console.error("Server error:", event.error?.message);
      },
    });
    ```
    
    ## Logging
    
    ```typescript
    import { setLogLevel } from "@azure/logger";
    
    // Enable verbose logging
    setLogLevel("info");
    
    // Or via environment variable
    // AZURE_LOG_LEVEL=info
    ```
    
    ## Browser Usage
    
    ```typescript
    // Browser requires bundler (Vite, webpack, etc.)
    import { VoiceLiveClient } from "@azure/ai-voicelive";
    import { InteractiveBrowserCredential } from "@azure/identity";
    
    // Use browser-compatible credential
    const credential = new InteractiveBrowserCredential({
      clientId: "your-client-id",
      tenantId: "your-tenant-id",
    });
    
    const client = new VoiceLiveClient(endpoint, credential);
    
    // Request microphone access
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    const audioContext = new AudioContext({ sampleRate: 24000 });
    
    // Process audio and send to session
    // ... (see samples for full implementation)
    ```
    
    ## Best Practices
    
    1. **Use `DefaultAzureCredential` for local development; use `ManagedIdentityCredential` or `WorkloadIdentityCredential` for production** — Never hardcode API keys
    2. **Set both modalities** — Include `["text", "audio"]` for voice assistants
    3. **Use Azure Semantic VAD** — Better turn detection than basic server VAD
    4. **Handle all error types** — Connection, auth, and protocol errors
    5. **Clean up subscriptions** — Call `subscription.close()` when done
    6. **Use appropriate audio format** — PCM16 at 24kHz for best quality
    
    ## Reference Links
    
    | Resource | URL |
    |----------|-----|
    | npm Package | https://www.npmjs.com/package/@azure/ai-voicelive |
    | GitHub Source | https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/ai/ai-voicelive |
    | Samples | https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/ai/ai-voicelive/samples |
    | API Reference | https://learn.microsoft.com/javascript/api/@azure/ai-voicelive |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related