update: 更新配置文件中的服务地址,修改模型管理相关代码,调整数据结构和逻辑,优化模型列表查询和会话模型设置,更新数据库表结构和索引,修改模块名称和依赖版本

This commit is contained in:
2026-05-15 14:56:26 +08:00
parent adf1d0ae6e
commit bac9d7713f
27 changed files with 286 additions and 292 deletions

View File

@@ -1,5 +1,5 @@
server: server:
address: ":8001" address: ":3004"
name: "model-gateway" name: "model-gateway"
workerId: 1 # 雪花算法worker ID用于 common/db/gfdb workerId: 1 # 雪花算法worker ID用于 common/db/gfdb
@@ -29,14 +29,14 @@ database:
redis: redis:
default: default:
address: 116.204.74.41:6379 address: 192.168.3.30:6379
db: 0 db: 0
consul: consul:
address: 116.204.74.41:8500 address: 192.168.3.30:8500
jaeger: jaeger:
addr: 116.204.74.41:4318 addr: 192.168.3.30:4318
# 本地调试用:可选自动执行 worker/cleaner默认关闭 # 本地调试用:可选自动执行 worker/cleaner默认关闭
asynch: asynch:

View File

@@ -1,9 +1,8 @@
package public package public
const ( const (
TableNameModel = "asynch_models" // 模型表 TableNameModel = "asynch_models" // 模型表
TableNameModelType = "asynch_models_type" // 模型类型 TableNameTask = "asynch_task" // 任务
TableNameTask = "asynch_task" // 任务 TableNameOpLog = "logs_model_op" // 操作日志
TableNameOpLog = "logs_model_op" // 操作日志表 TableNameStat = "logs_model_stat" // 按天统计表(请求次数)
TableNameStat = "logs_model_stat" // 按天统计表(请求次数)
) )

View File

