From 5bb3b41f1ea9c202a0f5eb93e12f458731046294 Mon Sep 17 00:00:00 2001 From: Myka Date: Tue, 17 Feb 2026 11:09:07 +0300 Subject: [PATCH] refactor(voice): introduce Transcriber interface for pluggable STT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently Discord, Slack, and Telegram all hardcode *voice.GroqTranscriber as their transcription dependency. This makes it impossible to swap in a different STT backend without changing each channel file. Add a Transcriber interface to pkg/voice/transcriber.go: type Transcriber interface { Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) IsAvailable() bool } GroqTranscriber already implements this interface (no change to its implementation). Update Discord, Slack, and Telegram to depend on the interface instead of the concrete type. No behaviour change — this is a pure refactor that enables future STT providers (e.g. local Whisper) to be dropped in without modifying channel code. --- pkg/channels/discord.go | 4 ++-- pkg/channels/slack.go | 4 ++-- pkg/channels/telegram.go | 4 ++-- pkg/voice/transcriber.go | 9 +++++++++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pkg/channels/discord.go b/pkg/channels/discord.go index 00aa8ab4d..b15a852a8 100644 --- a/pkg/channels/discord.go +++ b/pkg/channels/discord.go @@ -24,7 +24,7 @@ type DiscordChannel struct { *BaseChannel session *discordgo.Session config config.DiscordConfig - transcriber *voice.GroqTranscriber + transcriber voice.Transcriber ctx context.Context } @@ -45,7 +45,7 @@ func NewDiscordChannel(cfg config.DiscordConfig, bus *bus.MessageBus) (*DiscordC }, nil } -func (c *DiscordChannel) SetTranscriber(transcriber *voice.GroqTranscriber) { +func (c *DiscordChannel) SetTranscriber(transcriber voice.Transcriber) { c.transcriber = transcriber } diff --git a/pkg/channels/slack.go b/pkg/channels/slack.go index 5387e9213..6dc0faccf 100644 --- a/pkg/channels/slack.go +++ b/pkg/channels/slack.go @@ -25,7 +25,7 @@ type SlackChannel struct { api *slack.Client socketClient *socketmode.Client botUserID string - transcriber *voice.GroqTranscriber + transcriber voice.Transcriber ctx context.Context cancel context.CancelFunc pendingAcks sync.Map @@ -58,7 +58,7 @@ func NewSlackChannel(cfg config.SlackConfig, messageBus *bus.MessageBus) (*Slack }, nil } -func (c *SlackChannel) SetTranscriber(transcriber *voice.GroqTranscriber) { +func (c *SlackChannel) SetTranscriber(transcriber voice.Transcriber) { c.transcriber = transcriber } diff --git a/pkg/channels/telegram.go b/pkg/channels/telegram.go index 5601d508c..84407c57f 100644 --- a/pkg/channels/telegram.go +++ b/pkg/channels/telegram.go @@ -30,7 +30,7 @@ type TelegramChannel struct { commands TelegramCommander config *config.Config chatIDs map[string]int64 - transcriber *voice.GroqTranscriber + transcriber voice.Transcriber placeholders sync.Map // chatID -> messageID stopThinking sync.Map // chatID -> thinkingCancel } @@ -80,7 +80,7 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann }, nil } -func (c *TelegramChannel) SetTranscriber(transcriber *voice.GroqTranscriber) { +func (c *TelegramChannel) SetTranscriber(transcriber voice.Transcriber) { c.transcriber = transcriber } diff --git a/pkg/voice/transcriber.go b/pkg/voice/transcriber.go index 9af2ea6bb..62534b13d 100644 --- a/pkg/voice/transcriber.go +++ b/pkg/voice/transcriber.go @@ -16,6 +16,15 @@ import ( "github.com/sipeed/picoclaw/pkg/utils" ) +// Transcriber is the interface for speech-to-text providers. +// Any STT backend (Groq, Whisper, etc.) must implement this. +type Transcriber interface { + // Transcribe converts the audio file at audioFilePath to text. + Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) + // IsAvailable returns true if the provider is configured and reachable. + IsAvailable() bool +} + type GroqTranscriber struct { apiKey string apiBase string