You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

110 lines
3.5 KiB

package echomeet
import (
"context"
"yunyan/lego/sys/log"
"yunyan/pb"
"yunyan/sys/google/gemini"
"yunyan/sys/google/speech"
"yunyan/sys/google/translate"
"fmt"
)
// newGoogleProviders 初始化 Google 系单元服务(转写=Cloud Speech、翻译=Cloud Translation、总结=Gemini),
// 返回组合后的转写/翻译/总结三个 provider。
func newGoogleProviders(opt ProviderOptions) (Transcriber, Translator, Summarizer) {
if err := speech.OnInit(opt.Transcribe); err != nil {
log.Errorf("echomeet: init google speech failed: %v", err)
}
if err := translate.OnInit(opt.Translate); err != nil {
log.Errorf("echomeet: init google translate failed: %v", err)
}
sys, err := gemini.NewSys(
gemini.SetApikey(opt.Summary.Apikey),
gemini.SetModel(opt.Summary.Model),
gemini.SetEndpoint(opt.Summary.Endpoint),
)
if err != nil {
log.Errorf("echomeet: init gemini summarizer failed: %v", err)
}
return &googleTranscriber{}, &googleTranslator{}, &geminiSummarizer{sys: sys}
}
// Google Cloud Speech-to-Text V1 LongRunningRecognize(要求 gs:// 音频)
type googleTranscriber struct{}
func (this *googleTranscriber) Name() pb.TranscribeService {
return pb.TranscribeService_TranscribeService_Google
}
// Google Speech 支持的语种较广,不做白名单限制(gs:// 限制在 Submit 内)
func (this *googleTranscriber) SupportsLanguage(_ string) bool {
return true
}
func (this *googleTranscriber) Submit(_ context.Context, req SubmitRequest) (*SubmitResult, error) {
taskID, err := speech.CreateTask(req.AudioURL, req.Language, req.EnableSpeaker, req.CallbackURL)
if err != nil {
return nil, fmt.Errorf("google speech create task: %w", err)
}
return &SubmitResult{TaskID: taskID}, nil
}
func (this *googleTranscriber) Query(_ context.Context, taskID, _ string) (*QueryResult, error) {
status, contexts, err := speech.QueryTask(taskID)
switch status {
case speech.StatusSuccess:
out := make([]*pb.ContextStruct, 0, len(contexts))
for _, c := range contexts {
out = append(out, &pb.ContextStruct{
Content: c.Content,
Starttime: c.StartTime,
Endtime: c.EndTime,
Speaker: c.Speaker,
})
}
return &QueryResult{Status: StatusSuccess, Contexts: out}, nil
case speech.StatusRunning:
return &QueryResult{Status: StatusRunning}, nil
default:
return &QueryResult{Status: StatusFailed}, fmt.Errorf("google speech query failed: %w", err)
}
}
// Google Cloud Translation v3
type googleTranslator struct{}
func (this *googleTranslator) Name() pb.TranscribeService {
return pb.TranscribeService_TranscribeService_Google
}
func (this *googleTranslator) Translate(ctx context.Context, from, to string, texts []string) ([]string, error) {
return translate.Translate(ctx, toGoogleTranslateLang(from), toGoogleTranslateLang(to), texts)
}
// Gemini 总结
type geminiSummarizer struct {
sys gemini.ISys
}
func (this *geminiSummarizer) Name() pb.TranscribeService {
return pb.TranscribeService_TranscribeService_Google
}
func (this *geminiSummarizer) Chat(ctx context.Context, messages []ChatMessage) (string, error) {
if this.sys == nil {
return "", fmt.Errorf("gemini summarizer not initialized")
}
msgs := make([]gemini.Message, 0, len(messages))
for _, m := range messages {
msgs = append(msgs, gemini.Message{Role: m.Role, Content: m.Content, Images: m.Images})
}
resp, err := this.sys.Chat(ctx, msgs)
if err != nil {
return "", err
}
if resp == nil {
return "", fmt.Errorf("gemini chat: empty response")
}
return resp.Content, nil
}