add deepgram provider with nova-3 and nova-2 streaming models

This commit is contained in:
leonardotrapani
2026-02-01 01:43:50 +01:00
parent 29ea33bc9d
commit 5f614b19c5
5 changed files with 219 additions and 2 deletions
+94
View File
@@ -0,0 +1,94 @@
package provider
// DeepgramProvider implements Provider for Deepgram transcription services
type DeepgramProvider struct{}
func (p *DeepgramProvider) Name() string {
return "deepgram"
}
func (p *DeepgramProvider) RequiresAPIKey() bool {
return true
}
func (p *DeepgramProvider) ValidateAPIKey(key string) bool {
// Deepgram API keys are alphanumeric, just check non-empty
return len(key) > 0
}
func (p *DeepgramProvider) IsLocal() bool {
return false
}
func (p *DeepgramProvider) Models() []Model {
// Nova-3 language support - maps to our 57 language list
// from https://developers.deepgram.com/docs/models-languages-overview
nova3Langs := []string{
"ar", "be", "bs", "bg", "ca", "hr", "cs", "da", "nl", "en", "et", "fi",
"fr", "de", "el", "hi", "hu", "id", "it", "ja", "kn", "ko", "lv", "lt",
"mk", "ms", "mr", "no", "pl", "pt", "ro", "ru", "sr", "sk", "sl", "es",
"sv", "tl", "ta", "tr", "uk", "vi",
}
// Nova-2 language support - subset of nova-3
nova2Langs := []string{
"bg", "ca", "zh", "cs", "da", "nl", "en", "et", "fi", "fr", "de", "el",
"hi", "hu", "id", "it", "ja", "ko", "lv", "lt", "ms", "no", "pl", "pt",
"ro", "ru", "sk", "es", "sv", "th", "tr", "uk", "vi",
}
return []Model{
{
ID: "nova-3",
Name: "Nova-3",
Description: "Best accuracy, 40+ languages, real-time",
Type: Transcription,
Streaming: true,
Local: false,
AdapterType: "deepgram",
SupportedLanguages: nova3Langs,
Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"},
},
{
ID: "nova-3-general",
Name: "Nova-3 General",
Description: "General purpose, same as nova-3",
Type: Transcription,
Streaming: true,
Local: false,
AdapterType: "deepgram",
SupportedLanguages: nova3Langs,
Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"},
},
{
ID: "nova-2",
Name: "Nova-2",
Description: "Fast, 30+ languages, filler words",
Type: Transcription,
Streaming: true,
Local: false,
AdapterType: "deepgram",
SupportedLanguages: nova2Langs,
Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"},
},
{
ID: "nova-2-general",
Name: "Nova-2 General",
Description: "General purpose, same as nova-2",
Type: Transcription,
Streaming: true,
Local: false,
AdapterType: "deepgram",
SupportedLanguages: nova2Langs,
Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"},
},
}
}
func (p *DeepgramProvider) DefaultModel(t ModelType) string {
switch t {
case Transcription:
return "nova-3"
}
return ""
}
+110
View File
@@ -0,0 +1,110 @@
package provider
import "testing"
func TestDeepgramProvider(t *testing.T) {
p := GetProvider("deepgram")
if p == nil {
t.Fatal("deepgram provider not registered")
}
if p.Name() != "deepgram" {
t.Errorf("Name() = %q, want %q", p.Name(), "deepgram")
}
if !p.RequiresAPIKey() {
t.Error("RequiresAPIKey() should return true")
}
if p.IsLocal() {
t.Error("IsLocal() should return false")
}
}
func TestDeepgramProvider_Models(t *testing.T) {
p := &DeepgramProvider{}
models := p.Models()
if len(models) != 4 {
t.Errorf("Models() returned %d models, want 4", len(models))
}
// all models should be streaming
for _, m := range models {
if !m.Streaming {
t.Errorf("model %s should be streaming", m.ID)
}
if m.AdapterType != "deepgram" {
t.Errorf("model %s has AdapterType %q, want 'deepgram'", m.ID, m.AdapterType)
}
if m.Local {
t.Errorf("model %s should not be local", m.ID)
}
}
}
func TestDeepgramProvider_Nova3Languages(t *testing.T) {
p := &DeepgramProvider{}
models := p.Models()
var nova3 *Model
for i := range models {
if models[i].ID == "nova-3" {
nova3 = &models[i]
break
}
}
if nova3 == nil {
t.Fatal("nova-3 model not found")
}
// nova-3 should support many languages from our list
supportedTests := []struct {
code string
want bool
}{
{"en", true},
{"es", true},
{"fr", true},
{"de", true},
{"ja", true},
{"", true}, // auto always supported
}
for _, tt := range supportedTests {
got := nova3.SupportsLanguage(tt.code)
if got != tt.want {
t.Errorf("nova-3.SupportsLanguage(%q) = %v, want %v", tt.code, got, tt.want)
}
}
}
func TestDeepgramProvider_DefaultModel(t *testing.T) {
p := &DeepgramProvider{}
if got := p.DefaultModel(Transcription); got != "nova-3" {
t.Errorf("DefaultModel(Transcription) = %q, want 'nova-3'", got)
}
if got := p.DefaultModel(LLM); got != "" {
t.Errorf("DefaultModel(LLM) = %q, want empty (no LLM support)", got)
}
}
func TestDeepgramProvider_Endpoint(t *testing.T) {
p := &DeepgramProvider{}
models := p.Models()
for _, m := range models {
if m.Endpoint == nil {
t.Errorf("model %s has nil Endpoint", m.ID)
continue
}
if m.Endpoint.BaseURL != "wss://api.deepgram.com" {
t.Errorf("model %s has BaseURL %q, want 'wss://api.deepgram.com'", m.ID, m.Endpoint.BaseURL)
}
if m.Endpoint.Path != "/v1/listen" {
t.Errorf("model %s has Path %q, want '/v1/listen'", m.ID, m.Endpoint.Path)
}
}
}
+1
View File
@@ -29,6 +29,7 @@ func init() {
Register(&MistralProvider{}) Register(&MistralProvider{})
Register(&ElevenLabsProvider{}) Register(&ElevenLabsProvider{})
Register(&WhisperCppProvider{}) Register(&WhisperCppProvider{})
Register(&DeepgramProvider{})
} }
// Register adds a provider to the registry // Register adds a provider to the registry
+13 -1
View File
@@ -334,4 +334,16 @@ Started: Sun Feb 1 12:22:47 AM CET 2026
- Updated `SendChunk()` to call reconnect() on write errors, then retry chunk - Updated `SendChunk()` to call reconnect() on write errors, then retry chunk
- After max retries exhausted, sends final error and closes channel - After max retries exhausted, sends final error and closes channel
- Added tests: ReconnectOnReadError, ReconnectNotifiesClient, MaxRetriesExhausted, ReconnectExponentialBackoff - Added tests: ReconnectOnReadError, ReconnectNotifiesClient, MaxRetriesExhausted, ReconnectExponentialBackoff
- All tests passing with -race flag, typecheck passes - All tests passing with -race flag, typecheck passes
### Task 33: Create Deepgram Provider
- Created `internal/provider/deepgram.go` implementing Provider interface
- Researched Deepgram docs: Nova-3 and Nova-2 are main models, both streaming-only
- Models: nova-3, nova-3-general, nova-2, nova-2-general (all Streaming=true)
- Nova-3 supports 42 languages from our list (ar, be, bs, bg, ca, hr, cs, da, nl, en, et, fi, fr, de, el, hi, hu, id, it, ja, kn, ko, lv, lt, mk, ms, mr, no, pl, pt, ro, ru, sr, sk, sl, es, sv, tl, ta, tr, uk, vi)
- Nova-2 supports 33 languages (subset of nova-3)
- All models have AdapterType='deepgram', Endpoint.BaseURL='wss://api.deepgram.com'
- DefaultModel(Transcription) returns 'nova-3'
- Registered in provider.init()
- Comprehensive test file created: deepgram_test.go
- All tests passing, typecheck passes
+1 -1
View File
@@ -783,7 +783,7 @@
"RequiresAPIKey() returns true", "RequiresAPIKey() returns true",
"Typecheck passes" "Typecheck passes"
], ],
"passes": false "passes": true
}, },
{ {
"title": "Create Deepgram StreamingAdapter", "title": "Create Deepgram StreamingAdapter",