@@ -3,9 +3,9 @@ package controller
import ( import (
"context" "context"
"model-asynch/model/dto" "model-gateway/model/dto"
"model-asynch/model/entity" "model-gateway/model/entity"
"model-asynch/service" "model-gateway/service"
"gitea.com/red-future/common/beans" "gitea.com/red-future/common/beans"
) )
@@ -38,21 +38,15 @@ func (c *model) GetModel(ctx context.Context, req *dto.GetModelReq) (res *dto.Ge
if err != nil { if err != nil {
return nil, err return nil, err
} }
if model == nil {
return nil, nil
}
return &dto.GetModelRes{Model: model}, nil return &dto.GetModelRes{Model: model}, nil
} }
// ListModel 配置列表 // ListModel 配置列表
func (c *model) ListModel(ctx context.Context, req *dto.ListModelReq) (res *dto.ListModelRes, err error) { func (c *model) ListModel(ctx context.Context, req *dto.ListModelReq) (res *dto.ListModelRes, err error) {
pageNum, pageSize := 1, 10 //默认分页参数 list, total, err := service.Model.List(ctx, req)
if req != nil {
if req.PageNum > 0 {
pageNum = req.PageNum
}
if req.PageSize > 0 {
pageSize = req.PageSize
}
}
list, total, err := service.Model.List(ctx, pageNum, pageSize, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -3,8 +3,8 @@ package controller
import ( import (
"context" "context"
"model-asynch/model/dto" "model-gateway/model/dto"
"model-asynch/service" "model-gateway/service"
) )
type stat struct{} type stat struct{}

View File

@@ -3,8 +3,8 @@ package controller
import ( import (
"context" "context"
"model-asynch/model/dto" "model-gateway/model/dto"
"model-asynch/service" "model-gateway/service"
) )
type task struct{} type task struct{}

View File

@@ -4,11 +4,12 @@ import (
"context" "context"
"fmt" "fmt"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/dto" "model-gateway/model/dto"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
) )
@@ -17,8 +18,13 @@ var Model = &modelDao{}
type modelDao struct{} type modelDao struct{}
func (d *modelDao) Insert(ctx context.Context, m *entity.AsynchModel) (id int64, err error) { func (d *modelDao) Insert(ctx context.Context, req *dto.CreateModelReq) (id int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).Data(m).Insert() asyncModel := new(entity.AsynchModel)
err = gconv.Struct(req, &asyncModel)
if err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).Data(asyncModel).Insert()
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -38,20 +44,6 @@ func (d *modelDao) Update(ctx context.Context, m *dto.UpdateModelReq) (rows int6
return r.RowsAffected() return r.RowsAffected()
} }
func (d *modelDao) UpdateByID(ctx context.Context, m *dto.UpdateModelReq) (rows int64, err error) {
// 专用于切换会话模型,只更新 is_chat_model 字段
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).
Where(entity.AsynchModelCol.Id, m.ID).
Data(g.Map{
"is_chat_model": m.IsChatModel,
}).
Update()
if err != nil {
return 0, err
}
return r.RowsAffected()
}
func (d *modelDao) DeleteByID(ctx context.Context, id string) (rows int64, err error) { func (d *modelDao) DeleteByID(ctx context.Context, id string) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel). r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).
Where(entity.AsynchModelCol.Id, id). Where(entity.AsynchModelCol.Id, id).
@@ -78,6 +70,7 @@ func (d *modelDao) GetByModelName(ctx context.Context, modelName string) (m *ent
func (d *modelDao) Get(ctx context.Context, id int64) (m *entity.AsynchModel, err error) { func (d *modelDao) Get(ctx context.Context, id int64) (m *entity.AsynchModel, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel). r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).
NoTenantId(ctx).
Where(entity.AsynchModelCol.Id, id). Where(entity.AsynchModelCol.Id, id).
One() One()
if err != nil { if err != nil {
@@ -90,6 +83,13 @@ func (d *modelDao) Get(ctx context.Context, id int64) (m *entity.AsynchModel, er
return return
} }
func (d *modelDao) Count(ctx context.Context, req *dto.GetModelReq) (count int, err error) {
count, err = gfdb.DB(ctx).Model(ctx, public.TableNameModel).OmitEmpty().
Where(entity.AsynchModelCol.Creator, req.Creator).
Where(entity.AsynchModelCol.Id, req.ID).Count()
return
}
func (d *modelDao) List(ctx context.Context, pageNum, pageSize int, modelNameLike string, modelType int, isPrivate int) (list []*entity.AsynchModel, total int64, err error) { func (d *modelDao) List(ctx context.Context, pageNum, pageSize int, modelNameLike string, modelType int, isPrivate int) (list []*entity.AsynchModel, total int64, err error) {
model := gfdb.DB(ctx).Model(ctx, public.TableNameModel). model := gfdb.DB(ctx).Model(ctx, public.TableNameModel).
OrderDesc(entity.AsynchModelCol.CreatedAt) OrderDesc(entity.AsynchModelCol.CreatedAt)
@@ -97,7 +97,7 @@ func (d *modelDao) List(ctx context.Context, pageNum, pageSize int, modelNameLik
model = model.WhereLike(entity.AsynchModelCol.ModelName, "%"+modelNameLike+"%") model = model.WhereLike(entity.AsynchModelCol.ModelName, "%"+modelNameLike+"%")
} }
if modelType != 0 { if modelType != 0 {
model = model.Where(entity.AsynchModelCol.ModelsType, modelType) model = model.Where(entity.AsynchModelCol.ModelType, modelType)
} }
if isPrivate != 0 { if isPrivate != 0 {
model = model.Where(entity.AsynchModelCol.IsPrivate, isPrivate) model = model.Where(entity.AsynchModelCol.IsPrivate, isPrivate)
@@ -150,39 +150,64 @@ func (d *modelDao) ListByCreatorAndPlatform(ctx context.Context, creator string,
err = r.Structs(&list) err = r.Structs(&list)
return return
} }
func (d *modelDao) GetByCreatorAndPlatform(ctx context.Context, req *dto.ListModelReq) (list []*entity.AsynchModel, total int, err error) {
func (d *modelDao) GetByCreatorAndPlatform(ctx context.Context, creator string, modelNameLike string, modelType int, isPrivate int) (list []*entity.AsynchModel, err error) { // 基础 SQL
whereSQL := "deleted_at IS NULL AND (tenant_id = 1 OR creator = ?)" sql := `
args := []any{creator} SELECT DISTINCT ON (model_name) *
FROM asynch_models
if modelNameLike != "" { WHERE deleted_at IS NULL
whereSQL += " AND model_name LIKE ?" AND (? = '' OR model_name LIKE ?)
args = append(args, "%"+modelNameLike+"%") AND (? = 0 OR model_type = ?)
`
args := []any{
req.ModelName, "%" + req.ModelName + "%",
req.ModelType, req.ModelType,
} }
if modelType != 0 { if !g.IsEmpty(req.IsPrivate) {
whereSQL += " AND models_type = ?" sql += ` AND is_private = ? `
args = append(args, modelType) args = append(args, req.IsPrivate)
} }
if isPrivate != 0 { if req.IsOwner != nil && *req.IsOwner == 0 {
whereSQL += " AND is_private = ?" sql += ` AND creator = ? AND is_owner = ? `
args = append(args, isPrivate) args = append(args, req.Creator)
args = append(args, req.IsOwner)
} else if req.IsOwner != nil && *req.IsOwner == 1 {
if req.Enabled != nil && *req.Enabled == 1 {
sql += ` AND ((creator = ? AND is_owner = ? AND enabled=1) OR (is_owner = 0 AND enabled=1)) `
} else if req.Enabled != nil && *req.Enabled == 0 {
sql += ` AND ((creator = ? AND is_owner = ? AND enabled=0) OR (is_owner = 0 AND enabled=1)) `
} else {
sql += ` AND ((creator = ? AND is_owner = ?) OR (is_owner = 0 AND enabled=1)) `
}
args = append(args, req.Creator)
args = append(args, req.IsOwner)
} }
querySQL := fmt.Sprintf("SELECT * FROM %s WHERE %s ORDER BY created_at DESC", public.TableNameModel, whereSQL) // 最后拼接排序
sql += ` ORDER BY model_name, is_owner DESC, created_at DESC`
r, err := gfdb.DB(ctx).GetAll(ctx, querySQL, args...) r, err := gfdb.DB(ctx).GetAll(ctx, sql, args...)
if err != nil { if err != nil {
return nil, err return nil, 0, err
} }
err = r.Structs(&list) err = r.Structs(&list)
if err != nil {
return nil, 0, err
}
total = len(list)
return return
} }
func (d *modelDao) GetByIsChatModel(ctx context.Context, userName string) (m *entity.AsynchModel, err error) { func (d *modelDao) GetByIsChatModel(ctx context.Context) (m *entity.AsynchModel, err error) {
userInfo, err := utils.GetUserInfo(ctx)
if err != nil {
return nil, err
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel). r, err := gfdb.DB(ctx).Model(ctx, public.TableNameModel).
Where(entity.AsynchModelCol.IsChatModel, 1). Where(entity.AsynchModelCol.IsChatModel, 1).
Where(entity.AsynchModelCol.Creator, userName). Where(entity.AsynchModelCol.Creator, userInfo.UserName).
One() One()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -3,8 +3,8 @@ package dao
import ( import (
"context" "context"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
) )

View File

@@ -3,8 +3,8 @@ package dao
import ( import (
"context" "context"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
) )

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"time" "time"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"time" "time"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/database/gdb"

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"time" "time"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/database/gdb"

4
go.mod
View File

@@ -1,9 +1,9 @@
module model-asynch module model-gateway
go 1.26.0 go 1.26.0
require ( require (
gitea.com/red-future/common v0.0.19 // indirect gitea.com/red-future/common v0.0.19
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0 github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0 github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.0
github.com/gogf/gf/v2 v2.10.0 github.com/gogf/gf/v2 v2.10.0

1
go.sum
View File

@@ -603,6 +603,7 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

View File

@@ -7,8 +7,8 @@ import (
"syscall" "syscall"
"time" "time"
"model-asynch/controller" "model-gateway/controller"
"model-asynch/service" "model-gateway/service"
"gitea.com/red-future/common/http" "gitea.com/red-future/common/http"
"gitea.com/red-future/common/jaeger" "gitea.com/red-future/common/jaeger"

View File

@@ -1,6 +1,7 @@
package dto package dto
import ( import (
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )
@@ -8,14 +9,15 @@ import (
type CreateModelReq struct { type CreateModelReq struct {
g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"` g.Meta `path:"/createModel" method:"post" tags:"模型管理" summary:"创建模型配置" dc:"添加新的模型配置"`
ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"模型名称(唯一标识)"` ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"模型名称(唯一标识)"`
ModelsType int `p:"modelsType" json:"modelsType" v:"required#modelsType不能为空" dc:"模型类型1-文本生成 2-图像生成 3-语音 4-视频 5-多模态"` ModelType int `p:"modelType" json:"modelType" v:"required#modelType不能为空" dc:"模型类型1-文本生成 2-图像生成 3-语音 4-视频 5-多模态"`
BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#baseUrl不能为空" dc:"模型服务基础地址(如 http(s)://host:port"` BaseURL string `p:"baseUrl" json:"baseUrl" v:"required#baseUrl不能为空" dc:"模型服务基础地址(如 http(s)://host:port"`
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST默认POST"` HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST默认POST"`
HeadMsg string `p:"headMsg" json:"headMsg" dc:"请求头绑定支持多个逗号分隔示例Authorization:Bearer xxx,Content-Type:application/json"` HeadMsg string `p:"headMsg" json:"headMsg" dc:"请求头绑定支持多个逗号分隔示例Authorization:Bearer xxx,Content-Type:application/json"`
IsPrivate int `p:"isPrivate" json:"isPrivate" v:"in:0,1#私有化参数只能为0或1" dc:"是否私有化0-私有(默认) 1-公共"` IsPrivate *int `p:"isPrivate" json:"isPrivate" v:"in:0,1#私有化参数只能为0或1" dc:"是否私有化0-私有(默认) 1-公共"`
Enabled int `p:"enabled" json:"enabled" v:"in:0,1#启用参数只能为0或1" dc:"是否启用0-禁用1-启用默认1"` Enabled *int `p:"enabled" json:"enabled" v:"in:0,1#启用参数只能为0或1" dc:"是否启用0-禁用1-启用默认1"`
IsChatModel int `p:"isChatModel" json:"isChatModel" v:"in:0,1#对话模型参数只能为0或1" dc:"是否为对话模型0-否1-是默认0"` IsChatModel *int `p:"isChatModel" json:"isChatModel" v:"in:0,1#对话模型参数只能为0或1" dc:"是否为对话模型0-否1-是默认0"`
ApiKey string `p:"apiKey" json:"apiKey" v:"required-if:isPrivate,1#公共模型必须填写API密钥" dc:"调用凭证/密钥,用于模型认证"` IsOwner *int `p:"isOwner" json:"isOwner" v:"in:0,1#是否为所有者参数只能为0或1" dc:"是否为所有者0-否1-是默认0"`
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥,用于模型认证"`
Form any `p:"form" json:"form" dc:"动态表单配置JSON用于前端渲染配置项"` Form any `p:"form" json:"form" dc:"动态表单配置JSON用于前端渲染配置项"`
RequestMapping any `p:"requestMapping" json:"requestMapping" dc:"请求映射"` RequestMapping any `p:"requestMapping" json:"requestMapping" dc:"请求映射"`
ResponseMapping any `p:"responseMapping" json:"responseMapping" dc:"返回映射"` ResponseMapping any `p:"responseMapping" json:"responseMapping" dc:"返回映射"`
@@ -38,17 +40,21 @@ type CreateModelRes struct {
type UpdateModelReq struct { type UpdateModelReq struct {
g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"` g.Meta `path:"/updateModel" method:"put" tags:"模型管理" summary:"更新模型配置" dc:"更新指定ID的模型配置"`
ID int64 `p:"id" json:"id" v:"required#id不能为空" dc:"配置ID"` ID int64 `p:"id" json:"id" v:"required#id不能为空" dc:"配置ID"`
ModelsType string `p:"modelsType" json:"modelsType" dc:"模型类型ID列表逗号分隔可选更新"` ModelName string `p:"modelName" json:"modelName" dc:"模型名称(唯一标识"`
ModelType int `p:"modelType" json:"modelType" dc:"模型类型ID列表逗号分隔可选更新"`
BaseURL string `p:"baseUrl" json:"baseUrl" dc:"模型服务基础地址"` BaseURL string `p:"baseUrl" json:"baseUrl" dc:"模型服务基础地址"`
HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST可选更新"` HttpMethod string `p:"httpMethod" json:"httpMethod" dc:"请求方式GET/POST可选更新"`
HeadMsg string `p:"headMsg" json:"headMsg" dc:"请求头绑定(可选更新)"` HeadMsg string `p:"headMsg" json:"headMsg" dc:"请求头绑定(可选更新)"`
ApiKey string `p:"apiKey" json:"apiKey" dc:"调用凭证/密钥,用于模型认证(可选更新)"`
Form any `p:"form" json:"form" dc:"动态表单配置JSON可选更新"` Form any `p:"form" json:"form" dc:"动态表单配置JSON可选更新"`
RequestMapping any `p:"requestMapping" json:"requestMapping" dc:"请求参数映射(可选更新)"` RequestMapping any `p:"requestMapping" json:"requestMapping" dc:"请求参数映射(可选更新)"`
ResponseMapping any `p:"responseMapping" json:"responseMapping" dc:"返回参数映射(可选更新)"` ResponseMapping any `p:"responseMapping" json:"responseMapping" dc:"返回参数映射(可选更新)"`
ResponseBody any `p:"responseBody" json:"responseBody" dc:"返回主体(可选更新)"` ResponseBody any `p:"responseBody" json:"responseBody" dc:"返回主体(可选更新)"`
TokenMapping string `p:"tokenMapping" json:"tokenMapping" dc:"token映射可选更新"` TokenMapping string `p:"tokenMapping" json:"tokenMapping" dc:"token映射可选更新"`
Enabled int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用(可选更新)"` Enabled *int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用(可选更新)"`
IsChatModel int `p:"isChatModel" json:"isChatModel" v:"in:0,1#对话模型参数只能为0或1" dc:"是否为对话模型0-否1-是默认0"` IsPrivate *int `p:"isPrivate" json:"isPrivate" v:"in:0,1#私有化参数只能为0或1" dc:"是否私有化0-私有(默认) 1-公共"`
IsChatModel *int `p:"isChatModel" json:"isChatModel" v:"in:0,1#对话模型参数只能为0或1" dc:"是否为对话模型0-否1-是默认0"`
IsOwner *int `p:"isOwner" json:"isOwner" v:"in:0,1#是否为所有者参数只能为0或1" dc:"是否为所有者0-否1-是默认0"`
MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(可选更新)"` MaxConcurrency int `p:"maxConcurrency" json:"maxConcurrency" dc:"最大并发数(可选更新)"`
QueueLimit int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(可选更新)"` QueueLimit int `p:"queueLimit" json:"queueLimit" dc:"排队队列上限(可选更新)"`
TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒)(可选更新)"` TimeoutSeconds int `p:"timeoutSeconds" json:"timeoutSeconds" dc:"请求超时时间(秒)(可选更新)"`
@@ -67,8 +73,9 @@ type DeleteModelReq struct {
// GetModelReq 获取模型配置详情 // GetModelReq 获取模型配置详情
type GetModelReq struct { type GetModelReq struct {
g.Meta `path:"/getModel" method:"get" tags:"模型管理" summary:"获取模型配置" dc:"根据模型ID获取配置详情"` g.Meta `path:"/getModel" method:"get" tags:"模型管理" summary:"获取模型配置" dc:"根据模型ID获取配置详情"`
ID int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"` ID int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"`
Creator string `p:"creator" json:"creator" dc:"创建人"`
} }
type GetModelRes struct { type GetModelRes struct {
@@ -78,16 +85,18 @@ type GetModelRes struct {
// ListModelReq 配置列表 // ListModelReq 配置列表
type ListModelReq struct { type ListModelReq struct {
g.Meta `path:"/listModel" method:"get" tags:"模型管理" summary:"模型配置列表" dc:"分页获取模型配置列表"` g.Meta `path:"/listModel" method:"get" tags:"模型管理" summary:"模型配置列表" dc:"分页获取模型配置列表"`
PageNum int `p:"pageNum" json:"pageNum" dc:"页码默认1"` Page *beans.Page `json:"page"`
PageSize int `p:"pageSize" json:"pageSize" dc:"每页条数默认10"` ModelName string `p:"modelName" json:"modelName" dc:"模型名称(模糊查询,可选"`
ModelName string `p:"modelName" json:"modelName" dc:"模型名称(模糊查询,可选)"` ModelType int `p:"modelType" json:"modelType" dc:"模型类型"`
ModelType int `p:"modelType" json:"modelType" dc:"模型类型"` Enabled *int `p:"enabled" json:"enabled" dc:"是否启用0-禁用1-启用"`
IsPrivate int `p:"isPrivate" json:"isPrivate" dc:"是否私有化 0-私有 1-公共"` IsPrivate *int `p:"isPrivate" json:"isPrivate" dc:"是否私有化 0-私有 1-公共"`
IsOwner *int `p:"isOwner" json:"isOwner" dc:"是否为所有者 0-否 1-是"`
Creator string `p:"creator" json:"creator" dc:"创建人"`
} }
type ListModelRes struct { type ListModelRes struct {
List any `json:"list" dc:"列表数据"` List any `json:"list" dc:"列表数据"`
Total int64 `json:"total" dc:"总数"` Total int `json:"total" dc:"总数"`
} }
// AutoTuneReq 动态调参(由上层定时任务每小时触发一次) // AutoTuneReq 动态调参(由上层定时任务每小时触发一次)

View File

@@ -5,7 +5,7 @@ import "gitea.com/red-future/common/beans"
type asynchModelCol struct { type asynchModelCol struct {
beans.SQLBaseCol beans.SQLBaseCol
ModelName string ModelName string
ModelsType string ModelType string
BaseURL string BaseURL string
HttpMethod string HttpMethod string
HeadMsg string HeadMsg string
@@ -27,12 +27,13 @@ type asynchModelCol struct {
RetryQueueMaxSecs string RetryQueueMaxSecs string
AutoCleanSeconds string AutoCleanSeconds string
Remark string Remark string
IsOwner string
} }
var AsynchModelCol = asynchModelCol{ var AsynchModelCol = asynchModelCol{
SQLBaseCol: beans.DefSQLBaseCol, SQLBaseCol: beans.DefSQLBaseCol,
ModelName: "model_name", ModelName: "model_name",
ModelsType: "models_type", ModelType: "model_type",
BaseURL: "base_url", BaseURL: "base_url",
HttpMethod: "http_method", HttpMethod: "http_method",
HeadMsg: "head_msg", HeadMsg: "head_msg",
@@ -54,13 +55,14 @@ var AsynchModelCol = asynchModelCol{
RetryQueueMaxSecs: "retry_queue_max_seconds", RetryQueueMaxSecs: "retry_queue_max_seconds",
AutoCleanSeconds: "auto_clean_seconds", AutoCleanSeconds: "auto_clean_seconds",
Remark: "remark", Remark: "remark",
IsOwner: "is_owner",
} }
// AsynchModel 异步模型配置 // AsynchModel 异步模型配置
type AsynchModel struct { type AsynchModel struct {
beans.SQLBaseDO `orm:",inline"` beans.SQLBaseDO `orm:",inline"`
ModelName string `orm:"model_name" json:"modelName"` ModelName string `orm:"model_name" json:"modelName"`
ModelsType int `orm:"models_type" json:"modelsType"` ModelType int `orm:"model_type" json:"modelType"`
BaseURL string `orm:"base_url" json:"baseUrl"` BaseURL string `orm:"base_url" json:"baseUrl"`
HttpMethod string `orm:"http_method" json:"httpMethod"` HttpMethod string `orm:"http_method" json:"httpMethod"`
HeadMsg string `orm:"head_msg" json:"headMsg"` HeadMsg string `orm:"head_msg" json:"headMsg"`
@@ -70,10 +72,10 @@ type AsynchModel struct {
ResponseBody any `orm:"response_body" json:"responseBody"` ResponseBody any `orm:"response_body" json:"responseBody"`
TokenMapping string `orm:"token_mapping" json:"tokenMapping"` TokenMapping string `orm:"token_mapping" json:"tokenMapping"`
Prompt string `orm:"prompt" json:"prompt"` Prompt string `orm:"prompt" json:"prompt"`
IsPrivate int `orm:"is_private" json:"isPrivate"` IsPrivate *int `orm:"is_private" json:"isPrivate"`
IsChatModel int `orm:"is_chat_model" json:"isChatModel"` IsChatModel *int `orm:"is_chat_model" json:"isChatModel"`
ApiKey string `orm:"api_key" json:"apiKey"` ApiKey string `orm:"api_key" json:"apiKey"`
Enabled int `orm:"enabled" json:"enabled"` Enabled *int `orm:"enabled" json:"enabled"`
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"` MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
QueueLimit int `orm:"queue_limit" json:"queueLimit"` QueueLimit int `orm:"queue_limit" json:"queueLimit"`
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"` TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
@@ -82,4 +84,5 @@ type AsynchModel struct {
RetryQueueMaxSeconds int `orm:"retry_queue_max_seconds" json:"retryQueueMaxSeconds"` RetryQueueMaxSeconds int `orm:"retry_queue_max_seconds" json:"retryQueueMaxSeconds"`
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"` AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
Remark string `orm:"remark" json:"remark"` Remark string `orm:"remark" json:"remark"`
IsOwner *int `json:"isOwner" orm:"is_owner"` // 1=当前用户创建的0=超级管理员的
} }

View File

@@ -5,8 +5,8 @@ import (
"fmt" "fmt"
"math" "math"
"model-asynch/consts/public" "model-gateway/consts/public"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/db/gfdb" "gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -14,9 +14,9 @@ import (
// AutoTuneResult 单次调参结果(按 model_name // AutoTuneResult 单次调参结果(按 model_name
type AutoTuneResult struct { type AutoTuneResult struct {
ModelName string `json:"modelName"` // 模型名称asynch_models.model_name ModelName string `json:"modelName"` // 模型名称asynch_models.model_name
Samples int `json:"samples"` // 统计样本数(窗口内 state=2/3 且 started_at/finished_at 非空的任务数量) Samples int `json:"samples"` // 统计样本数(窗口内 state=2/3 且 started_at/finished_at 非空的任务数量)
P90Exec float64 `json:"p90ExecSeconds"` // 执行耗时 P90口径finished_at - started_at P90Exec float64 `json:"p90ExecSeconds"` // 执行耗时 P90口径finished_at - started_at
CapMaxConcurrency int `json:"capMaxConcurrency"` // 配置上限asynch_models.max_concurrencycap不会被动态调参覆盖 CapMaxConcurrency int `json:"capMaxConcurrency"` // 配置上限asynch_models.max_concurrencycap不会被动态调参覆盖
OldMaxConcurrency int `json:"oldMaxConcurrency"` // 调参前运行时值Redis若无则等于 cap OldMaxConcurrency int `json:"oldMaxConcurrency"` // 调参前运行时值Redis若无则等于 cap

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"model-asynch/model/entity" "model-gateway/model/entity"
"gitea.com/red-future/common/http" "gitea.com/red-future/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -65,23 +65,3 @@ func triggerPromptsCallback(ctx context.Context, t *entity.AsynchTask, epicycleI
} }
g.Log().Infof(ctx, "[提示词回调] 发送成功 epicycleId=%d 回调地址=%s 消息体大小=%d字节", t.EpicycleId, callbackURL, len(jsonData)) g.Log().Infof(ctx, "[提示词回调] 发送成功 epicycleId=%d 回调地址=%s 消息体大小=%d字节", t.EpicycleId, callbackURL, len(jsonData))
} }
// IsSuperAdmin 调用admin-go服务检查是否是超级管理员
func IsSuperAdmin(ctx context.Context) (res bool, err error) {
headers := forwardHeaders(ctx)
var r = make(map[string]bool)
if err = http.Get(ctx, "admin-go/api/v1/system/user/checkIsSuperAdmin", headers, &r); err != nil {
return false, err
}
return r["isSuperAdmin"], err
}
// IsAdmin 调用admin-go服务检查是否是管理员
func IsAdmin(ctx context.Context) (res bool, err error) {
headers := forwardHeaders(ctx)
var r = make(map[string]bool)
if err = http.Get(ctx, "admin-go/api/v1/system/user/checkIsSuperAdmin", headers, &r); err != nil {
return false, err
}
return r["isSuperAdmin"], err
}

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"time" "time"
"model-asynch/dao" "model-gateway/dao"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
) )

View File

@@ -11,7 +11,7 @@ import (
"strings" "strings"
"time" "time"
"model-asynch/model/entity" "model-gateway/model/entity"
"github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"

View File

@@ -3,13 +3,15 @@ package service
import ( import (
"context" "context"
"errors" "errors"
"sort" "model-gateway/dao"
"model-gateway/model/dto"
"model-asynch/dao" "model-gateway/model/entity"
"model-asynch/model/dto"
"model-asynch/model/entity"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/db/gfdb"
"gitea.com/red-future/common/http"
"gitea.com/red-future/common/utils" "gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
) )
@@ -18,32 +20,45 @@ var Model = &modelService{}
type modelService struct{} type modelService struct{}
func (s *modelService) Create(ctx context.Context, req *dto.CreateModelReq) (res *dto.CreateModelRes, err error) { // IsSuperAdmin 调用admin-go服务检查是否是超级管理员
m := &entity.AsynchModel{ func (s *modelService) IsSuperAdmin(ctx context.Context) (res bool, err error) {
ModelName: req.ModelName, headers := forwardHeaders(ctx)
ModelsType: req.ModelsType, var r = make(map[string]bool)
BaseURL: req.BaseURL, if err = http.Get(ctx, "admin-go/api/v1/system/user/checkIsSuperAdmin", headers, &r); err != nil {
HttpMethod: req.HttpMethod, return false, err
HeadMsg: req.HeadMsg,
IsPrivate: req.IsPrivate,
Enabled: req.Enabled,
IsChatModel: req.IsChatModel,
ApiKey: req.ApiKey,
Form: req.Form,
RequestMapping: req.RequestMapping,
ResponseMapping: req.ResponseMapping,
ResponseBody: req.ResponseBody,
TokenMapping: req.TokenMapping,
MaxConcurrency: req.MaxConcurrency,
QueueLimit: req.QueueLimit,
TimeoutSeconds: req.TimeoutSeconds,
ExpectedSeconds: req.ExpectedSeconds,
RetryTimes: req.RetryTimes,
RetryQueueMaxSeconds: req.RetryQueueMaxSeconds,
AutoCleanSeconds: req.AutoCleanSeconds,
Remark: req.Remark,
} }
id, err := dao.Model.Insert(ctx, m) return r["isSuperAdmin"], err
}
func (s *modelService) Create(ctx context.Context, req *dto.CreateModelReq) (res *dto.CreateModelRes, err error) {
// 获取当前会话模型
if !g.IsEmpty(req.IsChatModel) && *req.IsChatModel == 1 {
var model *entity.AsynchModel
model, err = dao.Model.GetByIsChatModel(ctx)
if err != nil {
return nil, err
}
// 如果有会话模型,那就改变为 0
if model != nil {
_, err = dao.Model.Update(ctx, &dto.UpdateModelReq{
ID: model.Id,
IsChatModel: gconv.PtrInt(0),
})
if err != nil {
return nil, err
}
}
}
req.IsOwner = gconv.PtrInt(1)
admin, err := s.IsSuperAdmin(ctx)
if err != nil {
return
}
if admin {
req.IsOwner = gconv.PtrInt(0)
}
id, err := dao.Model.Insert(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,23 +67,55 @@ func (s *modelService) Create(ctx context.Context, req *dto.CreateModelReq) (res
func (s *modelService) Update(ctx context.Context, req *dto.UpdateModelReq) error { func (s *modelService) Update(ctx context.Context, req *dto.UpdateModelReq) error {
//根据当前 isChatModel 来判断是否更新模型 //根据当前 isChatModel 来判断是否更新模型
if req.IsChatModel == 1 { if req.IsChatModel == gconv.PtrInt(1) {
user, err := utils.GetUserInfo(ctx)
if err != nil {
return err
}
//判断当前用户是否有会话模型 //判断当前用户是否有会话模型
model, err := dao.Model.GetByIsChatModel(ctx, user.UserName) model, err := dao.Model.GetByIsChatModel(ctx)
if err != nil { if err != nil {
return err return err
} }
if model != nil { if model != nil {
return errors.New("用户已存在会话模型,不能创建新的会话模型") return errors.New("用户已存在会话模型,不能创建")
} }
_, err = dao.Model.Update(ctx, req) }
req.IsOwner = gconv.PtrInt(1)
admin, err := s.IsSuperAdmin(ctx)
if err != nil {
return err return err
} }
_, err := dao.Model.Update(ctx, req) if admin {
req.IsOwner = gconv.PtrInt(0)
_, err = dao.Model.Update(ctx, req)
if err != nil {
return err
}
return nil
}
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return err
}
// 判断当前传过来的模型id的模型是否是超级管理员的。如果是超管的进行创建否则更新
var count int
count, err = dao.Model.Count(ctx, &dto.GetModelReq{
ID: req.ID,
Creator: user.UserName,
})
if err != nil {
return err
}
if count == 0 {
insertDto := new(dto.CreateModelReq)
err = gconv.Struct(req, insertDto)
if err != nil {
return err
}
_, err = dao.Model.Insert(ctx, insertDto)
return err
}
_, err = dao.Model.Update(ctx, req)
return err return err
} }
@@ -89,27 +136,29 @@ func (s *modelService) Get(ctx context.Context, id int64) (*entity.AsynchModel,
return model, nil return model, nil
} }
func (s *modelService) List(ctx context.Context, pageNum, pageSize int, req *dto.ListModelReq) (list []*entity.AsynchModel, total int64, err error) { func (s *modelService) List(ctx context.Context, req *dto.ListModelReq) (list []*entity.AsynchModel, total int, err error) {
isSuperAdmin, err := IsSuperAdmin(ctx)
if err != nil {
return nil, 0, err
}
user, err := utils.GetUserInfo(ctx)
if err != nil {
return nil, 0, err
}
var models []*entity.AsynchModel var models []*entity.AsynchModel
var count int64
if isSuperAdmin { req.IsOwner = gconv.PtrInt(1)
models, count, err = dao.Model.List(ctx, pageNum, pageSize, req.ModelName, req.ModelType, req.IsPrivate) admin, err := s.IsSuperAdmin(ctx)
} else { if err != nil {
models, count, err = s.getModelsWithDedup(ctx, user.UserName, pageNum, pageSize, req.ModelName, req.ModelType, req.IsPrivate) return
} }
if admin {
req.IsOwner = gconv.PtrInt(0)
}
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
req.Creator = user.UserName
models, total, err = dao.Model.GetByCreatorAndPlatform(ctx, req)
if err != nil {
return
}
// 处理列表中每条记录的 JSONB 字段 // 处理列表中每条记录的 JSONB 字段
for _, m := range models { for _, m := range models {
@@ -118,61 +167,7 @@ func (s *modelService) List(ctx context.Context, pageNum, pageSize int, req *dto
m.ResponseMapping = ParseJSONField(m.ResponseMapping) m.ResponseMapping = ParseJSONField(m.ResponseMapping)
m.ResponseBody = ParseJSONField(m.ResponseBody) m.ResponseBody = ParseJSONField(m.ResponseBody)
} }
return models, count, nil return models, total, nil
}
// getModelsWithDedup 获取普通用户的模型列表并去重
func (s *modelService) getModelsWithDedup(ctx context.Context, creator string, pageNum, pageSize int, modelNameLike string, modelType int, isPrivate int) (list []*entity.AsynchModel, total int64, err error) {
// 1. 查全量数据(不分页,便于去重)
allModels, err := dao.Model.GetByCreatorAndPlatform(ctx, creator, modelNameLike, modelType, isPrivate)
if err != nil {
return nil, 0, err
}
// 2. 按 modelName 去重,保留当前用户的
modelMap := make(map[string]*entity.AsynchModel)
for _, m := range allModels {
if m == nil {
continue
}
name := m.ModelName
_, ok := modelMap[name]
if !ok {
// 没有冲突,直接放进去
modelMap[name] = m
} else {
// 有冲突,保留当前用户创建的
if m.Creator == creator {
modelMap[name] = m
}
// 如果现有的就是当前用户的,不做任何替换
}
}
// 3. 转回切片并排序
deduped := make([]*entity.AsynchModel, 0, len(modelMap))
for _, m := range modelMap {
deduped = append(deduped, m)
}
sort.Slice(deduped, func(i, j int) bool {
return deduped[i].CreatedAt.After(deduped[j].CreatedAt)
})
// 4. 手动分页
total = int64(len(deduped))
if pageNum > 0 && pageSize > 0 {
start := (pageNum - 1) * pageSize
if start >= len(deduped) {
return []*entity.AsynchModel{}, total, nil
}
end := start + pageSize
if end > len(deduped) {
end = len(deduped)
}
deduped = deduped[start:end]
}
return deduped, total, nil
} }
// GetModelTypesFromConfig 从配置文件读取模型类型 // GetModelTypesFromConfig 从配置文件读取模型类型
@@ -202,11 +197,6 @@ func GetModelTypesFromConfig(ctx context.Context) map[int]string {
} }
func (s *modelService) UpdateChatModel(ctx context.Context, req *dto.UpdateChatModelReq) error { func (s *modelService) UpdateChatModel(ctx context.Context, req *dto.UpdateChatModelReq) error {
user, err := utils.GetUserInfo(ctx)
if err != nil {
return err
}
// 校验新会话模型是否存在 // 校验新会话模型是否存在
newModel, err := dao.Model.Get(ctx, req.Id) newModel, err := dao.Model.Get(ctx, req.Id)
if err != nil { if err != nil {
@@ -217,48 +207,40 @@ func (s *modelService) UpdateChatModel(ctx context.Context, req *dto.UpdateChatM
} }
// 获取当前用户会话模型 // 获取当前用户会话模型
currentModel, err := dao.Model.GetByIsChatModel(ctx, user.UserName) currentModel, err := dao.Model.GetByIsChatModel(ctx)
if err != nil { if err != nil {
return err return err
} }
if currentModel.ModelsType != 1 { err = gfdb.DB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
return errors.New("当前模型为非推理模型,不能设置为会话模型") if !g.IsEmpty(currentModel) {
} if currentModel.ModelType != 1 {
return errors.New("当前模型为非推理模型,不能设置为会话模型")
}
// 如果点击的就是当前会话模型已经是1取消它设为0 // 如果点击的就是当前会话模型已经是1取消它设为0
if currentModel != nil && currentModel.Id == req.Id { if currentModel.Id != req.Id {
_, err = dao.Model.UpdateByID(ctx, &dto.UpdateModelReq{ _, err = dao.Model.Update(ctx, &dto.UpdateModelReq{
ID: currentModel.Id,
IsChatModel: gconv.PtrInt(0),
})
if err != nil {
return err
}
}
}
// 设置当前为会话模型设为1
_, err = dao.Model.Update(ctx, &dto.UpdateModelReq{
ID: req.Id, ID: req.Id,
IsChatModel: 0, IsChatModel: gconv.PtrInt(1),
}) })
return err return err
}
// 如果之前有会话模型取消它设为0
if currentModel != nil {
_, err = dao.Model.UpdateByID(ctx, &dto.UpdateModelReq{
ID: currentModel.Id,
IsChatModel: 0,
})
if err != nil {
return err
}
}
// 设置当前为会话模型设为1
_, err = dao.Model.UpdateByID(ctx, &dto.UpdateModelReq{
ID: req.Id,
IsChatModel: 1,
}) })
return err return err
} }
func (s *modelService) GetIsChatModel(ctx context.Context) (*entity.AsynchModel, error) { func (s *modelService) GetIsChatModel(ctx context.Context) (*entity.AsynchModel, error) {
user, err := utils.GetUserInfo(ctx) model, err := dao.Model.GetByIsChatModel(ctx)
if err != nil {
return nil, err
}
model, err := dao.Model.GetByIsChatModel(ctx, user.UserName)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -3,8 +3,8 @@ package service
import ( import (
"context" "context"
"model-asynch/dao" "model-gateway/dao"
"model-asynch/model/dto" "model-gateway/model/dto"
) )
type statService struct{} type statService struct{}

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"errors" "errors"
"model-asynch/model/entity" "model-gateway/model/entity"
) )
// StorageService 结果存储OSS/MinIO抽象 // StorageService 结果存储OSS/MinIO抽象

View File

@@ -7,7 +7,7 @@ import (
"mime/multipart" "mime/multipart"
"time" "time"
"model-asynch/model/entity" "model-gateway/model/entity"
commonHttp "gitea.com/red-future/common/http" commonHttp "gitea.com/red-future/common/http"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"

View File

@@ -3,11 +3,12 @@ package service
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"time" "time"
"model-asynch/dao" "model-gateway/dao"
"model-asynch/model/dto" "model-gateway/model/dto"
"model-asynch/model/entity" "model-gateway/model/entity"
"github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
@@ -20,6 +21,7 @@ var Task = &taskService{}
type taskService struct{} type taskService struct{}
func (s *taskService) Create(ctx context.Context, req *dto.CreateTaskReq) (res *dto.CreateTaskRes, err error) { func (s *taskService) Create(ctx context.Context, req *dto.CreateTaskReq) (res *dto.CreateTaskRes, err error) {
fmt.Printf("打印请求:%+v", req)
startAt := time.Now() startAt := time.Now()
// 固化 token/user 等信息 // 固化 token/user 等信息
ctx = asyncCtx(ctx) ctx = asyncCtx(ctx)
@@ -29,7 +31,7 @@ func (s *taskService) Create(ctx context.Context, req *dto.CreateTaskReq) (res *
if err != nil { if err != nil {
return nil, err return nil, err
} }
if m == nil || m.Enabled != 1 { if m == nil || (m.Enabled != nil && *m.Enabled != 1) {
return nil, errors.New("模型不存在或未启用") return nil, errors.New("模型不存在或未启用")
} }

View File

@@ -7,8 +7,8 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"
"model-asynch/dao" "model-gateway/dao"
"model-asynch/model/entity" "model-gateway/model/entity"
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/grpool" "github.com/gogf/gf/v2/os/grpool"
@@ -95,7 +95,7 @@ func (w *asyncWorker) handleOne(ctx context.Context, t *entity.AsynchTask, epicy
// ================================ // ================================
return return
} }
if m == nil || m.Enabled != 1 { if m == nil || (m.Enabled != nil && *m.Enabled != 1) {
errMsg := "模型不存在或未启用" errMsg := "模型不存在或未启用"
_ = dao.Task.UpdateFailedGlobal(ctx, t.Id, errMsg) _ = dao.Task.UpdateFailedGlobal(ctx, t.Id, errMsg)
ReleaseQueueSlot(ctx, t.ModelName, t.TaskID) ReleaseQueueSlot(ctx, t.ModelName, t.TaskID)
@@ -172,9 +172,6 @@ func (w *asyncWorker) handleOne(ctx context.Context, t *entity.AsynchTask, epicy
contentType, ext = DetectFileType(data) contentType, ext = DetectFileType(data)
if utf8.Valid(data) && (strings.HasPrefix(contentType, "text/") || contentType == "application/json") { if utf8.Valid(data) && (strings.HasPrefix(contentType, "text/") || contentType == "application/json") {
textResult = string(data) textResult = string(data)
if len(textResult) > 20000 {
textResult = textResult[:20000]
}
} }
tmpPath, err := saveTmpResult(t.TaskID, data, ext) tmpPath, err := saveTmpResult(t.TaskID, data, ext)
if err == nil && tmpPath != "" { if err == nil && tmpPath != "" {

View File

@@ -18,13 +18,14 @@ CREATE TABLE IF NOT EXISTS asynch_models (
deleted_at TIMESTAMP(6), -- 删除时间(软删) deleted_at TIMESTAMP(6), -- 删除时间(软删)
-- 业务字段 -- 业务字段
model_name VARCHAR(128) NOT NULL, -- 模型名称 model_name VARCHAR(128) NOT NULL, -- 模型名称
models_type SMALLINT NOT NULL DEFAULT 0, -- 模型类型 model_type SMALLINT NOT NULL DEFAULT 0, -- 模型类型
base_url VARCHAR(256) NOT NULL, -- 模型地址 base_url VARCHAR(256) NOT NULL, -- 模型地址
http_method VARCHAR(8) NOT NULL DEFAULT 'POST', -- 请求方式 GET/POST http_method VARCHAR(8) NOT NULL DEFAULT 'POST', -- 请求方式 GET/POST
head_msg VARCHAR(1024) DEFAULT '', -- 请求头绑定(支持多个,逗号分隔)示例 X-API:xxx,operation:true head_msg VARCHAR(1024) DEFAULT '', -- 请求头绑定(支持多个,逗号分隔)示例 X-API:xxx,operation:true
is_private SMALLINT NOT NULL DEFAULT 0, -- 是否私有化 0-私有 1-公共 is_private SMALLINT NOT NULL DEFAULT 0, -- 是否私有化 0-私有 1-公共
enabled SMALLINT NOT NULL DEFAULT 1, -- 是否启用 0停用 1-启用 enabled SMALLINT NOT NULL DEFAULT 1, -- 是否启用 0停用 1-启用
is_chat_model SMALLINT NOT NULL DEFAULT 0, -- 是否为对话模型 0-否 1-是 is_chat_model SMALLINT NOT NULL DEFAULT 0, -- 是否为对话模型 0-否 1-是
is_owner SMALLINT NOT NULL DEFAULT 99, -- 1=当前用户创建的0=超级管理员的
api_key VARCHAR(256) NOT NULL DEFAULT '', -- 调用凭证,密钥 api_key VARCHAR(256) NOT NULL DEFAULT '', -- 调用凭证,密钥
prompt TEXT NOT NULL DEFAULT '', -- 提示词内容(文本) prompt TEXT NOT NULL DEFAULT '', -- 提示词内容(文本)
form_json JSONB NOT NULL DEFAULT '{}'::jsonb, -- 表单结构(用于前端渲染) form_json JSONB NOT NULL DEFAULT '{}'::jsonb, -- 表单结构(用于前端渲染)
@@ -46,7 +47,7 @@ CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_models_tenant_creator_chat ON asynch
CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_models_tenant_model_name ON asynch_models(tenant_id, creator, model_name); CREATE UNIQUE INDEX IF NOT EXISTS uk_asynch_models_tenant_model_name ON asynch_models(tenant_id, creator, model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_models_tenant_id ON asynch_models(tenant_id); CREATE INDEX IF NOT EXISTS idx_asynch_models_tenant_id ON asynch_models(tenant_id);
CREATE INDEX IF NOT EXISTS idx_asynch_models_model_name ON asynch_models(model_name); CREATE INDEX IF NOT EXISTS idx_asynch_models_model_name ON asynch_models(model_name);
CREATE INDEX IF NOT EXISTS idx_asynch_models_models_type ON asynch_models(models_type); CREATE INDEX IF NOT EXISTS idx_asynch_models_model_type ON asynch_models(model_type);
CREATE INDEX IF NOT EXISTS idx_asynch_models_enabled ON asynch_models(enabled); CREATE INDEX IF NOT EXISTS idx_asynch_models_enabled ON asynch_models(enabled);
CREATE INDEX IF NOT EXISTS idx_asynch_models_deleted_at ON asynch_models(deleted_at); CREATE INDEX IF NOT EXISTS idx_asynch_models_deleted_at ON asynch_models(deleted_at);
@@ -60,13 +61,14 @@ COMMENT ON COLUMN asynch_models.updated_at IS '更新时间';
COMMENT ON COLUMN asynch_models.deleted_at IS '删除时间(软删)'; COMMENT ON COLUMN asynch_models.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN asynch_models.model_name IS '模型名称'; COMMENT ON COLUMN asynch_models.model_name IS '模型名称';
COMMENT ON COLUMN asynch_models.models_type IS '模型类型'; COMMENT ON COLUMN asynch_models.model_type IS '模型类型';
COMMENT ON COLUMN asynch_models.base_url IS '模型地址'; COMMENT ON COLUMN asynch_models.base_url IS '模型地址';
COMMENT ON COLUMN asynch_models.http_method IS '请求方式 GET/POST'; COMMENT ON COLUMN asynch_models.http_method IS '请求方式 GET/POST';
COMMENT ON COLUMN asynch_models.head_msg IS '请求头绑定(支持多个,逗号分隔)示例 X-API:xxx,operation:true'; COMMENT ON COLUMN asynch_models.head_msg IS '请求头绑定(支持多个,逗号分隔)示例 X-API:xxx,operation:true';
COMMENT ON COLUMN asynch_models.is_private IS '是否私有化 0-私有 1-公共'; COMMENT ON COLUMN asynch_models.is_private IS '是否私有化 0-私有 1-公共';
COMMENT ON COLUMN asynch_models.enabled IS '是否启用 0停用 1-启用'; COMMENT ON COLUMN asynch_models.enabled IS '是否启用 0停用 1-启用';
COMMENT ON COLUMN asynch_models.is_chat_model IS '是否为对话模型 0-否 1-是'; COMMENT ON COLUMN asynch_models.is_chat_model IS '是否为对话模型 0-否 1-是';
COMMENT ON COLUMN asynch_models.is_owner IS '1=当前用户创建的0=超级管理员的';
COMMENT ON COLUMN asynch_models.api_key IS '调用凭证,密钥'; COMMENT ON COLUMN asynch_models.api_key IS '调用凭证,密钥';
COMMENT ON COLUMN asynch_models.prompt IS '提示词内容(文本)'; COMMENT ON COLUMN asynch_models.prompt IS '提示词内容(文本)';
COMMENT ON COLUMN asynch_models.form_json IS '表单结构(用于前端渲染,也用于后端校验)'; COMMENT ON COLUMN asynch_models.form_json IS '表单结构(用于前端渲染,也用于后端校验)';
@@ -97,7 +99,7 @@ CREATE TABLE IF NOT EXISTS asynch_task (
updater VARCHAR(64) NOT NULL, -- 更新人 updater VARCHAR(64) NOT NULL, -- 更新人
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间 updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
deleted_at TIMESTAMP(6), -- 删除时间(软删) deleted_at TIMESTAMP(6), -- 删除时间(软删)
-- 业务字段 -- 业务字段
model_name VARCHAR(128) NOT NULL, -- 模型名称 model_name VARCHAR(128) NOT NULL, -- 模型名称
task_id VARCHAR(64) NOT NULL, -- 任务ID(对外返回) task_id VARCHAR(64) NOT NULL, -- 任务ID(对外返回)