mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
local model first commit
This commit is contained in:
@@ -4,3 +4,4 @@
|
||||
/build/
|
||||
/apipark
|
||||
.gitlab-ci.yml
|
||||
/.vscode/
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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
@@ -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))
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
@@ -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:类型,0:online,1:local"`
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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:是"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user