Compare commits

...

2 Commits

Author SHA1 Message Date
ae73298bbf 🎨 优化ai调用接口为统一格式 2026-03-04 02:22:31 +08:00
1526acc85f 🎨 ai调用时新增详细日志 2026-03-04 02:15:16 +08:00
3 changed files with 98 additions and 0 deletions

View File

@@ -119,6 +119,9 @@ func Routers() *gin.Engine {
// 注册业务路由
initBizRouter(PrivateGroup, PublicGroup)
// 在根路径下注册 AI 通用接口路由(不带 /api 前缀,走 /v1/**
router.RouterGroupApp.App.InitAiProxyRootRouter(Router)
// 前端静态文件服务(放在最后,作为兜底路由)
setupFrontendRoutes(Router)

View File

@@ -8,6 +8,7 @@ import (
type AiProxyRouter struct{}
// InitAiProxyRouter 使用业务路由组(通常带有 /api 前缀),供后台管理前端调用
func (s *AiProxyRouter) InitAiProxyRouter(Router *gin.RouterGroup) {
aiProxyRouter := Router.Group("v1").Use(middleware.AiAuth())
aiProxyApi := v1.ApiGroupApp.AppApiGroup.AiProxyApi
@@ -17,3 +18,15 @@ func (s *AiProxyRouter) InitAiProxyRouter(Router *gin.RouterGroup) {
aiProxyRouter.POST("messages", aiProxyApi.ClaudeMessages) // Claude 兼容
}
}
// InitAiProxyRootRouter 在根路径下注册 AI 相关的通用接口,路径为 /v1/**
// 这样可以对外提供标准的 OpenAI/Claude 兼容地址:/v1/models、/v1/chat/completions、/v1/messages
func (s *AiProxyRouter) InitAiProxyRootRouter(engine *gin.Engine) {
aiProxyRouter := engine.Group("/v1").Use(middleware.AiAuth())
aiProxyApi := v1.ApiGroupApp.AppApiGroup.AiProxyApi
{
aiProxyRouter.GET("/models", aiProxyApi.ListModels) // 获取模型列表
aiProxyRouter.POST("/chat/completions", aiProxyApi.ChatCompletions) // OpenAI 兼容
aiProxyRouter.POST("/messages", aiProxyApi.ClaudeMessages) // Claude 兼容
}
}

View File

@@ -56,6 +56,49 @@ func (s *AiProxyService) ProcessChatCompletion(ctx context.Context, req *request
if regexLogs.TotalMatches > 0 || len(regexLogs.InputScripts) > 0 || len(regexLogs.OutputScripts) > 0 {
resp.RegexLogs = regexLogs
}
// 记录匹配到正则时的脚本名称
if regexLogs.TotalMatches > 0 {
matchedScripts := make([]string, 0)
for _, scriptLog := range regexLogs.InputScripts {
if scriptLog.MatchCount > 0 {
matchedScripts = append(matchedScripts, scriptLog.ScriptName)
}
}
for _, scriptLog := range regexLogs.OutputScripts {
if scriptLog.MatchCount > 0 {
matchedScripts = append(matchedScripts, scriptLog.ScriptName)
}
}
// 记录接口调用参数和 AI 输出内容
aiOutput := resp.Choices[0].Message.Content
global.GVA_LOG.Info("AI 请求完成",
zap.Any("request", req),
zap.String("ai_output", aiOutput),
zap.Strings("matched_regex_scripts", matchedScripts),
)
} else {
// 未匹配到正则时仅记录请求和输出
aiOutput := resp.Choices[0].Message.Content
global.GVA_LOG.Info("AI 请求完成(无正则匹配)",
zap.Any("request", req),
zap.String("ai_output", aiOutput),
)
}
} else {
// 无预设或无注入器时也记录基础请求与输出
if len(resp.Choices) > 0 {
aiOutput := resp.Choices[0].Message.Content
global.GVA_LOG.Info("AI 请求完成(无预设/注入器)",
zap.Any("request", req),
zap.String("ai_output", aiOutput),
)
} else {
global.GVA_LOG.Info("AI 请求完成(无输出)",
zap.Any("request", req),
)
}
}
return resp, nil
@@ -200,6 +243,9 @@ func (s *AiProxyService) forwardStreamRequest(c *gin.Context, provider *app.AiPr
return fmt.Errorf("不支持流式响应")
}
// 聚合 AI 输出内容用于日志
var fullContent bytes.Buffer
for {
line, err := reader.ReadBytes('\n')
if err != nil {
@@ -234,6 +280,8 @@ func (s *AiProxyService) forwardStreamRequest(c *gin.Context, provider *app.AiPr
// 应用输出正则处理
if injector != nil && len(chunk.Choices) > 0 && chunk.Choices[0].Delta.Content != "" {
// 先记录原始内容到日志汇总
fullContent.WriteString(chunk.Choices[0].Delta.Content)
chunk.Choices[0].Delta.Content = injector.ProcessResponse(chunk.Choices[0].Delta.Content)
}
@@ -246,6 +294,40 @@ func (s *AiProxyService) forwardStreamRequest(c *gin.Context, provider *app.AiPr
}
}
// 流式请求结束后记录日志
if injector != nil {
regexLogs := injector.GetRegexLogs()
if regexLogs != nil && (regexLogs.TotalMatches > 0 || len(regexLogs.InputScripts) > 0 || len(regexLogs.OutputScripts) > 0) {
matchedScripts := make([]string, 0)
for _, scriptLog := range regexLogs.InputScripts {
if scriptLog.MatchCount > 0 {
matchedScripts = append(matchedScripts, scriptLog.ScriptName)
}
}
for _, scriptLog := range regexLogs.OutputScripts {
if scriptLog.MatchCount > 0 {
matchedScripts = append(matchedScripts, scriptLog.ScriptName)
}
}
global.GVA_LOG.Info("AI 流式请求完成",
zap.Any("request", req),
zap.String("ai_output", fullContent.String()),
zap.Strings("matched_regex_scripts", matchedScripts),
)
} else {
global.GVA_LOG.Info("AI 流式请求完成(无正则匹配)",
zap.Any("request", req),
zap.String("ai_output", fullContent.String()),
)
}
} else {
global.GVA_LOG.Info("AI 流式请求完成(无预设/注入器)",
zap.Any("request", req),
)
}
return nil
}