mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
Merge remote-tracking branch 'origin/main' into feature/1.4
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
variables:
|
||||
PATH: /opt/go-1.21/go/bin/:/opt/node/node/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
|
||||
GOROOT: /opt/go-1.21/go
|
||||
GOPROXY: https://goproxy.cn
|
||||
VERSION: $CI_COMMIT_SHORT_SHA
|
||||
APP: apipark
|
||||
APP_PRE: ${APP}_${VERSION}
|
||||
BUILD_DIR: ${APP}-build
|
||||
DEPLOY_DESC: "DEV 环境"
|
||||
VIEW_ADDR: http://172.18.166.219:8288
|
||||
SAVE_DIR: /opt/${APP}
|
||||
NODE_OPTIONS: --max_old_space_size=8192
|
||||
|
||||
stages:
|
||||
- notice
|
||||
- prefix
|
||||
- build
|
||||
- deploy
|
||||
- webhook
|
||||
|
||||
feishu-informer: # 飞书回调
|
||||
stage: notice
|
||||
variables:
|
||||
DIFF_URL: "$CI_MERGE_REQUEST_PROJECT_URL/-/merge_requests/$CI_MERGE_REQUEST_IID/diffs"
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE=="merge_request_event" && $CI_COMMIT_BRANCH =~ "main"
|
||||
script:
|
||||
- echo "merge request"
|
||||
- |
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"项目:${CI_PROJECT_NAME}\\n提交人:${GITLAB_USER_NAME}\\n提交信息:${CI_MERGE_REQUEST_TITLE}\\n合并分支信息:${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME} -> ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}\\n差异性地址:${DIFF_URL}\\n请及时review代码\"}}" \
|
||||
https://open.feishu.cn/open-apis/bot/v2/hook/1c334752-2874-41a1-8f1b-3060f2d46b6c
|
||||
|
||||
prebuild:
|
||||
stage: prefix
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
script:
|
||||
- echo "prebuild"
|
||||
- chmod +x ./scripts/prefix.sh
|
||||
- ./scripts/prefix.sh
|
||||
|
||||
builder:
|
||||
stage: build
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
script:
|
||||
- set -e
|
||||
- |
|
||||
if [ ! -d "../artifacts" ]; then
|
||||
mkdir -p ../artifacts
|
||||
fi
|
||||
if [ -d "../artifacts/dist" ]; then
|
||||
cp -r ../artifacts/dist frontend/dist
|
||||
fi
|
||||
- |
|
||||
if [ -n "$(git diff --name-status HEAD~1 HEAD -- frontend)" ]; then
|
||||
./scripts/build.sh $BUILD_DIR ${VERSION} all ""
|
||||
else
|
||||
./scripts/build.sh $BUILD_DIR ${VERSION}
|
||||
fi
|
||||
if [ -d "frontend/dist" ]; then
|
||||
echo "copy frontend/dist to artifacts/dist"
|
||||
rm -fr ../artifacts/dist
|
||||
cp -r frontend/dist ../artifacts/dist
|
||||
fi
|
||||
cp $BUILD_DIR/${APP_PRE}_linux_amd64.tar.gz ${SAVE_DIR}
|
||||
|
||||
deployer:
|
||||
stage: deploy
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
variables:
|
||||
APIPARK_GUEST_MODE: allow
|
||||
APIPARK_GUEST_ID: dklejrfbhjqwdh
|
||||
script:
|
||||
- cd ${SAVE_DIR};mkdir -p ${APP_PRE};tar -zxvf ${APP_PRE}_linux_amd64.tar.gz -C ${APP_PRE};cd ${APP_PRE};./install.sh ${SAVE_DIR};./run.sh restart;cd ${SAVE_DIR} && ./clean.sh ${APP_PRE}
|
||||
when: on_success
|
||||
success:
|
||||
stage: webhook
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
script:
|
||||
- |
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署完成.\\n访问地址:${VIEW_ADDR}\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
|
||||
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
|
||||
when: on_success
|
||||
failure:
|
||||
stage: webhook
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "main"
|
||||
script:
|
||||
- |
|
||||
curl -X POST -H "Content-Type: application/json" \
|
||||
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"最近一次提交:${CI_COMMIT_TITLE}\\n提交人:${GITLAB_USER_NAME}\\n项目:${CI_PROJECT_NAME}\\n环境:${DEPLOY_DESC}\\n更新部署失败,请及时到gitlab上查看\\n工作流地址:${CI_PIPELINE_URL}\"}}" \
|
||||
https://open.feishu.cn/open-apis/bot/v2/hook/c3672932-4dfa-4989-8023-0128bae59338
|
||||
when: on_failure
|
||||
@@ -210,7 +210,7 @@ APIPark uses the Apache 2.0 License. For more details, please refer to the LICEN
|
||||
For enterprise-level features and professional technical support, contact our pre-sales experts for personalized demos, customized solutions, and pricing.
|
||||
|
||||
- Website: https://apipark.com
|
||||
- Email: dev@apipark.com
|
||||
- Email: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -40,7 +40,14 @@ func (c *Config) Check(cfg string) error {
|
||||
}
|
||||
|
||||
func (c *Config) GenConfig(target string, origin string) (string, error) {
|
||||
if target == "" {
|
||||
target = "{}"
|
||||
}
|
||||
if origin == "" {
|
||||
origin = "{}"
|
||||
}
|
||||
var targetData map[string]interface{}
|
||||
|
||||
err := json.Unmarshal([]byte(target), &targetData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/config.yml
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
nsq "github.com/nsqio/go-nsq"
|
||||
|
||||
"github.com/eolinker/go-common/cftool"
|
||||
|
||||
_ "github.com/eolinker/go-common/store/store_mysql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
var (
|
||||
version string
|
||||
confPath string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&confPath, "c", "config.yml", "`config` file path for server ")
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port int `yaml:"port"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 连接 MySQL 数据库
|
||||
cftool.Register[ServerConfig](fmt.Sprintf("root:%s", confPath))
|
||||
cftool.ReadFile(confPath)
|
||||
|
||||
handler := &NSQHandler{}
|
||||
autowire.Autowired(handler)
|
||||
err := autowire.CheckComplete()
|
||||
if err != nil {
|
||||
log.Fatal("check autowired:", err)
|
||||
return
|
||||
}
|
||||
// 2. 创建 NSQ 消费者
|
||||
config := nsq.NewConfig()
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get hostname: %v", err)
|
||||
return
|
||||
}
|
||||
nsqConfig := handler.nsqConfig
|
||||
consumer, err := nsq.NewConsumer(fmt.Sprintf("%s_ai_event", nsqConfig.TopicPrefix), hostname, config)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create NSQ consumer: %v", err)
|
||||
}
|
||||
|
||||
consumer.AddHandler(handler)
|
||||
|
||||
// 4. 连接到 NSQ
|
||||
//nsqAddress := "172.18.166.219:9150" // NSQ 地址
|
||||
err = consumer.ConnectToNSQD(nsqConfig.Addr)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to NSQ: %v", err)
|
||||
}
|
||||
log.Println("Connected to NSQ")
|
||||
|
||||
// 5. 捕获系统信号,优雅关闭
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
|
||||
// 优雅停止消费者
|
||||
consumer.Stop()
|
||||
<-consumer.StopChan
|
||||
log.Println("NSQ Consumer stopped")
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/go-common/cftool"
|
||||
|
||||
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
|
||||
|
||||
"github.com/eolinker/go-common/store"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/ai"
|
||||
|
||||
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
|
||||
|
||||
nsq "github.com/nsqio/go-nsq"
|
||||
|
||||
ai_api "github.com/APIParkLab/APIPark/service/ai-api"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cftool.Register[NSQConfig]("nsq")
|
||||
}
|
||||
|
||||
type NSQConfig struct {
|
||||
Addr string `json:"addr" yaml:"addr"`
|
||||
TopicPrefix string `json:"topic_prefix" yaml:"topic_prefix"`
|
||||
}
|
||||
|
||||
// 定义 NSQ 消息结构
|
||||
type AIProviderStatus struct {
|
||||
Provider string `json:"provider"`
|
||||
Model string `json:"model"`
|
||||
Key string `json:"key"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type AIInfo struct {
|
||||
Model string `json:"ai_model"`
|
||||
Cost interface{} `json:"ai_model_cost"`
|
||||
InputToken interface{} `json:"ai_model_input_token"`
|
||||
OutputToken interface{} `json:"ai_model_output_token"`
|
||||
TotalToken interface{} `json:"ai_model_total_token"`
|
||||
Provider string `json:"ai_provider"`
|
||||
ProviderStats []AIProviderStatus `json:"ai_provider_statuses"`
|
||||
}
|
||||
|
||||
type NSQMessage struct {
|
||||
AI AIInfo `json:"ai"`
|
||||
API string `json:"api"`
|
||||
Provider string `json:"provider"`
|
||||
RequestID string `json:"request_id"`
|
||||
TimeISO8601 string `json:"time_iso8601"`
|
||||
}
|
||||
|
||||
// NSQHandler 处理 NSQ 消息并写入 MySQL
|
||||
type NSQHandler struct {
|
||||
apiUseService ai_api.IAPIUseService `autowired:""`
|
||||
aiKeyService ai_key.IKeyService `autowired:""`
|
||||
aiService ai.IProviderService `autowired:""`
|
||||
transaction store.ITransaction `autowired:""`
|
||||
nsqConfig *NSQConfig `autowired:""`
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func convertInt(value interface{}) int {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
return v
|
||||
case float64:
|
||||
return int(v)
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func genAIKey(key string, provider string) string {
|
||||
keys := strings.Split(key, "@")
|
||||
return strings.TrimSuffix(keys[0], fmt.Sprintf("-%s", provider))
|
||||
}
|
||||
|
||||
// HandleMessage 处理从 NSQ 读取的消息
|
||||
func (h *NSQHandler) HandleMessage(message *nsq.Message) error {
|
||||
log.Printf("Received message: %s", string(message.Body))
|
||||
|
||||
// 解析消息为结构体
|
||||
var data NSQMessage
|
||||
err := json.Unmarshal(message.Body, &data)
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshal message: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将时间字符串转换为 time.Time
|
||||
timestamp, err := time.Parse(time.RFC3339, data.TimeISO8601)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse timestamp: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
day := time.Date(timestamp.Year(), timestamp.Month(), timestamp.Day(), 0, 0, 0, 0, timestamp.Location())
|
||||
hour := time.Date(timestamp.Year(), timestamp.Month(), timestamp.Day(), timestamp.Hour(), 0, 0, 0, timestamp.Location())
|
||||
minute := time.Date(timestamp.Year(), timestamp.Month(), timestamp.Day(), timestamp.Hour(), timestamp.Minute(), 0, 0, timestamp.Location())
|
||||
return h.transaction.Transaction(context.Background(), func(ctx context.Context) error {
|
||||
finalStatus := &AIProviderStatus{}
|
||||
for _, s := range data.AI.ProviderStats {
|
||||
status := ToKeyStatus(s.Status).Int()
|
||||
key := genAIKey(s.Key, s.Provider)
|
||||
err = h.aiKeyService.Save(ctx, key, &ai_key.Edit{
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to save AI key: %v", err)
|
||||
return nil
|
||||
}
|
||||
if s.Provider != data.AI.Provider {
|
||||
|
||||
pStatus := ai_dto.ProviderAbnormal.Int()
|
||||
err = h.aiService.Save(ctx, s.Provider, &ai.SetProvider{
|
||||
Status: &pStatus,
|
||||
})
|
||||
} else {
|
||||
pStatus := ai_dto.ProviderEnabled.Int()
|
||||
err = h.aiService.Save(ctx, s.Provider, &ai.SetProvider{
|
||||
Status: &pStatus,
|
||||
})
|
||||
}
|
||||
finalStatus = &s
|
||||
}
|
||||
if finalStatus != nil {
|
||||
//keys := strings.Split(finalStatus.Key, "@")
|
||||
key := genAIKey(finalStatus.Key, finalStatus.Provider)
|
||||
err = h.aiKeyService.IncrUseToken(ctx, key, convertInt(data.AI.TotalToken))
|
||||
if err != nil {
|
||||
log.Printf("Failed to increment AI key token: %v", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 AI API 接口
|
||||
err = h.apiUseService.Incr(context.Background(), &ai_api.IncrAPIUse{
|
||||
API: data.API,
|
||||
Service: data.Provider,
|
||||
Provider: data.AI.Provider,
|
||||
Model: data.AI.Model,
|
||||
Day: day.Unix(),
|
||||
Hour: hour.Unix(),
|
||||
Minute: minute.Unix(),
|
||||
InputToken: convertInt(data.AI.InputToken),
|
||||
OutputToken: convertInt(data.AI.OutputToken),
|
||||
TotalToken: convertInt(data.AI.TotalToken),
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to call AI API: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Message processed and saved to MySQL: %+v", data)
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
|
||||
|
||||
var (
|
||||
StatusNormal = "normal"
|
||||
StatusInvalidRequest = "invalid request"
|
||||
StatusQuotaExhausted = "quota exhausted"
|
||||
StatusExpired = "expired"
|
||||
StatusExceeded = "exceeded"
|
||||
StatusInvalid = "invalid"
|
||||
StatusTimeout = "timeout"
|
||||
)
|
||||
|
||||
func ToKeyStatus(status string) ai_key_dto.KeyStatus {
|
||||
switch status {
|
||||
case StatusNormal:
|
||||
return ai_key_dto.KeyNormal
|
||||
case StatusInvalidRequest:
|
||||
return ai_key_dto.KeyNormal
|
||||
case StatusQuotaExhausted:
|
||||
return ai_key_dto.KeyExceed
|
||||
case StatusExpired:
|
||||
return ai_key_dto.KeyExpired
|
||||
case StatusExceeded:
|
||||
return ai_key_dto.KeyNormal
|
||||
case StatusInvalid:
|
||||
return ai_key_dto.KeyError
|
||||
case StatusTimeout:
|
||||
return ai_key_dto.KeyError
|
||||
default:
|
||||
return ai_key_dto.KeyNormal
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package ai_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/APIParkLab/APIPark/model/plugin_model"
|
||||
@@ -52,7 +51,7 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
||||
plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"model": input.AiModel.Id,
|
||||
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
|
||||
"provider": input.AiModel.Provider,
|
||||
"config": input.AiModel.Config,
|
||||
},
|
||||
}
|
||||
@@ -73,8 +72,8 @@ func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_
|
||||
Retry: input.Retry,
|
||||
Plugins: plugins,
|
||||
},
|
||||
Upstream: input.AiModel.Provider,
|
||||
Disable: false,
|
||||
//Upstream: input.AiModel.Provider,
|
||||
Disable: false,
|
||||
})
|
||||
|
||||
return err
|
||||
@@ -101,16 +100,16 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
|
||||
Retry: apiInfo.Proxy.Retry,
|
||||
Plugins: apiInfo.Proxy.Plugins,
|
||||
}
|
||||
var upstream *string
|
||||
//var upstream *string
|
||||
if input.AiModel != nil {
|
||||
proxy.Plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"model": input.AiModel.Id,
|
||||
"provider": fmt.Sprintf("%s@ai-provider", input.AiModel.Provider),
|
||||
"provider": input.AiModel.Provider,
|
||||
"config": input.AiModel.Config,
|
||||
},
|
||||
}
|
||||
upstream = &input.AiModel.Provider
|
||||
//upstream = &input.AiModel.Provider
|
||||
}
|
||||
|
||||
if input.AiPrompt != nil {
|
||||
@@ -128,7 +127,7 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
|
||||
Path: input.Path,
|
||||
Disable: input.Disable,
|
||||
Methods: &apiInfo.Methods,
|
||||
Upstream: upstream,
|
||||
//Upstream: upstream,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -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, statuses 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))
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package ai_key
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"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, statuses 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 = 20
|
||||
}
|
||||
ss := make([]string, 0)
|
||||
if statuses != "" {
|
||||
err = json.Unmarshal([]byte(statuses), &ss)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
return i.module.List(ctx, providerId, keyword, p, ps, ss)
|
||||
}
|
||||
|
||||
func (i *imlAIKeyController) Sort(ctx *gin.Context, providerId string, input *ai_key_dto.Sort) error {
|
||||
return i.module.Sort(ctx, providerId, input)
|
||||
}
|
||||
@@ -1,25 +1,37 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
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, *ai_dto.BackupProvider, 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)
|
||||
Provider(ctx *gin.Context, id string) (*ai_dto.Provider, error)
|
||||
SimpleProvider(ctx *gin.Context, id string) (*ai_dto.SimpleProvider, error)
|
||||
LLMs(ctx *gin.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error)
|
||||
Enable(ctx *gin.Context, id string) error
|
||||
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
|
||||
}
|
||||
|
||||
type IStatisticController interface {
|
||||
APIs(ctx *gin.Context, keyword string, providerId string, start string, end string, page string, pageSize string, sortCondition string, asc string, models string, services string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IProviderController](func() reflect.Value {
|
||||
return reflect.ValueOf(&imlProviderController{})
|
||||
})
|
||||
autowire.Auto[IStatisticController](func() reflect.Value {
|
||||
return reflect.ValueOf(&imlStatisticController{})
|
||||
})
|
||||
}
|
||||
|
||||
+72
-5
@@ -1,6 +1,9 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/APIParkLab/APIPark/module/ai"
|
||||
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -14,28 +17,46 @@ 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) ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
|
||||
return i.module.ConfiguredProviders(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) Providers(ctx *gin.Context) ([]*ai_dto.ProviderItem, error) {
|
||||
return i.module.Providers(ctx)
|
||||
func (i *imlProviderController) SimpleConfiguredProviders(ctx *gin.Context) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error) {
|
||||
return i.module.SimpleConfiguredProviders(ctx)
|
||||
}
|
||||
|
||||
func (i *imlProviderController) Provider(ctx *gin.Context, id string) (*ai_dto.Provider, error) {
|
||||
return i.module.Provider(ctx, id)
|
||||
}
|
||||
|
||||
func (i *imlProviderController) SimpleProvider(ctx *gin.Context, id string) (*ai_dto.SimpleProvider, error) {
|
||||
return i.module.SimpleProvider(ctx, id)
|
||||
}
|
||||
|
||||
func (i *imlProviderController) LLMs(ctx *gin.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error) {
|
||||
return i.module.LLMs(ctx, driver)
|
||||
}
|
||||
|
||||
func (i *imlProviderController) Enable(ctx *gin.Context, id string) error {
|
||||
return i.module.UpdateProviderStatus(ctx, id, true)
|
||||
//return i.module.UpdateProviderStatus(ctx, id, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *imlProviderController) Disable(ctx *gin.Context, id string) error {
|
||||
return i.module.UpdateProviderStatus(ctx, id, false)
|
||||
//return i.module.UpdateProviderStatus(ctx, id, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *imlProviderController) UpdateProviderConfig(ctx *gin.Context, id string, input *ai_dto.UpdateConfig) error {
|
||||
@@ -43,5 +64,51 @@ func (i *imlProviderController) UpdateProviderConfig(ctx *gin.Context, id string
|
||||
}
|
||||
|
||||
func (i *imlProviderController) UpdateProviderDefaultLLM(ctx *gin.Context, id string, input *ai_dto.UpdateLLM) error {
|
||||
return i.module.UpdateProviderDefaultLLM(ctx, id, input)
|
||||
//return i.module.UpdateProviderDefaultLLM(ctx, id, input)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ IStatisticController = (*imlStatisticController)(nil)
|
||||
|
||||
type imlStatisticController struct {
|
||||
module ai.IAIAPIModule `autowired:""`
|
||||
}
|
||||
|
||||
func (i *imlStatisticController) APIs(ctx *gin.Context, keyword string, providerId string, start string, end string, page string, pageSize string, sortCondition string, asc string, models string, services string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error) {
|
||||
s, err := strconv.ParseInt(start, 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
e, err := strconv.ParseInt(end, 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
p, err := strconv.Atoi(page)
|
||||
if err != nil {
|
||||
if page != "" {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
p = 1
|
||||
}
|
||||
|
||||
ps, err := strconv.Atoi(pageSize)
|
||||
if err != nil {
|
||||
if pageSize != "" {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
ps = 20
|
||||
}
|
||||
ms := make([]string, 0)
|
||||
if models != "" {
|
||||
json.Unmarshal([]byte(models), &ms)
|
||||
ms = append(ms, models)
|
||||
}
|
||||
ss := make([]string, 0)
|
||||
if services != "" {
|
||||
json.Unmarshal([]byte(services), &ss)
|
||||
ss = append(ss, services)
|
||||
}
|
||||
return i.module.APIs(ctx, keyword, providerId, s, e, p, ps, sortCondition, asc == "true", ms, ss)
|
||||
}
|
||||
|
||||
@@ -318,8 +318,8 @@ func (i *imlServiceController) Get(ctx *gin.Context, id string) (*service_dto.Se
|
||||
return i.module.Get(ctx, id)
|
||||
}
|
||||
|
||||
func (i *imlServiceController) Search(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) {
|
||||
return i.module.Search(ctx, teamID, keyword)
|
||||
func (i *imlServiceController) Search(ctx *gin.Context, teamIDs string, keyword string) ([]*service_dto.ServiceItem, error) {
|
||||
return i.module.Search(ctx, teamIDs, keyword)
|
||||
}
|
||||
|
||||
func (i *imlServiceController) Create(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) {
|
||||
|
||||
@@ -15,7 +15,7 @@ type IServiceController interface {
|
||||
Get(ctx *gin.Context, id string) (*service_dto.Service, error)
|
||||
// SearchMyServices 搜索服务
|
||||
SearchMyServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error)
|
||||
Search(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error)
|
||||
Search(ctx *gin.Context, teamIDs string, keyword string) ([]*service_dto.ServiceItem, error)
|
||||
// Create 创建
|
||||
Create(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error)
|
||||
// Edit 编辑
|
||||
|
||||
@@ -437,7 +437,7 @@ func (i *imlInitController) createAIService(ctx context.Context, teamID string,
|
||||
plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"model": aiModel.Id,
|
||||
"provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id),
|
||||
"provider": info.Provider.Id,
|
||||
"config": aiModel.Config,
|
||||
},
|
||||
}
|
||||
@@ -457,8 +457,8 @@ func (i *imlInitController) createAIService(ctx context.Context, teamID string,
|
||||
Retry: retry,
|
||||
Plugins: plugins,
|
||||
},
|
||||
Disable: false,
|
||||
Upstream: info.Provider.Id,
|
||||
Disable: false,
|
||||
//Upstream: info.Provider.Id,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,48 +4,48 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { Button, Dropdown } from 'antd'
|
||||
import { memo, useEffect, useMemo } from 'react'
|
||||
|
||||
const LanguageItems = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label: (
|
||||
<Button key="en" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
English
|
||||
</Button>
|
||||
),
|
||||
title: 'English'
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: (
|
||||
<Button key="jp" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
日本語
|
||||
</Button>
|
||||
),
|
||||
title: '日本語'
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: (
|
||||
<Button key="tw" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
繁體中文
|
||||
</Button>
|
||||
),
|
||||
title: '繁體中文'
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: (
|
||||
<Button key="cn" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
简体中文
|
||||
</Button>
|
||||
),
|
||||
title: '简体中文'
|
||||
}
|
||||
]
|
||||
const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
const { dispatch, state } = useGlobalContext()
|
||||
const items = [
|
||||
{
|
||||
key: 'en-US',
|
||||
label: (
|
||||
<Button key="en" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
English
|
||||
</Button>
|
||||
),
|
||||
title: 'English'
|
||||
},
|
||||
{
|
||||
key: 'ja-JP',
|
||||
label: (
|
||||
<Button key="jp" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
日本語
|
||||
</Button>
|
||||
),
|
||||
title: '日本語'
|
||||
},
|
||||
{
|
||||
key: 'zh-TW',
|
||||
label: (
|
||||
<Button key="tw" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
繁體中文
|
||||
</Button>
|
||||
),
|
||||
title: '繁體中文'
|
||||
},
|
||||
{
|
||||
key: 'zh-CN',
|
||||
label: (
|
||||
<Button key="cn" type="text" className="flex items-center p-0 bg-transparent border-none">
|
||||
简体中文
|
||||
</Button>
|
||||
),
|
||||
title: '简体中文'
|
||||
}
|
||||
]
|
||||
|
||||
const langLabel = useMemo(() => items.find((item) => item?.key === state.language)?.title, [state.language])
|
||||
const langLabel = useMemo(() => LanguageItems.find((item) => item?.key === state.language)?.title, [state.language])
|
||||
|
||||
useEffect(() => {
|
||||
const savedLang = i18n.language || sessionStorage.getItem('i18nextLng')
|
||||
@@ -53,7 +53,8 @@ const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: savedLang })
|
||||
} else if (!savedLang) {
|
||||
const browserLang = navigator.language
|
||||
const supportedLang = items.find((item) => item.key === browserLang) ? browserLang : 'zh-CN'
|
||||
const supportedLang = LanguageItems.find((item) => item.key === browserLang) ? browserLang : 'zh-CN'
|
||||
if (state.language === supportedLang) return
|
||||
dispatch({ type: 'UPDATE_LANGUAGE', language: supportedLang })
|
||||
i18n.changeLanguage(supportedLang)
|
||||
}
|
||||
@@ -63,7 +64,7 @@ const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => {
|
||||
<Dropdown
|
||||
trigger={['hover']}
|
||||
menu={{
|
||||
items,
|
||||
items: LanguageItems,
|
||||
style: { minWidth: '80px' },
|
||||
onClick: (e) => {
|
||||
const { key } = e
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { PERMISSION_DEFINITION } from '@common/const/permissions'
|
||||
import { $t } from '@common/locales'
|
||||
import { Button, Tooltip, Upload } from 'antd'
|
||||
import { ReactElement, cloneElement, useEffect, useMemo, useState } from 'react'
|
||||
import { useGlobalContext } from '../../contexts/GlobalStateContext'
|
||||
import { PERMISSION_DEFINITION } from '@common/const/permissions'
|
||||
import { $t } from '@common/locales'
|
||||
|
||||
type WithPermissionProps = {
|
||||
access?: string | string[]
|
||||
|
||||
@@ -342,7 +342,7 @@ export const GlobalProvider: FC<{ children: ReactNode }> = ({ children }) => {
|
||||
updateDate: '2024-07-01',
|
||||
powered: 'Powered by https://apipark.com',
|
||||
mainPage: '/guide/page',
|
||||
language: 'en-US',
|
||||
language: sessionStorage.getItem('i18nextLng') || 'en-US',
|
||||
pluginsLoaded: false
|
||||
})
|
||||
const [accessData, setAccessData] = useState<Map<string, string[]>>(new Map())
|
||||
@@ -510,7 +510,7 @@ export const useGlobalContext = () => {
|
||||
updateDate: '',
|
||||
powered: '',
|
||||
mainPage: '',
|
||||
language: 'en-US',
|
||||
language: sessionStorage.getItem('i18nextLng') || 'en-US',
|
||||
pluginsLoaded: false
|
||||
},
|
||||
dispatch: () => {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
import { initReactI18next, useTranslation } from 'react-i18next'
|
||||
// i18next-browser-languagedetector插件 这是一个 i18next 语言检测插件,用于检测浏览器中的用户语言,
|
||||
import crc32 from 'crc/crc32'
|
||||
import LanguageDetector from 'i18next-browser-languagedetector'
|
||||
@@ -39,23 +39,22 @@ i18n
|
||||
.init({
|
||||
// 初始化
|
||||
resources, // 本地多语言数据
|
||||
// fallbackLng: config.lang, // 默认当前环境的语言
|
||||
supportedLngs: ['zh-CN', 'en-US', 'zh-TW', 'ja-JP'],
|
||||
detection: {
|
||||
caches: ['localStorage', 'sessionStorage', 'cookie']
|
||||
}
|
||||
})
|
||||
|
||||
// --------这里是i18next-scanner新增的配置-------------
|
||||
// 用于非 React 组件中的翻译
|
||||
export const $t = (key: string, params?: any[]): string => {
|
||||
const hashKey = `K${crc32(key).toString(16)}` // 将中文转换成crc32格式去匹配对应的json语言包
|
||||
// 将中文转换成crc32格式去匹配对应的json语言包
|
||||
const hashKey = `K${crc32(key).toString(16)}`
|
||||
let words = i18n.t(hashKey)
|
||||
// const { t } = useTranslation(); // 通过hooks
|
||||
// let words = t(hashKey);
|
||||
if (words === hashKey) {
|
||||
words = key
|
||||
}
|
||||
|
||||
// 配置传递参数的场景, 目前仅支持数组,可在此拓展
|
||||
if (Array.isArray(params)) {
|
||||
const reg = /\((\d)\)/g
|
||||
words = words.replace(reg, (a: string, b: number) => {
|
||||
@@ -65,4 +64,25 @@ export const $t = (key: string, params?: any[]): string => {
|
||||
return words
|
||||
}
|
||||
|
||||
// 用于 React 组件中的翻译
|
||||
export const useI18n = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (key: string, params?: any[]): string => {
|
||||
const hashKey = `K${crc32(key).toString(16)}`
|
||||
let words = t(hashKey)
|
||||
if (words === hashKey) {
|
||||
words = key
|
||||
}
|
||||
|
||||
if (Array.isArray(params)) {
|
||||
const reg = /\((\d)\)/g
|
||||
words = words.replace(reg, (a: string, b: number) => {
|
||||
return params[b]
|
||||
})
|
||||
}
|
||||
return words
|
||||
}
|
||||
}
|
||||
|
||||
export default i18n
|
||||
|
||||
@@ -18,7 +18,11 @@ export const checkAccess: (access: AccessDataType, accessData: Map<string, strin
|
||||
if (accLevel === 'team') {
|
||||
accessSet = new Set(Array.from(accessSet).concat(accessData?.get('team') || []))
|
||||
}
|
||||
return accessSet!.size > 0 ? hasIntersection(neededBackendAccessArr, accessSet) : false
|
||||
if (!accessSet!.size) {
|
||||
return false
|
||||
}
|
||||
const hasAccess = hasIntersection(neededBackendAccessArr, accessSet)
|
||||
return hasAccess
|
||||
}
|
||||
|
||||
const hasIntersection = (arr1: string[], set1: Set<string>) => {
|
||||
|
||||
@@ -151,15 +151,15 @@ function App() {
|
||||
form={{ validateMessages }}
|
||||
>
|
||||
<PluginEventHubProvider>
|
||||
<AppAntd className="h-full" message={{ maxCount: 1 }}>
|
||||
<PluginSlotHubProvider>
|
||||
<GlobalProvider>
|
||||
<GlobalProvider>
|
||||
<AppAntd className="h-full" message={{ maxCount: 1 }}>
|
||||
<PluginSlotHubProvider>
|
||||
<BreadcrumbProvider>
|
||||
<RenderRoutes />
|
||||
</BreadcrumbProvider>
|
||||
</GlobalProvider>
|
||||
</PluginSlotHubProvider>
|
||||
</AppAntd>
|
||||
</PluginSlotHubProvider>
|
||||
</AppAntd>
|
||||
</GlobalProvider>
|
||||
</PluginEventHubProvider>
|
||||
</ConfigProvider>
|
||||
</StyleProvider>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
import { $t } from '@common/locales'
|
||||
import { useI18n } from '@common/locales'
|
||||
import { Tabs } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
@@ -10,6 +10,7 @@ import { AiSettingProvider } from './contexts/AiSettingContext'
|
||||
const CONTENT_STYLE = { height: 'calc(-300px + 100vh)' } as const
|
||||
|
||||
const AiSettingContent = () => {
|
||||
const $t = useI18n()
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const [activeKey, setActiveKey] = useState(searchParams.get('status') === 'unconfigure' ? 'config' : 'flow')
|
||||
|
||||
|
||||
@@ -65,6 +65,11 @@ export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) =
|
||||
{defaultLlm}
|
||||
</div>
|
||||
</div>
|
||||
{status !== 'enabled' && alternativeModel && (
|
||||
<div className="mt-1 text-sm text-gray-500">
|
||||
{$t('关联 API 已转用')} {alternativeModel.name}/{alternativeModel.defaultLlm}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{status !== 'enabled' && alternativeModel && (
|
||||
<div className="ml-4 mt-1 text-sm text-gray-500">
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
import InsidePage from "@common/components/aoplatform/InsidePage";
|
||||
import { $t } from "@common/locales";
|
||||
import ServiceCategory from "./ServiceCategory";
|
||||
import ApiRequestSetting from "./ApiRequestSetting";
|
||||
import { Row, Col } from "antd";
|
||||
import InsidePage from '@common/components/aoplatform/InsidePage'
|
||||
import { useI18n } from '@common/locales'
|
||||
import { Col, Row } from 'antd'
|
||||
import ApiRequestSetting from './ApiRequestSetting'
|
||||
import ServiceCategory from './ServiceCategory'
|
||||
|
||||
export default function CommonPage(){
|
||||
return (
|
||||
<InsidePage
|
||||
pageTitle={$t('常规设置')}
|
||||
showBorder={false}
|
||||
contentClassName="pr-PAGE_INSIDE_X"
|
||||
scrollPage={false}
|
||||
>
|
||||
<Row className="mb-btnybase" >
|
||||
<Col >
|
||||
<span className="font-bold mr-[13px]">
|
||||
{$t('API 请求设置')}
|
||||
</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ApiRequestSetting />
|
||||
<Row className="mb-btnybase mt-[40px]">
|
||||
<Col >
|
||||
<span className="font-bold mr-[13px]">
|
||||
{$t('服务分类')}
|
||||
</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ServiceCategory />
|
||||
</InsidePage>
|
||||
)
|
||||
}
|
||||
export default function CommonPage() {
|
||||
const $t = useI18n()
|
||||
|
||||
return (
|
||||
<InsidePage pageTitle={$t('常规设置')} showBorder={false} contentClassName="pr-PAGE_INSIDE_X" scrollPage={false}>
|
||||
<Row className="mb-btnybase">
|
||||
<Col>
|
||||
<span className="font-bold mr-[13px]">{$t('API 请求设置')}</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ApiRequestSetting />
|
||||
<Row className="mb-btnybase mt-[40px]">
|
||||
<Col>
|
||||
<span className="font-bold mr-[13px]">{$t('服务分类')}</span>
|
||||
</Col>
|
||||
</Row>
|
||||
<ServiceCategory />
|
||||
</InsidePage>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function ServiceCategory() {
|
||||
const { accessData } = useGlobalContext()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
const onDrop: TreeProps['onDrop'] = info => {
|
||||
const onDrop: TreeProps['onDrop'] = (info) => {
|
||||
const dropKey = info.node.key
|
||||
const dragKey = info.dragNode.key
|
||||
const dropPos = info.node.pos.split('-')
|
||||
@@ -59,7 +59,7 @@ export default function ServiceCategory() {
|
||||
|
||||
if (!info.dropToGap) {
|
||||
// Drop on the content
|
||||
loop(data, dropKey, item => {
|
||||
loop(data, dropKey, (item) => {
|
||||
item.children = item.children || []
|
||||
// where to insert. New item was inserted to the start of the array in this example, but can be anywhere
|
||||
item.children.unshift(dragObj)
|
||||
@@ -129,9 +129,9 @@ export default function ServiceCategory() {
|
||||
const treeData = useMemo(() => {
|
||||
setExpandedKeys([])
|
||||
const loop = (data: CategorizesType[]): DataNode[] =>
|
||||
data?.map(item => {
|
||||
data?.map((item) => {
|
||||
if (item.children) {
|
||||
setExpandedKeys(prev => [...prev, item.id])
|
||||
setExpandedKeys((prev) => [...prev, item.id])
|
||||
return {
|
||||
title: (
|
||||
<TreeWithMore stopClick={false} dropdownMenu={dropdownMenu(item as CategorizesType)}>
|
||||
@@ -169,40 +169,22 @@ export default function ServiceCategory() {
|
||||
return !checkAccess(permission, accessData)
|
||||
}
|
||||
|
||||
const openModal = (
|
||||
type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete',
|
||||
entity?: CategorizesType
|
||||
) => {
|
||||
const openModal = (type: 'addCate' | 'addChildCate' | 'renameCate' | 'delete', entity?: CategorizesType) => {
|
||||
let title: string = ''
|
||||
let content: string | React.ReactNode = ''
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addCate': {
|
||||
title = $t('添加分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig WithPermission={WithPermission} ref={addRef} type={type} />
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={addRef} type={type} />
|
||||
break
|
||||
}
|
||||
case 'addChildCate':
|
||||
title = $t('添加子分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig
|
||||
WithPermission={WithPermission}
|
||||
ref={addChildRef}
|
||||
type={type}
|
||||
entity={entity}
|
||||
/>
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={addChildRef} type={type} entity={entity} />
|
||||
break
|
||||
case 'renameCate':
|
||||
title = $t('重命名分类')
|
||||
content = (
|
||||
<ServiceHubCategoryConfig
|
||||
WithPermission={WithPermission}
|
||||
ref={renameRef}
|
||||
type={type}
|
||||
entity={entity}
|
||||
/>
|
||||
)
|
||||
content = <ServiceHubCategoryConfig ref={renameRef} type={type} entity={entity} />
|
||||
break
|
||||
case 'delete':
|
||||
title = $t('删除')
|
||||
@@ -215,19 +197,19 @@ export default function ServiceCategory() {
|
||||
onOk: () => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
return addRef.current?.save().then(res => {
|
||||
return addRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'addChildCate':
|
||||
return addChildRef.current?.save().then(res => {
|
||||
return addChildRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'renameCate':
|
||||
return renameRef.current?.save().then(res => {
|
||||
return renameRef.current?.save().then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
case 'delete':
|
||||
return deleteCate(entity!).then(res => {
|
||||
return deleteCate(entity!).then((res) => {
|
||||
if (res === true) getCategoryList()
|
||||
})
|
||||
}
|
||||
@@ -249,7 +231,7 @@ export default function ServiceCategory() {
|
||||
method: 'DELETE',
|
||||
eoParams: { catalogue: entity.id }
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
@@ -259,14 +241,14 @@ export default function ServiceCategory() {
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
|
||||
const sortCategories = (newData: CategorizesType[]) => {
|
||||
setLoading(true)
|
||||
fetchData<BasicResponse<null>>('catalogue/sort', { method: 'PUT', eoBody: newData })
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
getCategoryList()
|
||||
@@ -288,7 +270,7 @@ export default function ServiceCategory() {
|
||||
fetchData<BasicResponse<{ catalogues: CategorizesType[]; tags: EntityItem[] }>>('catalogues', {
|
||||
method: 'GET'
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
const { code, data, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
setGData(data.catalogues)
|
||||
@@ -308,11 +290,7 @@ export default function ServiceCategory() {
|
||||
|
||||
return (
|
||||
<div className="border border-solid border-BORDER p-[20px] rounded-[10px] ">
|
||||
<Spin
|
||||
indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />}
|
||||
spinning={loading}
|
||||
className=""
|
||||
>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} spinning={loading} className="">
|
||||
<Tree
|
||||
showIcon
|
||||
draggable
|
||||
|
||||
@@ -10,132 +10,131 @@ import {
|
||||
import { App, Form, Input } from 'antd'
|
||||
import { forwardRef, useEffect, useImperativeHandle } from 'react'
|
||||
|
||||
export const ServiceHubCategoryConfig = forwardRef<
|
||||
ServiceHubCategoryConfigHandle,
|
||||
ServiceHubCategoryConfigProps
|
||||
>((props, ref) => {
|
||||
const { message } = App.useApp()
|
||||
const [form] = Form.useForm()
|
||||
const { type, entity } = props
|
||||
const { fetchData } = useFetch()
|
||||
export const ServiceHubCategoryConfig = forwardRef<ServiceHubCategoryConfigHandle, ServiceHubCategoryConfigProps>(
|
||||
(props, ref) => {
|
||||
const { message } = App.useApp()
|
||||
const [form] = Form.useForm()
|
||||
const { type, entity } = props
|
||||
const { fetchData } = useFetch()
|
||||
|
||||
const save: () => Promise<boolean | string> = () => {
|
||||
const url: string = 'catalogue'
|
||||
let method: string
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addChildCate':
|
||||
method = 'POST'
|
||||
break
|
||||
case 'renameCate':
|
||||
method = 'PUT'
|
||||
break
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url || !method) {
|
||||
reject($t(RESPONSE_TIPS.error))
|
||||
return
|
||||
const save: () => Promise<boolean | string> = () => {
|
||||
const url: string = 'catalogue'
|
||||
let method: string
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
case 'addChildCate':
|
||||
method = 'POST'
|
||||
break
|
||||
case 'renameCate':
|
||||
method = 'PUT'
|
||||
break
|
||||
}
|
||||
form
|
||||
.validateFields()
|
||||
.then(value => {
|
||||
fetchData<BasicResponse<null>>(url, {
|
||||
method,
|
||||
eoBody: value,
|
||||
eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) }
|
||||
})
|
||||
.then(response => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!url || !method) {
|
||||
reject($t(RESPONSE_TIPS.error))
|
||||
return
|
||||
}
|
||||
form
|
||||
.validateFields()
|
||||
.then((value) => {
|
||||
fetchData<BasicResponse<null>>(url, {
|
||||
method,
|
||||
eoBody: value,
|
||||
eoParams: { ...(type === 'renameCate' ? { catalogue: value.id } : undefined) }
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
})
|
||||
.catch(errorInfo => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
save
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
form.setFieldsValue({})
|
||||
break
|
||||
case 'addChildCate':
|
||||
form.setFieldsValue({ parent: entity!.id })
|
||||
break
|
||||
case 'renameCate':
|
||||
form.setFieldsValue(entity)
|
||||
break
|
||||
.then((response) => {
|
||||
const { code, msg } = response
|
||||
if (code === STATUS_CODE.SUCCESS) {
|
||||
message.success(msg || $t(RESPONSE_TIPS.success))
|
||||
resolve(true)
|
||||
} else {
|
||||
message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
reject(msg || $t(RESPONSE_TIPS.error))
|
||||
}
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
.catch((errorInfo) => reject(errorInfo))
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WithPermission
|
||||
access={
|
||||
type === 'addCate'
|
||||
? 'system.api_market.service_classification.add'
|
||||
: 'system.api_market.service_classification.edit'
|
||||
useImperativeHandle(ref, () => ({
|
||||
save
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
switch (type) {
|
||||
case 'addCate':
|
||||
form.setFieldsValue({})
|
||||
break
|
||||
case 'addChildCate':
|
||||
form.setFieldsValue({ parent: entity!.id })
|
||||
break
|
||||
case 'renameCate':
|
||||
form.setFieldsValue(entity)
|
||||
break
|
||||
}
|
||||
>
|
||||
<Form
|
||||
layout="vertical"
|
||||
scrollToFirstError
|
||||
labelAlign="left"
|
||||
form={form}
|
||||
className="mx-auto"
|
||||
name="serviceHubCategoryConfig"
|
||||
autoComplete="off"
|
||||
>
|
||||
{type === 'renameCate' && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('ID')}
|
||||
name="id"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
)}
|
||||
{(type === 'addCate' || type === 'renameCate') && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
)}
|
||||
}, [])
|
||||
|
||||
{type === 'addChildCate' && (
|
||||
<>
|
||||
return (
|
||||
<WithPermission
|
||||
access={
|
||||
type === 'addCate'
|
||||
? 'system.api_market.service_classification.add'
|
||||
: 'system.api_market.service_classification.edit'
|
||||
}
|
||||
>
|
||||
<Form
|
||||
layout="vertical"
|
||||
scrollToFirstError
|
||||
labelAlign="left"
|
||||
form={form}
|
||||
className="mx-auto"
|
||||
name="serviceHubCategoryConfig"
|
||||
autoComplete="off"
|
||||
>
|
||||
{type === 'renameCate' && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('父分类 ID')}
|
||||
name="parent"
|
||||
label={$t('ID')}
|
||||
name="id"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
)}
|
||||
{(type === 'addCate' || type === 'renameCate') && (
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('子分类名称')}
|
||||
label={$t('分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</WithPermission>
|
||||
)
|
||||
})
|
||||
)}
|
||||
|
||||
{type === 'addChildCate' && (
|
||||
<>
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('父分类 ID')}
|
||||
name="parent"
|
||||
hidden
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item<ServiceHubCategoryConfigFieldType>
|
||||
label={$t('子分类名称')}
|
||||
name="name"
|
||||
rules={[{ required: true, whitespace: true }]}
|
||||
>
|
||||
<Input className="w-INPUT_NORMAL" placeholder={$t(PLACEHOLDER.input)} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</WithPermission>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
import { EntityItem } from '@common/const/type'
|
||||
import { SubscribeEnum, SubscribeFromEnum } from '@core/const/system/const'
|
||||
import WithPermission from '@common/components/aoplatform/WithPermission'
|
||||
import { DefaultOptionType } from 'antd/es/select'
|
||||
|
||||
export type ServiceBasicInfoType = {
|
||||
app: EntityItem
|
||||
@@ -37,7 +36,6 @@ export type ServiceHubCategoryConfigFieldType = {
|
||||
export type ServiceHubCategoryConfigProps = {
|
||||
type: 'addCate' | 'addChildCate' | 'renameCate'
|
||||
entity?: { [k: string]: unknown }
|
||||
WithPermission: typeof WithPermission
|
||||
}
|
||||
|
||||
export type ServiceHubCategoryConfigHandle = {
|
||||
|
||||
@@ -5,7 +5,7 @@ import WithPermission from '@common/components/aoplatform/WithPermission'
|
||||
import { BasicResponse, DATA_SHOW_TYPE_OPTIONS, RESPONSE_TIPS, STATUS_CODE } from '@common/const/const'
|
||||
import { SimpleTeamItem } from '@common/const/type'
|
||||
import { useBreadcrumb } from '@common/contexts/BreadcrumbContext'
|
||||
import { GlobalProvider, useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { useGlobalContext } from '@common/contexts/GlobalStateContext'
|
||||
import { useFetch } from '@common/hooks/http'
|
||||
import { $t } from '@common/locales'
|
||||
import { RouterParams } from '@core/components/aoplatform/RenderRoutes'
|
||||
@@ -158,28 +158,8 @@ export default function ServiceHubManagement() {
|
||||
switch (type) {
|
||||
case 'add':
|
||||
title = $t('添加消费者')
|
||||
content = (
|
||||
<GlobalProvider>
|
||||
<ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} />
|
||||
</GlobalProvider>
|
||||
)
|
||||
content = <ManagementConfig ref={addManagementRef} dataShowType={dataShowType} type={type} teamId={teamId!} />
|
||||
break
|
||||
// case 'edit':{
|
||||
// title='配置 Open Api'
|
||||
// message.loading('正在加载数据')
|
||||
// const {code,data,msg} = await fetchData<BasicResponse<{app:ManagementConfigFieldType}>>('external-app',{method:'GET',eoParams:{id:entity!.id}})
|
||||
// message.destroy()
|
||||
// if(code === STATUS_CODE.SUCCESS){
|
||||
// content=<ManagementConfig ref={editManagementRef} type={type} entity={data.app}/>
|
||||
// }else{
|
||||
// message.error(msg || $t(RESPONSE_TIPS.error))
|
||||
// return
|
||||
// }
|
||||
// break;}
|
||||
// case 'delete':
|
||||
// title='删除'
|
||||
// content='该数据删除后将无法找回,请确认是否删除?'
|
||||
// break;
|
||||
}
|
||||
|
||||
modal.confirm({
|
||||
|
||||
@@ -161,7 +161,7 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string)
|
||||
labels = r.Labels
|
||||
}
|
||||
|
||||
return &Router{
|
||||
router := &Router{
|
||||
BasicInfo: &BasicInfo{
|
||||
ID: fmt.Sprintf("%s@router", r.ID),
|
||||
Name: r.ID,
|
||||
@@ -174,13 +174,16 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string)
|
||||
Method: r.Methods,
|
||||
Location: r.Path,
|
||||
Rules: rules,
|
||||
Service: fmt.Sprintf("%s@service", r.Service),
|
||||
Plugins: plugin,
|
||||
Retry: r.Retry,
|
||||
TimeOut: r.Timeout,
|
||||
Labels: labels,
|
||||
Protocols: []string{"http", "https"},
|
||||
}
|
||||
if r.Service != "" {
|
||||
router.Service = fmt.Sprintf("%s@service", r.Service)
|
||||
}
|
||||
return router
|
||||
}
|
||||
|
||||
// formatProxyPath 格式化转发路径上,用于转发重写插件正则替换 比如 请求路径 /path/{A}/{B} 原转发路径:/path/{B} 格式化后 新转发路径: /path/$2
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
b: "subscription_service:#{application}"
|
||||
response:
|
||||
status_code: 403
|
||||
content_typ: "text/plan"
|
||||
content_type: "text/plan"
|
||||
charset: "utf-8"
|
||||
body: "Forbidden"
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ const (
|
||||
ProfessionStrategy = "strategy"
|
||||
ProfessionService = "service"
|
||||
ProfessionAIProvider = "ai-provider"
|
||||
ProfessionAIResource = "ai-resource"
|
||||
)
|
||||
|
||||
func RegisterDynamicResourceDriver(key string, worker Worker) {
|
||||
@@ -61,6 +62,14 @@ var dynamicResourceMap = map[string]Worker{
|
||||
Profession: ProfessionOutput,
|
||||
Driver: "loki",
|
||||
},
|
||||
"ai-provider": {
|
||||
Profession: ProfessionAIResource,
|
||||
Driver: "ai-provider",
|
||||
},
|
||||
"ai-key": {
|
||||
Profession: ProfessionAIResource,
|
||||
Driver: "ai-key",
|
||||
},
|
||||
}
|
||||
|
||||
type Worker struct {
|
||||
|
||||
@@ -7,12 +7,14 @@ go 1.21
|
||||
require (
|
||||
github.com/eolinker/ap-account v1.0.15
|
||||
github.com/eolinker/eosc v0.18.3
|
||||
github.com/eolinker/go-common v1.1.1
|
||||
github.com/eolinker/go-common v1.1.4
|
||||
github.com/gabriel-vasile/mimetype v1.4.4
|
||||
github.com/getkin/kin-openapi v0.127.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
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/urfave/cli v1.22.16
|
||||
golang.org/x/crypto v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -37,8 +39,8 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.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
|
||||
@@ -81,4 +83,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
|
||||
|
||||
@@ -32,8 +32,8 @@ 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/eolinker/go-common v1.1.4 h1:U5AtQMr3RCudgeV6jcX5TemUGrTz8WqLu//KrZm3BzA=
|
||||
github.com/eolinker/go-common v1.1.4/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=
|
||||
@@ -64,6 +64,9 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -107,6 +110,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
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/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
_ "github.com/APIParkLab/APIPark/frontend"
|
||||
_ "github.com/APIParkLab/APIPark/gateway/apinto"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/core"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/openapi"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/permit"
|
||||
_ "github.com/APIParkLab/APIPark/plugins/publish_flow"
|
||||
_ "github.com/APIParkLab/APIPark/resources/locale"
|
||||
|
||||
@@ -5,7 +5,7 @@ type CreateAPI struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout int `json:"timeout"`
|
||||
@@ -33,7 +33,7 @@ type EditAPI struct {
|
||||
Name *string `json:"name"`
|
||||
Path *string `json:"path"`
|
||||
Description *string `json:"description"`
|
||||
Disable *bool `json:"disable"`
|
||||
Disable *bool `json:"disabled"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout *int `json:"timeout"`
|
||||
|
||||
@@ -9,7 +9,7 @@ type API struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout int `json:"timeout"`
|
||||
@@ -21,7 +21,7 @@ type APIItem struct {
|
||||
Name string `json:"name"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
Creator auto.Label `json:"creator" aolabel:"user"`
|
||||
Updater auto.Label `json:"updater" aolabel:"user"`
|
||||
CreateTime auto.TimeLabel `json:"create_time"`
|
||||
|
||||
@@ -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"
|
||||
@@ -110,10 +112,12 @@ func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_a
|
||||
Name: input.Name,
|
||||
Service: serviceId,
|
||||
Path: input.Path,
|
||||
Disable: input.Disable,
|
||||
Description: input.Description,
|
||||
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 +152,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,7 +170,9 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
|
||||
Timeout: input.Timeout,
|
||||
Retry: input.Retry,
|
||||
Model: modelId,
|
||||
Provider: providerId,
|
||||
AdditionalConfig: &apiInfo.AdditionalConfig,
|
||||
Disable: input.Disable,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -304,3 +312,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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ package ai_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type IAPIModule interface {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package ai_key_dto
|
||||
|
||||
var (
|
||||
KeyNormal KeyStatus = "normal"
|
||||
KeyExceed KeyStatus = "exceeded"
|
||||
KeyExpired KeyStatus = "expired"
|
||||
KeyDisable KeyStatus = "disabled"
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -0,0 +1,447 @@
|
||||
package ai_key
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/cluster"
|
||||
"github.com/eolinker/eosc/log"
|
||||
|
||||
"github.com/APIParkLab/APIPark/gateway"
|
||||
|
||||
"github.com/eolinker/go-common/utils"
|
||||
|
||||
"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:""`
|
||||
clusterService cluster.IClusterService `autowired:""`
|
||||
transaction store.ITransaction `autowired:""`
|
||||
}
|
||||
|
||||
func newKey(key *ai_key.Key) *gateway.DynamicRelease {
|
||||
|
||||
return &gateway.DynamicRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: fmt.Sprintf("%s-%s", key.Provider, key.ID),
|
||||
Description: key.Name,
|
||||
Resource: "ai-key",
|
||||
Version: time.Now().Format("20060102150405"),
|
||||
MatchLabels: map[string]string{
|
||||
"module": "ai-key",
|
||||
},
|
||||
},
|
||||
Attr: map[string]interface{}{
|
||||
"expired": key.ExpireTime,
|
||||
"config": key.Config,
|
||||
"provider": key.Provider,
|
||||
"priority": key.Priority,
|
||||
"disabled": key.Status == 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
p.URI()
|
||||
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()
|
||||
}
|
||||
|
||||
err = 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,
|
||||
})
|
||||
|
||||
info, _ := i.aiKeyService.Get(ctx, input.Id)
|
||||
releases := []*gateway.DynamicRelease{newKey(info)}
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlKeyModule) syncGateway(ctx context.Context, clusterId string, releases []*gateway.DynamicRelease, online bool) error {
|
||||
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 {
|
||||
err = dynamicClient.Offline(ctx, releaseInfo)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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(*input.Config, info.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()
|
||||
}
|
||||
|
||||
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
Name: input.Name,
|
||||
Config: input.Config,
|
||||
ExpireTime: input.ExpireTime,
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if status == ai_key_dto.KeyNormal.Int() {
|
||||
info, err = i.aiKeyService.Get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
releases := []*gateway.DynamicRelease{newKey(info)}
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
err = i.aiKeyService.Delete(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: fmt.Sprintf("%s-%s", providerId, id),
|
||||
Resource: "ai-key",
|
||||
},
|
||||
Attr: nil,
|
||||
},
|
||||
}, false)
|
||||
})
|
||||
}
|
||||
|
||||
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, statuses []string) ([]*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)
|
||||
}
|
||||
}
|
||||
w := map[string]interface{}{
|
||||
"provider": providerId,
|
||||
}
|
||||
hasExpired := true
|
||||
if len(statuses) > 0 {
|
||||
hasExpired = false
|
||||
statusConditions := make([]int, 0, len(statuses))
|
||||
for _, s := range statuses {
|
||||
status := ai_key_dto.KeyStatus(s)
|
||||
if status == ai_key_dto.KeyExpired {
|
||||
hasExpired = true
|
||||
}
|
||||
statusConditions = append(statusConditions, status.Int())
|
||||
}
|
||||
w["status"] = statusConditions
|
||||
}
|
||||
var list []*ai_key.Key
|
||||
var total int64
|
||||
if !hasExpired {
|
||||
if keyword != "" {
|
||||
list, err = i.aiKeyService.Search(ctx, keyword, w, "sort asc")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, 0, nil
|
||||
}
|
||||
uuids := utils.SliceToSlice(list, func(key *ai_key.Key) string {
|
||||
return key.ID
|
||||
})
|
||||
w["uuid"] = uuids
|
||||
}
|
||||
list, total, err = i.aiKeyService.SearchUnExpiredByPage(ctx, w, page, pageSize, "sort asc")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
} else {
|
||||
list, total, err = i.aiKeyService.SearchByPage(ctx, keyword, w, 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 {
|
||||
status := ai_key_dto.KeyDisable.Int()
|
||||
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
releases := []*gateway.DynamicRelease{{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: id,
|
||||
Resource: "ai-key",
|
||||
},
|
||||
Attr: nil,
|
||||
}}
|
||||
return i.syncGateway(ctx, providerId, releases, false)
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
status := ai_key_dto.KeyNormal.Int()
|
||||
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err = i.aiKeyService.Get(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
releases := []*gateway.DynamicRelease{newKey(info)}
|
||||
return i.syncGateway(ctx, providerId, releases, true)
|
||||
}
|
||||
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
|
||||
}
|
||||
list, err := i.aiKeyService.KeysByProvider(ctx, providerId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
releases := make([]*gateway.DynamicRelease, 0, len(list))
|
||||
for _, info := range list {
|
||||
releases = append(releases, newKey(info))
|
||||
}
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
|
||||
})
|
||||
}
|
||||
@@ -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, statuses []string) ([]*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))
|
||||
})
|
||||
}
|
||||
@@ -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 1
|
||||
case ProviderDisabled:
|
||||
return 0
|
||||
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 ProviderDisabled
|
||||
case 1:
|
||||
return ProviderEnabled
|
||||
default:
|
||||
return ProviderEnabled
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,10 @@ type UpdateLLM struct {
|
||||
type UpdateConfig struct {
|
||||
DefaultLLM string `json:"default_llm"`
|
||||
Config string `json:"config"`
|
||||
Priority *int `json:"priority"`
|
||||
Enable *bool `json:"enable"`
|
||||
}
|
||||
|
||||
type Sort struct {
|
||||
Providers []string `json:"providers"`
|
||||
}
|
||||
|
||||
+81
-20
@@ -1,33 +1,71 @@
|
||||
package ai_dto
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"github.com/eolinker/go-common/auto"
|
||||
)
|
||||
|
||||
type SimpleProvider struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DefaultConfig string `json:"default_config"`
|
||||
Logo string `json:"logo"`
|
||||
GetAPIKeyUrl string `json:"get_apikey_url"`
|
||||
}
|
||||
|
||||
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:"default_llm"`
|
||||
DefaultLLMConfig string `json:"-"`
|
||||
Priority int `json:"priority"`
|
||||
Status ProviderStatus `json:"status"`
|
||||
Configured bool `json:"configured"`
|
||||
}
|
||||
|
||||
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 []*KeyStatus `json:"keys"`
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
|
||||
type KeyStatus struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Priority int `json:"-"`
|
||||
}
|
||||
|
||||
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:"-"`
|
||||
}
|
||||
|
||||
type SimpleProviderItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Logo string `json:"logo"`
|
||||
Configured bool `json:"configured"`
|
||||
DefaultLLM string `json:"default_llm"`
|
||||
Sort int `json:"-"`
|
||||
}
|
||||
|
||||
type SimpleProviderItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Logo string `json:"logo"`
|
||||
Configured bool `json:"configured"`
|
||||
DefaultConfig string `json:"default_config"`
|
||||
Status ProviderStatus `json:"status"`
|
||||
Model *BasicInfo `json:"model,omitempty"`
|
||||
Priority int `json:"-"`
|
||||
}
|
||||
|
||||
type BackupProvider struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Model *BasicInfo `json:"model,omitempty"`
|
||||
}
|
||||
|
||||
type LLMItem struct {
|
||||
@@ -36,3 +74,26 @@ type LLMItem struct {
|
||||
Config string `json:"config"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type APIItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Service auto.Label `json:"service" aolabel:"service"`
|
||||
Team auto.Label `json:"team" aolabel:"team"`
|
||||
Method string `json:"method"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Model auto.Label `json:"model"`
|
||||
UpdateTime auto.TimeLabel `json:"update_time"`
|
||||
UseToken int `json:"use_token"`
|
||||
Disable bool `json:"disable"`
|
||||
}
|
||||
|
||||
type Condition struct {
|
||||
Models []*BasicInfo `json:"models"`
|
||||
Services []*BasicInfo `json:"services"`
|
||||
}
|
||||
|
||||
type BasicInfo struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
+536
-155
@@ -2,12 +2,22 @@ package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/service"
|
||||
|
||||
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"
|
||||
@@ -19,24 +29,24 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func newAIUpstream(provider string, uri model_runtime.IProviderURI) *gateway.DynamicRelease {
|
||||
func newKey(key *ai_key.Key) *gateway.DynamicRelease {
|
||||
|
||||
return &gateway.DynamicRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: provider,
|
||||
Description: fmt.Sprintf("auto create by ai provider %s", provider),
|
||||
Resource: "service",
|
||||
ID: fmt.Sprintf("%s-%s", key.Provider, key.ID),
|
||||
Description: key.Name,
|
||||
Resource: "ai-key",
|
||||
Version: time.Now().Format("20060102150405"),
|
||||
MatchLabels: map[string]string{
|
||||
"module": "service",
|
||||
"module": "ai-key",
|
||||
},
|
||||
},
|
||||
Attr: map[string]interface{}{
|
||||
"driver": "http",
|
||||
"balance": "round-robin",
|
||||
"nodes": []string{fmt.Sprintf("%s weight=100", uri.Host())},
|
||||
"pass_host": "node",
|
||||
"scheme": uri.Scheme(),
|
||||
"timeout": 300000,
|
||||
"expired": key.ExpireTime,
|
||||
"config": key.Config,
|
||||
"provider": key.Provider,
|
||||
"priority": key.Priority,
|
||||
"disabled": key.Status == 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -46,9 +56,192 @@ 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) SimpleProvider(ctx context.Context, id string) (*ai_dto.SimpleProvider, error) {
|
||||
p, has := model_runtime.GetProvider(id)
|
||||
if !has {
|
||||
return nil, fmt.Errorf("ai provider not found")
|
||||
}
|
||||
return &ai_dto.SimpleProvider{
|
||||
Id: p.ID(),
|
||||
Name: p.Name(),
|
||||
Logo: p.Logo(),
|
||||
DefaultConfig: p.DefaultConfig(),
|
||||
GetAPIKeyUrl: p.HelpUrl(),
|
||||
}, 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
|
||||
}
|
||||
|
||||
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) {
|
||||
// 获取已配置的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 {
|
||||
// 检查是否有默认Key
|
||||
_, err = i.aiKeyService.DefaultKey(ctx, l.Id)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil, err
|
||||
}
|
||||
err = i.aiKeyService.Create(ctx, &ai_key.Create{
|
||||
ID: l.Id,
|
||||
Name: l.Name,
|
||||
Config: l.Config,
|
||||
Provider: l.Id,
|
||||
Priority: 1,
|
||||
Status: 1,
|
||||
ExpireTime: 0,
|
||||
UseToken: 0,
|
||||
Default: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("create default key error:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
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([]*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,
|
||||
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 *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
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error) {
|
||||
list, err := i.providerService.List(ctx)
|
||||
if err != nil {
|
||||
@@ -62,19 +255,95 @@ func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.Simp
|
||||
items := make([]*ai_dto.SimpleProviderItem, 0, len(providers))
|
||||
for _, v := range providers {
|
||||
item := &ai_dto.SimpleProviderItem{
|
||||
Id: v.ID(),
|
||||
Name: v.Name(),
|
||||
Logo: v.Logo(),
|
||||
Id: v.ID(),
|
||||
Name: v.Name(),
|
||||
Logo: v.Logo(),
|
||||
DefaultConfig: v.DefaultConfig(),
|
||||
Status: ai_dto.ProviderDisabled,
|
||||
}
|
||||
if _, has := providerMap[v.ID()]; has {
|
||||
if info, has := providerMap[v.ID()]; has {
|
||||
item.Configured = true
|
||||
item.Status = ai_dto.ToProviderStatus(info.Status)
|
||||
item.Priority = info.Priority
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
if items[i].Priority != items[j].Priority {
|
||||
if items[i].Priority == 0 {
|
||||
return false
|
||||
}
|
||||
if items[j].Priority == 0 {
|
||||
return true
|
||||
}
|
||||
return items[i].Priority < items[j].Priority
|
||||
}
|
||||
return items[i].Name < items[j].Name
|
||||
})
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) Providers(ctx context.Context) ([]*ai_dto.ProviderItem, error) {
|
||||
func (i *imlProviderModule) SimpleConfiguredProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error) {
|
||||
list, err := i.providerService.List(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
items := make([]*ai_dto.SimpleProviderItem, 0, len(list))
|
||||
var backup *ai_dto.BackupProvider
|
||||
for _, l := range list {
|
||||
p, has := model_runtime.GetProvider(l.Id)
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
model, has := p.GetModel(l.DefaultLLM)
|
||||
if !has {
|
||||
model, has = p.DefaultModel(model_runtime.ModelTypeLLM)
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
}
|
||||
item := &ai_dto.SimpleProviderItem{
|
||||
Id: l.Id,
|
||||
Name: l.Name,
|
||||
Logo: p.Logo(),
|
||||
DefaultConfig: p.DefaultConfig(),
|
||||
Status: ai_dto.ToProviderStatus(l.Status),
|
||||
Priority: l.Priority,
|
||||
Configured: true,
|
||||
Model: &ai_dto.BasicInfo{
|
||||
Id: model.ID(),
|
||||
Name: model.ID(),
|
||||
},
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
sort.Slice(items, func(i, j int) bool {
|
||||
if items[i].Priority != items[j].Priority {
|
||||
if items[i].Priority == 0 {
|
||||
return false
|
||||
}
|
||||
if items[j].Priority == 0 {
|
||||
return true
|
||||
}
|
||||
return items[i].Priority < items[j].Priority
|
||||
}
|
||||
return items[i].Name < items[j].Name
|
||||
})
|
||||
for _, item := range items {
|
||||
if item.Status == ai_dto.ProviderEnabled {
|
||||
backup = &ai_dto.BackupProvider{
|
||||
Id: item.Id,
|
||||
Name: item.Name,
|
||||
Model: item.Model,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return items, backup, nil
|
||||
}
|
||||
|
||||
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 +354,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 +388,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 +411,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 +423,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 +434,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,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -196,78 +472,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
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) UpdateProviderStatus(ctx context.Context, id string, enable bool) error {
|
||||
driver, has := model_runtime.GetProvider(id)
|
||||
if !has {
|
||||
return fmt.Errorf("ai provider not found")
|
||||
}
|
||||
info, err := i.providerService.Get(ctx, id)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("ai provider not found")
|
||||
}
|
||||
|
||||
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
|
||||
err = i.providerService.Save(txCtx, id, &ai.SetProvider{
|
||||
Status: &enable,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if enable {
|
||||
cfg := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(info.Config), &cfg)
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal ai provider config error,id is %s,err is %v", info.Id, err)
|
||||
return err
|
||||
}
|
||||
cfg["driver"] = info.Id
|
||||
|
||||
return i.syncGateway(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: info.Id,
|
||||
Description: info.Name,
|
||||
Version: info.UpdateAt.Format("20060102150405"),
|
||||
MatchLabels: map[string]string{
|
||||
"module": "ai-provider",
|
||||
},
|
||||
},
|
||||
Attr: cfg,
|
||||
}, newAIUpstream(info.Id, driver.URI()),
|
||||
}, enable)
|
||||
} else {
|
||||
return i.syncGateway(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: info.Id,
|
||||
Resource: info.Id,
|
||||
},
|
||||
},
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: info.Id,
|
||||
Resource: "service",
|
||||
},
|
||||
},
|
||||
}, enable)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
return items, &ai_dto.ProviderItem{
|
||||
Id: info.Id,
|
||||
Name: info.Name,
|
||||
DefaultLLM: info.DefaultLLM,
|
||||
Logo: p.Logo(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, input *ai_dto.UpdateConfig) error {
|
||||
@@ -294,6 +511,10 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
Config: input.Config,
|
||||
}
|
||||
}
|
||||
model, has := p.GetModel(input.DefaultLLM)
|
||||
if !has {
|
||||
return fmt.Errorf("ai provider model not found")
|
||||
}
|
||||
err = p.Check(input.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -303,70 +524,120 @@ 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,
|
||||
DefaultLLM: &input.DefaultLLM,
|
||||
Config: &input.Config,
|
||||
})
|
||||
Priority: input.Priority,
|
||||
Status: &status,
|
||||
}
|
||||
_, 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,
|
||||
Priority: 1,
|
||||
})
|
||||
} 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
|
||||
}
|
||||
|
||||
if *pInfo.Status == 0 {
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: id,
|
||||
Resource: "ai-provider",
|
||||
},
|
||||
},
|
||||
}, false)
|
||||
}
|
||||
// 获取当前供应商所有Key信息
|
||||
defaultKey, err := i.aiKeyService.DefaultKey(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(input.Config), &cfg)
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal ai provider config error,id is %s,err is %v", id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
cfg["provider"] = info.Id
|
||||
cfg["model"] = info.DefaultLLM
|
||||
cfg["model_config"] = model.DefaultConfig()
|
||||
cfg["priority"] = info.Priority
|
||||
cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
|
||||
return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
|
||||
{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: id,
|
||||
Description: info.Name,
|
||||
Resource: id,
|
||||
Resource: "ai-provider",
|
||||
Version: info.UpdateAt.Format("20060102150405"),
|
||||
MatchLabels: map[string]string{
|
||||
"module": "ai-provider",
|
||||
},
|
||||
},
|
||||
Attr: cfg,
|
||||
}, newAIUpstream(id, p.URI()),
|
||||
}, newKey(defaultKey),
|
||||
}, true)
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) UpdateProviderDefaultLLM(ctx context.Context, id string, input *ai_dto.UpdateLLM) error {
|
||||
_, err := i.providerService.Get(ctx, id)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("ai provider not found")
|
||||
}
|
||||
return i.providerService.Save(ctx, id, &ai.SetProvider{
|
||||
DefaultLLM: &input.LLM,
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) getAiProviders(ctx context.Context) ([]*gateway.DynamicRelease, error) {
|
||||
list, err := i.providerService.List(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
providers := make([]*gateway.DynamicRelease, 0, len(list))
|
||||
for _, p := range list {
|
||||
cfg := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(p.Config), &cfg)
|
||||
if err != nil {
|
||||
log.Errorf("unmarshal ai provider config error,id is %s,err is %v", p.Id, err)
|
||||
continue
|
||||
for _, l := range list {
|
||||
// 获取当前供应商所有Key信息
|
||||
|
||||
driver, has := model_runtime.GetProvider(l.Id)
|
||||
if !has {
|
||||
return nil, fmt.Errorf("provider not found: %s", l.Id)
|
||||
}
|
||||
model, has := driver.GetModel(l.DefaultLLM)
|
||||
if !has {
|
||||
return nil, fmt.Errorf("model not found: %s", l.DefaultLLM)
|
||||
}
|
||||
cfg := make(map[string]interface{})
|
||||
cfg["provider"] = l.Id
|
||||
cfg["model"] = l.DefaultLLM
|
||||
cfg["model_config"] = model.DefaultConfig()
|
||||
cfg["priority"] = l.Priority
|
||||
providers = append(providers, &gateway.DynamicRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: p.Id,
|
||||
Description: p.Name,
|
||||
Resource: p.Id,
|
||||
Version: p.UpdateAt.Format("20060102150405"),
|
||||
ID: l.Id,
|
||||
Description: l.Name,
|
||||
Resource: "ai-provider",
|
||||
Version: l.UpdateAt.Format("20060102150405"),
|
||||
MatchLabels: map[string]string{
|
||||
"module": "ai-provider",
|
||||
},
|
||||
@@ -376,21 +647,15 @@ 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 {
|
||||
return err
|
||||
}
|
||||
serviceClient, err := clientDriver.Dynamic("service")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range providers {
|
||||
driver, has := model_runtime.GetProvider(p.ID)
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
client, err := clientDriver.Dynamic(p.ID)
|
||||
client, err := clientDriver.Dynamic(p.Resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -398,12 +663,6 @@ func (i *imlProviderModule) initGateway(ctx context.Context, clusterId string, c
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = serviceClient.Online(ctx, newAIUpstream(p.ID, driver.URI()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -429,7 +688,7 @@ func (i *imlProviderModule) syncGateway(ctx context.Context, clusterId string, r
|
||||
if online {
|
||||
err = dynamicClient.Online(ctx, releaseInfo)
|
||||
} else {
|
||||
err = dynamicClient.Offline(ctx, releaseInfo)
|
||||
dynamicClient.Offline(ctx, releaseInfo)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -438,3 +697,125 @@ func (i *imlProviderModule) syncGateway(ctx context.Context, clusterId string, r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ IAIAPIModule = (*imlAIApiModule)(nil)
|
||||
|
||||
type imlAIApiModule struct {
|
||||
aiAPIService ai_api.IAPIService `autowired:""`
|
||||
aiAPIUseService ai_api.IAPIUseService `autowired:""`
|
||||
serviceService service.IServiceService `autowired:""`
|
||||
}
|
||||
|
||||
func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId string, start int64, end int64, page int, pageSize int, sortCondition string, asc bool, models []string, serviceIds []string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error) {
|
||||
p, has := model_runtime.GetProvider(providerId)
|
||||
if !has {
|
||||
return nil, nil, 0, fmt.Errorf("ai provider not found")
|
||||
}
|
||||
sortRule := "desc"
|
||||
if asc {
|
||||
sortRule = "asc"
|
||||
}
|
||||
services, err := i.serviceService.ServiceListByKind(ctx, service.AIService)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
serviceItems := make([]*ai_dto.BasicInfo, 0, len(services))
|
||||
serviceTeamMap := make(map[string]string)
|
||||
for _, s := range services {
|
||||
serviceItems = append(serviceItems, &ai_dto.BasicInfo{
|
||||
Id: s.Id,
|
||||
Name: s.Name,
|
||||
})
|
||||
serviceTeamMap[s.Id] = s.Team
|
||||
|
||||
}
|
||||
|
||||
modelItems := utils.SliceToSlice(p.Models(), func(e model_runtime.IModel) *ai_dto.BasicInfo {
|
||||
return &ai_dto.BasicInfo{
|
||||
Id: e.ID(),
|
||||
Name: e.ID(),
|
||||
}
|
||||
})
|
||||
condition := &ai_dto.Condition{Services: serviceItems, Models: modelItems}
|
||||
switch sortCondition {
|
||||
default:
|
||||
w := map[string]interface{}{
|
||||
"provider": providerId,
|
||||
}
|
||||
if len(models) > 0 {
|
||||
w["model"] = models
|
||||
}
|
||||
if len(serviceIds) > 0 {
|
||||
w["service"] = serviceIds
|
||||
}
|
||||
apis, err := i.aiAPIService.Search(ctx, keyword, w, "update_at desc")
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
if len(apis) <= 0 {
|
||||
return nil, condition, 0, nil
|
||||
}
|
||||
apiMap := make(map[string]*ai_api.API)
|
||||
apiIds := make([]string, 0, len(apis))
|
||||
for _, a := range apis {
|
||||
apiMap[a.ID] = a
|
||||
apiIds = append(apiIds, a.ID)
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
results, _, err := i.aiAPIUseService.SumByApisPage(ctx, providerId, start, end, offset, pageSize, fmt.Sprintf("total_token %s", sortRule), apiIds...)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
apiItems := utils.SliceToSlice(results, func(e *ai_api.APIUse) *ai_dto.APIItem {
|
||||
info := apiMap[e.API]
|
||||
|
||||
delete(apiMap, e.API)
|
||||
return &ai_dto.APIItem{
|
||||
Id: e.API,
|
||||
Name: info.Name,
|
||||
Service: auto.UUID(info.Service),
|
||||
Team: auto.UUID(serviceTeamMap[info.Service]),
|
||||
Method: http.MethodPost,
|
||||
RequestPath: info.Path,
|
||||
Model: auto.Label{
|
||||
Id: info.Model,
|
||||
Name: info.Model,
|
||||
},
|
||||
UpdateTime: auto.TimeLabel(info.UpdateAt),
|
||||
UseToken: e.TotalToken,
|
||||
Disable: info.Disable,
|
||||
}
|
||||
})
|
||||
sortApis := make([]*ai_dto.APIItem, 0, len(apiMap))
|
||||
for _, a := range apiMap {
|
||||
sortApis = append(sortApis, &ai_dto.APIItem{
|
||||
Id: a.ID,
|
||||
Name: a.Name,
|
||||
Service: auto.UUID(a.Service),
|
||||
Team: auto.UUID(serviceTeamMap[a.Service]),
|
||||
Method: http.MethodPost,
|
||||
RequestPath: a.Path,
|
||||
Model: auto.Label{
|
||||
Id: a.Model,
|
||||
Name: a.Model,
|
||||
},
|
||||
UpdateTime: auto.TimeLabel(a.UpdateAt),
|
||||
UseToken: 0,
|
||||
Disable: a.Disable,
|
||||
})
|
||||
}
|
||||
// 排序
|
||||
sort.Slice(sortApis, func(i, j int) bool {
|
||||
return time.Time(sortApis[i].UpdateTime).After(time.Time(sortApis[j].UpdateTime))
|
||||
})
|
||||
size := pageSize - len(apiItems)
|
||||
for i := offset; i < offset+size && i < len(sortApis); i++ {
|
||||
apiItems = append(apiItems, sortApis[i])
|
||||
}
|
||||
|
||||
total := int64(len(apis))
|
||||
return apiItems, condition, total, nil
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -2,20 +2,29 @@ package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"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, *ai_dto.BackupProvider, 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
|
||||
//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
|
||||
//UpdateProviderDefaultLLM(ctx context.Context, id string, input *ai_dto.UpdateLLM) error
|
||||
Sort(ctx context.Context, input *ai_dto.Sort) 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)
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -24,4 +33,8 @@ func init() {
|
||||
gateway.RegisterInitHandleFunc(module.initGateway)
|
||||
return reflect.ValueOf(module)
|
||||
})
|
||||
|
||||
autowire.Auto[IAIAPIModule](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAIApiModule))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -176,19 +176,6 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal
|
||||
// 修改订阅表状态
|
||||
subscribers, err := i.subscribeService.ListByApplication(ctx, subscribeInfo.Service, appId)
|
||||
if err != nil {
|
||||
//if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
// return err
|
||||
//}
|
||||
//err = i.subscribeService.Create(ctx, &subscribe.CreateSubscribe{
|
||||
// Uuid: uuid.New().String(),
|
||||
// Service: subscribeInfo.Service,
|
||||
// Application: appId,
|
||||
// ApplyStatus: status,
|
||||
// From: subscribe.FromSubscribe,
|
||||
//})
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
return err
|
||||
} else {
|
||||
subscriberMap := utils.SliceToMap(subscribers, func(t *subscribe.Subscribe) string {
|
||||
|
||||
@@ -17,12 +17,12 @@ package cluster_dto
|
||||
|
||||
//type SaveMonitorConfig struct {
|
||||
// Driver string `json:"driver"`
|
||||
// Config map[string]interface{} `json:"config"`
|
||||
// DefaultConfig map[string]interface{} `json:"config"`
|
||||
//}
|
||||
|
||||
//type MonitorConfig struct {
|
||||
// Driver string `json:"driver"`
|
||||
// Config map[string]interface{} `json:"config"`
|
||||
// DefaultConfig map[string]interface{} `json:"config"`
|
||||
//}
|
||||
|
||||
//type MonitorPartition struct {
|
||||
|
||||
@@ -124,6 +124,7 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri
|
||||
Version: version,
|
||||
}
|
||||
apis := make([]*gateway.ApiRelease, 0, len(apiInfos))
|
||||
hasUpstream := len(upstreamCommitIds) > 0
|
||||
for _, a := range apiInfos {
|
||||
apiInfo := &gateway.ApiRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
@@ -133,7 +134,10 @@ func (m *imlPublishModule) getProjectRelease(ctx context.Context, projectID stri
|
||||
},
|
||||
Path: a.Path,
|
||||
Methods: a.Methods,
|
||||
Service: a.Upstream,
|
||||
//Service: a.Upstream,
|
||||
}
|
||||
if hasUpstream {
|
||||
apiInfo.Service = a.Upstream
|
||||
}
|
||||
proxy, ok := proxyCommitMap[a.UUID]
|
||||
if ok {
|
||||
|
||||
@@ -30,7 +30,7 @@ type Create struct {
|
||||
MatchRules []Match `json:"match"`
|
||||
Upstream string `json:"upstream"`
|
||||
Proxy *InputProxy `json:"proxy"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
}
|
||||
|
||||
type InputProxy struct {
|
||||
@@ -69,7 +69,7 @@ type Edit struct {
|
||||
Methods *[]string `json:"methods"`
|
||||
Protocols *[]string `json:"protocols"`
|
||||
MatchRules *[]Match `json:"match"`
|
||||
Disable *bool `json:"disable"`
|
||||
Disable *bool `json:"disabled"`
|
||||
Upstream *string `json:"upstream"`
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ type Item struct {
|
||||
Protocols []string `json:"protocols"`
|
||||
Path string `json:"request_path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
Creator auto.Label `json:"creator" aolabel:"user"`
|
||||
Updater auto.Label `json:"updater" aolabel:"user"`
|
||||
CreateTime auto.TimeLabel `json:"create_time"`
|
||||
@@ -34,7 +34,7 @@ type Detail struct {
|
||||
SimpleDetail
|
||||
Proxy *Proxy `json:"proxy"`
|
||||
Protocols []string `json:"protocols"`
|
||||
Disable bool `json:"disable"`
|
||||
Disable bool `json:"disabled"`
|
||||
//Doc map[string]interface{} `json:"doc"`
|
||||
}
|
||||
|
||||
|
||||
@@ -92,11 +92,6 @@ func (i *imlServiceModule) ExportAll(ctx context.Context) ([]*service_dto.Export
|
||||
serviceTagMap[st.Sid] = append(serviceTagMap[st.Sid], tagMap[st.Tid].Name)
|
||||
}
|
||||
|
||||
//docMap, err := i.serviceDocService.Map(ctx, serviceIds...)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
items := make([]*service_dto.ExportService, 0, len(services))
|
||||
for _, s := range services {
|
||||
info := &service_dto.ExportService{
|
||||
@@ -171,24 +166,6 @@ func (i *imlServiceModule) SearchMyServices(ctx context.Context, teamId string,
|
||||
return items, nil
|
||||
}
|
||||
|
||||
//func (i *imlServiceModule) SimpleAPPS(ctx context.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) {
|
||||
// w := make(map[string]interface{})
|
||||
// w["as_app"] = true
|
||||
// services, err := i.serviceService.SearchByDriver(ctx, keyword, w)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return utils.SliceToSlice(services, func(p *service.Service) *service_dto.SimpleServiceItem {
|
||||
// return &service_dto.SimpleServiceItem{
|
||||
// Id: p.Id,
|
||||
// Name: p.Name,
|
||||
// Description: p.Description,
|
||||
//
|
||||
// Team: auto.UUID(p.Team),
|
||||
// }
|
||||
// }), nil
|
||||
//}
|
||||
|
||||
func (i *imlServiceModule) Simple(ctx context.Context) ([]*service_dto.SimpleServiceItem, error) {
|
||||
w := make(map[string]interface{})
|
||||
w["as_server"] = true
|
||||
|
||||
+32
-19
@@ -218,33 +218,46 @@ func (i *imlSubscribeModule) AddSubscriber(ctx context.Context, serviceId string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = i.subscribeService.GetByServiceAndApplication(ctx, serviceId, input.Application)
|
||||
if err == nil {
|
||||
// 订阅方已存在
|
||||
return fmt.Errorf("subscriber is already exists")
|
||||
clusters, err := i.clusterService.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sub := &gateway.SubscribeRelease{
|
||||
Service: serviceId,
|
||||
Application: input.Application,
|
||||
Expired: "0",
|
||||
}
|
||||
clusters, err := i.clusterService.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
||||
err = i.subscribeService.Create(ctx, &subscribe.CreateSubscribe{
|
||||
Uuid: uuid.New().String(),
|
||||
Service: serviceId,
|
||||
Application: input.Application,
|
||||
ApplyStatus: subscribe.ApplyStatusSubscribe,
|
||||
From: subscribe.FromUser,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
info, err := i.subscribeService.GetByServiceAndApplication(ctx, serviceId, input.Application)
|
||||
if err == nil {
|
||||
// 订阅方已存在
|
||||
if info.ApplyStatus != subscribe.ApplyStatusSubscribe {
|
||||
// 更新订阅方状态
|
||||
status := subscribe.ApplyStatusSubscribe
|
||||
from := subscribe.FromUser
|
||||
err = i.subscribeService.Save(ctx, info.Id, &subscribe.UpdateSubscribe{
|
||||
ApplyStatus: &status,
|
||||
From: &from,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err = i.subscribeService.Create(ctx, &subscribe.CreateSubscribe{
|
||||
Uuid: uuid.New().String(),
|
||||
Service: serviceId,
|
||||
Application: input.Application,
|
||||
ApplyStatus: subscribe.ApplyStatusSubscribe,
|
||||
From: subscribe.FromUser,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range clusters {
|
||||
err = i.onlineSubscriber(ctx, c.Uuid, sub)
|
||||
if err != nil {
|
||||
|
||||
+20
-4
@@ -10,13 +10,29 @@ 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/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", []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/enable", []string{"context", "query:provider"}, nil, p.aiProviderController.isStop),
|
||||
//pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/disable", []string{"context", "query:provider"}, nil, p.aiProviderController.Disable),
|
||||
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.MethodPut, "/api/v1/ai/provider/default-llm", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderDefaultLLM),
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
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", "query:statuses"}, []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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 @@ type plugin struct {
|
||||
upstreamController upstream.IUpstreamController `autowired:""`
|
||||
routerController router.IRouterController `autowired:""`
|
||||
aiAPIController ai_api.IAPIController `autowired:""`
|
||||
aiStatisticController ai.IStatisticController `autowired:""`
|
||||
aiKeyController ai_key.IKeyController `autowired:""`
|
||||
apiDocController router.IAPIDocController `autowired:""`
|
||||
subscribeController subscribe.ISubscribeController `autowired:""`
|
||||
strategyController strategy.IStrategyController `autowired:""`
|
||||
@@ -111,6 +115,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()...)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
func (p *plugin) appAuthorizationApis() []pm3.Api {
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiWidthDoc(http.MethodPost, "/openapi/v1/app/authorization", []string{"context", "query:app", "body"}, []string{"authorization"}, p.authorizationController.AddAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodPut, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization", "body"}, []string{"authorization"}, p.authorizationController.EditAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodDelete, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization"}, nil, p.authorizationController.DeleteAuthorization),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorization", []string{"context", "query:app", "query:authorization"}, []string{"authorization"}, p.authorizationController.Info),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorizations", []string{"context", "query:app"}, []string{"authorizations"}, p.authorizationController.Authorizations),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/openapi/v1/app/authorization/details", []string{"context", "query:app", "query:authorization"}, []string{"details"}, p.authorizationController.Detail),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/eolinker/eosc/env"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultAPIKey = "37eb0ebf"
|
||||
openCheck = newOpenapiCheck()
|
||||
)
|
||||
|
||||
type openapiCheck struct {
|
||||
apikey string
|
||||
}
|
||||
|
||||
func newOpenapiCheck() *openapiCheck {
|
||||
apikey, has := env.GetEnv("API_KEY")
|
||||
if !has {
|
||||
apikey = defaultAPIKey
|
||||
}
|
||||
return &openapiCheck{apikey: apikey}
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Check(method string, path string) (bool, []gin.HandlerFunc) {
|
||||
if strings.HasPrefix(path, "/openapi/") {
|
||||
return true, []gin.HandlerFunc{o.Handler}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Sort() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (o *openapiCheck) Handler(ginCtx *gin.Context) {
|
||||
authorization := ginCtx.GetHeader("Authorization")
|
||||
if authorization == "" {
|
||||
ginCtx.AbortWithStatusJSON(403, gin.H{"code": -8, "msg": "invalid token", "success": "fail"})
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
pm3.Register("openapi", new(Driver))
|
||||
}
|
||||
|
||||
type Driver struct {
|
||||
}
|
||||
|
||||
func (d *Driver) Create() (pm3.IPlugin, error) {
|
||||
p := new(plugin)
|
||||
autowire.Autowired(p)
|
||||
return p, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package openapi
|
||||
|
||||
import (
|
||||
application_authorization "github.com/APIParkLab/APIPark/controller/application-authorization"
|
||||
"github.com/eolinker/go-common/pm3"
|
||||
)
|
||||
|
||||
var (
|
||||
_ pm3.IPlugin = (*plugin)(nil)
|
||||
_ pm3.IPluginMiddleware = (*plugin)(nil)
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
apis []pm3.Api
|
||||
authorizationController application_authorization.IAuthorizationController `autowired:""`
|
||||
}
|
||||
|
||||
func (p *plugin) Middlewares() []pm3.IMiddleware {
|
||||
return []pm3.IMiddleware{
|
||||
openCheck,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *plugin) APis() []pm3.Api {
|
||||
return p.apis
|
||||
}
|
||||
|
||||
func (p *plugin) Name() string {
|
||||
return "openapi"
|
||||
}
|
||||
func (p *plugin) OnComplete() {
|
||||
p.apis = p.appAuthorizationApis()
|
||||
}
|
||||
+1
-1
@@ -211,7 +211,7 @@ APIParkはApache 2.0ライセンスの下で提供されています。詳細に
|
||||
エンタープライズ機能や専門的な技術サポートについては、プリセールスの専門家に連絡し、個別デモ、カスタムソリューション、価格情報を入手してください。
|
||||
|
||||
- ウェブサイト: https://apipark.com
|
||||
- メール: dev@apipark.com
|
||||
- メール: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ APIPark 使用 Apache 2.0 许可证。更多详情请查看 LICENSE 文件。
|
||||
对于企业级功能和专业技术支持,请联系售前专家进行个性化演示、定制方案和获取报价。
|
||||
|
||||
- 网站: https://apipark.com
|
||||
- 电子邮件: dev@apipark.com
|
||||
- 电子邮件: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ APIPark 使用 Apache 2.0 授權條款。更多詳情請參閱 LICENSE 文件。
|
||||
如需企業級功能與專業技術支援,請聯絡我們的售前專家,獲取個性化演示、定制方案和報價。
|
||||
|
||||
- 網站: https://apipark.com
|
||||
- 電子郵件: dev@apipark.com
|
||||
- 電子郵件: contact@apipark.com
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -100,6 +100,36 @@ system:
|
||||
value: 'manager'
|
||||
dependents:
|
||||
- system.settings.ai_provider.view
|
||||
- name: ai key resource
|
||||
value: 'ai_key_resource'
|
||||
children:
|
||||
- name: view
|
||||
value: 'view'
|
||||
guest_allow: true
|
||||
- name: manager
|
||||
value: 'manager'
|
||||
dependents:
|
||||
- system.settings.ai_key_resource.view
|
||||
- name: ai api
|
||||
value: 'ai_api'
|
||||
children:
|
||||
- name: view
|
||||
value: 'view'
|
||||
guest_allow: true
|
||||
- name: manager
|
||||
value: 'manager'
|
||||
dependents:
|
||||
- system.settings.ai_api.view
|
||||
- name: ai log
|
||||
value: 'ai_log'
|
||||
children:
|
||||
- name: view
|
||||
value: 'view'
|
||||
guest_allow: true
|
||||
- name: manager
|
||||
value: 'manager'
|
||||
dependents:
|
||||
- system.settings.ai_log.view
|
||||
- name: ssl certificate
|
||||
cname: 证书
|
||||
value: 'ssl_certificate'
|
||||
|
||||
@@ -6,6 +6,12 @@ system:
|
||||
- system.api_portal.api_portal.view
|
||||
- system.settings.account.manager
|
||||
- system.settings.account.view
|
||||
- system.settings.ai_api.manager
|
||||
- system.settings.ai_api.view
|
||||
- system.settings.ai_key_resource.manager
|
||||
- system.settings.ai_key_resource.view
|
||||
- system.settings.ai_log.manager
|
||||
- system.settings.ai_log.view
|
||||
- system.settings.ai_provider.manager
|
||||
- system.settings.ai_provider.view
|
||||
- system.settings.api_gateway.manager
|
||||
@@ -39,6 +45,12 @@ system:
|
||||
permits:
|
||||
- system.analysis.run_view.view
|
||||
- system.api_portal.api_portal.view
|
||||
- system.settings.ai_api.manager
|
||||
- system.settings.ai_api.view
|
||||
- system.settings.ai_key_resource.manager
|
||||
- system.settings.ai_key_resource.view
|
||||
- system.settings.ai_log.manager
|
||||
- system.settings.ai_log.view
|
||||
- system.settings.ai_provider.manager
|
||||
- system.settings.ai_provider.view
|
||||
- system.settings.api_gateway.manager
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: v7
|
||||
version: v8
|
||||
sort:
|
||||
- "access_log"
|
||||
- "monitor"
|
||||
@@ -41,7 +41,7 @@ plugin:
|
||||
b: "subscription_service:#{application}"
|
||||
response:
|
||||
status_code: 403
|
||||
content_typ: "text/plan"
|
||||
content_type: "text/plan"
|
||||
charset: "utf-8"
|
||||
body: "Forbidden"
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ARG APP
|
||||
|
||||
ENV NSQ_ADDR=${APP}-nsq:4150
|
||||
ENV NSQ_TOPIC_PREFIX=${APP}
|
||||
|
||||
RUN mkdir -p /${APP}
|
||||
|
||||
COPY cmd/* /${APP}/
|
||||
|
||||
@@ -8,6 +8,8 @@ source ./scripts/common.sh
|
||||
OUTPUT_DIR=$(mkdir_output "$1")
|
||||
APP="apipark"
|
||||
OUTPUT_BIN="${OUTPUT_DIR}/${APP}"
|
||||
AI_EVENT_LISTEN_APP="apipark_ai_event_listen"
|
||||
AI_EVENT_LISTEN_BIN="${OUTPUT_DIR}/${AI_EVENT_LISTEN_APP}"
|
||||
VERSION=$(gen_version "$2")
|
||||
BUILD_TYPE=$3
|
||||
ARCH=$4
|
||||
@@ -104,6 +106,10 @@ build_backend() {
|
||||
# -ldflags="-w -s" means omit DWARF symbol table and the symbol table and debug information
|
||||
echo "GOOS=linux GOARCH=$ARCH CGO_ENABLED=0 go build $Tags -ldflags \"-w -s $flags\" -o \"${OUTPUT_BIN}\""
|
||||
GOOS=linux GOARCH=$ARCH CGO_ENABLED=0 go build ${Tags} -ldflags "-w -s $flags" -o ${OUTPUT_BIN}
|
||||
|
||||
echo "Build backend successfully..."
|
||||
echo "GOOS=linux GOARCH=$ARCH CGO_ENABLED=0 go build -ldflags \"-w -s\" -o \"${AI_EVENT_LISTEN_BIN}\" ./app/ai-event-handler"
|
||||
GOOS=linux GOARCH=$ARCH CGO_ENABLED=0 go build -ldflags "-w -s" -o "${AI_EVENT_LISTEN_BIN}" ./app/ai-event-handler
|
||||
return
|
||||
}
|
||||
|
||||
@@ -123,6 +129,9 @@ package() {
|
||||
|
||||
cp "${OUTPUT_BIN}" "${PACKAGE_DIR}"
|
||||
|
||||
echo "cp ${AI_EVENT_LISTEN_BIN} ${PACKAGE_DIR}"
|
||||
cp "${AI_EVENT_LISTEN_BIN}" "${PACKAGE_DIR}"
|
||||
|
||||
echo "tar -czvf ${PACKAGE_DIR}_linux_${ARCH}.tar.gz -C ${PACKAGE_DIR}/ ./"
|
||||
tar -czvf "${PACKAGE_DIR}_linux_${ARCH}.tar.gz" -C "${PACKAGE_DIR}/" "./"
|
||||
# rm -fr "${PACKAGE_DIR}"
|
||||
|
||||
@@ -17,7 +17,7 @@ source ./scripts/common.sh
|
||||
APP="apipark"
|
||||
|
||||
|
||||
mkdir -p scripts/cmd/ && cp cmd/${APP} scripts/cmd/
|
||||
mkdir -p scripts/cmd/ && cp cmd/${APP} scripts/cmd/ && cp cmd/apipark_ai_event_listen scripts/cmd/
|
||||
|
||||
VERSION=$(gen_version)
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ for s in ${arr[@]}
|
||||
do
|
||||
echo -e " - $s" >> config.yml
|
||||
done
|
||||
echo -e "nsq:" >> config.yml
|
||||
echo -e " addr: ${NSQ_ADDR}" >> config.yml
|
||||
echo -e " topic_prefix: ${NSQ_TOPIC_PREFIX}" >> config.yml
|
||||
echo -e "port: 8288" >> config.yml
|
||||
echo -e "error_log:" >> config.yml
|
||||
echo -e " dir: ${ERROR_DIR}" >> config.yml
|
||||
@@ -34,4 +37,6 @@ echo -e " log_expire: ${ERROR_EXPIRE}" >> config.yml
|
||||
echo -e " log_period: ${ERROR_PERIOD}" >> config.yml
|
||||
|
||||
cat config.yml
|
||||
./apipark
|
||||
nohup ./apipark >> run.log 2>&1 &
|
||||
nohup ./apipark_ai_event_listen >> run.log 2>&1 &
|
||||
tail -F run.log
|
||||
@@ -42,7 +42,8 @@ start() {
|
||||
cat "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 启动ai事件监听程序
|
||||
# nohup ./apipark_ai_event_listen >> "$LOG_FILE" 2>&1 &
|
||||
}
|
||||
|
||||
# 停止函数
|
||||
|
||||
+93
-2
@@ -1,10 +1,15 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/universally"
|
||||
"github.com/APIParkLab/APIPark/stores/api"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ IAPIService = (*imlAPIService)(nil)
|
||||
@@ -18,6 +23,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)
|
||||
@@ -26,7 +35,7 @@ func (i *imlAPIService) OnComplete() {
|
||||
}
|
||||
|
||||
func labelHandler(e *api.AiAPIInfo) []string {
|
||||
return []string{e.Name, e.Uuid}
|
||||
return []string{e.Name, e.Path}
|
||||
}
|
||||
func uniquestHandler(i *Create) []map[string]interface{} {
|
||||
return []map[string]interface{}{{"uuid": i.ID}}
|
||||
@@ -43,6 +52,8 @@ func createEntityHandler(i *Create) *api.AiAPIInfo {
|
||||
Timeout: i.Timeout,
|
||||
Retry: i.Retry,
|
||||
Model: i.Model,
|
||||
Provider: i.Provider,
|
||||
Disable: i.Disable,
|
||||
CreateAt: now,
|
||||
UpdateAt: now,
|
||||
AdditionalConfig: string(cfg),
|
||||
@@ -67,9 +78,89 @@ 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)
|
||||
}
|
||||
if i.Disable != nil {
|
||||
e.Disable = *i.Disable
|
||||
}
|
||||
if i.UseToken != nil {
|
||||
e.UseToken = *i.UseToken
|
||||
}
|
||||
e.UpdateAt = time.Now()
|
||||
}
|
||||
|
||||
var _ IAPIUseService = (*imlAPIUseService)(nil)
|
||||
|
||||
type imlAPIUseService struct {
|
||||
store api.IAiAPIUseStore `autowired:""`
|
||||
}
|
||||
|
||||
func (i *imlAPIUseService) Incr(ctx context.Context, incr *IncrAPIUse) error {
|
||||
info, err := i.store.First(ctx, map[string]interface{}{
|
||||
"api": incr.API,
|
||||
"service": incr.Service,
|
||||
"provider": incr.Provider,
|
||||
"model": incr.Model,
|
||||
"day": incr.Day,
|
||||
"hour": incr.Hour,
|
||||
"minute": incr.Minute,
|
||||
})
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
info = &api.AiAPIUse{
|
||||
API: incr.API,
|
||||
Service: incr.Service,
|
||||
Provider: incr.Provider,
|
||||
Model: incr.Model,
|
||||
Day: incr.Day,
|
||||
Hour: incr.Hour,
|
||||
Minute: incr.Minute,
|
||||
}
|
||||
}
|
||||
info.InputToken += incr.InputToken
|
||||
info.OutputToken += incr.OutputToken
|
||||
info.TotalToken += incr.TotalToken
|
||||
return i.store.Save(ctx, info)
|
||||
}
|
||||
|
||||
func (i *imlAPIUseService) SumByApisPage(ctx context.Context, providerId string, start, end int64, offset, limit int, order string, apiIds ...string) ([]*APIUse, int64, error) {
|
||||
list, total, err := i.store.SumByGroupPage(ctx, "api", order, offset, limit, "api,sum(input_token) as input_token,sum(output_token) as output_token,sum(total_token) as total_token", "provider = ? and api in (?) and minute >= ? and minute <= ?", providerId, apiIds, start, end)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
result := make([]*APIUse, 0, len(list))
|
||||
for _, v := range list {
|
||||
result = append(result, &APIUse{
|
||||
API: v.API,
|
||||
InputToken: v.InputToken,
|
||||
OutputToken: v.OutputToken,
|
||||
TotalToken: v.TotalToken,
|
||||
})
|
||||
}
|
||||
return result, total, nil
|
||||
}
|
||||
|
||||
func (i *imlAPIUseService) SumByApis(ctx context.Context, providerId string, start, end int64, apiIds ...string) ([]*APIUse, error) {
|
||||
//list, err := i.store.SumByGroup(ctx, "api", "api,sum(input_token) as input_token,sum(output_token) as output_token,sum(total_token) as total_token", "provider = ? and api in (?) and minute >= ? and minute <= ?", providerId, apiIds, start, end)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
//
|
||||
//return utils.SliceToSlice(list, func(v *api.AiAPIUse) *APIUse {
|
||||
// return &APIUse{
|
||||
// API: v.API,
|
||||
// InputToken: v.InputToken,
|
||||
// OutputToken: v.OutputToken,
|
||||
// TotalToken: v.TotalToken,
|
||||
// }
|
||||
//}), nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ type API struct {
|
||||
Timeout int
|
||||
Retry int
|
||||
Model string
|
||||
Provider string
|
||||
CreateAt time.Time
|
||||
UpdateAt time.Time
|
||||
UseToken int
|
||||
Creator string
|
||||
Updater string
|
||||
AdditionalConfig map[string]interface{}
|
||||
@@ -33,7 +35,9 @@ type Create struct {
|
||||
Timeout int
|
||||
Retry int
|
||||
Model string
|
||||
Provider string
|
||||
AdditionalConfig map[string]interface{}
|
||||
Disable bool
|
||||
}
|
||||
|
||||
type Edit struct {
|
||||
@@ -42,7 +46,10 @@ type Edit struct {
|
||||
Description *string
|
||||
Timeout *int
|
||||
Retry *int
|
||||
Provider *string
|
||||
Model *string
|
||||
Disable *bool
|
||||
UseToken *int
|
||||
AdditionalConfig *map[string]interface{}
|
||||
}
|
||||
|
||||
@@ -64,6 +71,28 @@ func FromEntity(e *api.AiAPIInfo) *API {
|
||||
UpdateAt: e.UpdateAt,
|
||||
Creator: e.Creator,
|
||||
Updater: e.Updater,
|
||||
Disable: e.Disable,
|
||||
UseToken: e.UseToken,
|
||||
AdditionalConfig: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
type APIUse struct {
|
||||
API string
|
||||
InputToken int
|
||||
OutputToken int
|
||||
TotalToken int
|
||||
}
|
||||
|
||||
type IncrAPIUse struct {
|
||||
API string
|
||||
Service string
|
||||
Provider string
|
||||
Model string
|
||||
Day int64
|
||||
Hour int64
|
||||
Minute int64
|
||||
InputToken int
|
||||
OutputToken int
|
||||
TotalToken int
|
||||
}
|
||||
|
||||
@@ -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,12 +13,20 @@ 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)
|
||||
type IAPIUseService interface {
|
||||
SumByApis(ctx context.Context, providerId string, start, end int64, apiIds ...string) ([]*APIUse, error)
|
||||
SumByApisPage(ctx context.Context, providerId string, start, end int64, page, pageSize int, order string, apiIds ...string) ([]*APIUse, int64, error)
|
||||
Incr(ctx context.Context, incr *IncrAPIUse) error
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IAPIService](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAPIService))
|
||||
})
|
||||
autowire.Auto[IAPIUseService](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAPIUseService))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
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) IncrUseToken(ctx context.Context, id string, useToken int) error {
|
||||
info, err := i.store.GetByUUID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info.UseToken += useToken
|
||||
return i.store.Save(ctx, info)
|
||||
}
|
||||
|
||||
func (i *imlAIKeyService) SearchUnExpiredByPage(ctx context.Context, w map[string]interface{}, page, pageSize int, order string) ([]*Key, int64, error) {
|
||||
sql := "(expire_time = 0 || expire_time > ?)"
|
||||
args := []interface{}{time.Now().Unix()}
|
||||
for k, v := range w {
|
||||
switch v.(type) {
|
||||
case []int:
|
||||
sql += fmt.Sprintf(" and `%s` in (?)", k)
|
||||
default:
|
||||
sql += fmt.Sprintf(" and `%s` = ?", k)
|
||||
}
|
||||
args = append(args, v)
|
||||
}
|
||||
list, total, err := i.store.ListPage(ctx, sql, page, pageSize, args, order)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var result []*Key
|
||||
for _, item := range list {
|
||||
result = append(result, FromEntity(item))
|
||||
}
|
||||
return result, total, nil
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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)
|
||||
SearchUnExpiredByPage(ctx context.Context, w map[string]interface{}, page, pageSize int, order string) ([]*Key, int64, error)
|
||||
IncrUseToken(ctx context.Context, id string, useToken int) error
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IKeyService](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAIKeyService))
|
||||
})
|
||||
}
|
||||
+25
-2
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -46,7 +46,7 @@ func (i *imlServiceService) ServiceListByKind(ctx context.Context, kind Kind, se
|
||||
w["uuid"] = serviceIds
|
||||
}
|
||||
w["as_server"] = true
|
||||
w["kind"] = kind
|
||||
w["kind"] = kind.Int()
|
||||
w["is_delete"] = false
|
||||
list, err := i.store.List(ctx, w)
|
||||
if err != nil {
|
||||
|
||||
@@ -200,6 +200,9 @@ func (i *imlSubscribeService) updateHandler(e *subscribe.Subscribe, t *UpdateSub
|
||||
if t.ApplyStatus != nil {
|
||||
e.ApplyStatus = *t.ApplyStatus
|
||||
}
|
||||
if t.From != nil {
|
||||
e.From = *t.From
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -2,14 +2,14 @@ package subscribe
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
|
||||
"github.com/APIParkLab/APIPark/stores/subscribe"
|
||||
)
|
||||
|
||||
type Subscribe struct {
|
||||
Id string
|
||||
Service string
|
||||
|
||||
|
||||
// 订阅方相关
|
||||
Application string
|
||||
From int
|
||||
@@ -28,6 +28,7 @@ type CreateSubscribe struct {
|
||||
}
|
||||
|
||||
type UpdateSubscribe struct {
|
||||
From *int
|
||||
ApplyStatus *int
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
@@ -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:异常;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:创建时间"`
|
||||
@@ -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
@@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,6 +31,14 @@ type imlAiAPIInfoStore struct {
|
||||
store.SearchStoreSoftDelete[AiAPIInfo]
|
||||
}
|
||||
|
||||
type IAiAPIUseStore interface {
|
||||
store.IStatisticsStore[AiAPIUse]
|
||||
}
|
||||
|
||||
type imlAiAPIUseStore struct {
|
||||
store.StatisticsStore[AiAPIUse]
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
autowire.Auto[IApiBaseStore](func() reflect.Value {
|
||||
@@ -48,4 +56,8 @@ func init() {
|
||||
autowire.Auto[IAiAPIInfoStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAiAPIInfoStore))
|
||||
})
|
||||
|
||||
autowire.Auto[IAiAPIUseStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAiAPIUseStore))
|
||||
})
|
||||
}
|
||||
|
||||
+26
-1
@@ -55,7 +55,7 @@ type Doc 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;"`
|
||||
Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"`
|
||||
Content string `gorm:"type:text;null;column:content;comment:文档内容"`
|
||||
Content string `gorm:"type:longtext;null;column:content;comment:文档内容"`
|
||||
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"`
|
||||
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
|
||||
APICount int64 `gorm:"type:int(11);not null;column:api_count;comment:接口数量"`
|
||||
@@ -79,11 +79,14 @@ 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"`
|
||||
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"`
|
||||
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:是"`
|
||||
}
|
||||
|
||||
@@ -94,3 +97,25 @@ func (a *AiAPIInfo) TableName() string {
|
||||
func (a *AiAPIInfo) IdValue() int64 {
|
||||
return a.Id
|
||||
}
|
||||
|
||||
type AiAPIUse struct {
|
||||
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
|
||||
API string `gorm:"size:36;not null;column:api;comment:API;index:api"`
|
||||
Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"`
|
||||
Provider string `gorm:"size:36;not null;column:provider;comment:提供者;index:provider"`
|
||||
Model string `gorm:"size:255;not null;column:model;comment:模型"`
|
||||
Day int64 `gorm:"type:int(11);not null;column:day;comment:当前日期"`
|
||||
Hour int64 `gorm:"type:int(11);not null;column:hour;comment:当前小时"`
|
||||
Minute int64 `gorm:"type:int(11);not null;column:minute;comment:当前分钟"`
|
||||
InputToken int `gorm:"type:int(11);not null;column:input_token;comment:输入token"`
|
||||
OutputToken int `gorm:"type:int(11);not null;column:output_token;comment:输出token"`
|
||||
TotalToken int `gorm:"type:int(11);not null;column:total_token;comment:总token"`
|
||||
}
|
||||
|
||||
func (a *AiAPIUse) TableName() string {
|
||||
return "ai_api_use"
|
||||
}
|
||||
|
||||
func (a *AiAPIUse) IdValue() int64 {
|
||||
return a.Id
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ type Commit[H any] struct {
|
||||
UUID string `gorm:"size:36;not null;column:uuid;comment:uuid;uniqueIndex:uuid;"`
|
||||
Target string `gorm:"column:target;type:varchar(36);NOT NULL;comment:目标id;index:target;"`
|
||||
Key string `gorm:"size:50;not null;column:key;comment:类型;index:key;"`
|
||||
Data *H `gorm:"type:text;not null;column:data;comment:数据;charset=utf8mb4;serializer:json"`
|
||||
Data *H `gorm:"type:longtext;not null;column:data;comment:数据;charset=utf8mb4;serializer:json"`
|
||||
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
|
||||
Operator string `gorm:"size:36;not null;column:operator;comment:操作人;index:operator;"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user