From 5565d31be2ff567e860ddfdeb37c10bd41b8a2f7 Mon Sep 17 00:00:00 2001 From: Harshita Jain Date: Wed, 3 Jun 2026 16:59:06 +0530 Subject: [PATCH 1/5] feat: add Smallest AI Pulse to the leaderboard Adds Smallest AI's Pulse model as a new STT provider. Uses the pre-recorded batch API (POST /waves/v1/stt?model=pulse) with octet-stream audio upload and word-level timestamp support. --- .env.example | 1 + prisma/seed.ts | 1 + public/providers/smallest.svg | 4 +++ src/lib/providers.ts | 1 + src/lib/providers/smallest.ts | 59 +++++++++++++++++++++++++++++++++++ src/lib/transcribe.ts | 3 ++ 6 files changed, 69 insertions(+) create mode 100644 public/providers/smallest.svg create mode 100644 src/lib/providers/smallest.ts diff --git a/.env.example b/.env.example index aa97fc8..c3c4ba7 100644 --- a/.env.example +++ b/.env.example @@ -13,3 +13,4 @@ ASSEMBLYAI_API_KEY="" ELEVENLABS_API_KEY="" SPEECHMATICS_API_KEY="" MISTRAL_API_KEY="" +SMALLEST_API_KEY="" diff --git a/prisma/seed.ts b/prisma/seed.ts index ed542ef..501fa2e 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -13,6 +13,7 @@ const providers = [ { name: "AssemblyAI", slug: "assemblyai", logoUrl: "/providers/assemblyai.svg" }, { name: "Speechmatics", slug: "speechmatics", logoUrl: "/providers/speechmatics.svg" }, { name: "Mistral", slug: "mistral", logoUrl: "/providers/mistral.svg" }, + { name: "Smallest AI", slug: "smallest", logoUrl: "/providers/smallest.svg" }, ]; const removedSlugs = ["soniox", "openai-whisper"]; diff --git a/public/providers/smallest.svg b/public/providers/smallest.svg new file mode 100644 index 0000000..5ef2272 --- /dev/null +++ b/public/providers/smallest.svg @@ -0,0 +1,4 @@ + + + S + diff --git a/src/lib/providers.ts b/src/lib/providers.ts index 24373e4..2283649 100644 --- a/src/lib/providers.ts +++ b/src/lib/providers.ts @@ -13,6 +13,7 @@ export const PROVIDERS: ProviderDef[] = [ { name: "AssemblyAI", slug: "assemblyai", logoUrl: "/providers/assemblyai.svg", color: "#0055FF", model: "Universal-3 Pro" }, { name: "Speechmatics", slug: "speechmatics", logoUrl: "/providers/speechmatics.svg", color: "#FF3D00", model: "Enhanced" }, { name: "Mistral", slug: "mistral", logoUrl: "/providers/mistral.svg", color: "#FF7000", model: "Voxtral Mini" }, + { name: "Smallest AI", slug: "smallest", logoUrl: "/providers/smallest.svg", color: "#1D4E52", model: "Pulse" }, ]; export function getProviderBySlug(slug: string): ProviderDef | undefined { diff --git a/src/lib/providers/smallest.ts b/src/lib/providers/smallest.ts new file mode 100644 index 0000000..4da3307 --- /dev/null +++ b/src/lib/providers/smallest.ts @@ -0,0 +1,59 @@ +import type { TranscribeResult, WordTimestamp } from "../transcribe"; + +const SMALLEST_BASE = "https://api.smallest.ai/waves/v1/stt"; + +interface SmallestWord { + word: string; + start: number; + end: number; + confidence?: number; +} + +interface SmallestResponse { + status: string; + transcription?: string; + words?: SmallestWord[]; +} + +export async function transcribeWithSmallest( + audio: Buffer, + _mimeType: string +): Promise { + const apiKey = process.env.SMALLEST_API_KEY; + if (!apiKey) throw new Error("SMALLEST_API_KEY not set"); + + const start = Date.now(); + + const url = new URL(SMALLEST_BASE); + url.searchParams.set("model", "pulse"); + url.searchParams.set("word_timestamps", "true"); + + const response = await fetch(url.toString(), { + method: "POST", + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/octet-stream", + }, + body: audio, + }); + + if (!response.ok) { + throw new Error(`Smallest AI request failed: ${response.status} ${await response.text()}`); + } + + const data: SmallestResponse = await response.json(); + + const words: WordTimestamp[] = (data.words || []).map((w) => ({ + word: w.word, + start: w.start, + end: w.end, + })); + + const durationMs = Date.now() - start; + + return { + transcript: data.transcription ?? "", + words, + durationMs, + }; +} diff --git a/src/lib/transcribe.ts b/src/lib/transcribe.ts index 4430f71..9b55db0 100644 --- a/src/lib/transcribe.ts +++ b/src/lib/transcribe.ts @@ -5,6 +5,7 @@ import { transcribeWithAssemblyAI } from "./providers/assemblyai"; import { transcribeWithElevenLabs } from "./providers/elevenlabs"; import { transcribeWithSpeechmatics } from "./providers/speechmatics"; import { transcribeWithMistral } from "./providers/mistral"; +import { transcribeWithSmallest } from "./providers/smallest"; export interface WordTimestamp { word: string; @@ -28,6 +29,7 @@ const PROVIDER_MAP: Record = { elevenlabs: transcribeWithElevenLabs, speechmatics: transcribeWithSpeechmatics, mistral: transcribeWithMistral, + smallest: transcribeWithSmallest, }; const MAX_RETRIES = 2; @@ -151,6 +153,7 @@ function sanitizeError(message: string, slug: string): string { elevenlabs: "ElevenLabs", speechmatics: "Speechmatics", mistral: "Mistral", + smallest: "Smallest AI", }; const name = providerNames[slug] || slug; From 8cb6e34736418f579c0d6d9b6cda4cba400d3d1d Mon Sep 17 00:00:00 2001 From: Harshita Jain Date: Wed, 3 Jun 2026 17:01:32 +0530 Subject: [PATCH 2/5] chore: switch Smallest AI model from pulse to pulse-pro --- src/lib/providers/smallest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/providers/smallest.ts b/src/lib/providers/smallest.ts index 4da3307..c5f8274 100644 --- a/src/lib/providers/smallest.ts +++ b/src/lib/providers/smallest.ts @@ -25,7 +25,7 @@ export async function transcribeWithSmallest( const start = Date.now(); const url = new URL(SMALLEST_BASE); - url.searchParams.set("model", "pulse"); + url.searchParams.set("model", "pulse-pro"); url.searchParams.set("word_timestamps", "true"); const response = await fetch(url.toString(), { From 81deab3a22af2bb3485c28c11680a7594d30bf68 Mon Sep 17 00:00:00 2001 From: Harshita Jain Date: Wed, 3 Jun 2026 17:04:56 +0530 Subject: [PATCH 3/5] chore: revert to pulse model for multilingual support --- src/lib/providers/smallest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/providers/smallest.ts b/src/lib/providers/smallest.ts index c5f8274..4da3307 100644 --- a/src/lib/providers/smallest.ts +++ b/src/lib/providers/smallest.ts @@ -25,7 +25,7 @@ export async function transcribeWithSmallest( const start = Date.now(); const url = new URL(SMALLEST_BASE); - url.searchParams.set("model", "pulse-pro"); + url.searchParams.set("model", "pulse"); url.searchParams.set("word_timestamps", "true"); const response = await fetch(url.toString(), { From c291f1494338c755cd9cc6cd572e1ac2bc50016a Mon Sep 17 00:00:00 2001 From: Harshita Jain Date: Wed, 3 Jun 2026 17:06:44 +0530 Subject: [PATCH 4/5] fix: replace placeholder logo with actual Smallest AI logomark --- public/providers/smallest.svg | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/public/providers/smallest.svg b/public/providers/smallest.svg index 5ef2272..c0d449a 100644 --- a/public/providers/smallest.svg +++ b/public/providers/smallest.svg @@ -1,4 +1,18 @@ - - - S + + + + + + + + + + + + + + + + + From 9bc117da2c0cbdf91c1273df18ac16ae3308dc7d Mon Sep 17 00:00:00 2001 From: Harshita Jain Date: Fri, 5 Jun 2026 16:13:26 +0530 Subject: [PATCH 5/5] fix: prefix error with HTTP status so isTransientError retries 429/5xx Co-Authored-By: Claude Sonnet 4.6 --- src/lib/providers/smallest.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/providers/smallest.ts b/src/lib/providers/smallest.ts index 4da3307..1a0938c 100644 --- a/src/lib/providers/smallest.ts +++ b/src/lib/providers/smallest.ts @@ -38,7 +38,8 @@ export async function transcribeWithSmallest( }); if (!response.ok) { - throw new Error(`Smallest AI request failed: ${response.status} ${await response.text()}`); + const body = await response.text(); + throw new Error(`HTTP ${response.status}: Smallest AI request failed: ${body}`); } const data: SmallestResponse = await response.json();