feat: 新增模型管理模块功能
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -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 *int8
|
||||||
|
|
||||||
|
// 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.PtrInt8(TypeInference))
|
||||||
|
ModelTypeImage = newItem(gconv.PtrInt8(TypeImage))
|
||||||
|
ModelTypeAudio = newItem(gconv.PtrInt8(TypeAudio))
|
||||||
|
ModelTypeVector = newItem(gconv.PtrInt8(TypeVector))
|
||||||
|
ModelTypeOmni = newItem(gconv.PtrInt8(TypeOmni))
|
||||||
|
ModelTypeVideo = newItem(gconv.PtrInt8(TypeVideo))
|
||||||
|
|
||||||
|
// 图片二级子类型
|
||||||
|
ModelImageSubTextToImage = newItem(gconv.PtrInt8(ImageSubTextToImage))
|
||||||
|
ModelImageSubImageToImage = newItem(gconv.PtrInt8(ImageSubImageToImage))
|
||||||
|
ModelImageSubImageEdit = newItem(gconv.PtrInt8(ImageSubImageEdit))
|
||||||
|
ModelImageSubImageVariation = newItem(gconv.PtrInt8(ImageSubImageVariation))
|
||||||
|
ModelImageSubImageTextToImage = newItem(gconv.PtrInt8(ImageSubImageTextToImage))
|
||||||
|
|
||||||
|
// 音频二级子类型
|
||||||
|
ModelAudioSubTextToSpeech = newItem(gconv.PtrInt8(AudioSubTextToSpeech))
|
||||||
|
ModelAudioSubSpeechToText = newItem(gconv.PtrInt8(AudioSubSpeechToText))
|
||||||
|
ModelAudioSubSpeechToSpeech = newItem(gconv.PtrInt8(AudioSubSpeechToSpeech))
|
||||||
|
|
||||||
|
// 向量化二级子类型
|
||||||
|
ModelVectorSubEmbedding = newItem(gconv.PtrInt8(VectorSubEmbedding))
|
||||||
|
ModelVectorSubRerank = newItem(gconv.PtrInt8(VectorSubRerank))
|
||||||
|
|
||||||
|
// 全模态二级子类型
|
||||||
|
ModelOmniSubTextImageAudio = newItem(gconv.PtrInt8(OmniSubTextImageAudio))
|
||||||
|
ModelOmniSubVision = newItem(gconv.PtrInt8(OmniSubVision))
|
||||||
|
|
||||||
|
// 视频二级子类型
|
||||||
|
ModelVideoSubTextToVideo = newItem(gconv.PtrInt8(VideoSubTextToVideo))
|
||||||
|
ModelVideoSubImageToVideo = newItem(gconv.PtrInt8(VideoSubImageToVideo))
|
||||||
|
ModelVideoSubImageTextToVideo = newItem(gconv.PtrInt8(VideoSubImageTextToVideo))
|
||||||
|
ModelVideoSubVideoToVideo = newItem(gconv.PtrInt8(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
|
||||||
|
}
|
||||||
@@ -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}
|
||||||
|
}
|
||||||
@@ -1,5 +1,11 @@
|
|||||||
package public
|
package public
|
||||||
|
|
||||||
|
// Option 通用下拉选项
|
||||||
|
type Option struct {
|
||||||
|
Value int `json:"value"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CallModeSync = 0 // 同步调用
|
CallModeSync = 0 // 同步调用
|
||||||
CallModeAsync = 1 // 异步调用
|
CallModeAsync = 1 // 异步调用
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TableNameModel = "model_gateway_models" // 模型表
|
TableNameModel = "model_gateway_models" // 模型表
|
||||||
TableNameTask = "model_gateway_task" // 任务表
|
TableNameTask = "model_gateway_task" // 任务表
|
||||||
TableNameOpLog = "model_gateway_logs_op" // 操作日志表
|
TableNameOpLog = "model_gateway_logs_op" // 操作日志表
|
||||||
TableNameStat = "model_gateway_logs_stat" // 按天统计表
|
TableNameStat = "model_gateway_logs_stat" // 按天统计表
|
||||||
|
TableNameModelManage = "model_gateway_model_manage"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ module model-gateway
|
|||||||
go 1.26.1
|
go 1.26.1
|
||||||
|
|
||||||
require (
|
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/drivers/pgsql/v2 v2.10.2
|
||||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
|
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
|
||||||
github.com/gogf/gf/v2 v2.10.2
|
github.com/gogf/gf/v2 v2.10.2
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
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 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.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 v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func main() {
|
|||||||
|
|
||||||
// 注册路由
|
// 注册路由
|
||||||
http.RouteRegister([]interface{}{
|
http.RouteRegister([]interface{}{
|
||||||
|
controller.ModelManage,
|
||||||
controller.ModelGatewayModels,
|
controller.ModelGatewayModels,
|
||||||
controller.ModelGatewayTask,
|
controller.ModelGatewayTask,
|
||||||
controller.ModelGatewayLogsStat,
|
controller.ModelGatewayLogsStat,
|
||||||
|
|||||||
@@ -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的映射"`
|
||||||
|
}
|
||||||
@@ -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:"任务状态-未知"`
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
+119
-1
@@ -230,4 +230,122 @@ COMMENT ON COLUMN model_gateway_logs_op.success IS '是否成功:1成功/0失
|
|||||||
COMMENT ON COLUMN model_gateway_logs_op.error_msg IS '错误信息(失败时)';
|
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.cost_ms IS '耗时(毫秒)';
|
||||||
COMMENT ON COLUMN model_gateway_logs_op.request_payload IS '请求 JSON';
|
COMMENT ON COLUMN model_gateway_logs_op.request_payload IS '请求 JSON';
|
||||||
COMMENT ON COLUMN model_gateway_logs_op.response_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 '模型管理表';
|
||||||
Reference in New Issue
Block a user