mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 08:15:29 +00:00
Implement STT proxy provider that wraps arbitrary HTTP-based STT services. Ported 12 test cases from legacy Telegram STT integration to validate multipart/form-data submission, language/model overrides, and API error handling.
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
// Package proxy_stt wraps internal/channels/media.TranscribeAudio as an
|
|
// audio.STTProvider, preserving all existing proxy behavior for backward compat.
|
|
package proxy_stt
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/audio"
|
|
"github.com/nextlevelbuilder/goclaw/internal/channels/media"
|
|
)
|
|
|
|
// Provider wraps media.TranscribeAudio as an audio.STTProvider.
|
|
// Preserves all proxy behaviors (Bearer auth, tenant_id field, semaphore, etc.)
|
|
// so channels can fall back to the proxy without any behavior change.
|
|
type Provider struct {
|
|
cfg media.STTConfig
|
|
}
|
|
|
|
// NewProvider returns a proxy STT provider backed by media.TranscribeAudio.
|
|
func NewProvider(cfg media.STTConfig) *Provider {
|
|
return &Provider{cfg: cfg}
|
|
}
|
|
|
|
// Name returns the stable provider identifier.
|
|
func (p *Provider) Name() string { return "proxy" }
|
|
|
|
// Transcribe delegates to media.TranscribeAudio. When in.FilePath is empty but
|
|
// in.Bytes is present, writes a 0600 temp file and defers cleanup.
|
|
// Returns ("", nil) when proxy URL is empty or filePath is empty (proxy no-op).
|
|
func (p *Provider) Transcribe(ctx context.Context, in audio.STTInput, _ audio.STTOptions) (*audio.TranscriptResult, error) {
|
|
filePath := in.FilePath
|
|
|
|
if filePath == "" && len(in.Bytes) > 0 {
|
|
ext := extFromMime(in.MimeType)
|
|
f, err := os.CreateTemp("", "stt-proxy-*"+ext)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("proxy_stt: create temp file: %w", err)
|
|
}
|
|
if err := os.Chmod(f.Name(), 0600); err != nil {
|
|
f.Close()
|
|
os.Remove(f.Name())
|
|
return nil, fmt.Errorf("proxy_stt: chmod temp file: %w", err)
|
|
}
|
|
if _, err := f.Write(in.Bytes); err != nil {
|
|
f.Close()
|
|
os.Remove(f.Name())
|
|
return nil, fmt.Errorf("proxy_stt: write temp file: %w", err)
|
|
}
|
|
f.Close()
|
|
defer os.Remove(f.Name())
|
|
filePath = f.Name()
|
|
}
|
|
|
|
text, err := media.TranscribeAudio(ctx, p.cfg, filePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &audio.TranscriptResult{
|
|
Text: text,
|
|
Provider: "proxy",
|
|
}, nil
|
|
}
|
|
|
|
// extFromMime returns a file extension for a MIME type.
|
|
func extFromMime(mime string) string {
|
|
switch strings.Split(mime, ";")[0] {
|
|
case "audio/ogg":
|
|
return ".ogg"
|
|
case "audio/mpeg", "audio/mp3":
|
|
return ".mp3"
|
|
case "audio/wav", "audio/wave":
|
|
return ".wav"
|
|
case "audio/mp4", "audio/m4a":
|
|
return ".m4a"
|
|
case "audio/webm":
|
|
return ".webm"
|
|
case "audio/flac":
|
|
return ".flac"
|
|
default:
|
|
return filepath.Ext(mime) // fallback; usually empty
|
|
}
|
|
}
|