Merge remote-tracking branch 'origin/dev未优化' into dev未优化

This commit is contained in:
2026-08-06 09:41:11 +08:00
14 changed files with 1137 additions and 6 deletions
+108
View File
@@ -0,0 +1,108 @@
package model
import (
"model-gateway/consts/public"
"github.com/gogf/gf/v2/util/gconv"
)
// 供应商编码常量
const (
SupplierAliyun = 1
SupplierVolcengine = 2
SupplierTencent = 3
SupplierHuawei = 4
SupplierBaidu = 5
SupplierOpenAI = 6
SupplierAzure = 7
SupplierAWS = 8
SupplierGoogle = 9
SupplierDeepSeek = 10
SupplierMoonshot = 11
SupplierZhipu = 12
SupplierBaichuan = 13
SupplierMinimax = 14
SupplierXunfei = 15
SupplierOthers = 16
)
// SupplierType 供应商编码类型
type SupplierType *int8
// SupplierItem 供应商项
type SupplierItem struct {
Code SupplierType `json:"code"`
Desc string `json:"desc"`
}
// 名称映射【唯一文案维护】
var supplierNameMap = map[int]string{
SupplierAliyun: "阿里云百炼",
SupplierVolcengine: "火山引擎",
SupplierTencent: "腾讯云",
SupplierHuawei: "华为云",
SupplierBaidu: "百度智能云",
SupplierOpenAI: "OpenAI",
SupplierAzure: "Azure OpenAI",
SupplierAWS: "AWS Bedrock",
SupplierGoogle: "Google Cloud",
SupplierDeepSeek: "DeepSeek",
SupplierMoonshot: "Moonshot",
SupplierZhipu: "智谱AI",
SupplierBaichuan: "百川智能",
SupplierMinimax: "MiniMax",
SupplierXunfei: "科大讯飞",
SupplierOthers: "其他",
}
// 供应商展示顺序
var supplierOrder = []int{
SupplierAliyun, SupplierVolcengine, SupplierTencent, SupplierHuawei, SupplierBaidu,
SupplierOpenAI, SupplierAzure, SupplierAWS, SupplierGoogle, SupplierDeepSeek,
SupplierMoonshot, SupplierZhipu, SupplierBaichuan, SupplierMinimax, SupplierXunfei, SupplierOthers,
}
// 全局供应商实例
var (
SupplierItemAliyun = newSupplierItem(gconv.PtrInt8(SupplierAliyun))
SupplierItemVolcengine = newSupplierItem(gconv.PtrInt8(SupplierVolcengine))
SupplierItemTencent = newSupplierItem(gconv.PtrInt8(SupplierTencent))
SupplierItemHuawei = newSupplierItem(gconv.PtrInt8(SupplierHuawei))
SupplierItemBaidu = newSupplierItem(gconv.PtrInt8(SupplierBaidu))
SupplierItemOpenAI = newSupplierItem(gconv.PtrInt8(SupplierOpenAI))
SupplierItemAzure = newSupplierItem(gconv.PtrInt8(SupplierAzure))
SupplierItemAWS = newSupplierItem(gconv.PtrInt8(SupplierAWS))
SupplierItemGoogle = newSupplierItem(gconv.PtrInt8(SupplierGoogle))
SupplierItemDeepSeek = newSupplierItem(gconv.PtrInt8(SupplierDeepSeek))
SupplierItemMoonshot = newSupplierItem(gconv.PtrInt8(SupplierMoonshot))
SupplierItemZhipu = newSupplierItem(gconv.PtrInt8(SupplierZhipu))
SupplierItemBaichuan = newSupplierItem(gconv.PtrInt8(SupplierBaichuan))
SupplierItemMinimax = newSupplierItem(gconv.PtrInt8(SupplierMinimax))
SupplierItemXunfei = newSupplierItem(gconv.PtrInt8(SupplierXunfei))
SupplierItemOthers = newSupplierItem(gconv.PtrInt8(SupplierOthers))
)
func newSupplierItem(code SupplierType) SupplierItem {
val := int(*code)
return SupplierItem{
Code: code,
Desc: supplierNameMap[val],
}
}
// GetSupplierDescByCode 根据编码获取供应商名称
func GetSupplierDescByCode(code int) string {
return supplierNameMap[code]
}
// GetSupplierOptionList 获取供应商下拉列表
func GetSupplierOptionList() []*public.Option {
var list []*public.Option
for _, code := range supplierOrder {
list = append(list, &public.Option{
Value: code,
Label: supplierNameMap[code],
})
}
return list
}
+186
View File
@@ -0,0 +1,186 @@
package model
import (
"model-gateway/consts/public"
"github.com/gogf/gf/v2/util/gconv"
)
// 模型类型编码常量
const (
TypeInference = 100 // 推理模型
TypeImage = 200 // 图片模型
TypeAudio = 300 // 音频模型
TypeVector = 400 // 向量化模型
TypeOmni = 500 // 全模态模型
TypeVideo = 600 // 视频模型
// 图片子类型
ImageSubTextToImage = 201
ImageSubImageToImage = 202
ImageSubImageEdit = 203
ImageSubImageVariation = 204
ImageSubImageTextToImage = 205
// 音频子类型
AudioSubTextToSpeech = 301
AudioSubSpeechToText = 302
AudioSubSpeechToSpeech = 303
// 向量化子类型
VectorSubEmbedding = 401
VectorSubRerank = 402
// 全模态子类型
OmniSubTextImageAudio = 501
OmniSubVision = 502
// 视频子类型
VideoSubTextToVideo = 601
VideoSubImageToVideo = 602
VideoSubImageTextToVideo = 603
VideoSubVideoToVideo = 604
)
// ModelType 编码类型
type ModelType *int
// ModelTypeItem 模型类型项
type ModelTypeItem struct {
Code ModelType `json:"code"`
Desc string `json:"desc"`
}
// TypeTree 树形结构
type TypeTree struct {
Value int `json:"value"`
Label string `json:"label"`
Children []*public.Option `json:"children"`
}
// 名称映射表【唯一文案维护入口】
var typeNameMap = map[int]string{
TypeInference: "推理模型",
TypeImage: "图片模型",
TypeAudio: "音频模型",
TypeVector: "向量化模型",
TypeOmni: "全模态模型",
TypeVideo: "视频模型",
ImageSubTextToImage: "文生图",
ImageSubImageToImage: "图生图",
ImageSubImageEdit: "图片编辑",
ImageSubImageVariation: "图片变体",
ImageSubImageTextToImage: "图文生图",
AudioSubTextToSpeech: "文生音",
AudioSubSpeechToText: "音生文",
AudioSubSpeechToSpeech: "音生音",
VectorSubEmbedding: "文本嵌入",
VectorSubRerank: "重排序",
OmniSubTextImageAudio: "文图音",
OmniSubVision: "视觉理解",
VideoSubTextToVideo: "文生视频",
VideoSubImageToVideo: "图生视频",
VideoSubImageTextToVideo: "图文生视频",
VideoSubVideoToVideo: "视频生视频",
}
// 父子级映射(仅存有子项的分类)
var parentChildMap = map[int][]int{
TypeImage: {ImageSubTextToImage, ImageSubImageToImage, ImageSubImageEdit, ImageSubImageVariation, ImageSubImageTextToImage},
TypeAudio: {AudioSubTextToSpeech, AudioSubSpeechToText, AudioSubSpeechToSpeech},
TypeVector: {VectorSubEmbedding, VectorSubRerank},
TypeOmni: {OmniSubTextImageAudio, OmniSubVision},
TypeVideo: {VideoSubTextToVideo, VideoSubImageToVideo, VideoSubImageTextToVideo, VideoSubVideoToVideo},
}
// 一级分类展示顺序
var parentTypeOrder = []int{
TypeInference, TypeImage, TypeAudio, TypeVector, TypeOmni, TypeVideo,
}
// 全局实例:一级 + 全部二级子类型,统一通过 newItem 构造,文案仅维护在 typeNameMap
var (
// 一级类型
ModelTypeInference = newItem(gconv.PtrInt(TypeInference))
ModelTypeImage = newItem(gconv.PtrInt(TypeImage))
ModelTypeAudio = newItem(gconv.PtrInt(TypeAudio))
ModelTypeVector = newItem(gconv.PtrInt(TypeVector))
ModelTypeOmni = newItem(gconv.PtrInt(TypeOmni))
ModelTypeVideo = newItem(gconv.PtrInt(TypeVideo))
// 图片二级子类型
ModelImageSubTextToImage = newItem(gconv.PtrInt(ImageSubTextToImage))
ModelImageSubImageToImage = newItem(gconv.PtrInt(ImageSubImageToImage))
ModelImageSubImageEdit = newItem(gconv.PtrInt(ImageSubImageEdit))
ModelImageSubImageVariation = newItem(gconv.PtrInt(ImageSubImageVariation))
ModelImageSubImageTextToImage = newItem(gconv.PtrInt(ImageSubImageTextToImage))
// 音频二级子类型
ModelAudioSubTextToSpeech = newItem(gconv.PtrInt(AudioSubTextToSpeech))
ModelAudioSubSpeechToText = newItem(gconv.PtrInt(AudioSubSpeechToText))
ModelAudioSubSpeechToSpeech = newItem(gconv.PtrInt(AudioSubSpeechToSpeech))
// 向量化二级子类型
ModelVectorSubEmbedding = newItem(gconv.PtrInt(VectorSubEmbedding))
ModelVectorSubRerank = newItem(gconv.PtrInt(VectorSubRerank))
// 全模态二级子类型
ModelOmniSubTextImageAudio = newItem(gconv.PtrInt(OmniSubTextImageAudio))
ModelOmniSubVision = newItem(gconv.PtrInt(OmniSubVision))
// 视频二级子类型
ModelVideoSubTextToVideo = newItem(gconv.PtrInt(VideoSubTextToVideo))
ModelVideoSubImageToVideo = newItem(gconv.PtrInt(VideoSubImageToVideo))
ModelVideoSubImageTextToVideo = newItem(gconv.PtrInt(VideoSubImageTextToVideo))
ModelVideoSubVideoToVideo = newItem(gconv.PtrInt(VideoSubVideoToVideo))
)
// newItem 构造方法:自动从 typeNameMap 读取描述
func newItem(code ModelType) ModelTypeItem {
val := int(*code)
return ModelTypeItem{
Code: code,
Desc: typeNameMap[val],
}
}
// GetDescByCode 根据编码获取名称
func GetDescByCode(code int) string {
return typeNameMap[code]
}
// GetTypeTreeList 生成树形数据
func GetTypeTreeList() []*TypeTree {
var list []*TypeTree
for _, parentCode := range parentTypeOrder {
tree := &TypeTree{
Value: parentCode,
Label: typeNameMap[parentCode],
Children: make([]*public.Option, 0),
}
if childCodes, ok := parentChildMap[parentCode]; ok {
for _, c := range childCodes {
tree.Children = append(tree.Children, &public.Option{
Value: c,
Label: typeNameMap[c],
})
}
}
list = append(list, tree)
}
return list
}
// GetAllTypeOption 全量平铺选项
func GetAllTypeOption() []*public.Option {
var list []*public.Option
for code, label := range typeNameMap {
list = append(list, &public.Option{Value: code, Label: label})
}
return list
}
+27
View File
@@ -0,0 +1,27 @@
package model
import "github.com/gogf/gf/v2/util/gconv"
var (
ResponseTypeSync = newResponseType(gconv.PtrInt8(1), "sync") // 同步
ResponseTypeAsync = newResponseType(gconv.PtrInt8(2), "async") // 异步
ResponseTypeStream = newResponseType(gconv.PtrInt8(3), "stream") // 流
)
type ResponseType *int8
type responseType struct {
code ResponseType
desc string
}
func (s responseType) Code() ResponseType {
return s.code
}
func (s responseType) Desc() string {
return s.desc
}
func newResponseType(code ResponseType, desc string) responseType {
return responseType{code: code, desc: desc}
}
+6
View File
@@ -1,5 +1,11 @@
package public
// Option 通用下拉选项
type Option struct {
Value int `json:"value"`
Label string `json:"label"`
}
const (
CallModeSync = 0 // 同步调用
CallModeAsync = 1 // 异步调用
+5 -4
View File
@@ -5,8 +5,9 @@ const (
)
const (
TableNameModel = "model_gateway_models" // 模型表
TableNameTask = "model_gateway_task" // 任务表
TableNameOpLog = "model_gateway_logs_op" // 操作日志表
TableNameStat = "model_gateway_logs_stat" // 按天统计表
TableNameModel = "model_gateway_models" // 模型表
TableNameTask = "model_gateway_task" // 任务表
TableNameOpLog = "model_gateway_logs_op" // 操作日志表
TableNameStat = "model_gateway_logs_stat" // 按天统计表
TableNameModelManage = "model_gateway_model_manage"
)
+56
View File
@@ -0,0 +1,56 @@
package controller
import (
"context"
"model-gateway/model/dto"
"model-gateway/service"
"gitea.redpowerfuture.com/red-future/common/beans"
)
// ModelManage 模型配置控制器
var ModelManage = new(modelManage)
type modelManage struct{}
// CreateModel 添加配置
func (c *modelManage) CreateModel(ctx context.Context, req *dto.CreateModelManageReq) (res *dto.CreateModelManageRes, err error) {
return service.ModelManage.Create(ctx, req)
}
// UpdateModel 更改配置
func (c *modelManage) UpdateModel(ctx context.Context, req *dto.UpdateModelManageReq) (res *beans.ResponseEmpty, err error) {
err = service.ModelManage.Update(ctx, req)
return
}
// DeleteModel 删除配置
func (c *modelManage) DeleteModel(ctx context.Context, req *dto.DeleteModelManageReq) (res *beans.ResponseEmpty, err error) {
err = service.ModelManage.Delete(ctx, req)
return
}
// GetModel 获取配置
func (c *modelManage) GetModel(ctx context.Context, req *dto.GetModelManageReq) (res *dto.GetModelManageRes, err error) {
return service.ModelManage.Get(ctx, req)
}
// ListModel 配置列表
func (c *modelManage) ListModel(ctx context.Context, req *dto.ListModelManageReq) (res *dto.ListModelManageRes, err error) {
return service.ModelManage.List(ctx, req)
}
// CheckChatModel 检查是否为聊天模型
func (c *modelManage) CheckChatModel(ctx context.Context, req *dto.CheckChatModelReq) (res *dto.CheckChatModelRes, err error) {
return service.ModelManage.CheckChatModel(ctx, req)
}
// ListType 模型类型列表
func (c *modelManage) ListType(ctx context.Context, req *dto.ModelTypeReq) (res *dto.ModelTypeRes, err error) {
return service.ModelManage.GetModelType(ctx, req)
}
// ListOperator 运营商列表
func (c *modelManage) ListOperator(ctx context.Context, req *dto.ModelSupplierReq) (res *dto.ModelSupplierRes, err error) {
return service.ModelManage.GetModelSupplier(ctx, req)
}
+193
View File
@@ -0,0 +1,193 @@
package dao
import (
"context"
"fmt"
"model-gateway/consts/public"
"model-gateway/model/dto"
"model-gateway/model/entity"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var ModelManage = &modelManageDao{}
type modelManageDao struct{}
// Insert 插入
func (d *modelManageDao) Insert(ctx context.Context, req *dto.CreateModelManageReq) (id int64, err error) {
var e = new(entity.ModelManage)
err = gconv.Struct(req, &e)
if err != nil {
return
}
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).Insert(e)
if err != nil {
return
}
return r.LastInsertId()
}
// Update 更新
func (d *modelManageDao) Update(ctx context.Context, req *dto.UpdateModelManageReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).OmitEmpty().Data(req).Where(entity.ModelManageCol.Id, req.Id).Update()
if err != nil {
return
}
return r.RowsAffected()
}
// Delete 删除
func (d *modelManageDao) Delete(ctx context.Context, req *dto.DeleteModelManageReq) (rows int64, err error) {
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).Where(entity.ModelManageCol.Id, req.Id).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *modelManageDao) Get(ctx context.Context, req *dto.GetModelManage, fields ...string) (res *entity.ModelManage, err error) {
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameModelManage).Cache(ctx).
OmitEmpty().
Where(entity.ModelManageCol.ModelName, req.ModelName).
Where(entity.ModelManageCol.ChatModel, req.ChatModel).
Where(entity.ModelManageCol.Creator, req.Creator).
Fields(fields).One()
if err != nil {
return
}
err = r.Struct(&res)
return
}
func (d *modelManageDao) GetNotTenantId(ctx context.Context, req *dto.GetModelManageReq, fields ...string) (res *entity.ModelManage, err error) {
// 获取表前缀
prefix := g.Cfg().MustGet(ctx, fmt.Sprintf("database.%s.0.prefix", public.DbNameModelGateway)).String()
table := prefix + public.TableNameModelManage
// 动态拼接 SELECT 列
var field string
if !g.IsEmpty(fields) {
for k, v := range fields {
if k == len(fields)-1 {
field = field + v
} else {
field = field + v + ","
}
}
} else {
field = "*"
}
// 动态拼接 WHERE 条件
var whereCondition string
var queryParams []interface{}
if !g.IsEmpty(req.Id) {
whereCondition = fmt.Sprintf(" AND %s=(?) ", entity.ModelManageCol.Id)
queryParams = append(queryParams, req.Id)
}
whereCondition = whereCondition + " AND " + entity.ModelManageCol.DeletedAt + " IS NULL "
sql := `SELECT ` + field + ` FROM ` + table + ` WHERE 1=1 ` + whereCondition + ``
// 执行查询
result, err := gfdb.DB(ctx, public.DbNameModelGateway).GetOne(ctx, sql, queryParams...)
if err != nil {
return nil, err
}
err = result.Struct(&res)
return
}
func (d *modelManageDao) ListNotTenantId(ctx context.Context, req *dto.ListModelManageReq, fields ...string) (res []*entity.ModelManage, total int, err error) {
// 获取表前缀
prefix := g.Cfg().MustGet(ctx, fmt.Sprintf("database.%s.0.prefix", public.DbNameModelGateway)).String()
table := prefix + public.TableNameModelManage
// 动态拼接 SELECT 列
var field string
if !g.IsEmpty(fields) {
for k, v := range fields {
if k == len(fields)-1 {
field = field + v
} else {
field = field + v + ","
}
}
} else {
field = "*"
}
// 动态拼接 WHERE 条件
var whereCondition string
var queryParams []interface{}
if !g.IsEmpty(req.ModelName) {
whereCondition += fmt.Sprintf(" AND %s=(?) ", entity.ModelManageCol.ModelName)
queryParams = append(queryParams, req.ModelName)
}
if !g.IsEmpty(req.ModelType) {
whereCondition += fmt.Sprintf(" AND %s=(?) ", entity.ModelManageCol.ModelType)
queryParams = append(queryParams, req.ModelType)
}
if !g.IsEmpty(req.Creator) {
whereCondition += fmt.Sprintf(" AND (%s=(?) OR %s=true) ", entity.ModelManageCol.Creator, entity.ModelManageCol.SystemModel)
queryParams = append(queryParams, req.Creator)
}
whereCondition = whereCondition + " AND " + entity.ModelManageCol.DeletedAt + " IS NULL "
// 1. 统计去重后总条数
countSql := fmt.Sprintf(
`SELECT COUNT(DISTINCT %s) FROM %s WHERE 1=1 %s`,
entity.ModelManageCol.ModelName,
table,
whereCondition,
)
countResult, err := gfdb.DB(ctx, public.DbNameModelGateway).GetOne(ctx, countSql, queryParams...)
if err != nil {
return nil, 0, err
}
type crr struct {
Count int64 `db:"count"`
}
var cr crr
if err = countResult.Struct(&cr); err != nil {
return nil, 0, err
}
total = int(cr.Count)
// 2. 分页处理
limitSql := ""
if req.Page != nil {
pageNum := int(req.Page.PageNum)
pageSize := int(req.Page.PageSize)
offset := (pageNum - 1) * pageSize
limitSql = fmt.Sprintf(" LIMIT ? OFFSET ? ")
// PG 语法 LIMIT 条数 OFFSET 偏移量
queryParams = append(queryParams, pageSize, offset)
}
// 排序优先级:1.分组字段ModelName 2.SystemModel升序(false在前,保留用户数据) 3.创建时间倒序
orderSql := fmt.Sprintf(
" ORDER BY %s, %s ASC, %s DESC ",
entity.ModelManageCol.ModelName,
entity.ModelManageCol.SystemModel,
entity.ModelManageCol.CreatedAt,
)
// PG DISTINCT ON 按模型名去重,同名只取第一条(用户数据)
sql := fmt.Sprintf(
`SELECT DISTINCT ON (%s) %s FROM %s WHERE 1=1 %s %s %s`,
entity.ModelManageCol.ModelName,
field,
table,
whereCondition,
orderSql,
limitSql,
)
// 执行查询
result, err := gfdb.DB(ctx, public.DbNameModelGateway).GetAll(ctx, sql, queryParams...)
if err != nil {
return nil, 0, err
}
err = result.Structs(&res)
return
}
+1 -1
View File
@@ -3,7 +3,7 @@ module model-gateway
go 1.26.1
require (
gitea.redpowerfuture.com/red-future/common v0.0.29
gitea.redpowerfuture.com/red-future/common v0.0.30
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
github.com/gogf/gf/v2 v2.10.2
+2
View File
@@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
gitea.redpowerfuture.com/red-future/common v0.0.29 h1:5McaN5pSewvrLUHQzWMX6EaUvD+B5I5bMYoU+clHJk4=
gitea.redpowerfuture.com/red-future/common v0.0.29/go.mod h1:50U1Xi+Ie56z09S5LQbZvaken0Mxv3OeS9LgR7U/ZRY=
gitea.redpowerfuture.com/red-future/common v0.0.30 h1:UkWYubUsLPJQUhEhc9Ca2UPg5iLC6jzURo3ngztINYg=
gitea.redpowerfuture.com/red-future/common v0.0.30/go.mod h1:zuhqbWHd/YICalYJnmecY8Vqo5j4dtZOD63P96fiFeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+1
View File
@@ -26,6 +26,7 @@ func main() {
// 注册路由
http.RouteRegister([]interface{}{
controller.ModelManage,
controller.ModelGatewayModels,
controller.ModelGatewayTask,
controller.ModelGatewayLogsStat,
+127
View File
@@ -0,0 +1,127 @@
package dto
import (
"model-gateway/consts/model"
"model-gateway/consts/public"
"model-gateway/model/entity"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
)
// CreateModelManageReq 添加模型配置
type CreateModelManageReq struct {
g.Meta `path:"/createModelManage" method:"post" tags:"new模型管理" summary:"new创建模型配置" dc:"new添加新的模型配置"`
ModelSupplier model.SupplierType `json:"modelSupplier" v:"required#模型供应商不能为空" dc:"模型供应商"`
ModelName string `json:"modelName" v:"required#模型名称不能为空" dc:"模型名称"`
ModelType model.ModelType `json:"modelType" v:"required#模型类型不能为空" dc:"模型类型"`
BaseURL string `json:"baseUrl" v:"required#模型服务地址不能为空" dc:"模型服务地址"`
SystemModel *bool `json:"systemModel" dc:"系统模型"`
HttpMethod string `json:"httpMethod" dc:"请求方式:GET/POST" d:"POST"`
ChatModel *bool `json:"chatModel" dc:"对话模型"`
ResponseType model.ResponseType `json:"responseType" v:"required#调用模式不能为空" dc:"调用模式:0-同步 1-异步 2-流式"`
ApiKey string `json:"apiKey" dc:"调用凭证/密钥"`
Enabled *bool `json:"enabled" dc:"启用"`
RequestHeadMapping map[string]string `json:"requestHeadMapping" dc:"请求头映射"`
RequestBodyMapping map[string]any `json:"requestBodyMapping" dc:"请求体映射"`
ResponseMapping map[string]any `json:"responseMapping" dc:"返回映射"`
ResponseBodyMapping map[string]string `json:"responseBodyMapping" dc:"返回体映射"`
MaxConcurrency int `json:"maxConcurrency" dc:"最大并发数(默认10"`
TokenMapping *entity.TokenMapping `json:"tokenMapping" dc:"token映射"`
AsyncTaskMapping *entity.AsyncTaskMapping `json:"asyncTaskMapping" dc:"异步任务映射"`
TokenPredictPrice float64 `json:"tokenPredictPrice" dc:"模型Token预估价格"`
TokenPredictPriceUnit string `json:"tokenPredictPriceUnit" dc:"模型Token预估价格单位"`
MaxTokens int `json:"maxTokens" dc:"最大token数"`
MaxDuration int `json:"maxDuration" dc:"最大时长"`
LastFrame string `json:"lastFrame" dc:"视频的尾帧图像"`
}
type CreateModelManageRes struct {
Id int64 `json:"id,string" dc:"配置ID"`
}
type UpdateModelManageReq struct {
g.Meta `path:"/updateModelManage" method:"put" tags:"new模型管理" summary:"new更新模型配置" dc:"new更新指定ID的模型配置"`
Id int64 `json:"id" v:"required#id不能为空" dc:"配置ID"`
ModelSupplier model.SupplierType `json:"modelSupplier" dc:"模型供应商"`
ModelName string `json:"modelName" dc:"模型名称"`
ModelType model.ModelType `json:"modelType" dc:"模型类型"`
BaseURL string `json:"baseUrl" dc:"模型服务地址"`
SystemModel *bool `json:"systemModel" dc:"系统模型"`
HttpMethod string `json:"httpMethod" dc:"请求方式:GET/POST"`
ChatModel *bool `json:"chatModel" dc:"对话模型"`
ResponseType model.ResponseType `json:"responseType" dc:"调用模式:0-同步 1-异步 2-流式"`
ApiKey string `json:"apiKey" dc:"调用凭证/密钥"`
Enabled *bool `json:"enabled" dc:"启用"`
RequestHeadMapping map[string]string `json:"requestHeadMapping" dc:"请求头映射"`
RequestBodyMapping map[string]any `json:"requestBodyMapping" dc:"请求体映射"`
ResponseMapping map[string]any `json:"responseMapping" dc:"返回映射"`
ResponseBodyMapping map[string]string `json:"responseBodyMapping" dc:"返回主体映射"`
MaxConcurrency int `json:"maxConcurrency" dc:"最大并发数(默认10"`
TokenMapping *entity.TokenMapping `json:"tokenMapping" dc:"token映射"`
AsyncTaskMapping *entity.AsyncTaskMapping `json:"asyncTaskMapping" dc:"异步任务映射"`
TokenPredictPrice float64 `json:"tokenPredictPrice" dc:"模型Token预估价格"`
TokenPredictPriceUnit string `json:"tokenPredictPriceUnit" dc:"模型Token预估价格单位"`
MaxTokens int `json:"maxTokens" dc:"最大token数"`
MaxDuration int `json:"maxDuration" dc:"最大时长"`
LastFrame string `json:"lastFrame" dc:"视频的尾帧图像"`
}
type DeleteModelManageReq struct {
g.Meta `path:"/deleteModelManage" method:"delete" tags:"new模型管理" summary:"new删除模型配置" dc:"new删除指定ID的模型配置"`
Id int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"`
}
type GetModelManage struct {
ChatModel *bool `json:"chatModel" dc:"对话模型"`
Creator string `json:"creator" dc:"创建人"`
ModelName string `json:"modelName" dc:"模型名称"`
}
type GetModelManageReq struct {
g.Meta `path:"/getModelManage" method:"get" tags:"new模型管理" summary:"new获取模型配置" dc:"new获取指定ID的模型配置"`
Id int64 `p:"id" json:"id,string" v:"required#id不能为空" dc:"配置ID"`
}
type GetModelManageRes struct {
*entity.ModelManage `json:"modelManage"`
}
// ListModelManageReq 配置列表
type ListModelManageReq struct {
g.Meta `path:"/listModelManage" method:"get" tags:"new模型管理" summary:"new模型配置列表" dc:"new分页获取模型配置列表"`
*beans.Page `json:"page"`
ModelName string `p:"modelName" json:"modelName" dc:"模型名称(模糊查询,可选)"`
ModelType int `p:"modelType" json:"modelType" dc:"模型类型"`
Creator string `json:"creator" dc:"创建人"`
}
type ListModelManageRes struct {
List []*entity.ModelManage `json:"list" dc:"列表数据"`
Total int `json:"total" dc:"总数"`
}
type CheckChatModelReq struct {
g.Meta `path:"/checkChatModel" method:"get" tags:"new模型管理" summary:"new检查是否为聊天模型" dc:"new检查是否为聊天模型"`
}
type CheckChatModelRes struct {
IsChatModel bool `json:"isChatModel" dc:"是否为聊天模型"`
}
// ModelTypeReq 模型类型列表(分页)
type ModelTypeReq struct {
g.Meta `path:"/modelType" method:"get" tags:"new模型管理" summary:"new模型类型列表" dc:"new分页获取模型类型列表"`
}
type ModelTypeRes struct {
List []*model.TypeTree `json:"list" dc:"模型类型ID到名称的映射"`
}
type ModelSupplierReq struct {
g.Meta `path:"/modelSupplier" method:"get" tags:"new模型管理" summary:"new获取运营商列表" dc:"new获取运营商列表"`
}
type ModelSupplierRes struct {
List []*public.Option `json:"list" dc:"运营商名称到ID的映射"`
}
+102
View File
@@ -0,0 +1,102 @@
package entity
import (
"model-gateway/consts/model"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type modelManageCol struct {
beans.SQLBaseCol
ModelSupplier string
ModelName string
ModelType string
BaseURL string
SystemModel string
HttpMethod string
ChatModel string
ResponseType string
ApiKey string
Enabled string
RequestHeadMapping string
RequestBodyMapping string
ResponseMapping string
ResponseBodyMapping string
MaxConcurrency string
TokenPredictPrice string
TokenPredictPriceUnit string
MaxTokens string
MaxDuration string
LastFrame string
}
var ModelManageCol = modelManageCol{
SQLBaseCol: beans.DefSQLBaseCol,
ModelSupplier: "model_supplier",
ModelName: "model_name",
ModelType: "model_type",
BaseURL: "base_url",
SystemModel: "system_model",
HttpMethod: "http_method",
ChatModel: "chat_model",
ResponseType: "response_type",
ApiKey: "api_key",
Enabled: "enabled",
RequestHeadMapping: "request_head_mapping",
RequestBodyMapping: "request_body_mapping",
ResponseMapping: "response_mapping",
ResponseBodyMapping: "response_body_mapping",
MaxConcurrency: "max_concurrency",
TokenPredictPrice: "token_predict_price",
TokenPredictPriceUnit: "token_predict_price_unit",
MaxTokens: "max_tokens",
MaxDuration: "max_duration",
LastFrame: "last_frame",
}
type ModelManage struct {
beans.SQLBaseDO `orm:",inline"`
ModelSupplier model.SupplierType `orm:"model_supplier" json:"modelSupplier" description:"模型供应商"`
ModelName string `orm:"model_name" json:"modelName" description:"模型名称"`
ModelType model.ModelType `orm:"model_type" json:"modelType" description:"模型类型"`
BaseURL string `orm:"base_url" json:"baseUrl" description:"模型地址"`
SystemModel *bool `orm:"system_model" json:"systemModel" description:"系统模型"`
HttpMethod string `orm:"http_method" json:"httpMethod" description:"http方法"`
ChatModel *bool `orm:"chat_model" json:"ChatModel" description:"是否聊天模型"`
ResponseType model.ResponseType `orm:"response_type" json:"responseType" description:"返回类型:1同步,2异步,3流"`
ApiKey string `orm:"api_key" json:"apiKey" description:"api key"`
Enabled *bool `orm:"enabled" json:"enabled" description:"是否启用"`
RequestHeadMapping map[string]string `orm:"request_head_mapping" json:"requestHeadMapping" description:"请求头映射"`
RequestBodyMapping map[string]any `orm:"request_body_mapping" json:"requestBodyMapping" description:"请求体映射"`
ResponseMapping map[string]any `orm:"response_mapping" json:"responseMapping" description:"响应映射"`
ResponseBodyMapping map[string]string `orm:"response_body_mapping" json:"responseBodyMapping" description:"响应主体映射"`
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency" description:"最大并发数"`
TokenMapping *TokenMapping `orm:"token_mapping" json:"tokenMapping" description:"token映射"`
AsyncTaskMapping *AsyncTaskMapping `orm:"async_task_mapping" json:"asyncTaskMapping" description:"异步任务映射"`
TokenPredictPrice float64 `orm:"token_predict_price" json:"tokenPredictPrice" description:"模型Token预估价格"`
TokenPredictPriceUnit string `orm:"token_predict_price_unit" json:"tokenPredictPriceUnit" description:"模型token预估价格单位(秒,百万Token,千Token,字数)"`
MaxTokens int `orm:"max_tokens" json:"maxTokens" description:"最大token数"`
MaxDuration int `orm:"max_duration" json:"maxDuration" description:"最大时长(秒)"`
LastFrame string `orm:"last_frame" json:"lastFrame" description:"视频的尾帧图像"`
}
type TokenMapping struct {
PromptTokens string `json:"promptTokens" dc:"输入token"`
CompletionTokens string `json:"completionTokens" dc:"输出token"`
TotalTokens string `json:"totalTokens" dc:"总token"`
}
type AsyncTaskMapping struct {
Url string `json:"url" dc:"url"`
HttpMethod string `json:"httpMethod" dc:"http方法" d:"POST"`
RequestHeadMapping map[string]string `json:"requestHeadMapping" description:"请求头映射"`
ResponseMapping map[string]any `json:"responseMapping" description:"响应映射"`
TaskId string `json:"taskId" dc:"任务id"`
TaskStatus string `json:"taskStatus" dc:"任务状态"`
TaskStatusPending string `json:"taskStatusPending" dc:"任务状态-待处理"`
TaskStatusRunning string `json:"taskStatusRunning" dc:"任务状态-运行中"`
TaskStatusSuccess string `json:"taskStatusSuccess" dc:"任务状态-成功"`
TaskStatusFailed string `json:"taskStatusFailed" dc:"任务状态-失败"`
TaskStatusCancel string `json:"taskStatusCancel" dc:"任务状态-取消"`
TaskStatusUnknown string `json:"taskStatusUnknown" dc:"任务状态-未知"`
}
+204
View File
@@ -0,0 +1,204 @@
package service
import (
"context"
"fmt"
"model-gateway/consts/model"
"model-gateway/consts/public"
"model-gateway/dao"
"model-gateway/model/dto"
"model-gateway/model/entity"
"model-gateway/service/gateway"
"gitea.redpowerfuture.com/red-future/common/beans"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"gitea.redpowerfuture.com/red-future/common/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var ModelManage = &modelManageService{}
type modelManageService struct{}
// Create 创建模型
func (s *modelManageService) Create(ctx context.Context, req *dto.CreateModelManageReq) (res *dto.CreateModelManageRes, err error) {
err = gfdb.DB(ctx, public.DbNameModelGateway).Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
// 1)检查是否是超管
var isSuperAdmin bool
isSuperAdmin, err = gateway.IsSuperAdmin(ctx)
if err != nil {
return
}
req.SystemModel = &isSuperAdmin
// 1)如果设为会话模型,先把该用户旧会话模型取消
err = s.CancelChatModel(ctx, req.ModelType, req.ChatModel, isSuperAdmin)
if err != nil {
return
}
// 2)插入数据
id, err := dao.ModelManage.Insert(ctx, req)
if err != nil {
return
}
res = &dto.CreateModelManageRes{Id: id}
return
})
return
}
// Update 更新模型配置
func (s *modelManageService) Update(ctx context.Context, req *dto.UpdateModelManageReq) (err error) {
err = gfdb.DB(ctx, public.DbNameModelGateway).Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
var get *entity.ModelManage
get, err = dao.ModelManage.GetNotTenantId(ctx, &dto.GetModelManageReq{
Id: req.Id,
})
if err != nil {
return
}
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
// 1)如果不是创建者,且是系统模型,则需要拷贝
if get.Creator != user.UserName {
if get.SystemModel != nil && *get.SystemModel {
if g.IsEmpty(req.ApiKey) {
return fmt.Errorf("模型apiKey不能为空")
}
d := new(dto.CreateModelManageReq)
err = gconv.Struct(req, d)
if err != nil {
return
}
_, err = s.Create(ctx, d)
if err != nil {
return err
}
return
}
return fmt.Errorf("无权限操作")
}
// 1)检查是否是超管
var isSuperAdmin bool
isSuperAdmin, err = gateway.IsSuperAdmin(ctx)
if err != nil {
return
}
// 1)如果设为会话模型,先把该用户旧会话模型取消
err = s.CancelChatModel(ctx, req.ModelType, req.ChatModel, isSuperAdmin)
if err != nil {
return
}
// 2)更新数据
_, err = dao.ModelManage.Update(ctx, req)
return
})
return
}
func (s *modelManageService) CancelChatModel(ctx context.Context, modelType model.ModelType, chatModel *bool, isSuperAdmin bool) (err error) {
if !g.IsEmpty(chatModel) && *chatModel {
if *modelType == *model.ModelTypeInference.Code {
if isSuperAdmin {
return fmt.Errorf("超级管理员不能设置会话模型")
}
// 2)获取该用户信息
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
// 3)取消该用户之前的会话模型
var get *entity.ModelManage
get, err = dao.ModelManage.Get(ctx, &dto.GetModelManage{
Creator: user.UserName,
ChatModel: chatModel,
})
if err != nil {
return
}
_, err = dao.ModelManage.Update(ctx, &dto.UpdateModelManageReq{
Id: get.Id,
ChatModel: gconv.PtrBool(false),
})
if err != nil {
return
}
} else {
return fmt.Errorf("只有推理模型可以设置成会话模型")
}
}
return
}
// Delete 删除模型
func (s *modelManageService) Delete(ctx context.Context, req *dto.DeleteModelManageReq) error {
_, err := dao.ModelManage.Delete(ctx, req)
return err
}
func (s *modelManageService) Get(ctx context.Context, req *dto.GetModelManageReq) (res *dto.GetModelManageRes, err error) {
get, err := dao.ModelManage.GetNotTenantId(ctx, req)
if err != nil {
return
}
err = gconv.Struct(get, &res)
return
}
// List 获取模型列表
func (s *modelManageService) List(ctx context.Context, req *dto.ListModelManageReq) (res *dto.ListModelManageRes, err error) {
var user *beans.User
user, err = utils.GetUserInfo(ctx)
if err != nil {
return
}
req.Creator = user.UserName
list, total, err := dao.ModelManage.ListNotTenantId(ctx, req)
if err != nil {
return
}
res = &dto.ListModelManageRes{
Total: total,
}
err = gconv.Struct(list, &res.List)
return
}
func (s *modelManageService) CheckChatModel(ctx context.Context, req *dto.CheckChatModelReq) (res *dto.CheckChatModelRes, err error) {
user, err := utils.GetUserInfo(ctx)
if err != nil {
return
}
get, err := dao.ModelManage.Get(ctx, &dto.GetModelManage{
Creator: user.UserName,
ChatModel: gconv.PtrBool(true),
})
if err != nil {
return
}
res = &dto.CheckChatModelRes{
IsChatModel: !g.IsEmpty(get),
}
return
}
// GetModelType 获取模型类型
func (s *modelManageService) GetModelType(ctx context.Context, req *dto.ModelTypeReq) (res *dto.ModelTypeRes, err error) {
res = &dto.ModelTypeRes{
List: model.GetTypeTreeList(),
}
return res, nil
}
// GetModelSupplier 获取运营商列表
func (s *modelManageService) GetModelSupplier(ctx context.Context, req *dto.ModelSupplierReq) (res *dto.ModelSupplierRes, err error) {
return &dto.ModelSupplierRes{
List: model.GetSupplierOptionList(),
}, nil
}
+118
View File
@@ -231,3 +231,121 @@ COMMENT ON COLUMN model_gateway_logs_op.error_msg IS '错误信息(失败时
COMMENT ON COLUMN model_gateway_logs_op.cost_ms IS '耗时(毫秒)';
COMMENT ON COLUMN model_gateway_logs_op.request_payload IS '请求 JSON';
COMMENT ON COLUMN model_gateway_logs_op.response_payload IS '响应 JSON';
-- =========================================================================================================================
CREATE TABLE "public"."model_gateway_model_manage" (
"id" int8 NOT NULL,
"tenant_id" int8 NOT NULL DEFAULT 0,
"creator" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"created_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar(64) COLLATE "pg_catalog"."default" NOT NULL,
"updated_at" timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted_at" timestamp(6),
"model_supplier" varchar(32) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"model_name" varchar(128) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"model_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"base_url" varchar(512) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"system_model" bool,
"http_method" varchar(32) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"chat_model" bool,
"response_type" int2 NOT NULL DEFAULT 0,
"api_key" varchar(255) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"enabled" bool,
"request_head_mapping" jsonb DEFAULT '{}'::jsonb,
"request_body_mapping" jsonb DEFAULT '{}'::jsonb,
"response_mapping" jsonb DEFAULT '{}'::jsonb,
"max_concurrency" int4 NOT NULL DEFAULT 0,
"token_mapping" jsonb,
"async_task_mapping" jsonb,
"token_predict_price" numeric(12,6) NOT NULL DEFAULT 0.000000,
"max_tokens" int4 NOT NULL DEFAULT 0,
"last_frame" varchar(512) COLLATE "pg_catalog"."default" NOT NULL DEFAULT ''::character varying,
"response_body_mapping" jsonb DEFAULT '{}'::jsonb,
"token_predict_price_unit" varchar(32) COLLATE "pg_catalog"."default",
"max_duration" int4,
CONSTRAINT "model_gateway_model_manage_pkey" PRIMARY KEY ("id")
)
;
ALTER TABLE "public"."model_gateway_model_manage"
OWNER TO "postgres";
CREATE INDEX "idx_model_manage_deleted_at" ON "public"."model_gateway_model_manage" USING btree (
"deleted_at" "pg_catalog"."timestamp_ops" ASC NULLS LAST
);
CREATE INDEX "idx_model_manage_model_type" ON "public"."model_gateway_model_manage" USING btree (
"model_type" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
CREATE INDEX "idx_model_manage_response_type" ON "public"."model_gateway_model_manage" USING btree (
"response_type" "pg_catalog"."int2_ops" ASC NULLS LAST
);
CREATE INDEX "idx_model_manage_supplier" ON "public"."model_gateway_model_manage" USING btree (
"model_supplier" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
CREATE INDEX "idx_model_manage_tenant_id" ON "public"."model_gateway_model_manage" USING btree (
"tenant_id" "pg_catalog"."int8_ops" ASC NULLS LAST
);
COMMENT ON COLUMN "public"."model_gateway_model_manage"."id" IS '主键ID';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."tenant_id" IS '租户ID';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."creator" IS '创建人';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."created_at" IS '创建时间';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."updater" IS '更新人';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."updated_at" IS '更新时间';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."deleted_at" IS '删除时间(软删)';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."model_supplier" IS '模型供应商';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."model_name" IS '模型名称';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."model_type" IS '模型类型';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."base_url" IS '模型地址';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."system_model" IS '是否系统模型';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."http_method" IS 'http请求方法';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."chat_model" IS '是否聊天模型';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."response_type" IS '返回类型:1同步,2异步,3流';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."api_key" IS '接口密钥';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."enabled" IS '是否启用';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."request_head_mapping" IS '请求头映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."request_body_mapping" IS '请求体映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."response_mapping" IS '响应映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."max_concurrency" IS '最大并发数';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."token_mapping" IS 'token映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."async_task_mapping" IS '异步任务映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."token_predict_price" IS '模型Token预估价格';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."max_tokens" IS '最大token数';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."last_frame" IS '视频尾帧图像地址';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."response_body_mapping" IS '响应主体映射';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."token_predict_price_unit" IS '模型token预估价格单位(秒,百万Token,千Token,字数)';
COMMENT ON COLUMN "public"."model_gateway_model_manage"."max_duration" IS '最大时长(秒)';
COMMENT ON TABLE "public"."model_gateway_model_manage" IS '模型管理表';