Compare commits
2 Commits
f4ff763b78
...
ae73298bbf
| Author | SHA1 | Date | |
|---|---|---|---|
| ae73298bbf | |||
| 1526acc85f |
@@ -119,6 +119,9 @@ func Routers() *gin.Engine {
|
||||
// 注册业务路由
|
||||
initBizRouter(PrivateGroup, PublicGroup)
|
||||
|
||||
// 在根路径下注册 AI 通用接口路由(不带 /api 前缀,走 /v1/**)
|
||||
router.RouterGroupApp.App.InitAiProxyRootRouter(Router)
|
||||
|
||||
// 前端静态文件服务(放在最后,作为兜底路由)
|
||||
setupFrontendRoutes(Router)
|
||||
|
||||
|
||||
@@ -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 兼容
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user