mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 20:15:42 +00:00
Add Transcribe() method to Manager with STT provider dispatch and channel-specific overrides. Implements channelSTTOverrides map, WithChannel context propagation, RegisterChannelSTT registration, and resolveSTTChain fallback logic. Includes manager-level unit tests.
89 lines
2.9 KiB
Go
89 lines
2.9 KiB
Go
package audio
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"log/slog"
|
|
)
|
|
|
|
// ErrAllSTTProvidersFailed is returned when every provider in the chain fails.
|
|
var ErrAllSTTProvidersFailed = errors.New("all STT providers failed")
|
|
|
|
// defaultSTTChain is the built-in fallback order when no explicit chain is set.
|
|
var defaultSTTChain = []string{"elevenlabs", "proxy"}
|
|
|
|
// Transcribe tries providers in chain order. Returns first success.
|
|
// Wraps last error with ErrAllSTTProvidersFailed on total failure.
|
|
func (m *Manager) Transcribe(ctx context.Context, in STTInput, opts STTOptions) (*TranscriptResult, error) {
|
|
chain := m.resolveSTTChain(ctx)
|
|
if len(chain) == 0 {
|
|
return nil, fmt.Errorf("%w: chain is empty", ErrAllSTTProvidersFailed)
|
|
}
|
|
|
|
var lastErr error
|
|
for _, name := range chain {
|
|
p, ok := m.sttProviders[name]
|
|
if !ok {
|
|
slog.Warn("audio.stt provider not registered, skipping", "provider", name)
|
|
continue
|
|
}
|
|
res, err := p.Transcribe(ctx, in, opts)
|
|
if err == nil {
|
|
return res, nil
|
|
}
|
|
slog.Warn("audio.stt provider failed", "provider", name, "error", err)
|
|
lastErr = err
|
|
}
|
|
|
|
if lastErr != nil {
|
|
return nil, fmt.Errorf("%w: %w", ErrAllSTTProvidersFailed, lastErr)
|
|
}
|
|
return nil, fmt.Errorf("%w: no providers matched in chain %v", ErrAllSTTProvidersFailed, chain)
|
|
}
|
|
|
|
// SetSTTChain sets an explicit provider order for STT dispatch.
|
|
// Call before first use; not thread-safe after concurrent Transcribe calls.
|
|
func (m *Manager) SetSTTChain(chain []string) {
|
|
m.sttChain = chain
|
|
}
|
|
|
|
// RegisterChannelSTT registers STT providers scoped to a specific channel name
|
|
// (e.g. "telegram"). Channel-scoped providers take precedence over the manager
|
|
// default chain when resolveSTTChain detects a matching channel in ctx.
|
|
func (m *Manager) RegisterChannelSTT(channelName string, providers ...STTProvider) {
|
|
if m.channelSTTOverrides == nil {
|
|
m.channelSTTOverrides = make(map[string][]string)
|
|
}
|
|
names := make([]string, 0, len(providers))
|
|
for _, p := range providers {
|
|
m.sttProviders[p.Name()+":"+channelName] = p
|
|
names = append(names, p.Name()+":"+channelName)
|
|
}
|
|
m.channelSTTOverrides[channelName] = names
|
|
}
|
|
|
|
// resolveSTTChain returns the ordered provider names for the current call.
|
|
// Precedence: (1) channel override from ctx, (2) explicit m.sttChain,
|
|
// (3) defaultSTTChain filtered to registered providers.
|
|
func (m *Manager) resolveSTTChain(ctx context.Context) []string {
|
|
// (1) Channel override: check if ctx carries a channel name.
|
|
if ch := channelFromCtx(ctx); ch != "" {
|
|
if overrides, ok := m.channelSTTOverrides[ch]; ok && len(overrides) > 0 {
|
|
return overrides
|
|
}
|
|
}
|
|
// (2) Explicit chain set via SetSTTChain.
|
|
if len(m.sttChain) > 0 {
|
|
return m.sttChain
|
|
}
|
|
// (3) Default: elevenlabs → proxy, filtered to what's registered.
|
|
var out []string
|
|
for _, name := range defaultSTTChain {
|
|
if _, ok := m.sttProviders[name]; ok {
|
|
out = append(out, name)
|
|
}
|
|
}
|
|
return out
|
|
}
|