refactor(util): 重构响应解析和表单验证功能
This commit is contained in:
+141
-183
@@ -1,37 +1,28 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"model-gateway/model/entity"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
tgjson "github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
// ParseAndValidate 解析模型响应,并返回标准格式
|
||||
func ParseAndValidate(raw map[string]any, model *entity.ModelGatewayModel) (map[string]any, error) {
|
||||
// ======================== 响应解析 ========================
|
||||
|
||||
// ParseAndValidate 解析模型响应,校验必填字段,返回标准 rounds 格式
|
||||
func ParseAndValidate(raw map[string]any, requiredFields []string) (map[string]any, error) {
|
||||
contentStr := gconv.String(raw[entity.ResponseBody])
|
||||
if strings.TrimSpace(contentStr) == "" {
|
||||
return raw, fmt.Errorf("字段 %s 为空", entity.ResponseBody)
|
||||
}
|
||||
|
||||
contentStr = strings.Map(func(r rune) rune {
|
||||
if r < 32 && r != ' ' {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, contentStr)
|
||||
// 过滤控制字符
|
||||
contentStr = cleanControlChars(contentStr)
|
||||
|
||||
var arr []any
|
||||
if err := json.Unmarshal([]byte(contentStr), &arr); err != nil {
|
||||
@@ -41,11 +32,17 @@ func ParseAndValidate(raw map[string]any, model *entity.ModelGatewayModel) (map[
|
||||
return raw, fmt.Errorf("解析后数组为空")
|
||||
}
|
||||
|
||||
for _, field := range model.RequiredFields {
|
||||
// 校验必填字段
|
||||
if len(requiredFields) > 0 {
|
||||
for i, r := range arr {
|
||||
round, _ := r.(map[string]any)
|
||||
if round != nil && gjson.New(round).Get(field).IsNil() {
|
||||
return raw, fmt.Errorf("rounds[%d] 缺少必填字段: %s", i, field)
|
||||
if round == nil {
|
||||
continue
|
||||
}
|
||||
for _, field := range requiredFields {
|
||||
if gjson.New(round).Get(field).IsNil() {
|
||||
return raw, fmt.Errorf("rounds[%d] 缺少必填字段: %s", i, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,11 +50,9 @@ func ParseAndValidate(raw map[string]any, model *entity.ModelGatewayModel) (map[
|
||||
return map[string]any{"total_rounds": len(arr), "rounds": arr}, nil
|
||||
}
|
||||
|
||||
// ParseStructResult 解析结构结果
|
||||
// ParseStructResult 解析结构化结果
|
||||
func ParseStructResult(raw map[string]any, responseBody string) map[string]any {
|
||||
contentVal := raw[responseBody]
|
||||
// 是字符串,尝试解析
|
||||
contentStr := gconv.String(contentVal)
|
||||
contentStr := gconv.String(raw[responseBody])
|
||||
if contentStr == "" || contentStr == "0" {
|
||||
return map[string]any{
|
||||
"total_rounds": 1,
|
||||
@@ -65,63 +60,38 @@ func ParseStructResult(raw map[string]any, responseBody string) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试解析为数组
|
||||
var arr []any
|
||||
if err := json.Unmarshal([]byte(contentStr), &arr); err == nil && len(arr) > 0 {
|
||||
if arr := tryParseArray(contentStr); arr != nil {
|
||||
return map[string]any{
|
||||
"total_rounds": 1,
|
||||
"rounds": []map[string]any{{responseBody: arr}},
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试解析为单个对象
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(contentStr), &parsed); err == nil {
|
||||
if parsed := tryParseAny(contentStr); parsed != nil {
|
||||
return map[string]any{
|
||||
"total_rounds": 1,
|
||||
"rounds": []map[string]any{{responseBody: parsed}},
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:原始字符串作为内容
|
||||
return map[string]any{
|
||||
"total_rounds": 1,
|
||||
"rounds": []map[string]any{{responseBody: contentStr}},
|
||||
}
|
||||
}
|
||||
|
||||
// ParseHeadMsgHeaders 从 head_msg JSON 中提取请求头
|
||||
// head_msg 格式示例:
|
||||
//
|
||||
// {
|
||||
// "Authorization": "Bearer xxx",
|
||||
// "Content-Type": "application/json",
|
||||
// "X-Api-App-Id": "5147401364",
|
||||
// "X-Api-Access-Key": "VCqRX7..."
|
||||
// }
|
||||
func ParseHeadMsgHeaders(headMsg map[string]any) map[string]string {
|
||||
if len(headMsg) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(headMsg))
|
||||
for k, v := range headMsg {
|
||||
out[k] = gconv.String(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
// ======================== 响应映射 ========================
|
||||
|
||||
// MapResponsePayload 映射模型响应为标准格式
|
||||
// MapResponsePayload 将模型响应按映射规则转为标准格式
|
||||
func MapResponsePayload(mapping map[string]any, result map[string]any) (map[string]any, error) {
|
||||
if len(mapping) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 把 result 转成 JSON 字符串,tidwall/gjson 需要字符串输入
|
||||
resultBytes, _ := json.Marshal(result)
|
||||
resultStr := string(resultBytes)
|
||||
|
||||
mapped := make(map[string]any)
|
||||
|
||||
for standardField, modelPath := range mapping {
|
||||
path := gconv.String(modelPath)
|
||||
if path == "" {
|
||||
@@ -132,7 +102,7 @@ func MapResponsePayload(mapping map[string]any, result map[string]any) (map[stri
|
||||
if !value.Exists() {
|
||||
continue
|
||||
}
|
||||
// 如果是数组路径(含 #),取 Array;否则取单值
|
||||
|
||||
if strings.Contains(path, "#") {
|
||||
var arr []any
|
||||
for _, v := range value.Array() {
|
||||
@@ -147,19 +117,108 @@ func MapResponsePayload(mapping map[string]any, result map[string]any) (map[stri
|
||||
return mapped, nil
|
||||
}
|
||||
|
||||
//
|
||||
//// GetModelBody 获取数据库中保存的模型信息
|
||||
//func GetModelBody(v map[string]any) map[string]any {
|
||||
// if v == nil {
|
||||
// return nil
|
||||
// }
|
||||
// if p, ok := v["body"]; ok {
|
||||
// return gconv.Map(p)
|
||||
// }
|
||||
// return v
|
||||
//}
|
||||
// ValidateAndParseForm 校验表单并转为嵌套 map
|
||||
func ValidateAndParseForm(forms []entity.Form) (map[string]any, error) {
|
||||
result := gjson.New("{}")
|
||||
|
||||
// BodyToQuery 将 body 转为 url.Values
|
||||
for _, form := range forms {
|
||||
if form.Key == "" {
|
||||
continue
|
||||
}
|
||||
if form.Required && (form.Value == nil || gconv.String(form.Value) == "") {
|
||||
return nil, fmt.Errorf("字段 %s 为必填", form.Label)
|
||||
}
|
||||
if form.Value == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := validateAndConvert(form)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = result.Set(form.Key, val)
|
||||
}
|
||||
|
||||
return result.Map(), nil
|
||||
}
|
||||
|
||||
// validateAndConvert 验证表单字段并转为标准格式
|
||||
func validateAndConvert(form entity.Form) (any, error) {
|
||||
val := form.Value
|
||||
fc := form.FieldConstraint
|
||||
switch form.Type {
|
||||
case "string":
|
||||
s := gconv.String(val)
|
||||
if fc.MaxLength > 0 && len(s) > fc.MaxLength {
|
||||
return nil, fmt.Errorf("字段 %s 超过最大长度 %d", form.Label, fc.MaxLength)
|
||||
}
|
||||
if fc.MinLength > 0 && len(s) < fc.MinLength {
|
||||
return nil, fmt.Errorf("字段 %s 不足最小长度 %d", form.Label, fc.MinLength)
|
||||
}
|
||||
return s, nil
|
||||
|
||||
case "number":
|
||||
f := gconv.Float64(val)
|
||||
if fc.Min != nil && f < gconv.Float64(fc.Min) {
|
||||
return nil, fmt.Errorf("字段 %s 不能小于 %v", form.Label, fc.Min)
|
||||
}
|
||||
if fc.Max != nil && f > gconv.Float64(fc.Max) {
|
||||
return nil, fmt.Errorf("字段 %s 不能大于 %v", form.Label, fc.Max)
|
||||
}
|
||||
// 根据 numberType 决定返回 int 还是 float64
|
||||
switch fc.NumberType {
|
||||
case "float", "positiveFloat", "negativeFloat":
|
||||
return f, nil
|
||||
default:
|
||||
return int(f), nil
|
||||
}
|
||||
|
||||
case "select", "radio":
|
||||
v, ok := val.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("字段 %s 格式错误", form.Label)
|
||||
}
|
||||
return v, nil
|
||||
|
||||
case "upload":
|
||||
var urls []string
|
||||
switch v := val.(type) {
|
||||
case []any:
|
||||
for _, u := range v {
|
||||
urls = append(urls, gconv.String(u))
|
||||
}
|
||||
case []string:
|
||||
urls = v
|
||||
case string:
|
||||
if v != "" {
|
||||
urls = []string{v}
|
||||
}
|
||||
}
|
||||
if fc.MaxCount > 0 && len(urls) > fc.MaxCount {
|
||||
return nil, fmt.Errorf("字段 %s 上传数量超过上限 %d", form.Label, fc.MaxCount)
|
||||
}
|
||||
return urls, nil
|
||||
|
||||
default:
|
||||
return gconv.String(val), nil
|
||||
}
|
||||
}
|
||||
|
||||
// ======================== 请求工具 ========================
|
||||
|
||||
// ParseHeadMsgHeaders 从 head_msg 中提取 HTTP 请求头
|
||||
func ParseHeadMsgHeaders(headMsg map[string]any) map[string]string {
|
||||
if len(headMsg) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]string, len(headMsg))
|
||||
for k, v := range headMsg {
|
||||
out[k] = gconv.String(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// BodyToQuery 将 body 转为 URL 查询参数
|
||||
func BodyToQuery(payload map[string]any) (url.Values, error) {
|
||||
q := url.Values{}
|
||||
for k, v := range payload {
|
||||
@@ -171,130 +230,29 @@ func BodyToQuery(payload map[string]any) (url.Values, error) {
|
||||
return q, nil
|
||||
}
|
||||
|
||||
// PullTaskResult 轮询查询异步任务结果直到完成
|
||||
func PullTaskResult(ctx context.Context, body map[string]any, queryConfig map[string]any, headMsg map[string]any) (map[string]any, error) {
|
||||
// 1) 解析配置
|
||||
// 1.1 提取 taskID
|
||||
taskIDPath := gconv.String(queryConfig["task_id"])
|
||||
taskID := gconv.String(gjson.New(body).Get(taskIDPath).Val())
|
||||
if taskID == "" {
|
||||
return nil, fmt.Errorf("无法从路径 %s 提取 taskID", taskIDPath)
|
||||
}
|
||||
g.Log().Infof(ctx, "[PullTaskResult] taskID=%s", taskID)
|
||||
// ======================== 内部辅助 ========================
|
||||
|
||||
// 1.2 请求地址,替换 {id}
|
||||
queryUrl := gconv.String(queryConfig["url"])
|
||||
queryUrl = replaceURLParams(queryUrl, map[string]any{"id": taskID})
|
||||
|
||||
// 1.3 请求方式
|
||||
method := gconv.String(queryConfig["method"])
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
// 1.4 状态判断配置
|
||||
statusPath := gconv.String(queryConfig["status_path"])
|
||||
statusValues, _ := queryConfig["status_values"].(map[string]any)
|
||||
if statusPath == "" {
|
||||
statusPath = "status"
|
||||
}
|
||||
|
||||
// 1.5 轮询间隔
|
||||
interval := gconv.Int(queryConfig["interval_seconds"])
|
||||
if interval <= 0 {
|
||||
interval = 2
|
||||
}
|
||||
|
||||
// 1.6 请求体
|
||||
reqBodyMap := map[string]any{"task_id": taskID}
|
||||
|
||||
// 2) 轮询请求
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
func cleanControlChars(s string) string {
|
||||
return strings.Map(func(r rune) rune {
|
||||
if r < 32 && r != ' ' {
|
||||
return -1
|
||||
}
|
||||
|
||||
var reqBody io.Reader
|
||||
if method == "POST" {
|
||||
bs, _ := json.Marshal(reqBodyMap)
|
||||
reqBody = bytes.NewReader(bs)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, queryUrl, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 统一用 headMsg 注入请求头
|
||||
for hk, hv := range ParseHeadMsgHeaders(headMsg) {
|
||||
req.Header.Set(hk, hv)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[PullTaskResult] 请求失败 taskID=%s err=%v", taskID, err)
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
|
||||
g.Log().Infof(ctx, "[PullTaskResult] taskID=%s statusCode=%d body=%s", taskID, resp.StatusCode, string(raw))
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
var result map[string]any
|
||||
_ = json.Unmarshal(raw, &result)
|
||||
|
||||
statusVal := gjson.New(result).Get(statusPath).Val()
|
||||
statusStr := gconv.String(statusVal)
|
||||
g.Log().Infof(ctx, "[PullTaskResult] 状态 taskID=%s status=%v", taskID, statusVal)
|
||||
|
||||
if matchStatus(statusStr, statusValues["succeeded"]) {
|
||||
g.Log().Infof(ctx, "[PullTaskResult] 任务成功 taskID=%s", taskID)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if matchStatus(statusStr, statusValues["failed"]) {
|
||||
g.Log().Errorf(ctx, "[PullTaskResult] 任务失败 taskID=%s", taskID)
|
||||
return result, fmt.Errorf("任务失败")
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
}
|
||||
return r
|
||||
}, s)
|
||||
}
|
||||
|
||||
func matchStatus(actual string, expected any) bool {
|
||||
expectedStr := gconv.String(expected)
|
||||
if actual == expectedStr {
|
||||
return true
|
||||
func tryParseArray(s string) []any {
|
||||
var arr []any
|
||||
if err := json.Unmarshal([]byte(s), &arr); err == nil && len(arr) > 0 {
|
||||
return arr
|
||||
}
|
||||
switch v := expected.(type) {
|
||||
case []any:
|
||||
for _, item := range v {
|
||||
if actual == gconv.String(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceURLParams 替换 URL 中的 {key}
|
||||
func replaceURLParams(url string, params map[string]any) string {
|
||||
re := regexp.MustCompile(`\{([^}]+)}`)
|
||||
return re.ReplaceAllStringFunc(url, func(s string) string {
|
||||
key := strings.Trim(s, "{}")
|
||||
if val, ok := params[key]; ok {
|
||||
return gconv.String(val)
|
||||
}
|
||||
return s
|
||||
})
|
||||
func tryParseAny(s string) any {
|
||||
var parsed any
|
||||
if err := json.Unmarshal([]byte(s), &parsed); err == nil {
|
||||
return parsed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// PullTaskResult 轮询查询异步任务结果
|
||||
func PullTaskResult(ctx context.Context, body map[string]any, queryConfig map[string]any, headMsg map[string]any) (map[string]any, error) {
|
||||
taskID, err := extractTaskID(body, queryConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Log().Infof(ctx, "[PullTaskResult] taskID=%s", taskID)
|
||||
|
||||
queryUrl := buildQueryURL(queryConfig, taskID)
|
||||
method := gconv.String(queryConfig["method"])
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
}
|
||||
interval := gconv.Int(queryConfig["interval_seconds"])
|
||||
if interval <= 0 {
|
||||
interval = 2
|
||||
}
|
||||
|
||||
statusPath := gconv.String(queryConfig["status_path"])
|
||||
if statusPath == "" {
|
||||
statusPath = "status"
|
||||
}
|
||||
statusValues, _ := queryConfig["status_values"].(map[string]any)
|
||||
|
||||
reqBodyMap := map[string]any{"task_id": taskID}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
result, err := doQueryRequest(ctx, method, queryUrl, reqBodyMap, headMsg)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[PullTaskResult] 请求失败 taskID=%s err=%v", taskID, err)
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
statusStr := gconv.String(gjson.New(result).Get(statusPath).Val())
|
||||
g.Log().Infof(ctx, "[PullTaskResult] 状态 taskID=%s status=%s", taskID, statusStr)
|
||||
|
||||
if matchStatus(statusStr, statusValues["succeeded"]) {
|
||||
g.Log().Infof(ctx, "[PullTaskResult] 任务成功 taskID=%s", taskID)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if matchStatus(statusStr, statusValues["failed"]) {
|
||||
g.Log().Errorf(ctx, "[PullTaskResult] 任务失败 taskID=%s", taskID)
|
||||
return result, fmt.Errorf("任务失败")
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(interval) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func extractTaskID(body, queryConfig map[string]any) (string, error) {
|
||||
taskIDPath := gconv.String(queryConfig["task_id"])
|
||||
taskID := gconv.String(gjson.New(body).Get(taskIDPath).Val())
|
||||
if taskID == "" {
|
||||
return "", fmt.Errorf("无法从路径 %s 提取 taskID", taskIDPath)
|
||||
}
|
||||
return taskID, nil
|
||||
}
|
||||
|
||||
func buildQueryURL(queryConfig map[string]any, taskID string) string {
|
||||
queryUrl := gconv.String(queryConfig["url"])
|
||||
return replaceURLParams(queryUrl, map[string]any{"id": taskID})
|
||||
}
|
||||
|
||||
func doQueryRequest(ctx context.Context, method, queryUrl string, reqBodyMap map[string]any, headMsg map[string]any) (map[string]any, error) {
|
||||
var reqBody io.Reader
|
||||
if method == "POST" {
|
||||
bs, _ := json.Marshal(reqBodyMap)
|
||||
reqBody = bytes.NewReader(bs)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, queryUrl, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
for hk, hv := range ParseHeadMsgHeaders(headMsg) {
|
||||
req.Header.Set(hk, hv)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 30 * time.Second}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
raw, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var result map[string]any
|
||||
_ = json.Unmarshal(raw, &result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func matchStatus(actual string, expected any) bool {
|
||||
expectedStr := gconv.String(expected)
|
||||
if actual == expectedStr {
|
||||
return true
|
||||
}
|
||||
if arr, ok := expected.([]any); ok {
|
||||
for _, item := range arr {
|
||||
if actual == gconv.String(item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func replaceURLParams(rawURL string, params map[string]any) string {
|
||||
re := regexp.MustCompile(`\{([^}]+)}`)
|
||||
return re.ReplaceAllStringFunc(rawURL, func(s string) string {
|
||||
key := strings.Trim(s, "{}")
|
||||
if val, ok := params[key]; ok {
|
||||
return gconv.String(val)
|
||||
}
|
||||
return s
|
||||
})
|
||||
}
|
||||
@@ -9,36 +9,34 @@ import (
|
||||
|
||||
// CreateModelReq 添加模型配置
|
||||
type CreateModelReq struct {
|
||||
g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"`
|
||||
ModelName string `p:"modelName" json:"modelName" v:"required#模型名称不能为空" dc:"模型名称(唯一标识)"`
|
||||
ModelType int `p:"modelType" json:"modelType" v:"required#模型类型不能为空" dc:"模型类型"`
|
||||
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#模型地址不能为空" dc:"模型服务地址"`
|
||||
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式:GET/POST(默认POST)"`
|
||||
HeadMsg map[string]any `p:"headMsg" json:"headMsg" dc:"请求头JSON结构"`
|
||||
IsPrivate *int `p:"isPrivate" json:"isPrivate" dc:"是否私有化:0-私有 1-公共"`
|
||||
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用:0-停用 1-启用"`
|
||||
IsChatModel *int `p:"isChatModel" json:"isChatModel" dc:"是否为对话模型:0-否 1-是"`
|
||||
CallModel *int `p:"callModel" json:"callModel" dc:"调用模式:0-同步 1-异步 2-流式"`
|
||||
RequiredFields []string `p:"requiredFields" json:"requiredFields" dc:"必填字段"`
|
||||
IsOwner *int `p:"isOwner" json:"isOwner" dc:"是否为所有者:0-否 1-是"`
|
||||
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥"`
|
||||
Form []map[string]any `p:"form" json:"form" dc:"动态表单配置"`
|
||||
RequestMapping map[string]any `p:"requestMapping" json:"requestMapping" dc:"请求映射"`
|
||||
ResponseMapping map[string]any `p:"responseMapping" json:"responseMapping" dc:"返回映射"`
|
||||
ResponseBody string `p:"responseBody" json:"responseBody" dc:"返回主体"`
|
||||
ResponseTokenField string `p:"responseTokenField" json:"responseTokenField" dc:"响应中消耗token的字段映射"`
|
||||
OperatorName string `p:"operatorName" json:"operatorName" dc:"运营商名称"`
|
||||
TokenConfig map[string]any `p:"tokenConfig" json:"tokenConfig" dc:"token计算配置"`
|
||||
ExtendMapping map[string]any `p:"extendMapping" json:"extendMapping" dc:"附加映射"`
|
||||
QueryConfig map[string]any `p:"queryConfig" json:"queryConfig" dc:"查询/回调配置"`
|
||||
StreamConfig map[string]any `p:"streamConfig" json:"streamConfig" dc:"流式输出配置"`
|
||||
FirstFrame string `p:"firstFrame" json:"firstFrame" dc:"首帧图片参数"`
|
||||
LastFrame string `p:"lastFrame" json:"lastFrame" dc:"尾帧图片参数"`
|
||||
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(默认10)"`
|
||||
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒,默认600)"`
|
||||
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数(默认3)"`
|
||||
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"任务完成后自动清理时间(秒,默认86400)"`
|
||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||
g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"`
|
||||
ModelName string `p:"modelName" json:"modelName" v:"required#模型名称不能为空" dc:"模型名称(唯一标识)"`
|
||||
ModelType int `p:"modelType" json:"modelType" v:"required#模型类型不能为空" dc:"模型类型"`
|
||||
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#模型地址不能为空" dc:"模型服务地址"`
|
||||
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式:GET/POST(默认POST)"`
|
||||
HeadMsg map[string]any `p:"headMsg" json:"headMsg" dc:"请求头JSON结构"`
|
||||
IsPrivate *int `p:"isPrivate" json:"isPrivate" dc:"是否私有化:0-私有 1-公共"`
|
||||
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用:0-停用 1-启用"`
|
||||
IsChatModel *int `p:"isChatModel" json:"isChatModel" dc:"是否为对话模型:0-否 1-是"`
|
||||
CallMode *int `p:"callMode" json:"callMode" dc:"调用模式:0-同步 1-异步 2-流式"`
|
||||
RequiredFields []string `p:"requiredFields" json:"requiredFields" dc:"必填字段"`
|
||||
IsOwner *int `p:"isOwner" json:"isOwner" dc:"是否为所有者:0-否 1-是"`
|
||||
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥"`
|
||||
Form []entity.Form `p:"form" json:"form" dc:"动态表单配置"`
|
||||
RequestMapping map[string]any `p:"requestMapping" json:"requestMapping" dc:"请求映射"`
|
||||
ResponseMapping map[string]any `p:"responseMapping" json:"responseMapping" dc:"返回映射"`
|
||||
OperatorName string `p:"operatorName" json:"operatorName" dc:"运营商名称"`
|
||||
TokenConfig map[string]any `p:"tokenConfig" json:"tokenConfig" dc:"token计算配置"`
|
||||
ExtendMapping map[string]any `p:"extendMapping" json:"extendMapping" dc:"附加映射"`
|
||||
QueryConfig map[string]any `p:"queryConfig" json:"queryConfig" dc:"查询/回调配置"`
|
||||
StreamConfig map[string]any `p:"streamConfig" json:"streamConfig" dc:"流式输出配置"`
|
||||
FirstFrame string `p:"firstFrame" json:"firstFrame" dc:"首帧图片参数"`
|
||||
LastFrame string `p:"lastFrame" json:"lastFrame" dc:"尾帧图片参数"`
|
||||
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(默认10)"`
|
||||
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒,默认600)"`
|
||||
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数(默认3)"`
|
||||
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"任务完成后自动清理时间(秒,默认86400)"`
|
||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||
}
|
||||
|
||||
type CreateModelRes struct {
|
||||
@@ -46,37 +44,35 @@ type CreateModelRes struct {
|
||||
}
|
||||
|
||||
type UpdateModelReq struct {
|
||||
g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"`
|
||||
ID int64 `p:"id" json:"id" v:"required#id不能为空" dc:"配置ID"`
|
||||
ModelName string `p:"modelName" json:"modelName" dc:"模型名称"`
|
||||
ModelType int `p:"modelType" json:"modelType" dc:"模型类型"`
|
||||
BaseURL string `p:"baseUrl" json:"baseUrl" dc:"模型服务地址"`
|
||||
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式:GET/POST"`
|
||||
HeadMsg map[string]any `p:"headMsg" json:"headMsg" dc:"请求头JSON结构"`
|
||||
IsPrivate *int `p:"isPrivate" json:"isPrivate" dc:"是否私有化:0-私有 1-公共"`
|
||||
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用:0-停用 1-启用"`
|
||||
IsChatModel *int `p:"isChatModel" json:"isChatModel" dc:"是否为对话模型:0-否 1-是"`
|
||||
CallModel *int `p:"callModel" json:"callModel" dc:"调用模式:0-同步 1-异步 2-流式"`
|
||||
RequiredFields []string `p:"requiredFields" json:"requiredFields" dc:"必填字段"`
|
||||
IsOwner *int `p:"isOwner" json:"isOwner" dc:"是否为所有者:0-否 1-是"`
|
||||
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥"`
|
||||
Form []map[string]any `p:"form" json:"form" dc:"动态表单配置"`
|
||||
RequestMapping map[string]any `p:"requestMapping" json:"requestMapping" dc:"请求映射"`
|
||||
ResponseMapping map[string]any `p:"responseMapping" json:"responseMapping" dc:"返回映射"`
|
||||
ResponseBody string `p:"responseBody" json:"responseBody" dc:"返回主体"`
|
||||
ResponseTokenField string `p:"responseTokenField" json:"responseTokenField" dc:"响应中消耗token的字段映射"`
|
||||
OperatorName string `p:"operatorName" json:"operatorName" dc:"运营商名称"`
|
||||
TokenConfig map[string]any `p:"tokenConfig" json:"tokenConfig" dc:"token计算配置"`
|
||||
ExtendMapping map[string]any `p:"extendMapping" json:"extendMapping" dc:"附加映射"`
|
||||
QueryConfig map[string]any `p:"queryConfig" json:"queryConfig" dc:"查询/回调配置"`
|
||||
StreamConfig map[string]any `p:"streamConfig" json:"streamConfig" dc:"流式输出配置"`
|
||||
FirstFrame string `p:"firstFrame" json:"firstFrame" dc:"首帧图片参数"`
|
||||
LastFrame string `p:"lastFrame" json:"lastFrame" dc:"尾帧图片参数"`
|
||||
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数"`
|
||||
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒)"`
|
||||
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数"`
|
||||
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"任务完成后自动清理时间(秒)"`
|
||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||
g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"`
|
||||
ID int64 `p:"id" json:"id" v:"required#id不能为空" dc:"配置ID"`
|
||||
ModelName string `p:"modelName" json:"modelName" v:"required#模型名称不能为空" dc:"模型名称(唯一标识)"`
|
||||
ModelType int `p:"modelType" json:"modelType" v:"required#模型类型不能为空" dc:"模型类型"`
|
||||
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#模型地址不能为空" dc:"模型服务地址"`
|
||||
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式:GET/POST(默认POST)"`
|
||||
HeadMsg map[string]any `p:"headMsg" json:"headMsg" dc:"请求头JSON结构"`
|
||||
IsPrivate *int `p:"isPrivate" json:"isPrivate" dc:"是否私有化:0-私有 1-公共"`
|
||||
Enabled *int `p:"enabled" json:"enabled" dc:"是否启用:0-停用 1-启用"`
|
||||
IsChatModel *int `p:"isChatModel" json:"isChatModel" dc:"是否为对话模型:0-否 1-是"`
|
||||
CallMode *int `p:"callMode" json:"callMode" dc:"调用模式:0-同步 1-异步 2-流式"`
|
||||
RequiredFields []string `p:"requiredFields" json:"requiredFields" dc:"必填字段"`
|
||||
IsOwner *int `p:"isOwner" json:"isOwner" dc:"是否为所有者:0-否 1-是"`
|
||||
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥"`
|
||||
Form []entity.Form `p:"form" json:"form" dc:"动态表单配置"`
|
||||
RequestMapping map[string]any `p:"requestMapping" json:"requestMapping" dc:"请求映射"`
|
||||
ResponseMapping map[string]any `p:"responseMapping" json:"responseMapping" dc:"返回映射"`
|
||||
OperatorName string `p:"operatorName" json:"operatorName" dc:"运营商名称"`
|
||||
TokenConfig map[string]any `p:"tokenConfig" json:"tokenConfig" dc:"token计算配置"`
|
||||
ExtendMapping map[string]any `p:"extendMapping" json:"extendMapping" dc:"附加映射"`
|
||||
QueryConfig map[string]any `p:"queryConfig" json:"queryConfig" dc:"查询/回调配置"`
|
||||
StreamConfig map[string]any `p:"streamConfig" json:"streamConfig" dc:"流式输出配置"`
|
||||
FirstFrame string `p:"firstFrame" json:"firstFrame" dc:"首帧图片参数"`
|
||||
LastFrame string `p:"lastFrame" json:"lastFrame" dc:"尾帧图片参数"`
|
||||
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(默认10)"`
|
||||
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒,默认600)"`
|
||||
RetryTimes int `p:"retryTimes" json:"retryTimes" dc:"失败重试次数(默认3)"`
|
||||
AutoCleanSeconds int `p:"autoCleanSeconds" json:"autoCleanSeconds" dc:"任务完成后自动清理时间(秒,默认86400)"`
|
||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||
}
|
||||
|
||||
type UpdateModelRes struct {
|
||||
|
||||
@@ -61,38 +61,64 @@ var ModelGatewayModelCol = modelGatewayModelCol{
|
||||
StreamConfig: "stream_config",
|
||||
FirstFrame: "first_frame",
|
||||
LastFrame: "last_frame",
|
||||
MaxTokens: "max_tokens",
|
||||
}
|
||||
|
||||
type ModelGatewayModel struct {
|
||||
beans.SQLBaseDO `orm:",inline"`
|
||||
ModelName string `orm:"model_name" json:"modelName"`
|
||||
ModelType int `orm:"model_type" json:"modelType"`
|
||||
BaseURL string `orm:"base_url" json:"baseUrl"`
|
||||
HttpMethod string `orm:"http_method" json:"httpMethod"`
|
||||
HeadMsg map[string]any `orm:"head_msg" json:"headMsg"`
|
||||
Form []map[string]any `orm:"form_json" json:"form"`
|
||||
RequestMapping map[string]any `orm:"request_mapping" json:"requestMapping"`
|
||||
ResponseMapping map[string]any `orm:"response_mapping" json:"responseMapping"`
|
||||
RequiredFields []string `orm:"required_fields" json:"requiredFields"`
|
||||
IsPrivate *int `orm:"is_private" json:"isPrivate"`
|
||||
IsChatModel *int `orm:"is_chat_model" json:"isChatModel"`
|
||||
CallMode *int `orm:"call_mode" json:"callMode"`
|
||||
ApiKey string `orm:"api_key" json:"apiKey"`
|
||||
Enabled *int `orm:"enabled" json:"enabled"`
|
||||
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
|
||||
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
|
||||
RetryTimes int `orm:"retry_times" json:"retryTimes"`
|
||||
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
|
||||
IsOwner *int `orm:"is_owner" json:"isOwner"`
|
||||
OperatorName string `orm:"operator_name" json:"operatorName"`
|
||||
TokenConfig map[string]any `orm:"token_config" json:"tokenConfig"`
|
||||
ExtendMapping map[string]any `orm:"extend_mapping" json:"extendMapping"`
|
||||
QueryConfig map[string]any `orm:"query_config" json:"queryConfig"`
|
||||
StreamConfig map[string]any `orm:"stream_config" json:"streamConfig"`
|
||||
FirstFrame string `orm:"first_frame" json:"firstFrame"`
|
||||
LastFrame string `orm:"last_frame" json:"lastFrame"`
|
||||
MaxTokens int `orm:"max_tokens" json:"maxTokens"`
|
||||
ModelName string `orm:"model_name" json:"modelName"`
|
||||
ModelType int `orm:"model_type" json:"modelType"`
|
||||
BaseURL string `orm:"base_url" json:"baseUrl"`
|
||||
HttpMethod string `orm:"http_method" json:"httpMethod"`
|
||||
HeadMsg map[string]any `orm:"head_msg" json:"headMsg"`
|
||||
Form []Form `orm:"form_json" json:"form"`
|
||||
RequestMapping map[string]any `orm:"request_mapping" json:"requestMapping"`
|
||||
ResponseMapping map[string]any `orm:"response_mapping" json:"responseMapping"`
|
||||
RequiredFields []string `orm:"required_fields" json:"requiredFields"`
|
||||
IsPrivate *int `orm:"is_private" json:"isPrivate"`
|
||||
IsChatModel *int `orm:"is_chat_model" json:"isChatModel"`
|
||||
CallMode *int `orm:"call_mode" json:"callMode"`
|
||||
ApiKey string `orm:"api_key" json:"apiKey"`
|
||||
Enabled *int `orm:"enabled" json:"enabled"`
|
||||
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
|
||||
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
|
||||
RetryTimes int `orm:"retry_times" json:"retryTimes"`
|
||||
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
|
||||
IsOwner *int `orm:"is_owner" json:"isOwner"`
|
||||
OperatorName string `orm:"operator_name" json:"operatorName"`
|
||||
TokenConfig map[string]any `orm:"token_config" json:"tokenConfig"`
|
||||
ExtendMapping map[string]any `orm:"extend_mapping" json:"extendMapping"`
|
||||
QueryConfig map[string]any `orm:"query_config" json:"queryConfig"`
|
||||
StreamConfig map[string]any `orm:"stream_config" json:"streamConfig"`
|
||||
FirstFrame string `orm:"first_frame" json:"firstFrame"`
|
||||
LastFrame string `orm:"last_frame" json:"lastFrame"`
|
||||
}
|
||||
|
||||
type Form struct {
|
||||
Key string `json:"key"` // 字段名
|
||||
Value any `json:"value"` // 值
|
||||
Label string `json:"label"` // 标签
|
||||
Type string `json:"type"` // 类型:string / number / boolean / select / radio / upload / json / array
|
||||
DefaultValue any `json:"defaultValue"` // 默认值
|
||||
Required bool `json:"required"` // 是否必填
|
||||
IsForm bool `json:"isForm"` // 是否作为表单(用作工作流展示)
|
||||
Options []map[string]any `json:"options"` // 选项(下拉/单选)
|
||||
FieldConstraint FieldConstraint `json:"fieldConstraint"` // 字段约束
|
||||
}
|
||||
|
||||
type FieldConstraint struct {
|
||||
// 字符串校验
|
||||
MaxLength int `json:"maxLength"` // 最大长度
|
||||
MinLength int `json:"minLength"` // 最小长度
|
||||
|
||||
// 数字校验
|
||||
NumberType string `json:"numberType"` // 数字类型:integer / float / positiveInteger / positiveFloat / negativeInteger / negativeFloat
|
||||
Min any `json:"min"` // 最小值
|
||||
Max any `json:"max"` // 最大值
|
||||
|
||||
// 文件上传校验
|
||||
MaxSize int `json:"maxSize"` // 最大文件(MB)
|
||||
MaxCount int `json:"maxCount"` // 最大上传数量
|
||||
Accept string `json:"accept"` // 允许格式(逗号分隔)
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -191,6 +191,10 @@ func (s *taskService) JobTask(ctx context.Context, req *dto.JobTaskReq) (res *dt
|
||||
func (s *taskService) executeTask(ctx context.Context, task *entity.ModelGatewayTask) error {
|
||||
// 1) 查询模型配置
|
||||
model, err := dao.ModelGatewayModels.Get(ctx, &entity.ModelGatewayModel{
|
||||
SQLBaseDO: beans.SQLBaseDO{
|
||||
TenantId: task.TenantId,
|
||||
Creator: task.Creator,
|
||||
},
|
||||
ModelName: task.ModelName,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -201,7 +205,7 @@ func (s *taskService) executeTask(ctx context.Context, task *entity.ModelGateway
|
||||
}
|
||||
|
||||
// 3) 调用 handleOne
|
||||
AsyncWorker.handleOne(ctx, task, model)
|
||||
AsyncWorker.handleOne(util.AsyncCtx(ctx), task, model)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +40,14 @@ func (w *asyncWorker) handleOne(ctx context.Context, task *entity.ModelGatewayTa
|
||||
err error
|
||||
)
|
||||
|
||||
g.Log().Infof(ctx, "[handleOne] 开始 taskId=%s model=%s", task.TaskID, task.ModelName)
|
||||
g.Log().Infof(ctx, "[任务执行] 开始 taskId=%s model=%s", task.TaskID, task.ModelName)
|
||||
|
||||
// ============================================
|
||||
// 1) 调用模型
|
||||
// ============================================
|
||||
for attempt := 0; ; attempt++ {
|
||||
if attempt > 0 {
|
||||
g.Log().Infof(ctx, "[handleOne] 调模型重试 第%d次 taskId=%s", attempt, task.TaskID)
|
||||
g.Log().Infof(ctx, "[任务执行] 调用模型重试 第%d次 taskId=%s", attempt, task.TaskID)
|
||||
time.Sleep(time.Duration(attempt) * time.Second)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (w *asyncWorker) handleOne(ctx context.Context, task *entity.ModelGatewayTa
|
||||
return
|
||||
}
|
||||
|
||||
g.Log().Warningf(ctx, "[handleOne] 调模型失败 taskId=%s attempt=%d err=%v", task.TaskID, attempt, err)
|
||||
g.Log().Warningf(ctx, "[任务执行] 调用模型失败 taskId=%s 第%d次 err=%v", task.TaskID, attempt, err)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
@@ -94,13 +94,13 @@ func (w *asyncWorker) handleOne(ctx context.Context, task *entity.ModelGatewayTa
|
||||
var oss *gateway.UploadFileResponse
|
||||
for attempt := 0; attempt <= maxRetry; attempt++ {
|
||||
if attempt > 0 {
|
||||
g.Log().Infof(ctx, "[handleOne] OSS上传重试 第%d/%d次 taskId=%s", attempt, maxRetry, task.TaskID)
|
||||
g.Log().Infof(ctx, "[任务执行] OSS上传重试 第%d/%d次 taskId=%s", attempt, maxRetry, task.TaskID)
|
||||
}
|
||||
oss, err = gateway.UploadByTask(ctx, gjson.New(result).MustToJson(), "json")
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
g.Log().Errorf(ctx, "[handleOne] OSS上传失败 taskId=%s attempt=%d/%d err=%v", task.TaskID, attempt, maxRetry, err)
|
||||
g.Log().Errorf(ctx, "[任务执行] OSS上传失败 taskId=%s 第%d/%d次 err=%v", task.TaskID, attempt, maxRetry, err)
|
||||
if attempt == maxRetry {
|
||||
w.failTask(ctx, task, startTime, fmt.Sprintf("OSS上传重试耗尽: %v", err))
|
||||
return
|
||||
@@ -119,7 +119,7 @@ func (w *asyncWorker) handleOne(ctx context.Context, task *entity.ModelGatewayTa
|
||||
}
|
||||
|
||||
if _, err = dao.ModelGatewayTask.Update(ctx, task); err != nil {
|
||||
g.Log().Errorf(ctx, "[handleOne] 更新DB失败 taskId=%s err=%v", task.TaskID, err)
|
||||
g.Log().Errorf(ctx, "[任务执行] 更新数据库失败 taskId=%s err=%v", task.TaskID, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func (w *asyncWorker) handleOne(ctx context.Context, task *entity.ModelGatewayTa
|
||||
go gateway.TriggerPromptsCallback(util.AsyncCtx(ctx), task)
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "[handleOne] 成功 taskId=%s duration=%ds fileType=%s",
|
||||
g.Log().Infof(ctx, "[任务执行] 成功 taskId=%s 耗时=%ds 文件类型=%s",
|
||||
task.TaskID, task.DurationSeconds, oss.FileFormat)
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ type asyncResult struct {
|
||||
// asyncTaskChan 全局异步任务等待通道
|
||||
var asyncTaskChan = sync.Map{} // taskID → chan asyncResult
|
||||
|
||||
func (w *asyncWorker) callModelAsync(ctx context.Context, task *entity.ModelGatewayTask, model *entity.ModelGatewayModel, body map[string]any) (map[string]any, error) {
|
||||
func (w *asyncWorker) callModelAsync(ctx context.Context, model *entity.ModelGatewayModel, body map[string]any) (map[string]any, error) {
|
||||
// 1. 提交异步任务
|
||||
body, err := w.callModel(ctx, model, body)
|
||||
if err != nil {
|
||||
@@ -235,7 +235,7 @@ func (w *asyncWorker) parseAndRetry(ctx context.Context, body map[string]any, ta
|
||||
var parsed map[string]any
|
||||
switch task.BuildType {
|
||||
case public.BuildTypePrompt, public.BuildTypeNode:
|
||||
parsed, err = util.ParseAndValidate(mapped, model)
|
||||
parsed, err = util.ParseAndValidate(mapped, model.RequiredFields)
|
||||
if err == nil {
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user