local model first commit

This commit is contained in:
Liujian
2025-02-14 15:34:41 +08:00
parent 88d4c5101e
commit 2ff7458c4e
47 changed files with 66868 additions and 266 deletions
+1
View File
@@ -4,3 +4,4 @@
/build/
/apipark
.gitlab-ci.yml
/.vscode/
+22
View File
@@ -0,0 +1,22 @@
package ai_provider_local
import "time"
type Model struct {
Name string `json:"name"`
Model string `json:"model"`
ModifiedAt time.Time `json:"modified_at"`
Size int64 `json:"size"`
Digest string `json:"digest"`
Details ModelDetails `json:"details,omitempty"`
}
// ModelDetails provides details about a model.
type ModelDetails struct {
ParentModel string `json:"parent_model"`
Format string `json:"format"`
Family string `json:"family"`
Families []string `json:"families"`
ParameterSize string `json:"parameter_size"`
QuantizationLevel string `json:"quantization_level"`
}
+98 -61
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"sync"
"time"
"github.com/ollama/ollama/progress"
@@ -19,11 +18,15 @@ var (
// Pipeline 结构体,表示每个用户的管道
type Pipeline struct {
id string
channel chan string
channel chan PullMessage
ctx context.Context
cancel context.CancelFunc
}
func (p *Pipeline) Message() <-chan PullMessage {
return p.channel
}
// AsyncExecutor 结构体,管理不同模型的管道和任务队列
type AsyncExecutor struct {
ctx context.Context
@@ -34,9 +37,10 @@ type AsyncExecutor struct {
}
type modelPipeline struct {
pipelines eosc.Untyped[string, *Pipeline]
ctx context.Context
cancel context.CancelFunc
pipelines eosc.Untyped[string, *Pipeline]
pullFn PullCallback
maxSize int
}
@@ -59,6 +63,21 @@ func (m *modelPipeline) Set(id string, p *Pipeline) error {
return nil
}
func (m *modelPipeline) AddPipeline(id string) (*Pipeline, error) {
ctx, cancel := context.WithCancel(m.ctx)
pipeline := &Pipeline{
ctx: ctx,
cancel: cancel,
id: id,
channel: make(chan PullMessage, 10), // 带缓冲,防止阻塞
}
err := m.Set(id, pipeline)
if err != nil {
return nil, err
}
return pipeline, nil
}
func (m *modelPipeline) Close() {
m.cancel()
ids := m.pipelines.Keys()
@@ -90,9 +109,16 @@ func newModelPipeline(ctx context.Context, maxSize int) *modelPipeline {
// messageTask 结构体,包含模型名和消息内容
type messageTask struct {
model string
message string
status string
message PullMessage
}
type PullMessage struct {
Model string
Status string
Digest string
Total int64
Completed int64
Msg string
}
// NewAsyncExecutor 创建一个新的异步任务执行器
@@ -109,27 +135,18 @@ func NewAsyncExecutor(queueSize int) *AsyncExecutor {
return executor
}
// AddPipeline 为指定模型的用户创建一个管道
func (e *AsyncExecutor) AddPipeline(model, id string) (*Pipeline, bool) {
func (e *AsyncExecutor) GetModelPipeline(model string) (*modelPipeline, bool) {
e.mu.Lock()
defer e.mu.Unlock()
hasModel := true
mp, ok := e.pipelines[model]
if !ok {
hasModel = false
mp = newModelPipeline(e.ctx, 100)
e.pipelines[model] = mp
}
ctx, cancel := context.WithCancel(mp.ctx)
pipeline := &Pipeline{
ctx: ctx,
cancel: cancel,
id: id,
channel: make(chan string, 10), // 带缓冲,防止阻塞
}
mp.Set(id, pipeline)
return pipeline, hasModel
return mp, ok
}
func (e *AsyncExecutor) SetModelPipeline(model string, mp *modelPipeline) {
e.mu.Lock()
defer e.mu.Unlock()
e.pipelines[model] = mp
}
// ClosePipeline 关闭管道并移除
@@ -159,18 +176,22 @@ func (e *AsyncExecutor) CloseModelPipeline(model string) {
func (e *AsyncExecutor) StartMessageDistributor() {
go func() {
for task := range e.msgQueue {
if task.status == "error" || task.status == "success" {
e.DistributeToModelPipelines(task.model, task.message)
e.CloseModelPipeline(task.model)
msg := task.message
e.DistributeToModelPipelines(msg.Model, msg)
if msg.Status == "error" || msg.Status == "success" {
mp, has := e.GetModelPipeline(msg.Model)
if has && mp.pullFn != nil {
mp.pullFn(msg)
}
e.CloseModelPipeline(msg.Model)
continue
}
e.DistributeToModelPipelines(task.model, task.message)
}
}()
}
// DistributeToModelPipelines 仅将消息分发给指定模型的管道
func (e *AsyncExecutor) DistributeToModelPipelines(model, message string) {
func (e *AsyncExecutor) DistributeToModelPipelines(model string, msg PullMessage) {
e.mu.Lock()
defer e.mu.Unlock()
pipelines, ok := e.pipelines[model]
@@ -179,15 +200,26 @@ func (e *AsyncExecutor) DistributeToModelPipelines(model, message string) {
}
for _, pipeline := range pipelines.List() {
select {
case pipeline.channel <- message:
case pipeline.channel <- msg:
default:
// 如果管道已满,跳过
}
}
}
func PullModel(model string, id string) (*Pipeline, error) {
p, has := taskExecutor.AddPipeline(model, id)
type PullCallback func(msg PullMessage) error
func PullModel(model string, id string, fn PullCallback) (*Pipeline, error) {
mp, has := taskExecutor.GetModelPipeline(model)
if !has {
mp = newModelPipeline(taskExecutor.ctx, 100)
mp.pullFn = fn
taskExecutor.SetModelPipeline(model, mp)
}
p, err := mp.AddPipeline(id)
if err != nil {
return nil, err
}
if !has {
var status string
bars := make(map[string]*progress.Bar)
@@ -201,33 +233,48 @@ func PullModel(model string, id string) (*Pipeline, error) {
bar.Set(resp.Completed)
taskExecutor.msgQueue <- messageTask{
model: model,
message: bar.String(),
status: resp.Status,
message: PullMessage{
Model: model,
Digest: resp.Digest,
Total: resp.Total,
Completed: resp.Completed,
Msg: bar.String(),
Status: resp.Status,
},
}
} else if status != resp.Status {
taskExecutor.msgQueue <- messageTask{
model: model,
message: resp.Status,
status: resp.Status,
message: PullMessage{
Model: model,
Digest: resp.Digest,
Total: resp.Total,
Completed: resp.Completed,
Msg: status,
Status: resp.Status,
},
}
}
return nil
}
go func() {
err := client.Pull(context.Background(), &api.PullRequest{Model: model}, fn)
err = client.Pull(mp.ctx, &api.PullRequest{Model: model}, fn)
if err != nil {
taskExecutor.msgQueue <- messageTask{
model: model,
message: err.Error(),
status: "error",
message: PullMessage{
Model: model,
Status: "error",
Digest: "",
Total: 0,
Completed: 0,
Msg: err.Error(),
},
}
}
}()
}
return p, nil
}
@@ -235,6 +282,15 @@ func StopPull(model string) {
taskExecutor.CloseModelPipeline(model)
}
func CancelPipeline(model string, id string) {
taskExecutor.ClosePipeline(model, id)
}
func RemoveModel(model string) error {
taskExecutor.CloseModelPipeline(model)
return client.Delete(context.Background(), &api.DeleteRequest{Model: model})
}
func ModelsInstalled() ([]Model, error) {
result, err := client.List(context.Background())
if err != nil {
@@ -260,22 +316,3 @@ func ModelsInstalled() ([]Model, error) {
}
return models, nil
}
type Model struct {
Name string `json:"name"`
Model string `json:"model"`
ModifiedAt time.Time `json:"modified_at"`
Size int64 `json:"size"`
Digest string `json:"digest"`
Details ModelDetails `json:"details,omitempty"`
}
// ModelDetails provides details about a model.
type ModelDetails struct {
ParentModel string `json:"parent_model"`
Format string `json:"format"`
Family string `json:"family"`
Families []string `json:"families"`
ParameterSize string `json:"parameter_size"`
QuantizationLevel string `json:"quantization_level"`
}
+68 -9
View File
@@ -1,21 +1,80 @@
package ai_provider_local
import "testing"
import (
"fmt"
"io"
"net/http"
"testing"
"github.com/gin-contrib/gzip"
"github.com/eolinker/eosc/log"
"github.com/google/uuid"
"github.com/gin-gonic/gin"
)
func TestPullModel(t *testing.T) {
p, err := PullModel("phi4", "deepseek-r1")
// 创建 Gin 引擎
r := gin.Default()
r.Use(gzip.Gzip(gzip.DefaultCompression))
// 设置路由,监听 "/stream" 路径
r.GET("/stream", streamHandler)
r.GET("/stop", stopPull)
r.GET("/models", models)
// 启动 HTTP 服务器
r.Run(":11180")
}
func streamHandler(c *gin.Context) {
// 创建一个通道,用于监测客户端关闭连接的信号
model := c.Query("model")
p, err := PullModel(model, uuid.NewString(), nil)
if err != nil {
t.Fatal(err)
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
for {
done := make(chan struct{})
// 启动一个 goroutine 监听客户端关闭连接
go func() {
select {
case <-c.Writer.CloseNotify():
log.Info("client closed connection,close pipeline")
taskExecutor.ClosePipeline(model, p.id)
case <-done:
}
}()
c.Stream(func(w io.Writer) bool {
select {
case msg, ok := <-p.channel:
if !ok {
return
return false
}
t.Log(msg)
case <-p.done:
return
_, err := w.Write([]byte(fmt.Sprintf("%s\n", msg.Msg)))
if err != nil {
log.Error("write message error: %v", err)
return false
}
return true
}
}
})
done <- struct{}{}
}
func stopPull(c *gin.Context) {
model := c.Query("model")
StopPull(model)
c.JSON(http.StatusOK, gin.H{"message": "stop pull model"})
}
func models(c *gin.Context) {
ms, err := ModelsInstalled()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"models": ms})
}
+70
View File
@@ -0,0 +1,70 @@
package ai_provider_local
import (
"embed"
"encoding/json"
"strings"
"github.com/eolinker/eosc/log"
)
var (
//go:embed models.json
modelsFs embed.FS
modelCanInstall []ModelDetail
modelVersion string
modelTags = make(map[string][]ModelDetail)
)
type ModelConfig struct {
Models []ModelDetail `json:"models"`
Version string `json:"version"`
}
type ModelDetail struct {
Id string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Size string `json:"size"`
Digest string `json:"digest"`
Provider string `json:"provider"`
IsPopular bool `json:"is_popular"`
Latest bool `json:"latest"`
}
func init() {
data, err := modelsFs.ReadFile("models.json")
if err != nil {
log.Info("read models.json error: ", err)
return
}
var cfg ModelConfig
err = json.Unmarshal(data, &cfg)
if err != nil {
log.Info("unmarshal models.json error: ", err)
return
}
modelVersion = cfg.Version
modelCanInstall = make([]ModelDetail, 0, len(cfg.Models))
for _, model := range cfg.Models {
if _, ok := modelTags[model.Id]; !ok {
modelTags[model.Id] = make([]ModelDetail, 0)
}
names := strings.Split(model.Id, ":")
modelTags[names[0]] = append(modelTags[names[0]], model)
if !model.Latest {
continue
}
modelCanInstall = append(modelCanInstall, model)
}
}
func ModelsCanInstall() ([]ModelDetail, string) {
return modelCanInstall, modelVersion
}
func ModelsCanInstallById(id string) []ModelDetail {
return modelTags[id]
}
+7
View File
@@ -0,0 +1,7 @@
package ai_provider_local
import "testing"
func TestModels(t *testing.T) {
t.Log(ModelsCanInstall())
}
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
package ai_local
import (
"reflect"
ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
)
type ILocalModelController interface {
Search(ctx *gin.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error)
ListCanInstall(ctx *gin.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error)
Deploy(ctx *gin.Context)
DeployStart(ctx *gin.Context, input *ai_local_dto.DeployInput) error
CancelDeploy(ctx *gin.Context, input *ai_local_dto.CancelDeploy) error
RemoveModel(ctx *gin.Context, model string) error
Update(ctx *gin.Context, model string, input *ai_local_dto.Update) error
State(ctx *gin.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error)
}
func init() {
autowire.Auto[ILocalModelController](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelController))
})
}
+331
View File
@@ -0,0 +1,331 @@
package ai_local
import (
"context"
"encoding/json"
"fmt"
"io"
"math"
"net/http"
"strings"
"github.com/APIParkLab/APIPark/module/router"
"github.com/APIParkLab/APIPark/model/plugin_model"
"github.com/APIParkLab/APIPark/service/api"
ai_api "github.com/APIParkLab/APIPark/module/ai-api"
"github.com/APIParkLab/APIPark/module/catalogue"
"github.com/APIParkLab/APIPark/module/service"
"github.com/eolinker/go-common/store"
service_dto "github.com/APIParkLab/APIPark/module/service/dto"
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
router_dto "github.com/APIParkLab/APIPark/module/router/dto"
ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
"github.com/eolinker/eosc/log"
ai_local "github.com/APIParkLab/APIPark/module/ai-local"
ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
var (
ollamaConfig = "{\n \"mirostat\": 0,\n \"mirostat_eta\": 0.1,\n \"mirostat_tau\": 5.0,\n \"num_ctx\": 4096,\n \"repeat_last_n\":64,\n \"repeat_penalty\": 1.1,\n \"temperature\": 0.7,\n \"seed\": 42,\n \"num_predict\": 42,\n \"top_k\": 40,\n \"top_p\": 0.9,\n \"min_p\": 0.5\n}\n"
)
var _ ILocalModelController = &imlLocalModelController{}
type imlLocalModelController struct {
module ai_local.ILocalModelModule `autowired:""`
serviceModule service.IServiceModule `autowired:""`
catalogueModule catalogue.ICatalogueModule `autowired:""`
aiAPIModule ai_api.IAPIModule `autowired:""`
routerModule router.IRouterModule `autowired:""`
docModule service.IServiceDocModule `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlLocalModelController) State(ctx *gin.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error) {
return i.module.ModelState(ctx, model)
}
// 自动选择合适的单位
func humanReadableSize(size int64) string {
const (
KB = 1000
MB = 1000 * KB
GB = 1000 * MB
)
switch {
case size >= GB:
return fmt.Sprintf("%.1f GB", math.Round(float64(size)/float64(GB)*10)/10)
case size >= MB:
return fmt.Sprintf("%.1f MB", math.Round(float64(size)/float64(MB)*10)/10)
case size >= KB:
return fmt.Sprintf("%.1f KB", math.Round(float64(size)/float64(KB)*10)/10)
default:
return fmt.Sprintf("%d B", size)
}
}
func (i *imlLocalModelController) Deploy(ctx *gin.Context) {
var input ai_local_dto.DeployInput
err := ctx.ShouldBindBodyWithJSON(&input)
if err != nil {
ctx.JSON(200, gin.H{
"code": -1, "msg": err.Error(), "success": "fail",
})
return
}
if input.Model == "" {
ctx.JSON(200, gin.H{
"code": -1, "msg": "model is required", "success": "fail",
})
return
}
err = i.initAILocalService(ctx, input.Model, input.Team)
if err != nil {
ctx.JSON(200, gin.H{
"code": -1, "msg": err.Error(), "success": "fail",
})
return
}
id := uuid.NewString()
p, err := i.module.Deploy(ctx, input.Model, id)
if err != nil {
ctx.JSON(200, gin.H{
"code": -1, "msg": err.Error(), "success": "fail",
})
return
}
done := make(chan struct{})
// 启动一个 goroutine 监听客户端关闭连接
go func() {
select {
case <-ctx.Writer.CloseNotify():
log.Info("client closed connection,close pipeline")
ai_provider_local.CancelPipeline(input.Model, id)
case <-done:
}
}()
var complete int64
var total int64
ctx.Stream(func(w io.Writer) bool {
msg, ok := <-p.Message()
if !ok {
return false
}
state := "download"
switch msg.Status {
case "verifying sha256 digest":
state = "deploy"
case "writing manifest":
state = "initializing"
case "removing any unused layers":
state = "initializing"
case "success":
state = "finish"
case "error":
state = "error"
}
if msg.Completed > complete {
complete = msg.Completed
}
if msg.Total > total {
total = msg.Total
}
result := map[string]interface{}{
"code": 0,
"msg": "",
"data": map[string]interface{}{
"message": msg.Msg,
"info": map[string]interface{}{
"current": humanReadableSize(complete),
"total": humanReadableSize(total),
},
"state": state,
},
}
data, _ := json.Marshal(result)
_, err = w.Write(data)
if err != nil {
log.Error("write message error: %v", err)
return false
}
_, err = w.Write([]byte("\n"))
if err != nil {
log.Error("write message error: %v", err)
return false
}
return true
})
close(done)
}
func (i *imlLocalModelController) DeployStart(ctx *gin.Context, input *ai_local_dto.DeployInput) error {
err := i.initAILocalService(ctx, input.Model, input.Team)
if err != nil {
return err
}
id := uuid.NewString()
_, err = i.module.Deploy(ctx, input.Model, id)
if err != nil {
return err
}
ai_provider_local.CancelPipeline(input.Model, id)
return nil
}
func (i *imlLocalModelController) initAILocalService(ctx context.Context, model string, teamID string) error {
err := i.transaction.Transaction(ctx, func(ctx context.Context) error {
catalogueInfo, err := i.catalogueModule.DefaultCatalogue(ctx)
if err != nil {
return err
}
providerId := "ollama"
prefix := "/" + model
info, err := i.serviceModule.Create(ctx, teamID, &service_dto.CreateService{
Id: model,
Name: model,
Prefix: prefix,
Description: "Auto generated service for AI model " + model,
ServiceType: "public",
Catalogue: catalogueInfo.Id,
ApprovalType: "auto",
Kind: "ai",
Provider: &providerId,
})
if err != nil {
return err
}
path := fmt.Sprintf("/%s/demo_translation_api", strings.Trim(prefix, "/"))
timeout := 300000
retry := 0
aiPrompt := &ai_api_dto.AiPrompt{
Variables: []*ai_api_dto.AiPromptVariable{
{
Key: "source_lang",
Description: "",
Require: true,
},
{
Key: "target_lang",
Description: "",
Require: true,
},
{
Key: "text",
Description: "",
Require: true,
},
},
Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}",
}
aiModel := &ai_api_dto.AiModel{
Id: model,
Config: ollamaConfig,
Provider: providerId,
Type: "local",
}
name := "Demo Translation API"
description := "A demo that shows you how to use a prompt to create a Translation API."
apiId := uuid.New().String()
err = i.aiAPIModule.Create(
ctx,
info.Id,
&ai_api_dto.CreateAPI{
Id: apiId,
Name: name,
Path: path,
Description: description,
Disable: false,
AiPrompt: aiPrompt,
AiModel: aiModel,
Timeout: timeout,
Retry: retry,
},
)
if err != nil {
return err
}
plugins := make(map[string]api.PluginSetting)
plugins["ai_prompt"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"prompt": aiPrompt.Prompt,
"variables": aiPrompt.Variables,
},
}
plugins["ai_formatter"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"model": aiModel.Id,
"provider": info.Provider.Id,
"config": aiModel.Config,
},
}
_, err = i.routerModule.Create(ctx, info.Id, &router_dto.Create{
Id: apiId,
Name: name,
Path: path,
Methods: []string{
http.MethodPost,
},
Description: description,
Protocols: []string{"http", "https"},
MatchRules: nil,
Proxy: &router_dto.InputProxy{
Path: path,
Timeout: timeout,
Retry: retry,
Plugins: plugins,
},
Disable: false,
})
if err != nil {
return err
}
return i.docModule.SaveServiceDoc(ctx, info.Id, &service_dto.SaveServiceDoc{
Doc: "The Translation API allows developers to translate text from one language to another. It supports multiple languages and enables easy integration of high-quality translation features into applications. With simple API requests, you can quickly translate content into different target languages.",
})
})
return err
}
func (i *imlLocalModelController) Search(ctx *gin.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error) {
return i.module.Search(ctx, keyword)
}
func (i *imlLocalModelController) ListCanInstall(ctx *gin.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error) {
return i.module.ListCanInstall(ctx, keyword)
}
func (i *imlLocalModelController) CancelDeploy(ctx *gin.Context, input *ai_local_dto.CancelDeploy) error {
return i.module.CancelDeploy(ctx, input.Model)
}
func (i *imlLocalModelController) RemoveModel(ctx *gin.Context, model string) error {
return i.module.RemoveModel(ctx, model)
}
func (i *imlLocalModelController) Update(ctx *gin.Context, model string, input *ai_local_dto.Update) error {
switch input.Disable {
case true:
return i.module.Disable(ctx, model)
default:
return i.module.Enable(ctx, model)
}
}
+2 -2
View File
@@ -9,7 +9,7 @@ import (
)
type IProviderController interface {
ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error)
ConfiguredProviders(ctx *gin.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error)
UnConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ProviderItem, error)
SimpleProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, error)
SimpleConfiguredProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error)
@@ -20,7 +20,7 @@ type IProviderController interface {
Disable(ctx *gin.Context, id string) error
UpdateProviderConfig(ctx *gin.Context, id string, input *ai_dto.UpdateConfig) error
UpdateProviderDefaultLLM(ctx *gin.Context, id string, input *ai_dto.UpdateLLM) error
Sort(ctx *gin.Context, input *ai_dto.Sort) error
//Sort(ctx *gin.Context, input *ai_dto.Sort) error
}
type IStatisticController interface {
+5 -5
View File
@@ -17,12 +17,12 @@ type imlProviderController struct {
module ai.IProviderModule `autowired:""`
}
func (i *imlProviderController) Sort(ctx *gin.Context, input *ai_dto.Sort) error {
return i.module.Sort(ctx, input)
}
//func (i *imlProviderController) Sort(ctx *gin.Context, input *ai_dto.Sort) error {
// return i.module.Sort(ctx, input)
//}
func (i *imlProviderController) ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
return i.module.ConfiguredProviders(ctx)
func (i *imlProviderController) ConfiguredProviders(ctx *gin.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error) {
return i.module.ConfiguredProviders(ctx, keyword)
}
func (i *imlProviderController) UnConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ProviderItem, error) {
+8 -6
View File
@@ -1,6 +1,8 @@
module github.com/APIParkLab/APIPark
go 1.21
go 1.23.4
toolchain go1.23.6
//toolchain go1.21.1
@@ -15,8 +17,9 @@ require (
github.com/google/uuid v1.6.0
github.com/influxdata/influxdb-client-go/v2 v2.14.0
github.com/nsqio/go-nsq v1.1.0
github.com/ollama/ollama v0.5.8
github.com/urfave/cli v1.22.16
golang.org/x/crypto v0.24.0
golang.org/x/crypto v0.31.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.5
)
@@ -41,7 +44,6 @@ require (
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/invopop/yaml v0.3.1 // indirect
@@ -59,7 +61,6 @@ require (
github.com/oapi-codegen/runtime v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/redis/go-redis/v9 v9.5.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
@@ -70,8 +71,9 @@ require (
go.uber.org/zap v1.23.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.5.2 // indirect
+10 -6
View File
@@ -114,6 +114,8 @@ github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE=
github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY=
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
github.com/ollama/ollama v0.5.8 h1:b2S6YdZ18/ntCsWzoy/HmB3BHGW4GX0Qp7RARrJtJXU=
github.com/ollama/ollama v0.5.8/go.mod h1:ibdmDvb/TjKY1OArBWIazL3pd1DHTk8eG2MMjEkWhiI=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
@@ -160,16 +162,18 @@ go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+1
View File
@@ -27,6 +27,7 @@ type AiModel struct {
Id string `json:"id"`
Config string `json:"config"`
Provider string `json:"provider"`
Type string `json:"type"`
}
type EditAPI struct {
+1 -6
View File
@@ -13,12 +13,7 @@ func genOpenAPI3Template(title string, description string) *openapi3.T {
Description: description,
Version: "beta",
}
//result.Tags = openapi3.Tags{
// {
// Name: title,
// Description: description,
// },
//}
result.Components = components
result.Paths = new(openapi3.Paths)
return result
+14
View File
@@ -0,0 +1,14 @@
package ai_balance_dto
type Create struct {
Id string `json:"id"`
Type string `json:"type"`
Provider string `json:"provider"`
Model string `json:"model"`
}
type Sort struct {
Origin string `json:"origin"`
Target string `json:"target"`
Sort string `json:"sort"`
}
+81
View File
@@ -0,0 +1,81 @@
package ai_balance_dto
const (
ModelTypeOnline = "online"
ModelTypeLocal = "local"
StateNormal = "normal"
StateAbnormal = "abnormal"
)
type ModelType string
func (m ModelType) String() string {
return string(m)
}
func (m ModelType) Int() int {
switch m {
case ModelTypeOnline:
return 0
case ModelTypeLocal:
return 1
default:
return -1
}
}
func ModelTypeFromInt(i int) ModelType {
switch i {
case 0:
return ModelTypeOnline
case 1:
return ModelTypeLocal
default:
return "unknown"
}
}
type ModelState string
func (m ModelState) String() string {
return string(m)
}
func (m ModelState) Int() int {
switch m {
case StateNormal:
return 1
case StateAbnormal:
return 0
default:
return -1
}
}
func ModelStateFromInt(i int) ModelState {
switch i {
case 1:
return StateNormal
case 0:
return StateAbnormal
default:
return "unknown"
}
}
type Item struct {
Id string `json:"id"`
Priority int `json:"priority"`
Provider *BasicItem `json:"provider"`
Model *BasicItem `json:"model"`
Type ModelType `json:"type"`
State ModelState `json:"state"`
APICount int64 `json:"api_count"`
KeyCount int64 `json:"key_count"`
}
type BasicItem struct {
Id string `json:"id"`
Name string `json:"name"`
}
+160
View File
@@ -0,0 +1,160 @@
package ai_balance
import (
"context"
"fmt"
"sort"
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
ai_api "github.com/APIParkLab/APIPark/service/ai-api"
"github.com/google/uuid"
ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
"github.com/eolinker/go-common/store"
"github.com/APIParkLab/APIPark/gateway"
"github.com/APIParkLab/APIPark/service/cluster"
ai_balance_dto "github.com/APIParkLab/APIPark/module/ai-balance/dto"
"github.com/eolinker/eosc/log"
)
var _ IBalanceModule = (*imlBalanceModule)(nil)
type imlBalanceModule struct {
clusterService cluster.IClusterService `autowired:""`
aiAPIService ai_api.IAPIService `autowired:""`
aiKeyService ai_key.IKeyService `autowired:""`
balanceService ai_balance.IBalanceService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlBalanceModule) Create(ctx context.Context, input *ai_balance_dto.Create) error {
priority, err := i.balanceService.MaxPriority(ctx)
if err != nil {
return err
}
if input.Id == "" {
input.Id = uuid.NewString()
}
providerName := ""
modelName := ""
// TODO: 名称进行优化
switch input.Type {
case ai_balance_dto.ModelTypeOnline:
case ai_balance_dto.ModelTypeLocal:
}
return i.balanceService.Create(ctx, &ai_balance.Create{
Id: input.Id,
Priority: priority + 1,
Provider: input.Provider,
ProviderName: providerName,
Model: input.Model,
ModelName: modelName,
Type: ai_balance_dto.ModelType(input.Type).Int(),
})
}
func newRelease(item *ai_balance.Balance) *gateway.DynamicRelease {
return &gateway.DynamicRelease{}
}
func (i *imlBalanceModule) Sort(ctx context.Context, input *ai_balance_dto.Sort) error {
var list []*ai_balance.Balance
var err error
switch input.Sort {
case "after":
list, err = i.balanceService.SortAfter(ctx, input.Origin, input.Target)
default:
list, err = i.balanceService.SortBefore(ctx, input.Origin, input.Target)
}
if err != nil {
return err
}
for _, item := range list {
err = i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{newRelease(item)}, true)
if err != nil {
return err
}
}
return nil
}
func (i *imlBalanceModule) List(ctx context.Context) ([]*ai_balance_dto.Item, error) {
list, err := i.balanceService.List(ctx)
if err != nil {
return nil, err
}
sort.Slice(list, func(i, j int) bool {
return list[i].Priority < list[j].Priority
})
aiAPIMap, err := i.aiAPIService.CountMapByProvider(ctx, "", nil)
if err != nil {
return nil, fmt.Errorf("get ai api count error:%v", err)
}
keyMap, err := i.aiKeyService.CountMapByProvider(ctx, "", nil)
if err != nil {
return nil, fmt.Errorf("get ai key count error:%v", err)
}
result := make([]*ai_balance_dto.Item, 0, len(list))
for i, item := range list {
priority := i + 1
result = append(result, &ai_balance_dto.Item{
Id: item.Id,
Provider: &ai_balance_dto.BasicItem{
Id: item.Provider,
Name: item.ProviderName,
},
Model: &ai_balance_dto.BasicItem{
Id: item.Model,
Name: item.ModelName,
},
Priority: priority,
Type: ai_balance_dto.ModelTypeFromInt(item.Type),
State: ai_balance_dto.ModelStateFromInt(item.State),
APICount: aiAPIMap[item.Model],
KeyCount: keyMap[item.Provider],
})
}
return result, nil
}
func (i *imlBalanceModule) Delete(ctx context.Context, id string) error {
return i.balanceService.Delete(ctx, id)
}
func (i *imlBalanceModule) syncGateway(ctx context.Context, clusterId string, releases []*gateway.DynamicRelease, online bool) error {
return nil
client, err := i.clusterService.GatewayClient(ctx, clusterId)
if err != nil {
log.Errorf("get apinto client error: %v", err)
return nil
}
defer func() {
err := client.Close(ctx)
if err != nil {
log.Warn("close apinto client:", err)
}
}()
for _, releaseInfo := range releases {
dynamicClient, err := client.Dynamic(releaseInfo.Resource)
if err != nil {
return err
}
if online {
err = dynamicClient.Online(ctx, releaseInfo)
} else {
dynamicClient.Offline(ctx, releaseInfo)
}
if err != nil {
return err
}
}
return nil
}
+23
View File
@@ -0,0 +1,23 @@
package ai_balance
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
ai_balance_dto "github.com/APIParkLab/APIPark/module/ai-balance/dto"
)
type IBalanceModule interface {
Create(ctx context.Context, input *ai_balance_dto.Create) error
Sort(ctx context.Context, input *ai_balance_dto.Sort) error
List(ctx context.Context) ([]*ai_balance_dto.Item, error)
Delete(ctx context.Context, id string) error
}
func init() {
autowire.Auto[IBalanceModule](func() reflect.Value {
return reflect.ValueOf(new(imlBalanceModule))
})
}
+14
View File
@@ -0,0 +1,14 @@
package ai_local_dto
type Update struct {
Disable bool `json:"disable"`
}
type CancelDeploy struct {
Model string `json:"model"`
}
type DeployInput struct {
Model string `json:"model"`
Team string `json:"team"`
}
+128
View File
@@ -0,0 +1,128 @@
package ai_local_dto
import "github.com/eolinker/go-common/auto"
type LocalModelState string
const (
LocalModelStateNormal LocalModelState = "normal"
LocalModelStateDisable LocalModelState = "disabled"
LocalModelStateDeployingError LocalModelState = "deploying_error"
LocalModelStateError LocalModelState = "error"
LocalModelStateDeploying LocalModelState = "deploying"
DeployStateDownload DeployState = "download"
DeployStateDeploy DeployState = "deploy"
DeployStateInitializing DeployState = "initializing"
DeployStateFinish DeployState = "finish"
DeployStateDownloadError DeployState = "download error"
DeployStateDeployError DeployState = "deploy error"
DeployStateInitializingError DeployState = "initializing error"
)
func (l LocalModelState) String() string {
return string(l)
}
func (l LocalModelState) Int() int {
switch l {
case LocalModelStateDisable:
return 0
case LocalModelStateNormal:
return 1
case LocalModelStateError:
return 2
case LocalModelStateDeploying:
return 3
case LocalModelStateDeployingError:
return 4
default:
return 0
}
}
func FromLocalModelState(state int) LocalModelState {
switch state {
case 0:
return LocalModelStateDisable
case 1:
return LocalModelStateNormal
case 2:
return LocalModelStateError
case 3:
return LocalModelStateDeploying
case 4:
return LocalModelStateDeployingError
default:
return LocalModelStateDisable
}
}
type LocalModelItem struct {
Id string `json:"id"`
Name string `json:"name"`
State LocalModelState `json:"state"`
APICount int64 `json:"api_count"`
UpdateTime auto.TimeLabel `json:"update_time"`
}
type LocalModelPackageItem struct {
Id string `json:"id"`
Name string `json:"name"`
Size string `json:"size"`
IsPopular bool `json:"is_popular"`
}
type DeployState string
func (d DeployState) String() string {
return string(d)
}
func (d DeployState) Int() int {
switch d {
case DeployStateDownload:
return 1
case DeployStateDeploy:
return 2
case DeployStateInitializing:
return 3
case DeployStateFinish:
return 4
case DeployStateDownloadError:
return 5
case DeployStateDeployError:
return 6
case DeployStateInitializingError:
return 7
default:
return 1
}
}
func FromDeployState(state int) DeployState {
switch state {
case 1:
return DeployStateDownload
case 2:
return DeployStateDeploy
case 3:
return DeployStateInitializing
case 4:
return DeployStateFinish
case 5:
return DeployStateDownloadError
case 6:
return DeployStateDeployError
case 7:
return DeployStateInitializingError
default:
return DeployStateDownload
}
}
type ModelInfo struct {
Current int64 `json:"current"`
Total int64 `json:"total"`
LastMessage string `json:"last_message"`
}
+297
View File
@@ -0,0 +1,297 @@
package ai_local
import (
"context"
"errors"
"fmt"
"strings"
"github.com/eolinker/go-common/auto"
ai_api "github.com/APIParkLab/APIPark/service/ai-api"
"github.com/eolinker/go-common/register"
"github.com/eolinker/go-common/server"
"github.com/eolinker/go-common/utils"
"gorm.io/gorm"
ai_local "github.com/APIParkLab/APIPark/service/ai-local"
"github.com/eolinker/go-common/store"
ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
)
var (
_ ILocalModelModule = (*imlLocalModel)(nil)
)
type imlLocalModel struct {
localModelService ai_local.ILocalModelService `autowired:""`
localModelPackageService ai_local.ILocalModelPackageService `autowired:""`
localModelStateService ai_local.ILocalModelInstallStateService `autowired:""`
aiAPIService ai_api.IAPIService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlLocalModel) ModelState(ctx context.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error) {
info, err := i.localModelStateService.Get(ctx, model)
if err != nil {
return nil, nil, err
}
state := ai_local_dto.FromDeployState(info.State)
return &state, &ai_local_dto.ModelInfo{
Current: info.Complete,
Total: info.Total,
LastMessage: info.Msg,
}, nil
}
func (i *imlLocalModel) Search(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error) {
list, err := i.localModelService.Search(ctx, keyword, nil, "update_at desc")
if err != nil {
return nil, err
}
apiCountMap, err := i.aiAPIService.CountMapByModel(ctx, "", map[string]interface{}{
"type": 1,
})
if err != nil {
return nil, err
}
return utils.SliceToSlice(list, func(s *ai_local.LocalModel) *ai_local_dto.LocalModelItem {
return &ai_local_dto.LocalModelItem{
Id: s.Id,
Name: s.Name,
State: ai_local_dto.FromLocalModelState(s.State),
APICount: apiCountMap[s.Id],
UpdateTime: auto.TimeLabel(s.UpdateAt),
}
}), nil
}
func (i *imlLocalModel) ListCanInstall(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error) {
list, err := i.localModelPackageService.Search(ctx, keyword, nil)
if err != nil {
return nil, err
}
if keyword != "" {
result := make([]*ai_local_dto.LocalModelPackageItem, 0)
for _, v := range list {
models := ai_provider_local.ModelsCanInstallById(v.Id)
for _, model := range models {
result = append(result, &ai_local_dto.LocalModelPackageItem{
Id: model.Id,
Name: model.Name,
Size: model.Size,
IsPopular: model.IsPopular,
})
}
}
return result, nil
}
return utils.SliceToSlice(list, func(s *ai_local.LocalModelPackage) *ai_local_dto.LocalModelPackageItem {
return &ai_local_dto.LocalModelPackageItem{
Id: s.Id,
Name: s.Name,
Size: s.Size,
IsPopular: s.IsPopular,
}
}), nil
}
func (i *imlLocalModel) pullHook() func(msg ai_provider_local.PullMessage) error {
return func(msg ai_provider_local.PullMessage) error {
return i.transaction.Transaction(context.Background(), func(ctx context.Context) error {
state := ai_local_dto.DeployStateFinish.Int()
modelState := ai_local_dto.LocalModelStateNormal.Int()
if msg.Status == "error" {
state = ai_local_dto.DeployStateDownloadError.Int()
modelState = ai_local_dto.LocalModelStateDeployingError.Int()
}
err := i.localModelService.Save(ctx, msg.Model, &ai_local.EditLocalModel{State: &modelState})
if err != nil {
return err
}
info, err := i.localModelStateService.Get(ctx, msg.Model)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
return i.localModelStateService.Create(ctx, &ai_local.CreateLocalModelInstallState{
Id: msg.Model,
Complete: msg.Completed,
Total: msg.Total,
State: state,
Msg: msg.Msg,
})
}
if info.Complete < msg.Completed {
info.Complete = msg.Completed
}
if info.Total < msg.Total {
info.Total = msg.Total
}
if msg.Msg != "" {
info.Msg = msg.Msg
}
return i.localModelStateService.Save(ctx, msg.Model, &ai_local.EditLocalModelInstallState{State: &state, Complete: &info.Complete, Total: &info.Total, Msg: &info.Msg})
})
}
}
func (i *imlLocalModel) Deploy(ctx context.Context, model string, session string) (*ai_provider_local.Pipeline, error) {
var p *ai_provider_local.Pipeline
err := i.transaction.Transaction(ctx, func(txCtx context.Context) error {
_, err := i.localModelService.Get(ctx, model)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
names := strings.Split(model, ":")
err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{
Id: model,
Name: model,
Provider: names[0],
})
} else {
state := ai_local_dto.LocalModelStateDeploying.Int()
err = i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &state})
}
if err != nil {
return err
}
p, err = ai_provider_local.PullModel(model, session, i.pullHook())
if err != nil {
return err
}
return nil
})
if err != nil {
return nil, err
}
return p, nil
}
func (i *imlLocalModel) CancelDeploy(ctx context.Context, model string) error {
ai_provider_local.StopPull(model)
// 删除模型
return i.localModelService.Delete(ctx, model)
}
func (i *imlLocalModel) RemoveModel(ctx context.Context, model string) error {
err := ai_provider_local.RemoveModel(model)
if err != nil {
return err
}
return i.localModelService.Delete(ctx, model)
}
func (i *imlLocalModel) Enable(ctx context.Context, model string) error {
info, err := i.localModelService.Get(ctx, model)
if err != nil {
return err
}
if info.State == ai_local_dto.LocalModelStateDisable.Int() || info.State == ai_local_dto.LocalModelStateError.Int() {
status := ai_local_dto.LocalModelStateNormal.Int()
return i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &status})
}
return fmt.Errorf("model %s is not disabled state,can not enable", model)
}
func (i *imlLocalModel) Disable(ctx context.Context, model string) error {
info, err := i.localModelService.Get(ctx, model)
if err != nil {
return err
}
if info.State == ai_local_dto.LocalModelStateNormal.Int() {
disable := ai_local_dto.LocalModelStateDisable.Int()
return i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &disable})
}
return fmt.Errorf("model %s is not enabled state,can not disable", model)
}
func (i *imlLocalModel) OnInit() {
register.Handle(func(v server.Server) {
ctx := context.Background()
list, err := i.localModelPackageService.List(ctx)
if err != nil {
return
}
oldModels := utils.SliceToMapO(list, func(s *ai_local.LocalModelPackage) (string, *ai_local.LocalModelPackage) {
return s.Id, s
})
models, version := ai_provider_local.ModelsCanInstall()
for _, model := range models {
if v, ok := oldModels[model.Id]; ok {
if v.Version == version {
continue
}
err = i.localModelPackageService.Save(ctx, model.Id, &ai_local.EditLocalModelPackage{
Size: &model.Size,
Hash: &model.Digest,
Description: &model.Description,
Version: &version,
Popular: &model.IsPopular,
})
if err != nil {
return
}
} else {
err = i.localModelPackageService.Create(ctx, &ai_local.CreateLocalModelPackage{
Id: model.Id,
Name: model.Name,
Size: model.Size,
Hash: model.Digest,
Description: model.Description,
Version: version,
Popular: model.IsPopular,
})
if err != nil {
return
}
}
delete(oldModels, model.Id)
}
for id := range oldModels {
err = i.localModelPackageService.Delete(ctx, id)
if err != nil {
return
}
}
installModels, err := ai_provider_local.ModelsInstalled()
if err != nil {
return
}
for _, model := range installModels {
id := strings.TrimSuffix(model.Name, ":latest")
name := strings.TrimSuffix(model.Name, ":latest")
_, err = i.localModelService.Get(ctx, id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return
}
err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{
Id: id,
Name: name,
State: 1,
})
if err != nil {
return
}
}
}
})
}
+29
View File
@@ -0,0 +1,29 @@
package ai_local
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
)
type ILocalModelModule interface {
Search(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error)
ListCanInstall(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error)
Deploy(ctx context.Context, model string, session string) (*ai_provider_local.Pipeline, error)
CancelDeploy(ctx context.Context, model string) error
RemoveModel(ctx context.Context, model string) error
Enable(ctx context.Context, model string) error
Disable(ctx context.Context, model string) error
ModelState(ctx context.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error)
}
func init() {
autowire.Auto[ILocalModelModule](func() reflect.Value {
return reflect.ValueOf(&imlLocalModel{})
})
}
+11 -12
View File
@@ -13,15 +13,15 @@ type SimpleProvider struct {
}
type Provider struct {
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
GetAPIKeyUrl string `json:"get_apikey_url"`
DefaultLLM string `json:"default_llm"`
DefaultLLMConfig string `json:"-"`
Priority int `json:"priority"`
Status ProviderStatus `json:"status"`
Configured bool `json:"configured"`
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
GetAPIKeyUrl string `json:"get_apikey_url"`
DefaultLLM string `json:"default_llm"`
DefaultLLMConfig string `json:"-"`
//Priority int `json:"priority"`
Status ProviderStatus `json:"status"`
Configured bool `json:"configured"`
}
type ConfiguredProviderItem struct {
@@ -31,9 +31,8 @@ type ConfiguredProviderItem struct {
DefaultLLM string `json:"default_llm"`
Status ProviderStatus `json:"status"`
APICount int64 `json:"api_count"`
KeyCount int `json:"key_count"`
KeyStatus []*KeyStatus `json:"keys"`
Priority int `json:"priority"`
KeyCount int64 `json:"key_count"`
CanDelete bool `json:"can_delete"`
}
type KeyStatus struct {
+104 -143
View File
@@ -8,9 +8,9 @@ import (
"sort"
"time"
"github.com/APIParkLab/APIPark/service/service"
ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
"github.com/APIParkLab/APIPark/service/service"
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
@@ -54,11 +54,19 @@ func newKey(key *ai_key.Key) *gateway.DynamicRelease {
var _ IProviderModule = (*imlProviderModule)(nil)
type imlProviderModule struct {
providerService ai.IProviderService `autowired:""`
clusterService cluster.IClusterService `autowired:""`
aiAPIService ai_api.IAPIService `autowired:""`
aiKeyService ai_key.IKeyService `autowired:""`
transaction store.ITransaction `autowired:""`
providerService ai.IProviderService `autowired:""`
clusterService cluster.IClusterService `autowired:""`
aiAPIService ai_api.IAPIService `autowired:""`
aiKeyService ai_key.IKeyService `autowired:""`
aiBalanceService ai_balance.IBalanceService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlProviderModule) Delete(ctx context.Context, id string) error {
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
// TODO: implement Delete
return nil
})
}
func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_dto.SimpleProvider, error) {
@@ -75,83 +83,87 @@ func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_
}, nil
}
func (i *imlProviderModule) Sort(ctx context.Context, input *ai_dto.Sort) error {
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
list, err := i.providerService.List(ctx)
if err != nil {
return err
}
providerMap := utils.SliceToMap(list, func(e *ai.Provider) string {
return e.Id
})
releases := make([]*gateway.DynamicRelease, 0, len(list))
offlineReleases := make([]*gateway.DynamicRelease, 0, len(list))
for index, id := range input.Providers {
p, has := model_runtime.GetProvider(id)
if !has {
continue
}
//func (i *imlProviderModule) Sort(ctx context.Context, input *ai_dto.Sort) error {
// return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
// list, err := i.providerService.List(ctx)
// if err != nil {
// return err
// }
// providerMap := utils.SliceToMap(list, func(e *ai.Provider) string {
// return e.Id
// })
// releases := make([]*gateway.DynamicRelease, 0, len(list))
// offlineReleases := make([]*gateway.DynamicRelease, 0, len(list))
// for index, id := range input.Providers {
// p, has := model_runtime.GetProvider(id)
// if !has {
// continue
// }
//
// l, has := providerMap[id]
// if !has {
// continue
// }
// model, has := p.GetModel(l.DefaultLLM)
// if !has {
// continue
// }
// priority := index + 1
// err = i.providerService.Save(txCtx, id, &ai.SetProvider{
// Priority: &priority,
// })
// if err != nil {
// return err
// }
// if ai_dto.ToProviderStatus(l.Status) == ai_dto.ProviderDisabled {
// offlineReleases = append(offlineReleases, &gateway.DynamicRelease{
// BasicItem: &gateway.BasicItem{
// ID: l.Id,
// Resource: "ai-provider",
// }})
// } else {
// cfg := make(map[string]interface{})
// cfg["provider"] = l.Id
// cfg["model"] = l.DefaultLLM
// cfg["model_config"] = model.DefaultConfig()
// cfg["priority"] = l.Priority
// cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
// releases = append(releases, &gateway.DynamicRelease{
// BasicItem: &gateway.BasicItem{
// ID: l.Id,
// Description: l.Name,
// Resource: "ai-provider",
// Version: l.UpdateAt.Format("20060102150405"),
// MatchLabels: map[string]string{
// "module": "ai-provider",
// },
// },
// Attr: cfg,
// })
// }
// }
// err = i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
// if err != nil {
// return err
// }
// return i.syncGateway(ctx, cluster.DefaultClusterID, offlineReleases, false)
//
// })
//}
l, has := providerMap[id]
if !has {
continue
}
model, has := p.GetModel(l.DefaultLLM)
if !has {
continue
}
priority := index + 1
err = i.providerService.Save(txCtx, id, &ai.SetProvider{
Priority: &priority,
})
if err != nil {
return err
}
if ai_dto.ToProviderStatus(l.Status) == ai_dto.ProviderDisabled {
offlineReleases = append(offlineReleases, &gateway.DynamicRelease{
BasicItem: &gateway.BasicItem{
ID: l.Id,
Resource: "ai-provider",
}})
} else {
cfg := make(map[string]interface{})
cfg["provider"] = l.Id
cfg["model"] = l.DefaultLLM
cfg["model_config"] = model.DefaultConfig()
cfg["priority"] = l.Priority
cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
releases = append(releases, &gateway.DynamicRelease{
BasicItem: &gateway.BasicItem{
ID: l.Id,
Description: l.Name,
Resource: "ai-provider",
Version: l.UpdateAt.Format("20060102150405"),
MatchLabels: map[string]string{
"module": "ai-provider",
},
},
Attr: cfg,
})
}
}
err = i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
if err != nil {
return err
}
return i.syncGateway(ctx, cluster.DefaultClusterID, offlineReleases, false)
})
}
func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
func (i *imlProviderModule) ConfiguredProviders(ctx context.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error) {
// 获取已配置的AI服务商
list, err := i.providerService.List(ctx)
list, err := i.providerService.Search(ctx, keyword, nil, "update_at")
if err != nil {
return nil, nil, fmt.Errorf("get provider list error:%v", err)
return nil, fmt.Errorf("get provider list error:%v", err)
}
aiAPIMap, err := i.aiAPIService.CountMapByProvider(ctx, "", nil)
if err != nil {
return nil, nil, fmt.Errorf("get ai api count error:%v", err)
return nil, fmt.Errorf("get ai api count error:%v", err)
}
keyMap, err := i.aiKeyService.CountMapByProvider(ctx, "", nil)
if err != nil {
return nil, fmt.Errorf("get ai key count error:%v", err)
}
providers := make([]*ai_dto.ConfiguredProviderItem, 0, len(list))
for _, l := range list {
@@ -159,7 +171,7 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
_, err = i.aiKeyService.DefaultKey(ctx, l.Id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil, err
return nil, err
}
err = i.aiKeyService.Create(ctx, &ai_key.Create{
ID: l.Id,
@@ -173,7 +185,7 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
Default: true,
})
if err != nil {
return nil, nil, fmt.Errorf("create default key error:%v", err)
return nil, fmt.Errorf("create default key error:%v", err)
}
}
@@ -181,29 +193,6 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
if !has {
continue
}
keys, err := i.aiKeyService.KeysByProvider(ctx, l.Id)
if err != nil {
return nil, nil, fmt.Errorf("get provider keys error:%v", err)
}
keysStatus := make([]*ai_dto.KeyStatus, 0, len(keys))
for _, k := range keys {
status := ai_key_dto.ToKeyStatus(k.Status)
switch status {
case ai_key_dto.KeyNormal, ai_key_dto.KeyDisable, ai_key_dto.KeyError:
default:
status = ai_key_dto.KeyError
}
keysStatus = append(keysStatus, &ai_dto.KeyStatus{
Id: k.ID,
Name: k.Name,
Status: status.String(),
Priority: k.Priority,
})
}
sort.Slice(keysStatus, func(i, j int) bool {
return keysStatus[i].Priority < keysStatus[j].Priority
})
providers = append(providers, &ai_dto.ConfiguredProviderItem{
Id: l.Id,
@@ -212,34 +201,12 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
DefaultLLM: l.DefaultLLM,
Status: ai_dto.ToProviderStatus(l.Status),
APICount: aiAPIMap[l.Id],
KeyCount: len(keysStatus),
KeyStatus: keysStatus,
Priority: l.Priority,
KeyCount: keyMap[l.Id],
CanDelete: len(list) > 1,
})
}
sort.Slice(providers, func(i, j int) bool {
if providers[i].Priority != providers[j].Priority {
if providers[i].Priority == 0 {
return false
}
if providers[j].Priority == 0 {
return true
}
return providers[i].Priority < providers[j].Priority
}
return providers[i].Name < providers[j].Name
})
var backup *ai_dto.BackupProvider
for _, p := range providers {
if p.Status == ai_dto.ProviderEnabled {
backup = &ai_dto.BackupProvider{
Id: p.Id,
Name: p.Name,
}
break
}
}
return providers, backup, nil
return providers, nil
}
func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error) {
@@ -388,13 +355,7 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
if !has {
return nil, fmt.Errorf("ai provider not found")
}
maxPriority, err := i.providerService.MaxPriority(ctx)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
}
maxPriority = maxPriority + 1
info, err := i.providerService.Get(ctx, id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
@@ -412,7 +373,7 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
DefaultLLM: defaultLLM.ID(),
DefaultLLMConfig: defaultLLM.Logo(),
Status: ai_dto.ProviderDisabled,
Priority: maxPriority,
//Priority: maxPriority,
}, nil
}
defaultLLM, has := p.GetModel(info.DefaultLLM)
@@ -423,9 +384,9 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
}
defaultLLM = model
}
if info.Priority == 0 {
info.Priority = maxPriority
}
//if info.Priority == 0 {
// info.Priority = maxPriority
//}
return &ai_dto.Provider{
Id: info.Id,
@@ -434,9 +395,9 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
GetAPIKeyUrl: p.HelpUrl(),
DefaultLLM: defaultLLM.ID(),
DefaultLLMConfig: defaultLLM.DefaultConfig(),
Priority: info.Priority,
Status: ai_dto.ToProviderStatus(info.Status),
Configured: true,
//Priority: info.Priority,
Status: ai_dto.ToProviderStatus(info.Status),
Configured: true,
}, nil
}
+5 -4
View File
@@ -10,23 +10,24 @@ import (
)
type IProviderModule interface {
ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error)
ConfiguredProviders(ctx context.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error)
UnConfiguredProviders(ctx context.Context) ([]*ai_dto.ProviderItem, error)
SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error)
SimpleConfiguredProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error)
Provider(ctx context.Context, id string) (*ai_dto.Provider, error)
SimpleProvider(ctx context.Context, id string) (*ai_dto.SimpleProvider, error)
LLMs(ctx context.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error)
//UpdateProviderStatus(ctx context.Context, id string, enable bool) error
UpdateProviderConfig(ctx context.Context, id string, input *ai_dto.UpdateConfig) error
//UpdateProviderDefaultLLM(ctx context.Context, id string, input *ai_dto.UpdateLLM) error
Sort(ctx context.Context, input *ai_dto.Sort) error
Delete(ctx context.Context, id string) error
}
type IAIAPIModule interface {
APIs(ctx context.Context, keyword string, providerId string, start int64, end int64, page int, pageSize int, sortCondition string, asc bool, models []string, services []string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error)
}
type ILocalModelModule interface {
}
func init() {
autowire.Auto[IProviderModule](func() reflect.Value {
module := new(imlProviderModule)
+3 -1
View File
@@ -2,9 +2,10 @@ package catalogue
import (
"context"
"github.com/APIParkLab/APIPark/module/system"
"reflect"
"github.com/APIParkLab/APIPark/module/system"
"github.com/eolinker/go-common/autowire"
catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto"
@@ -28,6 +29,7 @@ type ICatalogueModule interface {
// Subscribe 订阅服务
Subscribe(ctx context.Context, subscribeInfo *catalogue_dto.SubscribeService) error
Sort(ctx context.Context, sorts []*catalogue_dto.SortItem) error
DefaultCatalogue(ctx context.Context) (*catalogue_dto.Catalogue, error)
//ExportAll(ctx context.Context) ([]*catalogue_dto.ExportCatalogue, error)
}
+18
View File
@@ -66,6 +66,24 @@ type imlCatalogueModule struct {
root *Root
}
func (i *imlCatalogueModule) DefaultCatalogue(ctx context.Context) (*catalogue_dto.Catalogue, error) {
catalogues, err := i.catalogueService.List(ctx)
if err != nil {
return nil, err
}
for _, v := range catalogues {
if v.Parent == "" {
return &catalogue_dto.Catalogue{
Id: v.Id,
Name: v.Name,
Parent: v.Parent,
Sort: v.Sort,
}, nil
}
}
return nil, errors.New("no default catalogue")
}
func (i *imlCatalogueModule) onlineSubscriber(ctx context.Context, clusterId string, sub *gateway.SubscribeRelease) error {
client, err := i.clusterService.GatewayClient(ctx, clusterId)
if err != nil {
+2
View File
@@ -11,6 +11,7 @@ type CreateService struct {
Catalogue string `json:"catalogue"`
ApprovalType string `json:"approval_type"`
Kind string `json:"service_kind"`
State string `json:"state"`
Provider *string `json:"provider" aocheck:"ai_provider"`
AsApp *bool `json:"as_app"`
AsServer *bool `json:"as_server"`
@@ -25,6 +26,7 @@ type EditService struct {
Tags *[]string `json:"tags"`
Provider *string `json:"provider" aocheck:"ai_provider"`
ApprovalType *string `json:"approval_type"`
State *string `json:"state"`
}
type CreateApp struct {
+1
View File
@@ -62,6 +62,7 @@ type Service struct {
AsServer bool `json:"as_server"`
AsApp bool `json:"as_app"`
ServiceKind string `json:"service_kind"`
State string `json:"state"`
}
type App struct {
+20
View File
@@ -0,0 +1,20 @@
package core
import (
"net/http"
"github.com/eolinker/go-common/pm3"
)
func (p *plugin) aiLocalApis() []pm3.Api {
return []pm3.Api{
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/can_deploy", []string{"context", "query:keyword"}, []string{"models"}, p.aiLocalController.ListCanInstall),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/list", []string{"context", "query:keyword"}, []string{"models"}, p.aiLocalController.Search),
pm3.CreateApiSimple(http.MethodPost, "/api/v1/model/local/deploy", p.aiLocalController.Deploy),
pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/model/local/deploy/start", []string{"context", "body"}, nil, p.aiLocalController.DeployStart),
pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/model/local/cancel_deploy", []string{"context", "body"}, nil, p.aiLocalController.CancelDeploy),
pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/model/local", []string{"context", "query:model"}, nil, p.aiLocalController.RemoveModel),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/model/local/info", []string{"context", "query:model", "body"}, nil, p.aiLocalController.Update),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/state", []string{"context", "query:model"}, []string{"state", "info"}, p.aiLocalController.State),
}
}
+2 -2
View File
@@ -11,13 +11,13 @@ import (
func (p *plugin) aiAPIs() []pm3.Api {
return []pm3.Api{
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/unconfigured", []string{"context"}, []string{"providers"}, p.aiProviderController.UnConfiguredProviders, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.ConfiguredProviders, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/configured", []string{"context", "query:keyword"}, []string{"providers"}, p.aiProviderController.ConfiguredProviders, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.SimpleConfiguredProviders),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/config", []string{"context", "query:provider"}, []string{"provider"}, p.aiProviderController.Provider, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/provider", []string{"context", "query:provider"}, []string{"provider"}, p.aiProviderController.SimpleProvider),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/llms", []string{"context", "query:provider"}, []string{"llms", "provider"}, p.aiProviderController.LLMs),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/sort", []string{"context", "body"}, nil, p.aiProviderController.Sort),
//pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/sort", []string{"context", "body"}, nil, p.aiProviderController.Sort),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/config", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderConfig, access.SystemSettingsAiProviderManager),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/apis", []string{"context", "query:keyword", "query:provider", "query:start", "query:end", "query:page", "query:page_size", "query:sort", "query:asc", "query:models", "query:services"}, []string{"apis", "condition", "total"}, p.aiStatisticController.APIs),
+4
View File
@@ -3,6 +3,8 @@ package core
import (
"net/http"
ai_local "github.com/APIParkLab/APIPark/controller/ai-local"
ai_key "github.com/APIParkLab/APIPark/controller/ai-key"
"github.com/APIParkLab/APIPark/controller/log"
@@ -78,6 +80,7 @@ type plugin struct {
aiAPIController ai_api.IAPIController `autowired:""`
aiStatisticController ai.IStatisticController `autowired:""`
aiKeyController ai_key.IKeyController `autowired:""`
aiLocalController ai_local.ILocalModelController `autowired:""`
apiDocController router.IAPIDocController `autowired:""`
subscribeController subscribe.ISubscribeController `autowired:""`
strategyController strategy.IStrategyController `autowired:""`
@@ -118,6 +121,7 @@ func (p *plugin) OnComplete() {
p.apis = append(p.apis, p.aiKeyApis()...)
p.apis = append(p.apis, p.strategyApis()...)
p.apis = append(p.apis, p.logApis()...)
p.apis = append(p.apis, p.aiLocalApis()...)
}
func (p *plugin) Name() string {
+9 -1
View File
@@ -23,6 +23,10 @@ type imlAPIService struct {
universally.IServiceDelete
}
func (i *imlAPIService) CountMapByModel(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
return i.store.CountByGroup(ctx, keyword, conditions, "model")
}
func (i *imlAPIService) CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
return i.store.CountByGroup(ctx, keyword, conditions, "provider")
}
@@ -30,7 +34,7 @@ func (i *imlAPIService) CountMapByProvider(ctx context.Context, keyword string,
func (i *imlAPIService) OnComplete() {
i.IServiceGet = universally.NewGetSoftDelete[API, api.AiAPIInfo](i.store, FromEntity)
i.IServiceCreate = universally.NewCreatorSoftDelete[Create, api.AiAPIInfo](i.store, "ai_api_info", createEntityHandler, uniquestHandler, labelHandler)
i.IServiceEdit = universally.NewEdit[Edit, api.AiAPIInfo](i.store, updateHandler)
i.IServiceEdit = universally.NewEdit[Edit, api.AiAPIInfo](i.store, updateHandler, labelHandler)
i.IServiceDelete = universally.NewSoftDelete[api.AiAPIInfo](i.store)
}
@@ -54,6 +58,7 @@ func createEntityHandler(i *Create) *api.AiAPIInfo {
Model: i.Model,
Provider: i.Provider,
Disable: i.Disable,
Type: i.Type,
CreateAt: now,
UpdateAt: now,
AdditionalConfig: string(cfg),
@@ -91,6 +96,9 @@ func updateHandler(e *api.AiAPIInfo, i *Edit) {
if i.UseToken != nil {
e.UseToken = *i.UseToken
}
if i.Type != nil {
e.Type = *i.Type
}
e.UpdateAt = time.Now()
}
+4
View File
@@ -22,6 +22,7 @@ type API struct {
UseToken int
Creator string
Updater string
Type int
AdditionalConfig map[string]interface{}
Disable bool
}
@@ -37,6 +38,7 @@ type Create struct {
Model string
Provider string
AdditionalConfig map[string]interface{}
Type int
Disable bool
}
@@ -50,6 +52,7 @@ type Edit struct {
Model *string
Disable *bool
UseToken *int
Type *int
AdditionalConfig *map[string]interface{}
}
@@ -73,6 +76,7 @@ func FromEntity(e *api.AiAPIInfo) *API {
Updater: e.Updater,
Disable: e.Disable,
UseToken: e.UseToken,
Type: e.Type,
AdditionalConfig: cfg,
}
}
+1
View File
@@ -14,6 +14,7 @@ type IAPIService interface {
universally.IServiceEdit[Edit]
universally.IServiceDelete
CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
CountMapByModel(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
}
type IAPIUseService interface {
+172
View File
@@ -0,0 +1,172 @@
package ai_balance
import (
"context"
"fmt"
"sort"
"time"
"github.com/eolinker/go-common/store"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/ai"
)
var _ IBalanceService = (*imlBalanceService)(nil)
type imlBalanceService struct {
store ai.IBalanceStore `autowired:""`
transaction store.ITransaction `autowired:""`
universally.IServiceGet[Balance]
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
}
func (i *imlBalanceService) OnComplete() {
i.IServiceGet = universally.NewGet[Balance, ai.Balance](i.store, FromEntity)
i.IServiceCreate = universally.NewCreator[Create, ai.Balance](i.store, "ai_balance", createEntityHandler, uniquestHandler, labelHandler)
i.IServiceEdit = universally.NewEdit[Edit, ai.Balance](i.store, updateHandler, labelHandler)
i.IServiceDelete = universally.NewDelete[ai.Balance](i.store)
}
func (i *imlBalanceService) MaxPriority(ctx context.Context) (int, error) {
info, err := i.store.First(ctx, nil, "priority desc")
if err != nil {
return 0, err
}
return info.Priority, nil
}
func (i *imlBalanceService) SortBefore(ctx context.Context, originID string, targetID string) ([]*Balance, error) {
originKey, err := i.store.GetByUUID(ctx, originID)
if err != nil {
return nil, fmt.Errorf("get key error: %v,id is %s", err, originID)
}
targetKey, err := i.store.GetByUUID(ctx, targetID)
if err != nil {
return nil, fmt.Errorf("get key error: %v,id is %s", err, targetID)
}
originKeySort, targetKeySort := originKey.Priority, targetKey.Priority
// 初始化顺序,假设原始Key在目标Key之后,中间的key往后移动,原始Key移动到`targetKeySort`位置
originKey.Priority = targetKeySort
fn := func(priority int) int {
return priority + 1
}
sql := "sort < ? and sort >= ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的key往前移动,原始Key移动到`targetKeySort - 1`位置
sql = "sort > ? and sort < ?"
originKey.Priority = targetKeySort - 1
fn = func(priority int) int {
return priority - 1
}
}
list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
result := make([]*Balance, 0, len(list)+1)
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
for _, l := range list {
l.Priority = fn(l.Priority)
_, err := i.store.Update(ctx, l)
if err != nil {
return err
}
result = append(result, FromEntity(l))
}
_, err = i.store.Update(ctx, originKey)
return err
})
if err != nil {
return nil, err
}
result = append(result, FromEntity(originKey))
sort.Slice(list, func(i, j int) bool { return list[i].Priority < list[j].Priority })
return result, nil
}
func (i *imlBalanceService) SortAfter(ctx context.Context, originID string, targetID string) ([]*Balance, error) {
originKey, err := i.store.GetByUUID(ctx, originID)
if err != nil {
return nil, fmt.Errorf("get key error: %v,id is %s", err, originID)
}
targetKey, err := i.store.GetByUUID(ctx, targetID)
if err != nil {
return nil, fmt.Errorf("get key error: %v,id is %s", err, targetID)
}
originKeySort, targetKeySort := originKey.Priority, targetKey.Priority
// 初始化顺序,假设原始Key在目标Key之后,中间的Key往后移动,原始Key移动到`targetKeySort + 1`位置
originKey.Priority = targetKeySort + 1
fn := func(priority int) int {
return priority + 1
}
sql := "sort < ? and sort > ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的Key往前移动,原始Key移动到`targetKeySort`位置
sql = "sort > ? and sort <= ?"
originKey.Priority = targetKeySort
fn = func(priority int) int {
return priority - 1
}
}
list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
result := make([]*Balance, 0, len(list)+1)
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
for _, l := range list {
l.Priority = fn(l.Priority)
_, err = i.store.Update(ctx, l)
if err != nil {
return err
}
result = append(result, FromEntity(l))
}
_, err = i.store.Update(ctx, originKey)
return err
})
if err != nil {
return nil, err
}
result = append(result, FromEntity(originKey))
sort.Slice(list, func(i, j int) bool { return list[i].Priority < list[j].Priority })
return result, nil
}
func createEntityHandler(i *Create) *ai.Balance {
now := time.Now()
return &ai.Balance{
Uuid: i.Id,
Provider: i.Provider,
ProviderName: i.ProviderName,
Model: i.Model,
ModelName: i.ModelName,
Type: i.Type,
Priority: i.Priority,
CreateAt: now,
UpdateAt: now,
}
}
func uniquestHandler(i *Create) []map[string]interface{} {
return []map[string]interface{}{{"uuid": i.Id}}
}
func labelHandler(e *ai.Balance) []string {
return []string{e.ProviderName, e.ModelName}
}
func updateHandler(e *ai.Balance, i *Edit) {
if i.Priority != nil {
e.Priority = *i.Priority
}
if i.State != nil {
e.State = *i.State
}
e.UpdateAt = time.Now()
}
+54
View File
@@ -0,0 +1,54 @@
package ai_balance
import (
"time"
"github.com/APIParkLab/APIPark/stores/ai"
)
type Balance struct {
Id string
Priority int
Provider string
ProviderName string
Model string
ModelName string
Type int
State int
Creator string
Updater string
CreateAt time.Time
UpdateAt time.Time
}
func FromEntity(e *ai.Balance) *Balance {
return &Balance{
Id: e.Uuid,
Priority: e.Priority,
Provider: e.Provider,
ProviderName: e.ProviderName,
Model: e.Model,
ModelName: e.ModelName,
Type: e.Type,
State: e.State,
Creator: e.Creator,
Updater: e.Updater,
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
}
}
type Create struct {
Id string
Priority int
Provider string
ProviderName string
Model string
ModelName string
Type int
}
type Edit struct {
Priority *int
State *int
}
+26
View File
@@ -0,0 +1,26 @@
package ai_balance
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
"github.com/APIParkLab/APIPark/service/universally"
)
type IBalanceService interface {
universally.IServiceGet[Balance]
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
MaxPriority(ctx context.Context) (int, error)
SortBefore(ctx context.Context, originID string, targetID string) ([]*Balance, error)
SortAfter(ctx context.Context, originID string, targetID string) ([]*Balance, error)
}
func init() {
autowire.Auto[IBalanceService](func() reflect.Value {
return reflect.ValueOf(new(imlBalanceService))
})
}
+12 -8
View File
@@ -23,6 +23,10 @@ type imlAIKeyService struct {
universally.IServiceDelete
}
func (i *imlAIKeyService) CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
return i.store.CountByGroup(ctx, keyword, conditions, "provider")
}
func (i *imlAIKeyService) IncrUseToken(ctx context.Context, id string, useToken int) error {
info, err := i.store.GetByUUID(ctx, id)
if err != nil {
@@ -111,16 +115,16 @@ func (i *imlAIKeyService) SortBefore(ctx context.Context, provider string, origi
fn := func(priority int) int {
return priority + 1
}
sql := "sort < ? and sort >= ?"
sql := "provider = ? and sort < ? and sort >= ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的key往前移动,原始Key移动到`targetKeySort - 1`位置
sql = "sort > ? and sort < ?"
sql = "provider = ? and sort > ? and sort < ?"
originKey.Sort = targetKeySort - 1
fn = func(priority int) int {
return priority - 1
}
}
list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
list, err := i.store.ListQuery(ctx, sql, []interface{}{provider, originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
@@ -160,16 +164,16 @@ func (i *imlAIKeyService) SortAfter(ctx context.Context, provider string, origin
fn := func(priority int) int {
return priority + 1
}
sql := "sort < ? and sort > ?"
sql := "provider = ? and sort < ? and sort > ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的Key往前移动,原始Key移动到`targetKeySort`位置
sql = "sort > ? and sort <= ?"
sql = "provider = ? and sort > ? and sort <= ?"
originKey.Sort = targetKeySort
fn = func(priority int) int {
return priority - 1
}
}
list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
list, err := i.store.ListQuery(ctx, sql, []interface{}{provider, originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
@@ -196,8 +200,8 @@ func (i *imlAIKeyService) SortAfter(ctx context.Context, provider string, origin
func (i *imlAIKeyService) OnComplete() {
i.IServiceGet = universally.NewGet[Key, ai.Key](i.store, FromEntity)
i.IServiceCreate = universally.NewCreator[Create, ai.Key](i.store, "ai_api_info", createEntityHandler, uniquestHandler, labelHandler)
i.IServiceEdit = universally.NewEdit[Edit, ai.Key](i.store, updateHandler)
i.IServiceCreate = universally.NewCreator[Create, ai.Key](i.store, "ai_key", createEntityHandler, uniquestHandler, labelHandler)
i.IServiceEdit = universally.NewEdit[Edit, ai.Key](i.store, updateHandler, labelHandler)
i.IServiceDelete = universally.NewDelete[ai.Key](i.store)
}
+1
View File
@@ -16,6 +16,7 @@ type IKeyService interface {
universally.IServiceDelete
DefaultKey(ctx context.Context, providerId string) (*Key, error)
KeysByProvider(ctx context.Context, providerId string) ([]*Key, error)
CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
MaxPriority(ctx context.Context, providerId string) (int, error)
SortBefore(ctx context.Context, provider string, originID string, targetID string) ([]*Key, error)
SortAfter(ctx context.Context, provider string, originID string, targetID string) ([]*Key, error)
+189
View File
@@ -0,0 +1,189 @@
package ai_local
import (
"time"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/ai"
)
var _ ILocalModelService = &imlLocalModelService{}
type imlLocalModelService struct {
store ai.ILocalModelStore `autowired:""`
universally.IServiceGet[LocalModel]
universally.IServiceCreate[CreateLocalModel]
universally.IServiceEdit[EditLocalModel]
universally.IServiceDelete
}
func (i *imlLocalModelService) OnComplete() {
i.IServiceGet = universally.NewGet[LocalModel, ai.LocalModel](i.store, i.fromEntity)
i.IServiceCreate = universally.NewCreator[CreateLocalModel, ai.LocalModel](i.store, "ai_local_model", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
i.IServiceEdit = universally.NewEdit[EditLocalModel, ai.LocalModel](i.store, i.updateHandler, i.labelHandler)
i.IServiceDelete = universally.NewDelete[ai.LocalModel](i.store)
}
func (i *imlLocalModelService) labelHandler(e *ai.LocalModel) []string {
return []string{e.Name}
}
func (i *imlLocalModelService) uniquestHandler(c *CreateLocalModel) []map[string]interface{} {
return []map[string]interface{}{{"uuid": c.Id}}
}
func (i *imlLocalModelService) createEntityHandler(c *CreateLocalModel) *ai.LocalModel {
now := time.Now()
return &ai.LocalModel{
Uuid: c.Id,
Name: c.Name,
Provider: c.Provider,
State: c.State,
CreateAt: now,
UpdateAt: now,
}
}
func (i *imlLocalModelService) updateHandler(e *ai.LocalModel, c *EditLocalModel) {
if c.State != nil {
e.State = *c.State
}
e.UpdateAt = time.Now()
}
func (i *imlLocalModelService) fromEntity(e *ai.LocalModel) *LocalModel {
return &LocalModel{
Id: e.Uuid,
Name: e.Name,
Provider: e.Provider,
State: e.State,
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
Creator: e.Creator,
Updater: e.Updater,
}
}
type imlLocalModelPackageService struct {
store ai.ILocalModelPackageStore `autowired:""`
universally.IServiceGet[LocalModelPackage]
universally.IServiceCreate[CreateLocalModelPackage]
universally.IServiceEdit[EditLocalModelPackage]
universally.IServiceDelete
}
func (i *imlLocalModelPackageService) OnComplete() {
i.IServiceGet = universally.NewGet[LocalModelPackage, ai.LocalModelPackage](i.store, i.fromEntity)
i.IServiceCreate = universally.NewCreator[CreateLocalModelPackage, ai.LocalModelPackage](i.store, "ai_local_model_package", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
i.IServiceEdit = universally.NewEdit[EditLocalModelPackage, ai.LocalModelPackage](i.store, i.updateHandler, i.labelHandler)
i.IServiceDelete = universally.NewDelete[ai.LocalModelPackage](i.store)
}
func (i *imlLocalModelPackageService) labelHandler(e *ai.LocalModelPackage) []string {
return []string{e.Uuid}
}
func (i *imlLocalModelPackageService) uniquestHandler(c *CreateLocalModelPackage) []map[string]interface{} {
return []map[string]interface{}{{"uuid": c.Id}}
}
func (i *imlLocalModelPackageService) createEntityHandler(c *CreateLocalModelPackage) *ai.LocalModelPackage {
return &ai.LocalModelPackage{
Uuid: c.Id,
Name: c.Name,
Size: c.Size,
Hash: c.Hash,
Description: c.Description,
IsPopular: c.Popular,
}
}
func (i *imlLocalModelPackageService) updateHandler(e *ai.LocalModelPackage, c *EditLocalModelPackage) {
if c.Size != nil {
e.Size = *c.Size
}
if c.Hash != nil {
e.Hash = *c.Hash
}
if c.Description != nil {
e.Description = *c.Description
}
if c.Popular != nil {
e.IsPopular = *c.Popular
}
if c.Version != nil {
e.Version = *c.Version
}
}
func (i *imlLocalModelPackageService) fromEntity(e *ai.LocalModelPackage) *LocalModelPackage {
return &LocalModelPackage{
Id: e.Uuid,
Name: e.Name,
Size: e.Size,
Hash: e.Hash,
Description: e.Description,
Version: e.Version,
IsPopular: e.IsPopular,
}
}
type imlLocalModelInstallStateService struct {
store ai.ILocalModelInstallStateStore `autowired:""`
universally.IServiceGet[LocalModelInstallState]
universally.IServiceCreate[CreateLocalModelInstallState]
universally.IServiceEdit[EditLocalModelInstallState]
universally.IServiceDelete
}
func (i *imlLocalModelInstallStateService) OnComplete() {
i.IServiceGet = universally.NewGet[LocalModelInstallState, ai.LocalModelInstallState](i.store, i.fromEntity)
i.IServiceCreate = universally.NewCreator[CreateLocalModelInstallState, ai.LocalModelInstallState](i.store, "ai_local_model_install_state", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
i.IServiceEdit = universally.NewEdit[EditLocalModelInstallState, ai.LocalModelInstallState](i.store, i.updateHandler, i.labelHandler)
i.IServiceDelete = universally.NewDelete[ai.LocalModelInstallState](i.store)
}
func (i *imlLocalModelInstallStateService) fromEntity(e *ai.LocalModelInstallState) *LocalModelInstallState {
return &LocalModelInstallState{
Id: e.Uuid,
Complete: e.Complete,
Total: e.Total,
State: e.State,
Msg: e.LastMsg,
UpdateAt: e.UpdateAt,
}
}
func (i *imlLocalModelInstallStateService) labelHandler(e *ai.LocalModelInstallState) []string {
return []string{e.Uuid}
}
func (i *imlLocalModelInstallStateService) uniquestHandler(c *CreateLocalModelInstallState) []map[string]interface{} {
return []map[string]interface{}{{"uuid": c.Id}}
}
func (i *imlLocalModelInstallStateService) createEntityHandler(c *CreateLocalModelInstallState) *ai.LocalModelInstallState {
return &ai.LocalModelInstallState{
Uuid: c.Id,
Complete: c.Complete,
Total: c.Total,
State: c.State,
LastMsg: c.Msg,
UpdateAt: time.Now(),
}
}
func (i *imlLocalModelInstallStateService) updateHandler(e *ai.LocalModelInstallState, c *EditLocalModelInstallState) {
if c.Complete != nil {
e.Complete = *c.Complete
}
if c.Total != nil {
e.Total = *c.Total
}
if c.State != nil {
e.State = *c.State
}
if c.Msg != nil {
e.LastMsg = *c.Msg
}
e.UpdateAt = time.Now()
}
+77
View File
@@ -0,0 +1,77 @@
package ai_local
import "time"
type LocalModel struct {
Id string
Name string
Provider string
State int
CreateAt time.Time
UpdateAt time.Time
Creator string
Updater string
}
type CreateLocalModel struct {
Id string
Name string
Provider string
State int
}
type EditLocalModel struct {
State *int
}
type LocalModelPackage struct {
Id string
Name string
Size string
Hash string
Description string
Version string
IsPopular bool
}
type CreateLocalModelPackage struct {
Id string
Name string
Size string
Hash string
Description string
Version string
Popular bool
}
type EditLocalModelPackage struct {
Size *string
Hash *string
Description *string
Version *string
Popular *bool
}
type LocalModelInstallState struct {
Id string
Complete int64
Total int64
State int
Msg string
UpdateAt time.Time
}
type CreateLocalModelInstallState struct {
Id string
Complete int64
Total int64
State int
Msg string
}
type EditLocalModelInstallState struct {
Complete *int64
Total *int64
State *int
Msg *string
}
+42
View File
@@ -0,0 +1,42 @@
package ai_local
import (
"reflect"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/eolinker/go-common/autowire"
)
type ILocalModelService interface {
universally.IServiceGet[LocalModel]
universally.IServiceCreate[CreateLocalModel]
universally.IServiceEdit[EditLocalModel]
universally.IServiceDelete
}
type ILocalModelPackageService interface {
universally.IServiceGet[LocalModelPackage]
universally.IServiceCreate[CreateLocalModelPackage]
universally.IServiceEdit[EditLocalModelPackage]
universally.IServiceDelete
}
type ILocalModelInstallStateService interface {
universally.IServiceGet[LocalModelInstallState]
universally.IServiceCreate[CreateLocalModelInstallState]
universally.IServiceEdit[EditLocalModelInstallState]
universally.IServiceDelete
}
func init() {
autowire.Auto[ILocalModelService](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelService))
})
autowire.Auto[ILocalModelPackageService](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelPackageService))
})
autowire.Auto[ILocalModelInstallStateService](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelInstallStateService))
})
}
+81
View File
@@ -68,3 +68,84 @@ func (i *Key) TableName() string {
func (i *Key) IdValue() int64 {
return i.Id
}
type Balance struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
Provider string `gorm:"type:varchar(36);not null;column:provider;comment:供应商ID"`
ProviderName string `gorm:"type:varchar(100);not null;column:provider_name;comment:供应商名称"`
Model string `gorm:"type:varchar(36);not null;column:model;comment:模型ID"`
ModelName string `gorm:"type:varchar(100);not null;column:model_name;comment:模型名称"`
Type int `gorm:"type:tinyint(1);not null;column:type;comment:类型,0online1local"`
State int `gorm:"type:tinyint(1);not null;column:state;comment:状态,0:异常,1:正常;default:1"`
Priority int `gorm:"type:int;not null;column:priority;comment:优先级,数字越小优先级越大"`
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
}
func (i *Balance) TableName() string {
return "ai_balance"
}
func (i *Balance) IdValue() int64 {
return i.Id
}
type LocalModel struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
Name string `gorm:"type:varchar(100);not null;column:name;comment:名称"`
Provider string `gorm:"type:varchar(36);not null;column:provider;comment:供应商ID"`
State int `gorm:"type:tinyint(1);not null;column:state;comment:状态,0:关闭,1:正常;default:1"`
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
}
func (i *LocalModel) TableName() string {
return "ai_local_model"
}
func (i *LocalModel) IdValue() int64 {
return i.Id
}
type LocalModelInstallState struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
Complete int64 `gorm:"type:BIGINT(20);not null;column:complete;comment:已下载大小"`
Total int64 `gorm:"type:BIGINT(20);not null;column:total;comment:总大小"`
State int `gorm:"type:tinyint(1);not null;column:state;comment:状态"`
LastMsg string `gorm:"type:text;not null;column:last_msg;comment:最后一次消息"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
}
func (i *LocalModelInstallState) TableName() string {
return "ai_local_model_install_state"
}
func (i *LocalModelInstallState) IdValue() int64 {
return i.Id
}
type LocalModelPackage struct {
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
Uuid string `gorm:"type:varchar(100);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
Name string `gorm:"type:varchar(100);not null;column:name;comment:名称"`
Size string `gorm:"type:varchar(100);not null;column:size;comment:模型大小"`
Hash string `gorm:"type:varchar(100);not null;column:hash;comment:模型hash"`
Description string `gorm:"type:varchar(255);not null;column:description;comment:描述"`
Version string `gorm:"type:varchar(100);not null;column:version;comment:版本"`
IsPopular bool `gorm:"type:tinyint(1);not null;column:is_popular;comment:是否热门"`
}
func (i *LocalModelPackage) TableName() string {
return "ai_local_model_package"
}
func (i *LocalModelPackage) IdValue() int64 {
return i.Id
}
+48
View File
@@ -31,6 +31,38 @@ type imlKeyStore struct {
store.SearchStore[Key]
}
type IBalanceStore interface {
store.ISearchStore[Balance]
}
type imlBalanceStore struct {
store.SearchStore[Balance]
}
type ILocalModelStore interface {
store.ISearchStore[LocalModel]
}
type imlLocalModelStore struct {
store.SearchStore[LocalModel]
}
type ILocalModelPackageStore interface {
store.ISearchStore[LocalModelPackage]
}
type imlLocalModelPackageStore struct {
store.SearchStore[LocalModelPackage]
}
type ILocalModelInstallStateStore interface {
store.ISearchStore[LocalModelInstallState]
}
type imlLocalModelInstallStateStore struct {
store.SearchStore[LocalModelInstallState]
}
func init() {
autowire.Auto[IProviderStore](func() reflect.Value {
return reflect.ValueOf(new(imlProviderStore))
@@ -40,7 +72,23 @@ func init() {
return reflect.ValueOf(new(imlLogMetricsStore))
})
autowire.Auto[IBalanceStore](func() reflect.Value {
return reflect.ValueOf(new(imlBalanceStore))
})
autowire.Auto[ILocalModelStore](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelStore))
})
autowire.Auto[ILocalModelPackageStore](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelPackageStore))
})
autowire.Auto[IKeyStore](func() reflect.Value {
return reflect.ValueOf(new(imlKeyStore))
})
autowire.Auto[ILocalModelInstallStateStore](func() reflect.Value {
return reflect.ValueOf(new(imlLocalModelInstallStateStore))
})
}
+1
View File
@@ -86,6 +86,7 @@ type AiAPIInfo struct {
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
AdditionalConfig string `gorm:"type:text;null;column:additional_config;comment:额外配置"`
UseToken int `gorm:"type:int(11);not null;column:use_token;comment:使用token"`
Type int `gorm:"type:tinyint(1);not null;column:type;comment:类型 0:online api 1:local api"`
Disable bool `gorm:"type:tinyint(1);not null;column:disable;comment:是否禁用 0:否 1:是"`
IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:否 1:是"`
}