init commit

This commit is contained in:
Huaaudio 2026-03-21 05:52:21 +01:00
parent 241eb6c770
commit e49f65a2be
2 changed files with 47 additions and 29 deletions

View file

@ -16,6 +16,12 @@ import (
"github.com/sipeed/picoclaw/pkg/utils" "github.com/sipeed/picoclaw/pkg/utils"
) )
type Transcriber interface {
Name() string
Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error)
TranscribeData(ctx context.Context, data []byte, filename string) (*TranscriptionResponse, error)
}
type GroqTranscriber struct { type GroqTranscriber struct {
apiKey string apiKey string
apiBase string apiBase string
@ -35,45 +41,24 @@ func NewGroqTranscriber(apiKey string) *GroqTranscriber {
} }
} }
func (t *GroqTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) { func (t *GroqTranscriber) TranscribeData(ctx context.Context, data []byte, filename string) (*TranscriptionResponse, error) {
logger.InfoCF("voice", "Starting transcription", map[string]any{"audio_file": audioFilePath}) logger.InfoCF("voice", "Starting memory transcription", map[string]any{"filename": filename, "bytes": len(data)})
audioFile, err := os.Open(audioFilePath)
if err != nil {
logger.ErrorCF("voice", "Failed to open audio file", map[string]any{"path": audioFilePath, "error": err})
return nil, fmt.Errorf("failed to open audio file: %w", err)
}
defer audioFile.Close()
fileInfo, err := audioFile.Stat()
if err != nil {
logger.ErrorCF("voice", "Failed to get file info", map[string]any{"path": audioFilePath, "error": err})
return nil, fmt.Errorf("failed to get file info: %w", err)
}
logger.DebugCF("voice", "Audio file details", map[string]any{
"size_bytes": fileInfo.Size(),
"file_name": filepath.Base(audioFilePath),
})
var requestBody bytes.Buffer var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody) writer := multipart.NewWriter(&requestBody)
part, err := writer.CreateFormFile("file", filepath.Base(audioFilePath)) part, err := writer.CreateFormFile("file", filename)
if err != nil { if err != nil {
logger.ErrorCF("voice", "Failed to create form file", map[string]any{"error": err}) logger.ErrorCF("voice", "Failed to create form file", map[string]any{"error": err})
return nil, fmt.Errorf("failed to create form file: %w", err) return nil, fmt.Errorf("failed to create form file: %w", err)
} }
copied, err := io.Copy(part, audioFile) if _, err := io.Copy(part, bytes.NewReader(data)); err != nil {
if err != nil {
logger.ErrorCF("voice", "Failed to copy file content", map[string]any{"error": err}) logger.ErrorCF("voice", "Failed to copy file content", map[string]any{"error": err})
return nil, fmt.Errorf("failed to copy file content: %w", err) return nil, fmt.Errorf("failed to copy file content: %w", err)
} }
logger.DebugCF("voice", "File copied to request", map[string]any{"bytes_copied": copied}) if err = writer.WriteField("model", "whisper-large-v3-turbo"); err != nil {
if err = writer.WriteField("model", "whisper-large-v3"); err != nil {
logger.ErrorCF("voice", "Failed to write model field", map[string]any{"error": err}) logger.ErrorCF("voice", "Failed to write model field", map[string]any{"error": err})
return nil, fmt.Errorf("failed to write model field: %w", err) return nil, fmt.Errorf("failed to write model field: %w", err)
} }
@ -88,20 +73,52 @@ func (t *GroqTranscriber) Transcribe(ctx context.Context, audioFilePath string)
return nil, fmt.Errorf("failed to close multipart writer: %w", err) return nil, fmt.Errorf("failed to close multipart writer: %w", err)
} }
return t.doRequest(ctx, &requestBody, writer.FormDataContentType(), int64(len(data)))
}
func (t *GroqTranscriber) Transcribe(ctx context.Context, audioFilePath string) (*TranscriptionResponse, error) {
logger.InfoCF("voice", "Starting transcription", map[string]any{"audio_file": audioFilePath})
audioFile, err := os.Open(audioFilePath)
if err != nil {
return nil, fmt.Errorf("failed to open audio file: %w", err)
}
defer audioFile.Close()
fileInfo, err := audioFile.Stat()
if err != nil {
return nil, err
}
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
part, err := writer.CreateFormFile("file", filepath.Base(audioFilePath))
if err != nil {
return nil, err
}
io.Copy(part, audioFile)
writer.WriteField("model", "whisper-large-v3")
writer.WriteField("response_format", "json")
writer.Close()
return t.doRequest(ctx, &requestBody, writer.FormDataContentType(), fileInfo.Size())
}
func (t *GroqTranscriber) doRequest(ctx context.Context, requestBody *bytes.Buffer, contentType string, fileSize int64) (*TranscriptionResponse, error) {
url := t.apiBase + "/audio/transcriptions" url := t.apiBase + "/audio/transcriptions"
req, err := http.NewRequestWithContext(ctx, "POST", url, &requestBody) req, err := http.NewRequestWithContext(ctx, "POST", url, requestBody)
if err != nil { if err != nil {
logger.ErrorCF("voice", "Failed to create request", map[string]any{"error": err}) logger.ErrorCF("voice", "Failed to create request", map[string]any{"error": err})
return nil, fmt.Errorf("failed to create request: %w", err) return nil, fmt.Errorf("failed to create request: %w", err)
} }
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", contentType)
req.Header.Set("Authorization", "Bearer "+t.apiKey) req.Header.Set("Authorization", "Bearer "+t.apiKey)
logger.DebugCF("voice", "Sending transcription request to Groq API", map[string]any{ logger.DebugCF("voice", "Sending transcription request to Groq API", map[string]any{
"url": url, "url": url,
"request_size_bytes": requestBody.Len(), "request_size_bytes": requestBody.Len(),
"file_size_bytes": fileInfo.Size(), "file_size_bytes": fileSize,
}) })
resp, err := t.httpClient.Do(req) resp, err := t.httpClient.Do(req)

View file

@ -83,6 +83,7 @@ func NewDiscordChannel(cfg config.DiscordConfig, bus *bus.MessageBus) (*DiscordC
ctx: context.Background(), ctx: context.Background(),
typingStop: make(map[string]chan struct{}), typingStop: make(map[string]chan struct{}),
bus: bus, bus: bus,
bus: bus,
}, nil }, nil
} }