diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index b9a4ae7..6df726c 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -122,7 +122,7 @@ func MockAudioFrame(data []byte) recording.AudioFrame { } } -// MockTranscriberAdapter implements transcriber.TranscriptionAdapter for testing +// MockTranscriberAdapter implements transcriber.BatchAdapter for testing type MockTranscriberAdapter struct { TranscribeFunc func(ctx context.Context, audioData []byte) (string, error) } diff --git a/internal/transcriber/adapter_elevenlabs.go b/internal/transcriber/adapter_elevenlabs.go index 90092c6..2207805 100644 --- a/internal/transcriber/adapter_elevenlabs.go +++ b/internal/transcriber/adapter_elevenlabs.go @@ -12,7 +12,7 @@ import ( "time" ) -// ElevenLabsAdapter implements TranscriptionAdapter for ElevenLabs Scribe API +// ElevenLabsAdapter implements BatchAdapter for ElevenLabs Scribe API type ElevenLabsAdapter struct { client *http.Client config Config diff --git a/internal/transcriber/adapter_groq_transcription.go b/internal/transcriber/adapter_groq_transcription.go index 9511ccc..54d0fe0 100644 --- a/internal/transcriber/adapter_groq_transcription.go +++ b/internal/transcriber/adapter_groq_transcription.go @@ -11,7 +11,7 @@ import ( "github.com/sashabaranov/go-openai" ) -// GroqTranscriptionAdapter implements TranscriptionAdapter for Groq Whisper API +// GroqTranscriptionAdapter implements BatchAdapter for Groq Whisper API type GroqTranscriptionAdapter struct { client *openai.Client config Config diff --git a/internal/transcriber/adapter_groq_translation.go b/internal/transcriber/adapter_groq_translation.go index 05afc9f..4e6a2f2 100644 --- a/internal/transcriber/adapter_groq_translation.go +++ b/internal/transcriber/adapter_groq_translation.go @@ -11,7 +11,7 @@ import ( "github.com/sashabaranov/go-openai" ) -// GroqTranslationAdapter implements TranscriptionAdapter for Groq Translation API +// GroqTranslationAdapter implements BatchAdapter for Groq Translation API // Translates audio to English text. The Language field in config hints at the source language. type GroqTranslationAdapter struct { client *openai.Client diff --git a/internal/transcriber/adapter_mistral.go b/internal/transcriber/adapter_mistral.go index 518a30d..4d8d0a9 100644 --- a/internal/transcriber/adapter_mistral.go +++ b/internal/transcriber/adapter_mistral.go @@ -10,7 +10,7 @@ import ( "github.com/sashabaranov/go-openai" ) -// MistralAdapter implements TranscriptionAdapter for Mistral Voxtral API +// MistralAdapter implements BatchAdapter for Mistral Voxtral API type MistralAdapter struct { client *openai.Client config Config diff --git a/internal/transcriber/adapter_openai.go b/internal/transcriber/adapter_openai.go index 68622c0..12887fd 100644 --- a/internal/transcriber/adapter_openai.go +++ b/internal/transcriber/adapter_openai.go @@ -11,7 +11,7 @@ import ( "github.com/sashabaranov/go-openai" ) -// OpenAIAdapter implements TranscriptionAdapter for OpenAI Whisper API +// OpenAIAdapter implements BatchAdapter for OpenAI Whisper API type OpenAIAdapter struct { client *openai.Client config Config diff --git a/internal/transcriber/simple_transcriber.go b/internal/transcriber/simple_transcriber.go index 53a7da4..296a9df 100644 --- a/internal/transcriber/simple_transcriber.go +++ b/internal/transcriber/simple_transcriber.go @@ -11,7 +11,7 @@ import ( // SimpleTranscriber collects all audio and transcribes when stopped type SimpleTranscriber struct { - adapter TranscriptionAdapter + adapter BatchAdapter config Config // Audio collection @@ -27,7 +27,7 @@ type SimpleTranscriber struct { transcriptionText string } -func NewSimpleTranscriber(config Config, adapter TranscriptionAdapter) *SimpleTranscriber { +func NewSimpleTranscriber(config Config, adapter BatchAdapter) *SimpleTranscriber { return &SimpleTranscriber{ adapter: adapter, config: config, diff --git a/internal/transcriber/streaming.go b/internal/transcriber/streaming.go new file mode 100644 index 0000000..8d5d734 --- /dev/null +++ b/internal/transcriber/streaming.go @@ -0,0 +1,25 @@ +package transcriber + +import "context" + +// TranscriptionResult represents a single transcription result from a streaming adapter +type TranscriptionResult struct { + Text string // the transcription text (partial or final) + IsFinal bool // true if this is a final result, false for interim results + Error error // non-nil if an error occurred +} + +// StreamingAdapter interface for streaming transcription backends (send audio in real-time) +type StreamingAdapter interface { + // Start initiates the streaming connection with the given language setting + Start(ctx context.Context, language string) error + + // SendChunk sends a chunk of audio data to the transcription service + SendChunk(audio []byte) error + + // Results returns a channel that receives transcription results (partial and final) + Results() <-chan TranscriptionResult + + // Close gracefully closes the streaming connection + Close() error +} diff --git a/internal/transcriber/transcriber.go b/internal/transcriber/transcriber.go index 55428bb..a321fa3 100644 --- a/internal/transcriber/transcriber.go +++ b/internal/transcriber/transcriber.go @@ -14,8 +14,8 @@ type Transcriber interface { GetFinalTranscription() (string, error) } -// Adapter interface for different transcription backends -type TranscriptionAdapter interface { +// BatchAdapter interface for batch transcription backends (collect all audio, transcribe at end) +type BatchAdapter interface { Transcribe(ctx context.Context, audioData []byte) (string, error) } @@ -31,7 +31,7 @@ type Config struct { // NewTranscriber creates a new simple transcriber func NewTranscriber(config Config) (Transcriber, error) { // Create the appropriate adapter - var adapter TranscriptionAdapter + var adapter BatchAdapter switch config.Provider { case "openai": diff --git a/internal/transcriber/transcriber_test.go b/internal/transcriber/transcriber_test.go index fa552a6..3e1adf0 100644 --- a/internal/transcriber/transcriber_test.go +++ b/internal/transcriber/transcriber_test.go @@ -195,12 +195,12 @@ func TestConfig(t *testing.T) { } } -// MockTranscriptionAdapter implements TranscriptionAdapter for testing -type MockTranscriptionAdapter struct { +// MockBatchAdapter implements BatchAdapter for testing +type MockBatchAdapter struct { TranscribeFunc func(ctx context.Context, audioData []byte) (string, error) } -func (m *MockTranscriptionAdapter) Transcribe(ctx context.Context, audioData []byte) (string, error) { +func (m *MockBatchAdapter) Transcribe(ctx context.Context, audioData []byte) (string, error) { if m.TranscribeFunc != nil { return m.TranscribeFunc(ctx, audioData) } @@ -215,7 +215,7 @@ func TestSimpleTranscriber_Start(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{} + adapter := &MockBatchAdapter{} transcriber := NewSimpleTranscriber(config, adapter) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -255,7 +255,7 @@ func TestSimpleTranscriber_Stop(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{} + adapter := &MockBatchAdapter{} transcriber := NewSimpleTranscriber(config, adapter) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) @@ -299,7 +299,7 @@ func TestSimpleTranscriber_GetFinalTranscription(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{ + adapter := &MockBatchAdapter{ TranscribeFunc: func(ctx context.Context, audioData []byte) (string, error) { return "test transcription", nil }, @@ -327,7 +327,7 @@ func TestSimpleTranscriber_CollectAudio(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{} + adapter := &MockBatchAdapter{} transcriber := NewSimpleTranscriber(config, adapter) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) @@ -411,7 +411,7 @@ func TestSimpleTranscriber_TranscribeAll(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{ + adapter := &MockBatchAdapter{ TranscribeFunc: func(ctx context.Context, audioData []byte) (string, error) { return tt.mockResult, tt.mockError }, @@ -452,7 +452,7 @@ func TestNewSimpleTranscriber(t *testing.T) { Model: "whisper-1", } - adapter := &MockTranscriptionAdapter{} + adapter := &MockBatchAdapter{} transcriber := NewSimpleTranscriber(config, adapter) if transcriber == nil { @@ -478,7 +478,7 @@ func TestNewSimpleTranscriber(t *testing.T) { } func TestTranscriptionAdapter(t *testing.T) { - adapter := &MockTranscriptionAdapter{ + adapter := &MockBatchAdapter{ TranscribeFunc: func(ctx context.Context, audioData []byte) (string, error) { return "test result", nil }, diff --git a/progress.txt b/progress.txt index a8776ca..b0864a0 100644 --- a/progress.txt +++ b/progress.txt @@ -40,3 +40,13 @@ Started: Sun Feb 1 12:22:47 AM CET 2026 - Updated TUI files to use ModelsOfType instead of old SupportsTranscription/SupportsLLM - Added comprehensive tests for all new helper functions - All tests passing, typecheck passes + +### Task 5: Define BatchAdapter and StreamingAdapter interfaces +- Renamed `TranscriptionAdapter` to `BatchAdapter` in transcriber.go +- Updated all adapters (openai, groq, mistral, elevenlabs) to reference BatchAdapter in comments +- Updated SimpleTranscriber to use BatchAdapter +- Updated test mocks (MockTranscriptionAdapter -> MockBatchAdapter) +- Created `internal/transcriber/streaming.go` with: + - `TranscriptionResult` struct: Text, IsFinal, Error fields + - `StreamingAdapter` interface: Start, SendChunk, Results, Close methods +- All tests passing, typecheck passes diff --git a/tasks/prd.jsonc b/tasks/prd.jsonc index 9516ae2..d2366de 100644 --- a/tasks/prd.jsonc +++ b/tasks/prd.jsonc @@ -129,7 +129,7 @@ "TranscriptionResult has Text, IsFinal, Error fields", "Typecheck passes" ], - "passes": false + "passes": true }, { "title": "Create StreamingTranscriber wrapper",