mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Merge pull request #162 from APIParkLab/feature/ai-balance
Feature/ai balance
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
|
||||
@@ -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,161 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"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"`
|
||||
TopicPrefix string `json:"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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 err
|
||||
}
|
||||
|
||||
// 将时间字符串转换为 time.Time
|
||||
timestamp, err := time.Parse(time.RFC3339, data.TimeISO8601)
|
||||
if err != nil {
|
||||
log.Printf("Failed to parse timestamp: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
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()
|
||||
keys := strings.Split(s.Key, "@")
|
||||
key := keys[0]
|
||||
err = h.aiKeyService.Save(ctx, key, &ai_key.Edit{
|
||||
Status: &status,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Failed to save AI key: %v", err)
|
||||
return err
|
||||
}
|
||||
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, "@")
|
||||
err = h.aiKeyService.IncrUseToken(ctx, keys[0], convertInt(data.AI.TotalToken))
|
||||
if err != nil {
|
||||
log.Printf("Failed to increment AI key token: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 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 err
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -13,7 +13,7 @@ type IKeyController interface {
|
||||
Edit(ctx *gin.Context, providerId string, id string, input *ai_key_dto.Edit) error
|
||||
Delete(ctx *gin.Context, providerId string, id string) error
|
||||
Get(ctx *gin.Context, providerId string, id string) (*ai_key_dto.Key, error)
|
||||
List(ctx *gin.Context, providerId string, keyword string, page string, pageSize string) ([]*ai_key_dto.Item, int64, error)
|
||||
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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai_key
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
ai_key "github.com/APIParkLab/APIPark/module/ai-key"
|
||||
@@ -38,7 +39,7 @@ func (i *imlAIKeyController) Get(ctx *gin.Context, providerId string, id string)
|
||||
return i.module.Get(ctx, providerId, id)
|
||||
}
|
||||
|
||||
func (i *imlAIKeyController) List(ctx *gin.Context, providerId string, keyword string, page string, pageSize string) ([]*ai_key_dto.Item, int64, error) {
|
||||
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 != "" {
|
||||
@@ -51,9 +52,16 @@ func (i *imlAIKeyController) List(ctx *gin.Context, providerId string, keyword s
|
||||
if pageSize != "" {
|
||||
return nil, 0, err
|
||||
}
|
||||
ps = 15
|
||||
ps = 20
|
||||
}
|
||||
return i.module.List(ctx, providerId, keyword, p, ps)
|
||||
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 {
|
||||
|
||||
@@ -3,17 +3,16 @@ package ai
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/go-common/auto"
|
||||
|
||||
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type IProviderController interface {
|
||||
ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, 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)
|
||||
@@ -25,7 +24,7 @@ type IProviderController interface {
|
||||
}
|
||||
|
||||
type IStatisticController interface {
|
||||
APIs(ctx *gin.Context, keyword string, providerId string, start string, end string, page string, pageSize string, sortCondition string, asc string) ([]*ai_dto.APIItem, int64, error)
|
||||
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() {
|
||||
|
||||
+29
-12
@@ -1,11 +1,11 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/APIParkLab/APIPark/module/ai"
|
||||
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
|
||||
"github.com/eolinker/go-common/auto"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ 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, *auto.Label, error) {
|
||||
func (i *imlProviderController) ConfiguredProviders(ctx *gin.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
|
||||
return i.module.ConfiguredProviders(ctx)
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ func (i *imlProviderController) SimpleProviders(ctx *gin.Context) ([]*ai_dto.Sim
|
||||
return i.module.SimpleProviders(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)
|
||||
}
|
||||
@@ -46,11 +50,13 @@ func (i *imlProviderController) LLMs(ctx *gin.Context, driver string) ([]*ai_dto
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -58,7 +64,8 @@ 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)
|
||||
@@ -67,21 +74,21 @@ 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) ([]*ai_dto.APIItem, int64, error) {
|
||||
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, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
e, err := strconv.ParseInt(end, 10, 64)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
p, err := strconv.Atoi(page)
|
||||
if err != nil {
|
||||
if page != "" {
|
||||
return nil, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
p = 1
|
||||
}
|
||||
@@ -89,9 +96,19 @@ func (i *imlStatisticController) APIs(ctx *gin.Context, keyword string, provider
|
||||
ps, err := strconv.Atoi(pageSize)
|
||||
if err != nil {
|
||||
if pageSize != "" {
|
||||
return nil, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
ps = 15
|
||||
ps = 20
|
||||
}
|
||||
return i.module.APIs(ctx, keyword, providerId, s, e, p, ps, sortCondition, asc == "true")
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.3
|
||||
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
|
||||
|
||||
@@ -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.3 h1:Chb0x6sj0hZKpIN6Qo9IMdi9pX2KLNUPmqcaAD8mmWs=
|
||||
github.com/eolinker/go-common v1.1.3/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,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"`
|
||||
@@ -30,19 +30,6 @@ type APIItem struct {
|
||||
Model ModelItem `json:"model"`
|
||||
}
|
||||
|
||||
type AIAPIItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Service auto.Label `json:"service" aolabel:"service"`
|
||||
Method string `json:"method"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Model ModelItem `json:"model"`
|
||||
Provider ProviderItem `json:"provider"`
|
||||
UpdateTime auto.TimeLabel `json:"update_time"`
|
||||
UseToken int64 `json:"use_token"`
|
||||
Disable bool `json:"disable"`
|
||||
}
|
||||
|
||||
type ModelItem struct {
|
||||
Id string `json:"id"`
|
||||
Logo string `json:"logo"`
|
||||
|
||||
+168
-25
@@ -6,6 +6,13 @@ import (
|
||||
"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"
|
||||
@@ -25,9 +32,32 @@ import (
|
||||
var _ IKeyModule = &imlKeyModule{}
|
||||
|
||||
type imlKeyModule struct {
|
||||
providerService ai.IProviderService `autowired:""`
|
||||
aiKeyService ai_key.IKeyService `autowired:""`
|
||||
transaction store.ITransaction `autowired:""`
|
||||
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 {
|
||||
@@ -39,6 +69,7 @@ func (i *imlKeyModule) Create(ctx context.Context, providerId string, input *ai_
|
||||
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)
|
||||
@@ -54,11 +85,9 @@ func (i *imlKeyModule) Create(ctx context.Context, providerId string, input *ai_
|
||||
status := ai_key_dto.KeyNormal.Int()
|
||||
if input.ExpireTime > 0 && time.Unix(int64(input.ExpireTime), 0).Before(time.Now()) {
|
||||
status = ai_key_dto.KeyExpired.Int()
|
||||
} else {
|
||||
// TODO: 发布Key到网关
|
||||
}
|
||||
|
||||
return i.aiKeyService.Create(ctx, &ai_key.Create{
|
||||
err = i.aiKeyService.Create(ctx, &ai_key.Create{
|
||||
ID: input.Id,
|
||||
Name: input.Name,
|
||||
Config: input.Config,
|
||||
@@ -67,9 +96,43 @@ func (i *imlKeyModule) Create(ctx context.Context, providerId string, input *ai_
|
||||
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 {
|
||||
@@ -89,7 +152,7 @@ func (i *imlKeyModule) Edit(ctx context.Context, providerId string, id string, i
|
||||
if err != nil {
|
||||
return fmt.Errorf("config check failed: %w", err)
|
||||
}
|
||||
cfg, err := p.GenConfig(info.Config, *input.Config)
|
||||
cfg, err := p.GenConfig(*input.Config, info.Config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("config gen failed: %w", err)
|
||||
}
|
||||
@@ -121,16 +184,25 @@ func (i *imlKeyModule) Edit(ctx context.Context, providerId string, id string, i
|
||||
// 停用、超额需要启用,所以维持原状态
|
||||
status = orgStatus.Int()
|
||||
}
|
||||
if status == ai_key_dto.KeyNormal.Int() {
|
||||
// TODO: 发布Key到网关
|
||||
}
|
||||
|
||||
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
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
|
||||
})
|
||||
|
||||
}
|
||||
@@ -165,8 +237,18 @@ func (i *imlKeyModule) Delete(ctx context.Context, providerId string, id string)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 操作网关下线Key
|
||||
return i.aiKeyService.Delete(ctx, id)
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -192,7 +274,7 @@ func (i *imlKeyModule) Get(ctx context.Context, providerId string, id string) (*
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (i *imlKeyModule) List(ctx context.Context, providerId string, keyword string, page, pageSize int) ([]*ai_key_dto.Item, int64, error) {
|
||||
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) {
|
||||
@@ -217,12 +299,49 @@ func (i *imlKeyModule) List(ctx context.Context, providerId string, keyword stri
|
||||
return nil, 0, fmt.Errorf("create default key failed: %w", err)
|
||||
}
|
||||
}
|
||||
list, total, err := i.aiKeyService.SearchByPage(ctx, keyword, map[string]interface{}{
|
||||
w := map[string]interface{}{
|
||||
"provider": providerId,
|
||||
}, page, pageSize, "sort asc")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
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)
|
||||
@@ -255,11 +374,21 @@ func (i *imlKeyModule) UpdateKeyStatus(ctx context.Context, providerId string, i
|
||||
}
|
||||
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
|
||||
if !enable {
|
||||
// TODO:下线Key
|
||||
status := ai_key_dto.KeyDisable.Int()
|
||||
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
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() {
|
||||
// 超额 或 停用状态,可启用
|
||||
@@ -270,11 +399,19 @@ func (i *imlKeyModule) UpdateKeyStatus(ctx context.Context, providerId string, i
|
||||
Status: &status,
|
||||
})
|
||||
}
|
||||
// TODO:发布Key到网关
|
||||
status := ai_key_dto.KeyNormal.Int()
|
||||
return i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
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
|
||||
})
|
||||
@@ -297,8 +434,14 @@ func (i *imlKeyModule) Sort(ctx context.Context, providerId string, input *ai_ke
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: 全量更新key配置到网关
|
||||
|
||||
return nil
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type IKeyModule interface {
|
||||
Edit(ctx context.Context, providerId string, id string, input *ai_key_dto.Edit) error
|
||||
Delete(ctx context.Context, providerId string, id string) error
|
||||
Get(ctx context.Context, providerId string, id string) (*ai_key_dto.Key, error)
|
||||
List(ctx context.Context, providerId string, keyword string, page, pageSize int) ([]*ai_key_dto.Item, int64, error)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ func (p ProviderStatus) Int() int {
|
||||
case ProviderAbnormal:
|
||||
return 2
|
||||
case ProviderEnabled:
|
||||
return 0
|
||||
case ProviderDisabled:
|
||||
return 1
|
||||
case ProviderDisabled:
|
||||
return 0
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
@@ -30,10 +30,10 @@ func ToProviderStatus(status int) ProviderStatus {
|
||||
case 2:
|
||||
return ProviderAbnormal
|
||||
case 0:
|
||||
return ProviderEnabled
|
||||
return ProviderDisabled
|
||||
case 1:
|
||||
return ProviderDisabled
|
||||
return ProviderEnabled
|
||||
default:
|
||||
return ProviderDisabled
|
||||
return ProviderEnabled
|
||||
}
|
||||
}
|
||||
|
||||
+37
-14
@@ -5,10 +5,11 @@ import (
|
||||
)
|
||||
|
||||
type SimpleProvider struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Logo string `json:"logo"`
|
||||
GetAPIKeyUrl string `json:"get_apikey_url"`
|
||||
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 {
|
||||
@@ -16,10 +17,11 @@ type Provider struct {
|
||||
Name string `json:"name"`
|
||||
Config string `json:"config"`
|
||||
GetAPIKeyUrl string `json:"get_apikey_url"`
|
||||
DefaultLLM string `json:"defaultLLM"`
|
||||
DefaultLLM string `json:"default_llm"`
|
||||
DefaultLLMConfig string `json:"-"`
|
||||
Priority int `json:"priority"`
|
||||
Status ProviderStatus `json:"status"`
|
||||
Configured bool `json:"configured"`
|
||||
}
|
||||
|
||||
type ConfiguredProviderItem struct {
|
||||
@@ -30,14 +32,15 @@ type ConfiguredProviderItem struct {
|
||||
Status ProviderStatus `json:"status"`
|
||||
APICount int64 `json:"api_count"`
|
||||
KeyCount int `json:"key_count"`
|
||||
KeyStatus []*KeyStatus `json:"key_status"`
|
||||
KeyStatus []*KeyStatus `json:"keys"`
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
|
||||
type KeyStatus struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Status string `json:"status"`
|
||||
Priority int `json:"-"`
|
||||
}
|
||||
|
||||
type ProviderItem struct {
|
||||
@@ -49,11 +52,20 @@ type ProviderItem struct {
|
||||
}
|
||||
|
||||
type SimpleProviderItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Logo string `json:"logo"`
|
||||
Configured bool `json:"configured"`
|
||||
Status ProviderStatus `json:"status"`
|
||||
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 {
|
||||
@@ -67,6 +79,7 @@ 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"`
|
||||
@@ -74,3 +87,13 @@ type APIItem struct {
|
||||
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"`
|
||||
}
|
||||
|
||||
+247
-149
@@ -2,13 +2,14 @@ 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"
|
||||
@@ -28,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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -66,10 +67,11 @@ func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_
|
||||
return nil, fmt.Errorf("ai provider not found")
|
||||
}
|
||||
return &ai_dto.SimpleProvider{
|
||||
Id: p.ID(),
|
||||
Name: p.Name(),
|
||||
Logo: p.Logo(),
|
||||
GetAPIKeyUrl: p.HelpUrl(),
|
||||
Id: p.ID(),
|
||||
Name: p.Name(),
|
||||
Logo: p.Logo(),
|
||||
DefaultConfig: p.DefaultConfig(),
|
||||
GetAPIKeyUrl: p.HelpUrl(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -82,8 +84,19 @@ func (i *imlProviderModule) Sort(ctx context.Context, input *ai_dto.Sort) error
|
||||
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 {
|
||||
_, has := providerMap[id]
|
||||
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
|
||||
}
|
||||
@@ -94,12 +107,43 @@ func (i *imlProviderModule) Sort(ctx context.Context, input *ai_dto.Sort) error
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
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, *auto.Label, error) {
|
||||
func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
|
||||
// 获取已配置的AI服务商
|
||||
list, err := i.providerService.List(ctx)
|
||||
if err != nil {
|
||||
@@ -151,11 +195,15 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
|
||||
status = ai_key_dto.KeyError
|
||||
}
|
||||
keysStatus = append(keysStatus, &ai_dto.KeyStatus{
|
||||
Id: k.ID,
|
||||
Name: k.Name,
|
||||
Status: status.String(),
|
||||
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,
|
||||
@@ -181,10 +229,10 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
|
||||
}
|
||||
return providers[i].Name < providers[j].Name
|
||||
})
|
||||
var backup *auto.Label
|
||||
var backup *ai_dto.BackupProvider
|
||||
for _, p := range providers {
|
||||
if p.Status == ai_dto.ProviderEnabled {
|
||||
backup = &auto.Label{
|
||||
backup = &ai_dto.BackupProvider{
|
||||
Id: p.Id,
|
||||
Name: p.Name,
|
||||
}
|
||||
@@ -207,20 +255,94 @@ 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(),
|
||||
Status: ai_dto.ProviderDisabled,
|
||||
Id: v.ID(),
|
||||
Name: v.Name(),
|
||||
Logo: v.Logo(),
|
||||
DefaultConfig: v.DefaultConfig(),
|
||||
Status: ai_dto.ProviderDisabled,
|
||||
}
|
||||
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) 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 {
|
||||
@@ -313,7 +435,8 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
|
||||
DefaultLLM: defaultLLM.ID(),
|
||||
DefaultLLMConfig: defaultLLM.DefaultConfig(),
|
||||
Priority: info.Priority,
|
||||
Status: ai_dto.ProviderEnabled,
|
||||
Status: ai_dto.ToProviderStatus(info.Status),
|
||||
Configured: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -364,72 +487,6 @@ func (i *imlProviderModule) LLMs(ctx context.Context, driver string) ([]*ai_dto.
|
||||
}, 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 {
|
||||
status := 0
|
||||
if enable {
|
||||
status = 1
|
||||
}
|
||||
err = i.providerService.Save(txCtx, id, &ai.SetProvider{
|
||||
Status: &status,
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, input *ai_dto.UpdateConfig) error {
|
||||
p, has := model_runtime.GetProvider(id)
|
||||
if !has {
|
||||
@@ -454,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
|
||||
@@ -486,6 +547,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
Status: 1,
|
||||
ExpireTime: 0,
|
||||
Default: true,
|
||||
Priority: 1,
|
||||
})
|
||||
} else {
|
||||
err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
|
||||
@@ -495,6 +557,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if input.Enable != nil {
|
||||
status = 0
|
||||
if *input.Enable {
|
||||
@@ -506,62 +569,73 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(input.Config), &cfg)
|
||||
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 {
|
||||
log.Errorf("unmarshal ai provider config error,id is %s,err is %v", id, err)
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := make(map[string]interface{})
|
||||
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",
|
||||
},
|
||||
@@ -577,16 +651,9 @@ func (i *imlProviderModule) initGateway(ctx context.Context, clusterId string, c
|
||||
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
|
||||
}
|
||||
@@ -594,12 +661,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
|
||||
@@ -625,7 +686,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
|
||||
@@ -638,26 +699,60 @@ func (i *imlProviderModule) syncGateway(ctx context.Context, clusterId string, r
|
||||
var _ IAIAPIModule = (*imlAIApiModule)(nil)
|
||||
|
||||
type imlAIApiModule struct {
|
||||
aiAPIService ai_api.IAPIService `autowired:""`
|
||||
aiAPIUseService ai_api.IAPIUseService `autowired:""`
|
||||
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) ([]*ai_dto.APIItem, int64, error) {
|
||||
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:
|
||||
apis, err := i.aiAPIService.Search(ctx, keyword, map[string]interface{}{
|
||||
w := map[string]interface{}{
|
||||
"provider": providerId,
|
||||
}, "update_at desc")
|
||||
}
|
||||
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, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
if len(apis) <= 0 {
|
||||
return nil, 0, nil
|
||||
return nil, condition, 0, nil
|
||||
}
|
||||
apiMap := make(map[string]*ai_api.API)
|
||||
apiIds := make([]string, 0, len(apis))
|
||||
@@ -668,7 +763,7 @@ func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId st
|
||||
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, 0, err
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
apiItems := utils.SliceToSlice(results, func(e *ai_api.APIUse) *ai_dto.APIItem {
|
||||
@@ -679,6 +774,7 @@ func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId st
|
||||
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{
|
||||
@@ -696,6 +792,7 @@ func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId st
|
||||
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{
|
||||
@@ -715,7 +812,8 @@ func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId st
|
||||
for i := offset; i < offset+size && i < len(sortApis); i++ {
|
||||
apiItems = append(apiItems, sortApis[i])
|
||||
}
|
||||
|
||||
total := int64(len(apis))
|
||||
return apiItems, total, nil
|
||||
return apiItems, condition, total, nil
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -4,28 +4,27 @@ import (
|
||||
"context"
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/go-common/auto"
|
||||
|
||||
"github.com/APIParkLab/APIPark/gateway"
|
||||
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
)
|
||||
|
||||
type IProviderModule interface {
|
||||
ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *auto.Label, 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) ([]*ai_dto.APIItem, int64, error)
|
||||
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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+3
-2
@@ -13,20 +13,21 @@ func (p *plugin) aiAPIs() []pm3.Api {
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/unconfigured", []string{"context"}, []string{"providers"}, p.aiProviderController.UnConfiguredProviders, access.SystemSettingsAiProviderView),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.ConfiguredProviders, access.SystemSettingsAiProviderView),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.SimpleConfiguredProviders),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/config", []string{"context", "query:provider"}, []string{"provider"}, p.aiProviderController.Provider, access.SystemSettingsAiProviderView),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/provider", []string{"context", "query:provider"}, []string{"provider"}, p.aiProviderController.SimpleProvider),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/provider/llms", []string{"context", "query:provider"}, []string{"llms", "provider"}, p.aiProviderController.LLMs),
|
||||
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/sort", []string{"context", "body"}, nil, p.aiProviderController.Sort),
|
||||
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/config", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderConfig, access.SystemSettingsAiProviderManager),
|
||||
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/apis", []string{"context", "query:keyword", "query:provider", "query:start", "query:end", "query:page", "query:page_size", "query:sort", "query:asc"}, []string{"apis", "total"}, p.aiStatisticController.APIs),
|
||||
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"}, []string{"keys", "total"}, p.aiKeyController.List),
|
||||
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),
|
||||
|
||||
@@ -8,6 +8,9 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ARG APP
|
||||
|
||||
ENV NSQ_ADDR=nsq:4150
|
||||
ENV NSQ_TOPIC_PREFIX=apipark
|
||||
|
||||
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}"
|
||||
|
||||
@@ -21,6 +21,9 @@ echo -e "redis:" >> config.yml
|
||||
echo -e " user_name: ${REDIS_USER_NAME}" >> config.yml
|
||||
echo -e " password: ${REDIS_PWD}" >> config.yml
|
||||
echo -e " addr: " >> config.yml
|
||||
echo -e "nsq:" >> config.yml
|
||||
echo -e " addr: ${NSQ_ADDR}" >> config.yml
|
||||
echo -e " topic: ${NSQ_TOPIC}" >> config.yml
|
||||
for s in ${arr[@]}
|
||||
do
|
||||
echo -e " - $s" >> 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 &
|
||||
}
|
||||
|
||||
# 停止函数
|
||||
|
||||
+50
-16
@@ -3,9 +3,10 @@ package ai_api
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/go-common/utils"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/universally"
|
||||
"github.com/APIParkLab/APIPark/stores/api"
|
||||
@@ -34,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}}
|
||||
@@ -87,7 +88,9 @@ func updateHandler(e *api.AiAPIInfo, i *Edit) {
|
||||
if i.Disable != nil {
|
||||
e.Disable = *i.Disable
|
||||
}
|
||||
|
||||
if i.UseToken != nil {
|
||||
e.UseToken = *i.UseToken
|
||||
}
|
||||
e.UpdateAt = time.Now()
|
||||
}
|
||||
|
||||
@@ -97,6 +100,36 @@ 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 {
|
||||
@@ -116,17 +149,18 @@ func (i *imlAPIUseService) SumByApisPage(ctx context.Context, providerId string,
|
||||
}
|
||||
|
||||
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
|
||||
//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
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type API struct {
|
||||
Provider string
|
||||
CreateAt time.Time
|
||||
UpdateAt time.Time
|
||||
UseToken int
|
||||
Creator string
|
||||
Updater string
|
||||
AdditionalConfig map[string]interface{}
|
||||
@@ -48,6 +49,7 @@ type Edit struct {
|
||||
Provider *string
|
||||
Model *string
|
||||
Disable *bool
|
||||
UseToken *int
|
||||
AdditionalConfig *map[string]interface{}
|
||||
}
|
||||
|
||||
@@ -70,6 +72,7 @@ func FromEntity(e *api.AiAPIInfo) *API {
|
||||
Creator: e.Creator,
|
||||
Updater: e.Updater,
|
||||
Disable: e.Disable,
|
||||
UseToken: e.UseToken,
|
||||
AdditionalConfig: cfg,
|
||||
}
|
||||
}
|
||||
@@ -80,3 +83,16 @@ type APIUse struct {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type IAPIService interface {
|
||||
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() {
|
||||
|
||||
+35
-1
@@ -23,6 +23,39 @@ type imlAIKeyService struct {
|
||||
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 {
|
||||
@@ -169,7 +202,7 @@ func (i *imlAIKeyService) OnComplete() {
|
||||
}
|
||||
|
||||
func labelHandler(e *ai.Key) []string {
|
||||
return []string{e.Name, e.Uuid}
|
||||
return []string{e.Name}
|
||||
}
|
||||
func uniquestHandler(i *Create) []map[string]interface{} {
|
||||
return []map[string]interface{}{{"uuid": i.ID}}
|
||||
@@ -206,5 +239,6 @@ func updateHandler(e *ai.Key, i *Edit) {
|
||||
if i.Priority != nil {
|
||||
e.Sort = *i.Priority
|
||||
}
|
||||
|
||||
e.UpdateAt = time.Now()
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ type IKeyService interface {
|
||||
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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -103,9 +103,10 @@ type AiAPIUse struct {
|
||||
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"`
|
||||
Day int `gorm:"type:int(11);not null;column:day;comment:当前日期"`
|
||||
Hour int `gorm:"type:int(11);not null;column:hour;comment:当前小时"`
|
||||
Minute int `gorm:"type:int(11);not null;column:minute;comment:当前分钟"`
|
||||
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"`
|
||||
|
||||
Reference in New Issue
Block a user