The SQL pipeline is fully wired. All it needs is an edge function that takes an audio blob, generates a 192-dim speaker embedding, and calls register_voice_fingerprint or check_voice_against_banned. Here's your vendor options.
| Vendor | Per-request cost | Embedding dim | Quality | Verdict |
|---|---|---|---|---|
| Azure Speaker Recognition | ~$0.004/req | Internal (use Verify API instead of raw embedding) | Excellent | Recommended — cheapest, Microsoft-hosted, BAA available for HIPAA |
| Resemble AI | ~$0.01/req | 256 (trim/pad to 192) | Very good | Good alternative, simpler API |
| pyannote-audio (self-hosted) | Your compute | 192 native | Excellent | Free but operational load |
| ElevenLabs Voice ID | ~$0.015/req | Internal | Great | Premium; overkill for this use |
| SpeechBrain ECAPA-TDNN | Your compute | 192 native | Research SOTA | Free, fits in Cloudflare Workers AI |
Azure's Speaker Recognition returns a verification score directly (no raw embedding exposure), so you'd adapt: call their API, take the returned score, and use it as similarity. For our pgvector-based approach, use a vendor that returns raw embeddings (Resemble or pyannote).
Cloudflare has an AI runtime that can host ONNX models. ECAPA-TDNN exported to ONNX is ~30MB, runs in ~200ms per call, and produces native 192-dim embeddings. Free in Workers AI for modest volume. Your Cloudflare account already has Workers set up.
// supabase/functions/voice-embed/index.ts
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
Deno.serve(async (req) => {
const { audio_url, subject_kind, subject_id, display_name, phone,
source, source_event_id, resident_state, mode } = await req.json();
// 1. Fetch the audio blob
const audioRes = await fetch(audio_url);
const audioBlob = await audioRes.blob();
// 2. Generate embedding (swap in whichever vendor you pick)
const embeddingVendorRes = await fetch("https://YOUR_VENDOR/embed", {
method: "POST",
headers: { "Authorization": `Bearer ${Deno.env.get("VOICE_API_KEY")}` },
body: audioBlob
});
const { embedding } = await embeddingVendorRes.json(); // array of 192 floats
const sb = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);
// 3. Two modes: register new baseline, or check against banned
if (mode === "check") {
const { data } = await sb.rpc("check_voice_against_banned", {
p_embedding: embedding,
p_calling_phone: phone,
p_caller_name: display_name,
p_call_event_id: source_event_id,
p_threshold: 0.85
});
return new Response(JSON.stringify(data));
}
// Register as new baseline
const { data } = await sb.rpc("register_voice_fingerprint", {
p_embedding: embedding,
p_subject_kind: subject_kind,
p_subject_id: subject_id,
p_display_name: display_name,
p_phone: phone,
p_source: source,
p_source_audio_url: audio_url,
p_source_event_id: source_event_id,
p_consent_method: "tos_acceptance",
p_resident_state: resident_state || "TX",
p_embedding_model: "azure-speaker-recognition-v2"
});
return new Response(JSON.stringify(data));
});
When a call completes, Twilio gives you a recording URL. At the end of nana-voice:
// After the call finishes, async fire-and-forget:
const firstCallForThisCaretaker = /* check daisy_calls table */;
const mode = firstCallForThisCaretaker ? "register" : "check";
fetch(`${SB}/functions/v1/voice-embed`, {
method: "POST", headers: { Authorization: `Bearer ${SERVICE_KEY}` },
body: JSON.stringify({
audio_url: twilioRecordingUrl,
subject_kind: "daisy_caretaker",
subject_id: caretakerId,
display_name: caretakerName,
phone: fromNumber,
source: "daisy_phone_call",
source_event_id: callSid,
resident_state: customerState,
mode
})
});
The log_driver_sos RPC already uploads audio. Add a follow-up call:
// In /drive triggerSos() after the audio upload completes:
await fetch(`${SB}/functions/v1/voice-embed`, {
method: "POST", headers: { apikey: KEY, Authorization: `Bearer ${KEY}` },
body: JSON.stringify({
audio_url: audioUrl,
subject_kind: "unknown_caller", // it's the hostile party, not the driver
display_name: "SOS incident — aggressor",
source: "sos_clip",
source_event_id: runId,
resident_state: "TX",
mode: "check" // see if this voice is banned
})
});
check_voice_against_banned only files a critical trust signal and SMSes ops. A human clicks "Ban this voice" in /ops Voice Lab.voice_fingerprint_allowed_for_state() blocks processing for IL / CA / WA / NY residents unless they explicitly opt in at signup.daisy_voice_fingerprints with pgvector HNSW cosine index)voice_fingerprint_match added to trust systemvoice-embed (this guide)nana-voice and /drive SOS flow