finish ai key api

This commit is contained in:
Liujian
2024-12-23 15:48:19 +08:00
parent 2a3e016dc2
commit 00451ae78f
33 changed files with 1283 additions and 70 deletions
+26
View File
@@ -0,0 +1,26 @@
package ai_key
import (
"reflect"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
)
type IKeyController interface {
Create(ctx *gin.Context, providerId string, input *ai_key_dto.Create) error
Edit(ctx *gin.Context, providerId string, id string, input *ai_key_dto.Edit) error
Delete(ctx *gin.Context, providerId string, id string) error
Get(ctx *gin.Context, providerId string, id string) (*ai_key_dto.Key, error)
List(ctx *gin.Context, providerId string, keyword string, page string, pageSize string) ([]*ai_key_dto.Item, int64, error)
Enable(ctx *gin.Context, providerId string, id string) error
Disable(ctx *gin.Context, providerId string, id string) error
Sort(ctx *gin.Context, providerId string, input *ai_key_dto.Sort) error
}
func init() {
autowire.Auto[IKeyController](func() reflect.Value {
return reflect.ValueOf(new(imlAIKeyController))
})
}
+61
View File
@@ -0,0 +1,61 @@
package ai_key
import (
"strconv"
ai_key "github.com/APIParkLab/APIPark/module/ai-key"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
"github.com/gin-gonic/gin"
)
var _ IKeyController = &imlAIKeyController{}
type imlAIKeyController struct {
module ai_key.IKeyModule `autowired:""`
}
func (i *imlAIKeyController) Enable(ctx *gin.Context, providerId string, id string) error {
return i.module.UpdateKeyStatus(ctx, providerId, id, true)
}
func (i *imlAIKeyController) Disable(ctx *gin.Context, providerId string, id string) error {
return i.module.UpdateKeyStatus(ctx, providerId, id, false)
}
func (i *imlAIKeyController) Create(ctx *gin.Context, providerId string, input *ai_key_dto.Create) error {
return i.module.Create(ctx, providerId, input)
}
func (i *imlAIKeyController) Edit(ctx *gin.Context, providerId string, id string, input *ai_key_dto.Edit) error {
return i.module.Edit(ctx, providerId, id, input)
}
func (i *imlAIKeyController) Delete(ctx *gin.Context, providerId string, id string) error {
return i.module.Delete(ctx, providerId, id)
}
func (i *imlAIKeyController) Get(ctx *gin.Context, providerId string, id string) (*ai_key_dto.Key, error) {
return i.module.Get(ctx, providerId, id)
}
func (i *imlAIKeyController) List(ctx *gin.Context, providerId string, keyword string, page string, pageSize string) ([]*ai_key_dto.Item, int64, error) {
p, err := strconv.Atoi(page)
if err != nil {
if page != "" {
return nil, 0, err
}
p = 1
}
ps, err := strconv.Atoi(pageSize)
if err != nil {
if pageSize != "" {
return nil, 0, err
}
ps = 15
}
return i.module.List(ctx, providerId, keyword, p, ps)
}
func (i *imlAIKeyController) Sort(ctx *gin.Context, providerId string, input *ai_key_dto.Sort) error {
return i.module.Sort(ctx, providerId, input)
}
+6 -2
View File
@@ -1,14 +1,18 @@
package ai
import (
"reflect"
"github.com/eolinker/go-common/auto"
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
"github.com/eolinker/go-common/autowire"
"github.com/gin-gonic/gin"
"reflect"
)
type IProviderController interface {
Providers(ctx *gin.Context) ([]*ai_dto.ProviderItem, error)
ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, error)
UnConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ProviderItem, error)
SimpleProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, error)
Provider(ctx *gin.Context, id string) (*ai_dto.Provider, error)
LLMs(ctx *gin.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error)
+9 -4
View File
@@ -3,6 +3,7 @@ package ai
import (
"github.com/APIParkLab/APIPark/module/ai"
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
"github.com/eolinker/go-common/auto"
"github.com/gin-gonic/gin"
)
@@ -14,12 +15,16 @@ type imlProviderController struct {
module ai.IProviderModule `autowired:""`
}
func (i *imlProviderController) SimpleProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, error) {
return i.module.SimpleProviders(ctx)
func (i *imlProviderController) ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, error) {
return i.module.ConfiguredProviders(ctx)
}
func (i *imlProviderController) Providers(ctx *gin.Context) ([]*ai_dto.ProviderItem, error) {
return i.module.Providers(ctx)
func (i *imlProviderController) UnConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ProviderItem, error) {
return i.module.UnConfiguredProviders(ctx)
}
func (i *imlProviderController) SimpleProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, error) {
return i.module.SimpleProviders(ctx)
}
func (i *imlProviderController) Provider(ctx *gin.Context, id string) (*ai_dto.Provider, error) {
+1 -1
View File
@@ -81,4 +81,4 @@ require (
//replace github.com/eolinker/ap-account => ../aoaccount
//
//replace github.com/eolinker/go-common => ../go-common
replace github.com/eolinker/go-common => ../../eolinker/go-common
-2
View File
@@ -32,8 +32,6 @@ github.com/eolinker/ap-account v1.0.15 h1:n6DJeL6RHZ8eLlZUcY2U3H4d/GPaA5oelAx3R0
github.com/eolinker/ap-account v1.0.15/go.mod h1:zm/Ivs6waJ/M/nEszhpPmM6g50y/MKO+5eABFAdeD0g=
github.com/eolinker/eosc v0.18.3 h1:3IK5HkAPnJRfLbQ0FR7kWsZr6Y/OiqqGazvN1q2BL5A=
github.com/eolinker/eosc v0.18.3/go.mod h1:O9PQQXFCpB6fjHf+oFt/LN6EOAv779ItbMixMKCfTfk=
github.com/eolinker/go-common v1.1.1 h1:3WqqecGqcHDgpa8Ljp156c1uWeZKP1CKScdU+6sOfcc=
github.com/eolinker/go-common v1.1.1/go.mod h1:Kb/jENMN1mApnodvRgV4YwO9FJby1Jkt2EUjrBjvSX4=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/getkin/kin-openapi v0.127.0 h1:Mghqi3Dhryf3F8vR370nN67pAERW+3a95vomb3MAREY=
+30
View File
@@ -8,6 +8,8 @@ import (
"net/http"
"strings"
"github.com/eolinker/eosc/log"
model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
@@ -114,6 +116,7 @@ func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_a
Timeout: input.Timeout,
Retry: input.Retry,
Model: input.AiModel.Id,
Provider: input.AiModel.Provider,
AdditionalConfig: map[string]interface{}{
"ai_prompt": input.AiPrompt,
"ai_model": input.AiModel,
@@ -148,8 +151,10 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
return err
}
var modelId *string
var providerId *string
if input.AiModel != nil {
modelId = &input.AiModel.Id
providerId = &input.AiModel.Provider
}
if input.AiPrompt != nil {
apiInfo.AdditionalConfig["ai_prompt"] = input.AiPrompt
@@ -164,6 +169,7 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
Timeout: input.Timeout,
Retry: input.Retry,
Model: modelId,
Provider: providerId,
AdditionalConfig: &apiInfo.AdditionalConfig,
})
})
@@ -304,3 +310,27 @@ func (i *imlAPIModule) Prefix(ctx context.Context, serviceId string) (string, er
}
return strings.TrimSuffix(pInfo.Prefix, "/"), nil
}
func (i *imlAPIModule) OnInit() {
ctx := context.Background()
list, err := i.aiAPIService.List(ctx)
if err != nil {
return
}
for _, item := range list {
if item.Provider == "" {
aiModel, err := ConvertStruct[ai_api_dto.AiModel](item.AdditionalConfig["ai_model"])
if err != nil {
log.Errorf("convert ai model error:%v", err)
continue
}
err = i.aiAPIService.Save(ctx, item.ID, &ai_api.Edit{
Provider: &aiModel.Provider,
})
if err != nil {
log.Errorf("update ai api provider error:%v", err)
continue
}
}
}
}
+49
View File
@@ -0,0 +1,49 @@
package ai_key_dto
var (
KeyNormal KeyStatus = "normal"
KeyExceed KeyStatus = "exceed"
KeyExpired KeyStatus = "expired"
KeyDisable KeyStatus = "disable"
KeyError KeyStatus = "error"
)
type KeyStatus string
func (s KeyStatus) String() string {
return string(s)
}
func (s KeyStatus) Int() int {
switch s {
case KeyDisable:
return 0
case KeyNormal:
return 1
case KeyError:
return 2
case KeyExceed:
return 3
case KeyExpired:
return 4
default:
return 0
}
}
func ToKeyStatus(status int) KeyStatus {
switch status {
case 0:
return KeyDisable
case 1:
return KeyNormal
case 2:
return KeyError
case 3:
return KeyExceed
case 4:
return KeyExpired
default:
return KeyDisable
}
}
+20
View File
@@ -0,0 +1,20 @@
package ai_key_dto
type Create struct {
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
ExpireTime int `json:"expire_time"`
}
type Edit struct {
Name *string `json:"name"`
Config *string `json:"config"`
ExpireTime *int `json:"expire_time"`
}
type Sort struct {
Origin string `json:"origin"`
Target string `json:"target"`
Sort string `json:"sort"`
}
+21
View File
@@ -0,0 +1,21 @@
package ai_key_dto
import "github.com/eolinker/go-common/auto"
type Item struct {
Id string `json:"id"`
Name string `json:"name"`
Status KeyStatus `json:"status"`
UseToken int `json:"use_token"`
UpdateTime auto.TimeLabel `json:"update_time"`
ExpireTime int `json:"expire_time"`
Priority int `json:"priority"`
CanDelete bool `json:"can_delete"`
}
type Key struct {
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
ExpireTime int `json:"expire_time"`
}
+304
View File
@@ -0,0 +1,304 @@
package ai_key
import (
"context"
"errors"
"fmt"
"time"
"gorm.io/gorm"
"github.com/eolinker/go-common/auto"
model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
"github.com/google/uuid"
"github.com/eolinker/go-common/store"
"github.com/APIParkLab/APIPark/service/ai"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
)
var _ IKeyModule = &imlKeyModule{}
type imlKeyModule struct {
providerService ai.IProviderService `autowired:""`
aiKeyService ai_key.IKeyService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlKeyModule) Create(ctx context.Context, providerId string, input *ai_key_dto.Create) error {
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return fmt.Errorf("provider not found: %w", err)
}
p, has := model_runtime.GetProvider(providerId)
if !has {
return fmt.Errorf("provider not found: %w", err)
}
err = p.Check(input.Config)
if err != nil {
return fmt.Errorf("config check failed: %w", err)
}
priority, err := i.aiKeyService.MaxPriority(ctx, providerId)
if err != nil {
return fmt.Errorf("get key error: %v", err)
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
if input.Id == "" {
input.Id = uuid.NewString()
}
status := ai_key_dto.KeyNormal.Int()
if input.ExpireTime > 0 && time.Unix(int64(input.ExpireTime), 0).Before(time.Now()) {
status = ai_key_dto.KeyExpired.Int()
} else {
// TODO: 发布Key到网关
}
return i.aiKeyService.Create(ctx, &ai_key.Create{
ID: input.Id,
Name: input.Name,
Config: input.Config,
Provider: providerId,
Status: status,
ExpireTime: input.ExpireTime,
Priority: priority + 1,
})
})
}
func (i *imlKeyModule) Edit(ctx context.Context, providerId string, id string, input *ai_key_dto.Edit) error {
p, has := model_runtime.GetProvider(providerId)
if !has {
return fmt.Errorf("provider not found: %s", providerId)
}
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return fmt.Errorf("provider not found: %w", err)
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
info, err := i.aiKeyService.Get(ctx, id)
if err != nil {
return fmt.Errorf("key not found: %w", err)
}
if input.Config != nil {
err = p.Check(*input.Config)
if err != nil {
return fmt.Errorf("config check failed: %w", err)
}
cfg, err := p.GenConfig(info.Config, *input.Config)
if err != nil {
return fmt.Errorf("config gen failed: %w", err)
}
input.Config = &cfg
if info.Default {
err = i.providerService.Save(ctx, info.Provider, &ai.SetProvider{
Config: input.Config,
})
if err != nil {
return err
}
}
}
status := ai_key_dto.KeyNormal.Int()
orgStatus := ai_key_dto.ToKeyStatus(info.Status)
switch orgStatus {
case ai_key_dto.KeyNormal, ai_key_dto.KeyError, ai_key_dto.KeyExpired:
if input.ExpireTime != nil {
expireTime := *input.ExpireTime
if expireTime > 0 && time.Unix(int64(expireTime), 0).Before(time.Now()) {
status = ai_key_dto.KeyExpired.Int()
}
} else if info.ExpireTime > 0 && time.Unix(int64(info.ExpireTime), 0).Before(time.Now()) {
// 如果过期时间未更改,且已过期,则设置为过期状态
status = ai_key_dto.KeyExpired.Int()
}
default:
// 停用、超额需要启用,所以维持原状态
status = orgStatus.Int()
}
if status == ai_key_dto.KeyNormal.Int() {
// TODO: 发布Key到网关
}
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Name: input.Name,
Config: input.Config,
ExpireTime: input.ExpireTime,
Status: &status,
})
})
}
func (i *imlKeyModule) Delete(ctx context.Context, providerId string, id string) error {
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return fmt.Errorf("provider not found: %w", err)
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
info, err := i.aiKeyService.Get(ctx, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}
if info.Default {
return fmt.Errorf("default key can't be deleted: %s", id)
}
keys, err := i.aiKeyService.KeysAfterPriority(ctx, providerId, info.Priority)
if err != nil {
return err
}
for _, key := range keys {
key.Priority--
err = i.aiKeyService.Save(ctx, key.ID, &ai_key.Edit{
Priority: &key.Priority,
})
if err != nil {
return err
}
}
// TODO: 操作网关下线Key
return i.aiKeyService.Delete(ctx, id)
})
}
func (i *imlKeyModule) Get(ctx context.Context, providerId string, id string) (*ai_key_dto.Key, error) {
p, has := model_runtime.GetProvider(providerId)
if !has {
return nil, fmt.Errorf("provider not found: %s", providerId)
}
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return nil, fmt.Errorf("provider not found: %w", err)
}
info, err := i.aiKeyService.Get(ctx, id)
if err != nil {
return nil, fmt.Errorf("key not found: %w", err)
}
return &ai_key_dto.Key{
Id: info.ID,
Name: info.Name,
Config: p.MaskConfig(info.Config),
ExpireTime: info.ExpireTime,
}, nil
}
func (i *imlKeyModule) List(ctx context.Context, providerId string, keyword string, page, pageSize int) ([]*ai_key_dto.Item, int64, error) {
_, err := i.aiKeyService.DefaultKey(ctx, providerId)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, 0, fmt.Errorf("get default key failed: %w", err)
}
info, err := i.providerService.Get(ctx, providerId)
if err != nil {
return nil, 0, fmt.Errorf("provider is unconfigued,id is %s", providerId)
}
err = i.aiKeyService.Create(ctx, &ai_key.Create{
ID: info.Id,
Name: info.Name,
Config: info.Config,
Provider: info.Id,
Status: ai_key_dto.KeyNormal.Int(),
Priority: 1,
ExpireTime: 0,
UseToken: 0,
Default: true,
})
if err != nil {
return nil, 0, fmt.Errorf("create default key failed: %w", err)
}
}
list, total, err := i.aiKeyService.SearchByPage(ctx, keyword, map[string]interface{}{
"provider": providerId,
}, page, pageSize, "sort asc")
if err != nil {
return nil, 0, err
}
var result []*ai_key_dto.Item
for _, item := range list {
status := ai_key_dto.ToKeyStatus(item.Status)
if item.ExpireTime > 0 && time.Unix(int64(item.ExpireTime), 0).Before(time.Now()) {
status = ai_key_dto.KeyExpired
}
result = append(result, &ai_key_dto.Item{
Id: item.ID,
Name: item.Name,
Status: status,
UseToken: item.UseToken,
UpdateTime: auto.TimeLabel(item.UpdateAt),
ExpireTime: item.ExpireTime,
CanDelete: !item.Default,
Priority: item.Priority,
})
}
return result, total, nil
}
func (i *imlKeyModule) UpdateKeyStatus(ctx context.Context, providerId string, id string, enable bool) error {
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return fmt.Errorf("provider not found: %w", err)
}
info, err := i.aiKeyService.Get(ctx, id)
if err != nil {
return fmt.Errorf("key not found: %w", err)
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
if !enable {
// TODO:下线Key
status := ai_key_dto.KeyDisable.Int()
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Status: &status,
})
}
if info.Status == ai_key_dto.KeyDisable.Int() || info.Status == ai_key_dto.KeyExceed.Int() {
// 超额 或 停用状态,可启用
if info.ExpireTime > 0 && time.Unix(int64(info.ExpireTime), 0).Before(time.Now()) {
// 如果过期时间未更改,且已过期,则设置为过期状态
status := ai_key_dto.KeyExpired.Int()
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Status: &status,
})
}
// TODO:发布Key到网关
status := ai_key_dto.KeyNormal.Int()
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Status: &status,
})
}
return nil
})
}
func (i *imlKeyModule) Sort(ctx context.Context, providerId string, input *ai_key_dto.Sort) error {
_, err := i.providerService.Get(ctx, providerId)
if err != nil {
return fmt.Errorf("provider not found: %w", err)
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
switch input.Sort {
case "before":
_, err = i.aiKeyService.SortBefore(ctx, providerId, input.Origin, input.Target)
case "after":
_, err = i.aiKeyService.SortAfter(ctx, providerId, input.Origin, input.Target)
default:
return fmt.Errorf("invalid sort type: %s", input.Sort)
}
if err != nil {
return err
}
// TODO: 全量更新key配置到网关
return nil
})
}
+25
View File
@@ -0,0 +1,25 @@
package ai_key
import (
"context"
"reflect"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
"github.com/eolinker/go-common/autowire"
)
type IKeyModule interface {
Create(ctx context.Context, providerId string, input *ai_key_dto.Create) error
Edit(ctx context.Context, providerId string, id string, input *ai_key_dto.Edit) error
Delete(ctx context.Context, providerId string, id string) error
Get(ctx context.Context, providerId string, id string) (*ai_key_dto.Key, error)
List(ctx context.Context, providerId string, keyword string, page, pageSize int) ([]*ai_key_dto.Item, int64, error)
UpdateKeyStatus(ctx context.Context, providerId string, id string, enable bool) error
Sort(ctx context.Context, providerId string, input *ai_key_dto.Sort) error
}
func init() {
autowire.Auto[IKeyModule](func() reflect.Value {
return reflect.ValueOf(new(imlKeyModule))
})
}
+39
View File
@@ -0,0 +1,39 @@
package ai_dto
var (
ProviderEnabled ProviderStatus = "enabled"
ProviderDisabled ProviderStatus = "disabled"
ProviderAbnormal ProviderStatus = "abnormal"
)
type ProviderStatus string
func (p ProviderStatus) Int() int {
switch p {
case ProviderAbnormal:
return 2
case ProviderEnabled:
return 0
case ProviderDisabled:
return 1
default:
return 1
}
}
func (p ProviderStatus) String() string {
return string(p)
}
func ToProviderStatus(status int) ProviderStatus {
switch status {
case 2:
return ProviderAbnormal
case 0:
return ProviderEnabled
case 1:
return ProviderDisabled
default:
return ProviderDisabled
}
}
+2
View File
@@ -7,4 +7,6 @@ type UpdateLLM struct {
type UpdateConfig struct {
DefaultLLM string `json:"default_llm"`
Config string `json:"config"`
Priority *int `json:"priority"`
Enable *bool `json:"enable"`
}
+25 -17
View File
@@ -1,26 +1,34 @@
package ai_dto
import "time"
type Provider struct {
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
GetAPIKeyUrl string `json:"get_apikey_url"`
DefaultLLM string `json:"defaultLLM"`
DefaultLLMConfig string `json:"-"`
Id string `json:"id"`
Name string `json:"name"`
Config string `json:"config"`
GetAPIKeyUrl string `json:"get_apikey_url"`
DefaultLLM string `json:"defaultLLM"`
DefaultLLMConfig string `json:"-"`
Priority int `json:"priority"`
Status ProviderStatus `json:"status"`
}
type ConfiguredProviderItem struct {
Id string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLLM string `json:"default_llm"`
Status ProviderStatus `json:"status"`
APICount int64 `json:"api_count"`
KeyCount int `json:"key_count"`
KeyStatus []string `json:"key_status"`
Priority int `json:"priority"`
}
type ProviderItem struct {
Id string `json:"id"`
Name string `json:"name"`
DefaultLLM string `json:"default_llm"`
DefaultLLMLogo string `json:"default_llm_logo"`
Logo string `json:"logo"`
Configured bool `json:"configured"`
Recommend bool `json:"recommend"`
Sort int `json:"sort"`
UpdateTime time.Time `json:"-"`
Id string `json:"id"`
Name string `json:"name"`
Logo string `json:"logo"`
DefaultLLM string `json:"default_llm"`
Sort int `json:"-"`
}
type SimpleProviderItem struct {
+160 -30
View File
@@ -8,6 +8,14 @@ import (
"sort"
"time"
ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
"github.com/eolinker/go-common/auto"
ai_api "github.com/APIParkLab/APIPark/service/ai-api"
model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
"github.com/APIParkLab/APIPark/gateway"
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
@@ -46,9 +54,82 @@ 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:""`
}
func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, error) {
// 获取已配置的AI服务商
list, err := i.providerService.List(ctx)
if err != nil {
return nil, 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)
}
providers := make([]*ai_dto.ConfiguredProviderItem, 0, len(list))
for _, l := range list {
p, has := model_runtime.GetProvider(l.Id)
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([]string, 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, status.String())
}
if len(keysStatus) == 0 {
keysStatus = []string{ai_key_dto.KeyNormal.String()}
}
providers = append(providers, &ai_dto.ConfiguredProviderItem{
Id: l.Id,
Name: l.Name,
Logo: p.Logo(),
DefaultLLM: l.DefaultLLM,
Status: ai_dto.ToProviderStatus(l.Status),
APICount: aiAPIMap[l.Id],
KeyCount: len(keysStatus),
KeyStatus: keysStatus,
Priority: l.Priority,
})
}
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 *auto.Label
for _, p := range providers {
if p.Status == ai_dto.ProviderEnabled {
backup = &auto.Label{
Id: p.Id,
Name: p.Name,
}
break
}
}
return providers, backup, nil
}
func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error) {
list, err := i.providerService.List(ctx)
if err != nil {
@@ -74,7 +155,7 @@ func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.Simp
return items, nil
}
func (i *imlProviderModule) Providers(ctx context.Context) ([]*ai_dto.ProviderItem, error) {
func (i *imlProviderModule) UnConfiguredProviders(ctx context.Context) ([]*ai_dto.ProviderItem, error) {
list, err := i.providerService.List(ctx)
if err != nil {
return nil, err
@@ -85,30 +166,21 @@ func (i *imlProviderModule) Providers(ctx context.Context) ([]*ai_dto.ProviderIt
})
items := make([]*ai_dto.ProviderItem, 0, len(providers))
for _, v := range providers {
item := &ai_dto.ProviderItem{
Id: v.ID(),
Name: v.Name(),
Logo: v.Logo(),
Recommend: v.Recommend(),
Sort: v.Sort(),
if _, has := providerMap[v.ID()]; has {
// 已配置,跳过
continue
}
if info, has := providerMap[v.ID()]; has {
defaultLLM, has := v.GetModel(info.DefaultLLM)
if !has {
continue
}
item.Configured = true
item.DefaultLLM = defaultLLM.ID()
item.DefaultLLMLogo = defaultLLM.Logo()
item.UpdateTime = info.UpdateAt
defaultLLM, _ := v.DefaultModel(model_runtime.ModelTypeLLM)
item := &ai_dto.ProviderItem{
Id: v.ID(),
Name: v.Name(),
Logo: v.Logo(),
DefaultLLM: defaultLLM.ID(),
Sort: v.Sort(),
}
items = append(items, item)
}
sort.Slice(items, func(i, j int) bool {
if items[i].Configured == items[j].Configured && items[i].Configured {
return items[i].Name < items[j].Name
}
if items[i].Sort != items[j].Sort {
if items[i].Sort == 0 {
return false
@@ -128,6 +200,13 @@ 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) {
@@ -144,6 +223,8 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
GetAPIKeyUrl: p.HelpUrl(),
DefaultLLM: defaultLLM.ID(),
DefaultLLMConfig: defaultLLM.Logo(),
Status: ai_dto.ProviderDisabled,
Priority: maxPriority,
}, nil
}
defaultLLM, has := p.GetModel(info.DefaultLLM)
@@ -154,6 +235,10 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
}
defaultLLM = model
}
if info.Priority == 0 {
info.Priority = maxPriority
}
return &ai_dto.Provider{
Id: info.Id,
Name: info.Name,
@@ -161,6 +246,8 @@ 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.ProviderEnabled,
}, nil
}
@@ -196,16 +283,19 @@ func (i *imlProviderModule) LLMs(ctx context.Context, driver string) ([]*ai_dto.
return nil, nil, fmt.Errorf("ai provider default llm not found")
}
return items, &ai_dto.ProviderItem{
Id: p.ID(),
Name: p.Name(),
DefaultLLM: defaultLLM.ID(),
DefaultLLMLogo: defaultLLM.Logo(),
Logo: p.Logo(),
Configured: false,
Id: p.ID(),
Name: p.Name(),
DefaultLLM: defaultLLM.ID(),
Logo: p.Logo(),
}, nil
}
return items, &ai_dto.ProviderItem{Id: info.Id, Name: info.Name, DefaultLLM: info.DefaultLLM, Logo: p.Logo(), Configured: true}, nil
return items, &ai_dto.ProviderItem{
Id: info.Id,
Name: info.Name,
DefaultLLM: info.DefaultLLM,
Logo: p.Logo(),
}, nil
}
func (i *imlProviderModule) UpdateProviderStatus(ctx context.Context, id string, enable bool) error {
@@ -222,8 +312,12 @@ func (i *imlProviderModule) UpdateProviderStatus(ctx context.Context, id string,
}
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
status := 0
if enable {
status = 1
}
err = i.providerService.Save(txCtx, id, &ai.SetProvider{
Status: &enable,
Status: &status,
})
if err != nil {
return err
@@ -303,11 +397,46 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
return err
}
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
err = i.providerService.Save(ctx, id, &ai.SetProvider{
status := 0
if input.Enable != nil && *input.Enable {
status = 1
}
pInfo := &ai.SetProvider{
Name: &info.Name,
DefaultLLM: &info.DefaultLLM,
Config: &input.Config,
})
Priority: input.Priority,
}
_, err = i.aiKeyService.DefaultKey(ctx, id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
err = i.aiKeyService.Create(ctx, &ai_key.Create{
ID: id,
Name: info.Name,
Config: info.Config,
Provider: id,
Status: 1,
ExpireTime: 0,
Default: true,
})
} else {
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Config: &info.Config,
})
}
if err != nil {
return err
}
if input.Enable != nil {
status = 0
if *input.Enable {
status = 1
}
pInfo.Status = &status
}
err = i.providerService.Save(ctx, id, pInfo)
if err != nil {
return err
}
@@ -376,6 +505,7 @@ func (i *imlProviderModule) getAiProviders(ctx context.Context) ([]*gateway.Dyna
}
return providers, nil
}
func (i *imlProviderModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
providers, err := i.getAiProviders(ctx)
if err != nil {
+6 -2
View File
@@ -2,14 +2,18 @@ package ai
import (
"context"
"reflect"
"github.com/eolinker/go-common/auto"
"github.com/APIParkLab/APIPark/gateway"
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
"github.com/eolinker/go-common/autowire"
"reflect"
)
type IProviderModule interface {
Providers(ctx context.Context) ([]*ai_dto.ProviderItem, error)
ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, error)
UnConfiguredProviders(ctx context.Context) ([]*ai_dto.ProviderItem, error)
SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error)
Provider(ctx context.Context, id string) (*ai_dto.Provider, error)
LLMs(ctx context.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error)
+16 -2
View File
@@ -10,8 +10,9 @@ import (
func (p *plugin) aiAPIs() []pm3.Api {
return []pm3.Api{
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.Providers, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders),
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/simple/ai/providers/configured", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders),
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/ai/provider/llms", []string{"context", "query:provider"}, []string{"llms", "provider"}, p.aiProviderController.LLMs),
//pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/enable", []string{"context", "query:provider"}, nil, p.aiProviderController.isStop),
@@ -20,3 +21,16 @@ func (p *plugin) aiAPIs() []pm3.Api {
//pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/default-llm", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderDefaultLLM),
}
}
func (p *plugin) aiKeyApis() []pm3.Api {
return []pm3.Api{
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/resource/key", []string{"context", "query:provider", "query:id"}, []string{"info"}, p.aiKeyController.Get),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/resource/keys", []string{"context", "query:provider", "query:keyword", "query:page", "query:page_size"}, []string{"keys", "total"}, p.aiKeyController.List),
pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/ai/resource/key", []string{"context", "query:provider", "body"}, nil, p.aiKeyController.Create),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/resource/key", []string{"context", "query:provider", "query:id", "body"}, nil, p.aiKeyController.Edit),
pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/ai/resource/key", []string{"context", "query:provider", "query:id"}, nil, p.aiKeyController.Delete),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/resource/key/enable", []string{"context", "query:provider", "query:id"}, nil, p.aiKeyController.Enable),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/resource/key/disable", []string{"context", "query:provider", "query:id"}, nil, p.aiKeyController.Disable),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/resource/key/sort", []string{"context", "query:provider", "body"}, nil, p.aiKeyController.Sort),
}
}
+4
View File
@@ -3,6 +3,8 @@ package core
import (
"net/http"
ai_key "github.com/APIParkLab/APIPark/controller/ai-key"
"github.com/APIParkLab/APIPark/controller/log"
"github.com/APIParkLab/APIPark/controller/strategy"
@@ -74,6 +76,7 @@ type plugin struct {
upstreamController upstream.IUpstreamController `autowired:""`
routerController router.IRouterController `autowired:""`
aiAPIController ai_api.IAPIController `autowired:""`
aiKeyController ai_key.IKeyController `autowired:""`
apiDocController router.IAPIDocController `autowired:""`
subscribeController subscribe.ISubscribeController `autowired:""`
strategyController strategy.IStrategyController `autowired:""`
@@ -111,6 +114,7 @@ func (p *plugin) OnComplete() {
p.apis = append(p.apis, p.commonApis()...)
p.apis = append(p.apis, p.systemApis()...)
p.apis = append(p.apis, p.aiAPIs()...)
p.apis = append(p.apis, p.aiKeyApis()...)
p.apis = append(p.apis, p.strategyApis()...)
p.apis = append(p.apis, p.logApis()...)
}
+11 -1
View File
@@ -1,10 +1,12 @@
package ai_api
import (
"context"
"encoding/json"
"time"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/api"
"time"
)
var _ IAPIService = (*imlAPIService)(nil)
@@ -18,6 +20,10 @@ type imlAPIService struct {
universally.IServiceDelete
}
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")
}
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)
@@ -43,6 +49,7 @@ func createEntityHandler(i *Create) *api.AiAPIInfo {
Timeout: i.Timeout,
Retry: i.Retry,
Model: i.Model,
Provider: i.Provider,
CreateAt: now,
UpdateAt: now,
AdditionalConfig: string(cfg),
@@ -67,6 +74,9 @@ func updateHandler(e *api.AiAPIInfo, i *Edit) {
if i.Model != nil {
e.Model = *i.Model
}
if i.Provider != nil {
e.Provider = *i.Provider
}
if i.AdditionalConfig != nil {
cfg, _ := json.Marshal(i.AdditionalConfig)
e.AdditionalConfig = string(cfg)
+3
View File
@@ -16,6 +16,7 @@ type API struct {
Timeout int
Retry int
Model string
Provider string
CreateAt time.Time
UpdateAt time.Time
Creator string
@@ -33,6 +34,7 @@ type Create struct {
Timeout int
Retry int
Model string
Provider string
AdditionalConfig map[string]interface{}
}
@@ -42,6 +44,7 @@ type Edit struct {
Description *string
Timeout *int
Retry *int
Provider *string
Model *string
AdditionalConfig *map[string]interface{}
}
+4 -1
View File
@@ -1,9 +1,11 @@
package ai_api
import (
"context"
"reflect"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/eolinker/go-common/autowire"
"reflect"
)
type IAPIService interface {
@@ -11,6 +13,7 @@ type IAPIService interface {
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
//ListByServices(ctx context.Context, serviceIds ...string) ([]*API, error)
}
+210
View File
@@ -0,0 +1,210 @@
package ai_key
import (
"context"
"fmt"
"sort"
"time"
"github.com/eolinker/go-common/store"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/ai"
)
var _ IKeyService = &imlAIKeyService{}
type imlAIKeyService struct {
store ai.IKeyStore `autowired:""`
transaction store.ITransaction `autowired:""`
universally.IServiceGet[Key]
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
}
func (i *imlAIKeyService) KeysAfterPriority(ctx context.Context, providerId string, priority int) ([]*Key, error) {
list, err := i.store.ListQuery(ctx, "sort > ? and provider = ?", []interface{}{priority, providerId}, "sort asc")
if err != nil {
return nil, err
}
var result []*Key
for _, item := range list {
result = append(result, FromEntity(item))
}
return result, nil
}
func (i *imlAIKeyService) MaxPriority(ctx context.Context, providerId string) (int, error) {
info, err := i.store.First(ctx, map[string]interface{}{"provider": providerId}, "sort desc")
if err != nil {
return 0, err
}
return info.Sort, nil
}
func (i *imlAIKeyService) DefaultKey(ctx context.Context, providerId string) (*Key, error) {
info, err := i.store.First(ctx, map[string]interface{}{"provider": providerId, "default": true})
if err != nil {
return nil, err
}
return FromEntity(info), nil
}
func (i *imlAIKeyService) KeysByProvider(ctx context.Context, providerId string) ([]*Key, error) {
list, err := i.store.List(ctx, map[string]interface{}{"provider": providerId})
if err != nil {
return nil, err
}
var result []*Key
for _, item := range list {
result = append(result, FromEntity(item))
}
return result, nil
}
func (i *imlAIKeyService) SortBefore(ctx context.Context, provider string, originID string, targetID string) ([]*Key, 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.Sort, targetKey.Sort
// 初始化顺序,假设原始Key在目标Key之后,中间的key往后移动,原始Key移动到`targetKeySort`位置
originKey.Sort = 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.Sort = 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([]*Key, 0, len(list)+1)
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
for _, l := range list {
l.Sort = fn(l.Sort)
_, 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].Sort < list[j].Sort })
return result, nil
}
func (i *imlAIKeyService) SortAfter(ctx context.Context, provider string, originID string, targetID string) ([]*Key, 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.Sort, targetKey.Sort
// 初始化顺序,假设原始Key在目标Key之后,中间的Key往后移动,原始Key移动到`targetKeySort + 1`位置
originKey.Sort = 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.Sort = 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([]*Key, 0, len(list)+1)
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
for _, l := range list {
l.Sort = fn(l.Sort)
_, 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].Sort < list[j].Sort })
return result, nil
}
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.IServiceDelete = universally.NewDelete[ai.Key](i.store)
}
func labelHandler(e *ai.Key) []string {
return []string{e.Name, e.Uuid}
}
func uniquestHandler(i *Create) []map[string]interface{} {
return []map[string]interface{}{{"uuid": i.ID}}
}
func createEntityHandler(i *Create) *ai.Key {
now := time.Now()
return &ai.Key{
Uuid: i.ID,
Name: i.Name,
Config: i.Config,
Provider: i.Provider,
Status: i.Status,
ExpireTime: i.ExpireTime,
Sort: i.Priority,
UseToken: 0,
CreateAt: now,
UpdateAt: now,
Default: i.Default,
}
}
func updateHandler(e *ai.Key, i *Edit) {
if i.Name != nil {
e.Name = *i.Name
}
if i.Config != nil {
e.Config = *i.Config
}
if i.Status != nil {
e.Status = *i.Status
}
if i.ExpireTime != nil {
e.ExpireTime = *i.ExpireTime
}
if i.Priority != nil {
e.Sort = *i.Priority
}
e.UpdateAt = time.Now()
}
+61
View File
@@ -0,0 +1,61 @@
package ai_key
import (
"time"
"github.com/APIParkLab/APIPark/stores/ai"
)
type Key struct {
ID string
Name string
Config string
Provider string
Status int
ExpireTime int
UseToken int
Creator string
Updater string
Priority int
CreateAt time.Time
UpdateAt time.Time
Default bool
}
func FromEntity(e *ai.Key) *Key {
return &Key{
ID: e.Uuid,
Name: e.Name,
Config: e.Config,
Provider: e.Provider,
Status: e.Status,
ExpireTime: e.ExpireTime,
UseToken: e.UseToken,
Creator: e.Creator,
Updater: e.Updater,
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
Priority: e.Sort,
Default: e.Default,
}
}
type Create struct {
ID string
Name string
Config string
Provider string
Priority int
Status int
ExpireTime int
UseToken int
Default bool
}
type Edit struct {
Name *string
Config *string
Status *int
ExpireTime *int
Priority *int
}
+29
View File
@@ -0,0 +1,29 @@
package ai_key
import (
"context"
"reflect"
"github.com/eolinker/go-common/autowire"
"github.com/APIParkLab/APIPark/service/universally"
)
type IKeyService interface {
universally.IServiceGet[Key]
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
DefaultKey(ctx context.Context, providerId string) (*Key, error)
KeysByProvider(ctx context.Context, providerId string) ([]*Key, 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)
KeysAfterPriority(ctx context.Context, providerId string, priority int) ([]*Key, error)
}
func init() {
autowire.Auto[IKeyService](func() reflect.Value {
return reflect.ValueOf(new(imlAIKeyService))
})
}
+25 -2
View File
@@ -4,12 +4,13 @@ import (
"context"
"encoding/base64"
"errors"
"time"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/ai"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/go-common/utils"
"gorm.io/gorm"
"time"
)
var _ IProviderService = (*imlProviderService)(nil)
@@ -19,6 +20,14 @@ type imlProviderService struct {
store ai.IProviderStore `autowired:""`
}
func (i *imlProviderService) MaxPriority(ctx context.Context) (int, error) {
t, err := i.store.First(ctx, nil, "priority desc")
if err != nil {
return 0, err
}
return t.Priority, nil
}
func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvider) error {
userId := utils.UserId(ctx)
now := time.Now()
@@ -30,10 +39,20 @@ func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvid
if cfg.Name == nil || cfg.Config == nil || cfg.DefaultLLM == nil {
return errors.New("invalid params")
}
status := false
status := 1
if cfg.Status != nil {
status = *cfg.Status
}
priority := 1
if cfg.Priority == nil {
count, err := i.store.Count(ctx, "", nil)
if err != nil {
return err
}
priority = int(count) + 1
} else {
priority = *cfg.Priority
}
info = &ai.Provider{
UUID: id,
Name: *cfg.Name,
@@ -42,6 +61,7 @@ func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvid
Status: status,
Creator: userId,
Updater: userId,
Priority: priority,
CreateAt: now,
UpdateAt: now,
}
@@ -58,6 +78,9 @@ func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvid
if cfg.Status != nil {
info.Status = *cfg.Status
}
if cfg.Priority != nil {
info.Priority = *cfg.Priority
}
info.Updater = userId
info.UpdateAt = now
}
+7 -3
View File
@@ -2,8 +2,9 @@ package ai
import (
"encoding/base64"
"github.com/APIParkLab/APIPark/stores/ai"
"time"
"github.com/APIParkLab/APIPark/stores/ai"
)
type Provider struct {
@@ -13,7 +14,8 @@ type Provider struct {
Config string
Creator string
Updater string
Status bool
Status int
Priority int
CreateAt time.Time
UpdateAt time.Time
}
@@ -22,7 +24,8 @@ type SetProvider struct {
Name *string
DefaultLLM *string
Config *string
Status *bool
Status *int
Priority *int
}
func FromEntity(e *ai.Provider) *Provider {
@@ -40,5 +43,6 @@ func FromEntity(e *ai.Provider) *Provider {
CreateAt: e.CreateAt,
UpdateAt: e.UpdateAt,
Status: e.Status,
Priority: e.Priority,
}
}
+3 -1
View File
@@ -2,14 +2,16 @@ package ai
import (
"context"
"reflect"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/eolinker/go-common/autowire"
"reflect"
)
type IProviderService interface {
universally.IServiceGet[Provider]
Save(ctx context.Context, id string, cfg *SetProvider) error
MaxPriority(ctx context.Context) (int, error)
}
func init() {
+30
View File
@@ -0,0 +1,30 @@
package access_log
import "time"
type Log 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;"`
Cluster string `gorm:"column:cluster;type:varchar(36);NOT NULL;comment:集群ID"`
Node string `gorm:"column:node;type:varchar(36);NOT NULL;comment:节点ID"`
Service string `gorm:"column:service;type:varchar(36);NOT NULL;comment:服务ID"`
API string `gorm:"column:api;type:varchar(36);NOT NULL;comment:API ID"`
Application string `gorm:"column:application;type:varchar(36);NOT NULL;comment:应用ID"`
Auth string `gorm:"column:auth;type:varchar(36);NOT NULL;comment:认证ID"`
Type string `gorm:"column:type;type:varchar(36);NOT NULL;comment:日志类型;index:idx_type"`
Target string `gorm:"column:target;type:varchar(36);NOT NULL;comment:目标ID"`
IP string `gorm:"column:ip;type:varchar(36);NOT NULL;comment:IP"`
RequestPath string `gorm:"column:request_path;type:varchar(255);NOT NULL;comment:请求路径"`
Method string `gorm:"column:method;type:varchar(36);NOT NULL;comment:请求方法"`
ResponseTime float64 `gorm:"column:response_time;type:float;NOT NULL;comment:响应时间"`
StatusCode int64 `gorm:"column:status_code;type:BIGINT(20);NOT NULL;comment:响应状态码"`
ReportTime time.Time `gorm:"column:report_time;type:timestamp;NOT NULL;comment:上报时间;index:idx_report_time"`
}
func (c *Log) IdValue() int64 {
return c.Id
}
func (c *Log) TableName() string {
return "access_log"
}
+22
View File
@@ -0,0 +1,22 @@
package access_log
import (
"reflect"
"github.com/eolinker/go-common/autowire"
"github.com/eolinker/go-common/store"
)
type ILogStore interface {
store.ISearchStore[Log]
}
type imlLogStore struct {
store.SearchStore[Log]
}
func init() {
autowire.Auto[ILogStore](func() reflect.Value {
return reflect.ValueOf(new(imlLogStore))
})
}
+47 -1
View File
@@ -8,7 +8,8 @@ type Provider struct {
Name string `gorm:"type:varchar(100);not null;column:name;comment:name"`
DefaultLLM string `gorm:"type:varchar(255);not null;column:default_llm;comment:默认模型ID"`
Config string `gorm:"type:text;not null;column:config;comment:配置信息"`
Status bool `gorm:"type:tinyint(1);not null;column:status;comment:状态"`
Status int `gorm:"type:tinyint(1);not null;column:status;comment:状态0:停用;1:启用,2:异常"`
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:创建时间"`
@@ -22,3 +23,48 @@ func (i *Provider) TableName() string {
func (i *Provider) IdValue() int64 {
return i.Id
}
type LogMetrics 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"`
Model string `gorm:"type:varchar(36);not null;column:model;comment:模型ID"`
InputToken int `gorm:"type:int;not null;column:input_token;comment:输入token"`
OutputToken int `gorm:"type:int;not null;column:output_token;comment:输出token"`
TotalToken int `gorm:"type:int;not null;column:total_token;comment:总token"`
Cost float64 `gorm:"type:int;not null;column:cost;comment:费用"`
Per float64 `gorm:"type:int;not null;column:per;comment:每个token的价格"`
}
func (i *LogMetrics) TableName() string {
return "ai_log_metrics"
}
func (i *LogMetrics) IdValue() int64 {
return i.Id
}
type Key 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:名称"`
Config string `gorm:"type:text;not null;column:config;comment:配置"`
Provider string `gorm:"type:varchar(36);not null;column:provider;comment:供应商ID"`
Status int `gorm:"type:tinyint(1);not null;column:status;comment:状态,0:停用;1:启用,2:错误;3:超额;4:过期"`
ExpireTime int `gorm:"type:int;not null;column:expire_time;comment:过期时间"`
UseToken int `gorm:"type:int;not null;column:use_token;comment:使用token数"`
Sort int `gorm:"type:int;not null;column:sort;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:更新时间"`
Default bool `gorm:"type:tinyint(1);not null;column:default;comment:是否默认"`
}
func (i *Key) TableName() string {
return "ai_key"
}
func (i *Key) IdValue() int64 {
return i.Id
}
+26 -1
View File
@@ -1,9 +1,10 @@
package ai
import (
"reflect"
"github.com/eolinker/go-common/autowire"
"github.com/eolinker/go-common/store"
"reflect"
)
type IProviderStore interface {
@@ -14,8 +15,32 @@ type imlProviderStore struct {
store.SearchStore[Provider]
}
type ILogMetricsStore interface {
store.ISearchStore[LogMetrics]
}
type imlLogMetricsStore struct {
store.SearchStore[LogMetrics]
}
type IKeyStore interface {
store.ISearchStore[Key]
}
type imlKeyStore struct {
store.SearchStore[Key]
}
func init() {
autowire.Auto[IProviderStore](func() reflect.Value {
return reflect.ValueOf(new(imlProviderStore))
})
autowire.Auto[ILogMetricsStore](func() reflect.Value {
return reflect.ValueOf(new(imlLogMetricsStore))
})
autowire.Auto[IKeyStore](func() reflect.Value {
return reflect.ValueOf(new(imlKeyStore))
})
}
+1
View File
@@ -79,6 +79,7 @@ type AiAPIInfo struct {
Timeout int `gorm:"type:int(11);not null;column:timeout;comment:超时时间"`
Retry int `gorm:"type:int(11);not null;column:retry;comment:重试次数"`
Model string `gorm:"size:255;not null;column:model;comment:模型"`
Provider string `gorm:"size:36;not null;column:provider;comment:提供者;index:provider"`
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"`
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"`