Files
gin-vue-admin/server/service/system/auto_code_llm.go
T
PiexlMax(奇淼 375b21c222 发布2.9.1版本 (#2189)
* feat: mcp能力调整为 streamhttp的独立服务 不再依赖gva本体

* feat: 增加MCP启动能力,增加AI工作流需求整理能力。

* feat: 增加需求梳理能力

* feat: 清理无用日志

* feat: 移除无用的Dify Chatflow文本和代码生成器按钮
2026-03-27 12:14:13 +08:00

126 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package system
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common"
commonResp "github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/utils/request"
"github.com/goccy/go-json"
)
func (s *AutoCodeService) LLMAuto(ctx context.Context, llm common.JSONMap) (interface{}, error) {
path, err := buildLLMAutoPath(llm)
if err != nil {
return nil, err
}
res, err := request.HttpRequestWithContextAndTimeout(
ctx,
path,
http.MethodPost,
nil,
nil,
llm,
)
if err != nil {
return nil, fmt.Errorf("调用上游大模型服务失败: %w", err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("读取大模型响应失败: %w", err)
}
bodyPreview := previewResponseBody(body)
contentType := res.Header.Get("Content-Type")
if res.StatusCode < 200 || res.StatusCode >= 300 {
return nil, fmt.Errorf("上游大模型服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, contentType, bodyPreview)
}
var resStruct commonResp.Response
if err = json.Unmarshal(body, &resStruct); err != nil {
return nil, fmt.Errorf("解析大模型响应失败: status=%d content-type=%s body=%s err=%w", res.StatusCode, contentType, bodyPreview, err)
}
if resStruct.Code != commonResp.SUCCESS {
return nil, fmt.Errorf("大模型服务返回业务错误: code=%d msg=%s body=%s", resStruct.Code, resStruct.Msg, bodyPreview)
}
return resStruct.Data, nil
}
func (s *AutoCodeService) LLMAutoStream(ctx context.Context, llm common.JSONMap) (*http.Response, error) {
path, err := buildLLMAutoPath(llm)
if err != nil {
return nil, err
}
payload := cloneLLMAutoJSONMap(llm)
responseMode := strings.ToLower(strings.TrimSpace(fmt.Sprintf("%v", payload["response_mode"])))
if responseMode == "" {
payload["response_mode"] = "streaming"
}
res, err := request.HttpRequestWithContextAndTimeout(
ctx,
path,
http.MethodPost,
map[string]string{
"Accept": "text/event-stream",
"Accept-Encoding": "identity", // 禁止 gzip,避免 SSE 流被压缩导致缓冲卡住
"Cache-Control": "no-cache",
},
nil,
payload,
-1, // 不设置 client.Timeout,SSE 流的生命周期由 ctx 控制
)
if err != nil {
return nil, fmt.Errorf("调用上游大模型流式服务失败: %w", err)
}
return res, nil
}
func buildLLMAutoPath(llm common.JSONMap) (string, error) {
if global.GVA_CONFIG.AutoCode.AiPath == "" {
return "", errors.New("请先前往插件市场个人中心获取 AiPath 并填写到 config.yaml 中")
}
mode := strings.TrimSpace(fmt.Sprintf("%v", llm["mode"]))
if mode == "" {
return "", errors.New("llmAuto 缺少 mode 参数")
}
return strings.ReplaceAll(global.GVA_CONFIG.AutoCode.AiPath, "{FUNC}", mode), nil
}
func cloneLLMAutoJSONMap(src common.JSONMap) common.JSONMap {
dst := make(common.JSONMap, len(src))
for key, value := range src {
dst[key] = value
}
return dst
}
func previewResponseBody(body []byte) string {
text := strings.TrimSpace(string(body))
text = strings.ReplaceAll(text, "\r", " ")
text = strings.ReplaceAll(text, "\n", " ")
text = strings.Join(strings.Fields(text), " ")
if text == "" {
return "<empty>"
}
runes := []rune(text)
if len(runes) > 300 {
return string(runes[:300]) + "..."
}
return text
}