Skip to content

Commit

Permalink
feat: add test
Browse files Browse the repository at this point in the history
Signed-off-by: Xinwei Xiong <[email protected]>
  • Loading branch information
cubxxw committed Oct 28, 2024
1 parent da48f1d commit 35e31ff
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 68 deletions.
25 changes: 13 additions & 12 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
# MinIO 配置
MINIO_ENDPOINT=''
MINIO_ACCESS_KEY=''
MINIO_SECRET_KEY=''
VOICEFLOW_MINIO_ENDPOINT='localhost:9000' # MinIO 服务地址
VOICEFLOW_MINIO_ACCESS_KEY='minioadmin' # MinIO 访问密钥
VOICEFLOW_MINIO_SECRET_KEY='minioadmin' # MinIO 密钥

# Azure 配置
AZURE_STT_KEY=''
AZURE_TTS_KEY=''
VOICEFLOW_AZURE_STT_KEY='your_azure_stt_key' # Azure 语音转文本密钥
VOICEFLOW_AZURE_TTS_KEY='your_azure_tts_key' # Azure 文本转语音密钥
VOICEFLOW_AZURE_REGION='eastus' # Azure 服务区域

# Google 配置
GOOGLE_STT_KEY=''
GOOGLE_TTS_KEY=''
VOICEFLOW_GOOGLE_STT_KEY='your_google_stt_key' # Google 语音转文本密钥
VOICEFLOW_GOOGLE_TTS_KEY='your_google_tts_key' # Google 文本转语音密钥

# OpenAI 配置
OPENAI_API_KEY=''

# 语音服务端口
VOICE_SERVER_PORT=18080
VOICEFLOW_OPENAI_API_KEY='your_openai_api_key' # OpenAI API 密钥

# AssemblyAI 配置
ASSEMBLYAI_API_KEY=''
VOICEFLOW_ASSEMBLYAI_API_KEY='your_assemblyai_api_key' # AssemblyAI API 密钥

# 语音服务端口配置
VOICEFLOW_SERVER_PORT=18080 # VoiceFlow 服务端口
85 changes: 45 additions & 40 deletions cmd/voiceflow/root.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// root.go
package main

import (
"context"
"fmt"
"github.com/joho/godotenv"
"github.com/telepace/voiceflow/pkg/config"
"net/http"
"os"
Expand All @@ -12,7 +14,7 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/telepace/voiceflow/internal/server"
serverpkg "github.com/telepace/voiceflow/internal/server"
"github.com/telepace/voiceflow/pkg/logger"
)

Expand All @@ -28,67 +30,59 @@ var rootCmd = &cobra.Command{
func run(cmd *cobra.Command, args []string) error {
ctx := context.Background()

// 初始化日志配置
// Load configuration
cfg, err := config.GetConfig()
if err != nil {
return fmt.Errorf("failed to get config: %w", err)
}

// Initialize logger
logCfg := logger.Config{
Level: viper.GetString("logging.level"),
Format: viper.GetString("logging.format"),
Filename: viper.GetString("logging.filename"),
MaxSize: viper.GetInt("logging.max_size"),
MaxBackups: viper.GetInt("logging.max_backups"),
MaxAge: viper.GetInt("logging.max_age"),
Compress: viper.GetBool("logging.compress"),
ReportCaller: true,
Level: cfg.Logging.Level,
Format: cfg.Logging.Format,
Filename: cfg.Logging.Filename,
MaxSize: cfg.Logging.MaxSize,
MaxBackups: cfg.Logging.MaxBackups,
MaxAge: cfg.Logging.MaxAge,
Compress: cfg.Logging.Compress,
ReportCaller: cfg.Logging.ReportCaller,
}

// 服务标识信息
fields := logger.StandardFields{
ServiceID: "voiceflow",
InstanceID: fmt.Sprintf("instance-%d", time.Now().Unix()),
}

// 初始化日志系统
if err := logger.Init(logCfg, fields); err != nil {
return fmt.Errorf("failed to initialize logger: %w", err)
}

// 记录启动信息
logger.Info(ctx, "Starting VoiceFlow server")

// 初始化配置
cfg, err := config.GetConfig()
if err != nil {
logger.Error(ctx, "Failed to load configuration", "error", err)
return fmt.Errorf("failed to get config: %w", err)
}
logger.InfoContextf(ctx, "Starting VoiceFlow server with config: %+v", cfg)

// 记录配置信息
logger.Info(ctx, "Configuration loaded",
"server_port", cfg.Server.Port,
"enable_tls", cfg.Server.EnableTLS,
)

// 创建服务器实例
// Set up HTTP server
mux := http.NewServeMux()

// 注册路由处理器
logger.Debug(ctx, "Registering HTTP handlers")
mux.Handle("/", http.FileServer(http.Dir("./web")))
mux.Handle("/audio_files/", http.StripPrefix("/audio_files/", http.FileServer(http.Dir("./audio_files"))))

// 初始化 WebSocket 服务器
wsServer := server.NewServer()
// Initialize WebSocket server
wsServer := serverpkg.NewServer()
if wsServer == nil {
logger.Fatal("Failed to create Server instance")
}

wsServer.SetupRoutes(mux)

// 创建 HTTP 服务器
// Create HTTP server
srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Server.Port),
Handler: loggingMiddleware(mux),
}

