From 811ca71c0883c08d05e7fe18d099a78a3de02ef4 Mon Sep 17 00:00:00 2001 From: leonardotrapani Date: Sun, 1 Feb 2026 13:31:53 +0100 Subject: [PATCH] add DocsURL field to Model struct for language support documentation --- internal/provider/deepgram.go | 6 +++++ internal/provider/elevenlabs.go | 5 +++++ internal/provider/groq.go | 4 ++++ internal/provider/mistral.go | 3 +++ internal/provider/model.go | 1 + internal/provider/model_test.go | 38 ++++++++++++++++++++++++++++++++ internal/provider/openai.go | 6 +++++ internal/provider/whisper_cpp.go | 2 ++ progress.txt | 13 +++++++++++ tasks/prd.jsonc | 2 +- 10 files changed, 79 insertions(+), 1 deletion(-) diff --git a/internal/provider/deepgram.go b/internal/provider/deepgram.go index 563d50f..ab5c0da 100644 --- a/internal/provider/deepgram.go +++ b/internal/provider/deepgram.go @@ -37,6 +37,8 @@ func (p *DeepgramProvider) Models() []Model { "ro", "ru", "sk", "es", "sv", "th", "tr", "uk", "vi", } + docsURL := "https://developers.deepgram.com/docs/language" + return []Model{ { ID: "nova-3", @@ -48,6 +50,7 @@ func (p *DeepgramProvider) Models() []Model { AdapterType: "deepgram", SupportedLanguages: nova3Langs, Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"}, + DocsURL: docsURL, }, { ID: "nova-3-general", @@ -59,6 +62,7 @@ func (p *DeepgramProvider) Models() []Model { AdapterType: "deepgram", SupportedLanguages: nova3Langs, Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"}, + DocsURL: docsURL, }, { ID: "nova-2", @@ -70,6 +74,7 @@ func (p *DeepgramProvider) Models() []Model { AdapterType: "deepgram", SupportedLanguages: nova2Langs, Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"}, + DocsURL: docsURL, }, { ID: "nova-2-general", @@ -81,6 +86,7 @@ func (p *DeepgramProvider) Models() []Model { AdapterType: "deepgram", SupportedLanguages: nova2Langs, Endpoint: &EndpointConfig{BaseURL: "wss://api.deepgram.com", Path: "/v1/listen"}, + DocsURL: docsURL, }, } } diff --git a/internal/provider/elevenlabs.go b/internal/provider/elevenlabs.go index a3e6d42..909522c 100644 --- a/internal/provider/elevenlabs.go +++ b/internal/provider/elevenlabs.go @@ -26,6 +26,7 @@ func (p *ElevenLabsProvider) Models() []Model { // ElevenLabs Scribe supports 90+ languages, including all 57 from our master list // See: https://elevenlabs.io/speech-to-text allLangs := language.AllLanguageCodes() + docsURL := "https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages" return []Model{ // batch models @@ -39,6 +40,7 @@ func (p *ElevenLabsProvider) Models() []Model { AdapterType: "elevenlabs", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.elevenlabs.io", Path: "/v1/speech-to-text"}, + DocsURL: docsURL, }, { ID: "scribe_v2", @@ -50,6 +52,7 @@ func (p *ElevenLabsProvider) Models() []Model { AdapterType: "elevenlabs", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.elevenlabs.io", Path: "/v1/speech-to-text"}, + DocsURL: docsURL, }, // streaming models { @@ -62,6 +65,7 @@ func (p *ElevenLabsProvider) Models() []Model { AdapterType: "elevenlabs-streaming", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"}, + DocsURL: docsURL, }, { ID: "scribe_v2-streaming", @@ -73,6 +77,7 @@ func (p *ElevenLabsProvider) Models() []Model { AdapterType: "elevenlabs-streaming", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "wss://api.elevenlabs.io", Path: "/v1/speech-to-text/realtime"}, + DocsURL: docsURL, }, } } diff --git a/internal/provider/groq.go b/internal/provider/groq.go index 8d5ef38..f205d70 100644 --- a/internal/provider/groq.go +++ b/internal/provider/groq.go @@ -27,6 +27,7 @@ func (p *GroqProvider) IsLocal() bool { func (p *GroqProvider) Models() []Model { allLangs := language.AllLanguageCodes() + docsURL := "https://console.groq.com/docs/speech-to-text#supported-languages" return []Model{ // transcription models @@ -40,6 +41,7 @@ func (p *GroqProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "whisper-large-v3-turbo", @@ -51,6 +53,7 @@ func (p *GroqProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "distil-whisper-large-v3-en", @@ -62,6 +65,7 @@ func (p *GroqProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: []string{"en"}, Endpoint: &EndpointConfig{BaseURL: "https://api.groq.com/openai", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, // LLM models { diff --git a/internal/provider/mistral.go b/internal/provider/mistral.go index 9bd28b8..e7f2f74 100644 --- a/internal/provider/mistral.go +++ b/internal/provider/mistral.go @@ -24,6 +24,7 @@ func (p *MistralProvider) IsLocal() bool { func (p *MistralProvider) Models() []Model { allLangs := language.AllLanguageCodes() + docsURL := "https://docs.mistral.ai/capabilities/speech/" return []Model{ { @@ -36,6 +37,7 @@ func (p *MistralProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.mistral.ai", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "voxtral-mini-2507", @@ -47,6 +49,7 @@ func (p *MistralProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.mistral.ai", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, } } diff --git a/internal/provider/model.go b/internal/provider/model.go index 24bc24c..061cf71 100644 --- a/internal/provider/model.go +++ b/internal/provider/model.go @@ -22,6 +22,7 @@ type Model struct { SupportedLanguages []string // explicit list of supported language codes Endpoint *EndpointConfig // nil for local models LocalInfo *LocalModelInfo // nil for cloud models + DocsURL string // URL to provider's language support documentation } // EndpointConfig holds HTTP/WebSocket endpoint configuration diff --git a/internal/provider/model_test.go b/internal/provider/model_test.go index d8f59f7..df60b1f 100644 --- a/internal/provider/model_test.go +++ b/internal/provider/model_test.go @@ -296,6 +296,7 @@ func TestModel_AllFields(t *testing.T) { Size: "100MB", DownloadURL: "https://example.com/test.bin", }, + DocsURL: "https://example.com/docs/languages", } if model.ID != "test-model" { @@ -328,4 +329,41 @@ func TestModel_AllFields(t *testing.T) { if model.LocalInfo == nil { t.Error("LocalInfo should not be nil") } + if model.DocsURL != "https://example.com/docs/languages" { + t.Errorf("DocsURL = %q, want 'https://example.com/docs/languages'", model.DocsURL) + } +} + +func TestAllTranscriptionModels_HaveDocsURL(t *testing.T) { + // verify all transcription models have DocsURL set + providers := []string{"openai", "groq", "mistral", "elevenlabs", "deepgram", "whisper-cpp"} + + expectedDocsURLs := map[string]string{ + "openai": "https://platform.openai.com/docs/guides/speech-to-text#supported-languages", + "groq": "https://console.groq.com/docs/speech-to-text#supported-languages", + "mistral": "https://docs.mistral.ai/capabilities/speech/", + "elevenlabs": "https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages", + "deepgram": "https://developers.deepgram.com/docs/language", + "whisper-cpp": "https://github.com/openai/whisper#available-models-and-languages", + } + + for _, pName := range providers { + p := GetProvider(pName) + if p == nil { + t.Errorf("GetProvider(%q) returned nil", pName) + continue + } + + expectedURL := expectedDocsURLs[pName] + for _, m := range p.Models() { + if m.Type != Transcription { + continue + } + if m.DocsURL == "" { + t.Errorf("%s/%s: DocsURL is empty", pName, m.ID) + } else if m.DocsURL != expectedURL { + t.Errorf("%s/%s: DocsURL = %q, want %q", pName, m.ID, m.DocsURL, expectedURL) + } + } + } } diff --git a/internal/provider/openai.go b/internal/provider/openai.go index 0d51e7c..a17bb07 100644 --- a/internal/provider/openai.go +++ b/internal/provider/openai.go @@ -28,6 +28,8 @@ func (p *OpenAIProvider) IsLocal() bool { func (p *OpenAIProvider) Models() []Model { allLangs := language.AllLanguageCodes() + docsURL := "https://platform.openai.com/docs/guides/speech-to-text#supported-languages" + return []Model{ // transcription models { @@ -40,6 +42,7 @@ func (p *OpenAIProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "gpt-4o-transcribe", @@ -51,6 +54,7 @@ func (p *OpenAIProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "gpt-4o-mini-transcribe", @@ -62,6 +66,7 @@ func (p *OpenAIProvider) Models() []Model { AdapterType: "openai", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "https://api.openai.com", Path: "/v1/audio/transcriptions"}, + DocsURL: docsURL, }, { ID: "gpt-4o-realtime-preview", @@ -73,6 +78,7 @@ func (p *OpenAIProvider) Models() []Model { AdapterType: "openai-realtime", SupportedLanguages: allLangs, Endpoint: &EndpointConfig{BaseURL: "wss://api.openai.com", Path: "/v1/realtime"}, + DocsURL: docsURL, }, // LLM models { diff --git a/internal/provider/whisper_cpp.go b/internal/provider/whisper_cpp.go index 566419e..c1efb0d 100644 --- a/internal/provider/whisper_cpp.go +++ b/internal/provider/whisper_cpp.go @@ -27,6 +27,7 @@ func (p *WhisperCppProvider) IsLocal() bool { func (p *WhisperCppProvider) Models() []Model { allLangs := language.AllLanguageCodes() englishOnly := []string{"en"} + docsURL := "https://github.com/openai/whisper#available-models-and-languages" whisperModels := whisper.ListModels() result := make([]Model, 0, len(whisperModels)) @@ -54,6 +55,7 @@ func (p *WhisperCppProvider) Models() []Model { Size: wm.Size, DownloadURL: whisper.GetDownloadURL(wm.ID), }, + DocsURL: docsURL, }) } diff --git a/progress.txt b/progress.txt index ad47bf7..79ef6ab 100644 --- a/progress.txt +++ b/progress.txt @@ -571,4 +571,17 @@ Started: Sun Feb 1 12:22:47 AM CET 2026 - Default selection skips headers to find first real model - Providers with only one type (e.g., Groq=batch, Deepgram=streaming) show no headers - Added unit tests: GroupsModels, NoHeadersForSingleType, OpenAI_GroupsCorrectly +- All tests passing, typecheck passes + +### Task 9: Add docs URLs to provider models +- Added `DocsURL string` field to Model struct in internal/provider/model.go +- Updated all 6 providers to set DocsURL for transcription models: + - OpenAI: https://platform.openai.com/docs/guides/speech-to-text#supported-languages + - Groq: https://console.groq.com/docs/speech-to-text#supported-languages + - Mistral: https://docs.mistral.ai/capabilities/speech/ + - ElevenLabs: https://elevenlabs.io/docs/capabilities/speech-to-text#supported-languages + - Deepgram: https://developers.deepgram.com/docs/language + - whisper-cpp: https://github.com/openai/whisper#available-models-and-languages +- LLM models don't have DocsURL (not needed - no language restrictions) +- Added TestAllTranscriptionModels_HaveDocsURL test verifying all transcription models have correct URLs - All tests passing, typecheck passes \ No newline at end of file diff --git a/tasks/prd.jsonc b/tasks/prd.jsonc index 12d0c68..9406721 100644 --- a/tasks/prd.jsonc +++ b/tasks/prd.jsonc @@ -150,7 +150,7 @@ "URLs point to correct language support documentation", "Typecheck passes" ], - "passes": false + "passes": true }, { "title": "Improve language-model compatibility error messages",