// 启动服务器
logger.Info(ctx, "Server starting", "address", srv.Addr)
// Start server
logger.InfoContext(ctx, "Server starting", "address", srv.Addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Error(ctx, "Server failed to start", "error", err)
logger.ErrorContext(ctx, "Server failed to start", "error", err)
return fmt.Errorf("server error: %w", err)
}

Expand Down Expand Up @@ -134,16 +128,27 @@ func init() {
rootCmd.PersistentFlags().Bool("server.enable_tls", false, "enable TLS")

// 日志相关配置项
rootCmd.PersistentFlags().String("logging.level", "info", "log level")
// Logging flags
rootCmd.PersistentFlags().String("logging.level", "info", "log level (debug, info, warn, error, fatal)")
rootCmd.PersistentFlags().String("logging.format", "json", "log format (json/text)")
rootCmd.PersistentFlags().String("logging.filename", "", "log file path")
rootCmd.PersistentFlags().Int("logging.max_size", 100, "maximum size in MB before log file rotation")
rootCmd.PersistentFlags().Int("logging.max_backups", 3, "maximum number of old log files to retain")
rootCmd.PersistentFlags().Int("logging.max_age", 28, "maximum number of days to retain old log files")
rootCmd.PersistentFlags().Bool("logging.compress", true, "whether to compress old log files")
rootCmd.PersistentFlags().Bool("logging.report_caller", true, "whether to include caller information in logs")

// 绑定到 viper
viper.BindPFlags(rootCmd.PersistentFlags())
}

func initConfig() {
//ctx := context.Background()
// 加载 .env 文件
if err := godotenv.Load(); err != nil {
logger.Fatal("No .env file found or failed to load, proceeding without it")
} else {
logger.Info(".env file loaded")
}

if cfgFile != "" {
viper.SetConfigFile(cfgFile)
Expand Down Expand Up @@ -176,15 +181,15 @@ func setDefaults() {
// 日志默认配置
viper.SetDefault("logging.level", "info")
viper.SetDefault("logging.format", "json")
viper.SetDefault("logging.filename", "") // 默认输出到标准输出
viper.SetDefault("logging.filename", "")
viper.SetDefault("logging.max_size", 100)
viper.SetDefault("logging.max_backups", 3)
viper.SetDefault("logging.max_age", 28)
viper.SetDefault("logging.compress", true)
viper.SetDefault("logging.report_caller", true)

// 其他服务配置...
viper.SetDefault("web.port", 18090)
viper.SetDefault("minio.enabled", true)
viper.SetDefault("minio.endpoint", "localhost:9000")
// ... 其他配置项
}
18 changes: 17 additions & 1 deletion configs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,24 @@ google:
openai:
api_key: "your_openai_api_key"

# 日志配置
logging:
level: Debug
# 日志级别(选项:debug(调试)、info(信息)、warn(警告)、error(错误)、fatal(致命错误))
level: "info"
# 日志格式(选项:json(JSON 格式)、text(文本格式))
format: "text"
# 日志文件路径(留空则仅输出到标准输出)
filename: ""
# 日志文件轮转前的最大大小(单位:MB)
max_size: 100
# 要保留的旧日志文件的最大数量
max_backups: 3
# 保留旧日志文件的最大天数
max_age: 28
# 是否压缩旧日志文件
compress: true
# 是否在日志中包含调用者信息
report_caller: false

assemblyai:
api_key: "your_assemblyai_api_key"
28 changes: 19 additions & 9 deletions internal/server/handlers.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// handlers.go - 服务器处理函数
package server

import (
"encoding/json"
"github.com/telepace/voiceflow/pkg/config"
"github.com/telepace/voiceflow/pkg/logger"
"log"
"net/http"
"sync"

Expand All @@ -31,24 +31,34 @@ func initServices() {
logger.Fatalf("配置初始化失败: %v", err)
}
sttService = stt.NewService(cfg.STT.Provider)
ttsService = tts.NewService(cfg.TTS.Provider)
llmService = llm.NewService(cfg.LLM.Provider)
storageService = storage.NewService()
//ttsService = tts.NewService(cfg.TTS.Provider)
//llmService = llm.NewService(cfg.LLM.Provider)
//storageService = storage.NewService()
}

func (s *Server) handleConnections(w http.ResponseWriter, r *http.Request) {

// 检查服务实例是否为空
if s == nil {
logger.Error("Server instance is nil in handleConnections")
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
} else {
logger.Infof("Server instance is not nil in handleConnections: %v", s)
}

// 升级 WebSocket 连接
ws, err := s.upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("WebSocket Upgrade error: %v", err)
logger.Errorf("WebSocket Upgrade error: %v", err)
return
}
defer ws.Close()

for {
mt, data, err := ws.ReadMessage()
if err != nil {
log.Printf("Read error: %v", err)
logger.Errorf("Read error: %v", err)
break
}

Expand All @@ -65,22 +75,22 @@ func (s *Server) handleConnections(w http.ResponseWriter, r *http.Request) {
// 处理文字消息
var msg map[string]string
if err := json.Unmarshal(data, &msg); err != nil {
log.Printf("JSON parse error: %v", err)
logger.Error("JSON parse error: %v", err)
continue
}
text := msg["text"]

// 调用 TTS 服务,将文字转换为语音
audioData, err := currentTTSService.Synthesize(text)
if err != nil {
log.Printf("TTS error: %v", err)
logger.Error("TTS error: %v", err)
continue
}

// 存储音频并获取 URL
audioURL, err := currentStorageService.StoreAudio(audioData)
if err != nil {
log.Printf("Storage error: %v", err)
logger.Error("Storage error: %v", err)
continue
}

Expand Down
21 changes: 16 additions & 5 deletions internal/server/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// server.go
package server

import (
"github.com/telepace/voiceflow/pkg/logger"
"net/http"

"github.com/gorilla/websocket"
Expand All @@ -16,16 +18,25 @@ func NewServer() *Server {
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // 根据需要进行跨域处理
return true
},
},
}
}

func (s *Server) SetupRoutes(mux *http.ServeMux) {
// WebSocket 路由
mux.HandleFunc("/ws", s.handleConnections)
if s == nil {
logger.Error("Server instance is nil in SetupRoutes")
} else {
logger.Info("Server instance is not nil in SetupRoutes")
}

// 使用闭包来包装方法调用,确保正确捕获接收者 s
mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
s.handleConnections(w, r)
})

// 配置更改的 RESTful API
mux.HandleFunc("/config", s.HandleConfig)
mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
s.HandleConfig(w, r)
})
}
2 changes: 2 additions & 0 deletions internal/stt/assemblyai/assemblyai.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// assemblyai.go
package assemblyai

import (
Expand All @@ -19,6 +20,7 @@ type AssemblyAI struct {
}

func NewAssemblyAI() *AssemblyAI {
logger.Info("Using AssemblyAI STT provider")
cfg, err := config.GetConfig()
if err != nil {
logger.Fatalf("Failed to get config: %v", err)
Expand Down
1 change: 1 addition & 0 deletions internal/stt/stt.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// stt.go
package stt

import (
Expand Down
10 changes: 9 additions & 1 deletion pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// config.go
package config

import (
Expand Down Expand Up @@ -48,7 +49,14 @@ type Config struct {
Secure bool `mapstructure:"secure"`
}
Logging struct {
Level string
Level string
Format string
Filename string `mapstructure:"filename"`
MaxSize int `mapstructure:"max_size"`
MaxBackups int `mapstructure:"max_backups"`
MaxAge int `mapstructure:"max_age"`
Compress bool `mapstructure:"compress"`
ReportCaller bool `mapstructure:"report_caller"`
}
}

Expand Down

0 comments on commit 35e31ff

Please sign in to comment.