>('model/local/state', {
+ method: 'GET',
+ eoParams: {
+ model: record.id
+ }
+ })
+ .then((response) => {
+ if (response.code === STATUS_CODE.SUCCESS) {
+ updateStepItems(getCurrentStep(response.data?.state), `${response.data?.info?.current} / ${response.data?.info?.total}`, response.data?.state)
+ setScriptStr(response?.data?.info?.last_message || '')
+ } else {
+ message.error(response.msg || RESPONSE_TIPS.error)
+ }
+ })
+ .catch((error) => {
+ message.error(RESPONSE_TIPS.error)
+ })
+ }
+ useEffect(() => {
+ if (['deploying_error', 'error'].includes(record.state)) {
+ getLocalModelState()
+ } else {
+ fetchData(
+ 'model/local/deploy',
+ {
+ method: 'POST',
+ eoBody: { model: record.id, team: record.team?.id },
+ isStream: true,
+ callback: (cancel: () => void) => {
+ cancelCb?.(cancel)
+ },
+ handleStream: (chunk) => {
+ const parsedChunk = JSON.parse(chunk)
+ // 下载中
+ if (parsedChunk?.data?.state.includes('download')) {
+ updateStepItems(0, `${parsedChunk?.data?.info?.current} / ${parsedChunk?.data?.info?.total}`);
+ // 部署中
+ } else if (parsedChunk?.data?.state.includes('deploy')) {
+ updateStepItems(1);
+ // 初始化中
+ } else if (parsedChunk?.data?.state.includes('initializing')) {
+ updateStepItems(2);
+ // 完成
+ } else if (parsedChunk?.data?.state.includes('finish')) {
+ updateStepItems(4);
+ setTimeout(() => {
+ closeModal?.()
+ }, 500)
+ } else if (parsedChunk?.data?.state.includes('error')) {
+ updateFooter?.()
+ setStepItem((prevItems) =>
+ prevItems.map((item, index) => {
+ return { ...item, status: index === step.current ? 'error' : item.status }
+ })
+ )
+ }
+ setScriptStr(parsedChunk?.data?.message || '')
+ }
+ }
+ )
+ }
+ }, [])
+
+ return (
+ <>
+
+
+ {stepItem.map((item, index) => (
+
+ ))}
+
+
+
+ )
+ }
+ ]}
+ >
+ >
+ )
+}
diff --git a/frontend/packages/market/src/pages/serviceHub/integrate.tsx b/frontend/packages/market/src/pages/serviceHub/integrate.tsx
index 823f3be8..c674bb8c 100644
--- a/frontend/packages/market/src/pages/serviceHub/integrate.tsx
+++ b/frontend/packages/market/src/pages/serviceHub/integrate.tsx
@@ -68,12 +68,12 @@ const Integrate = ({ service }: { service: ServiceDetailType }) => {
{$t('可通过以下 URL 或 下载 Json 文件,导入 API 文档数据到 Agent 平台中。')}
-
+
{$t('复制 URL')}
- OR
+ {$t('或')}
{$t('下载 Json 文件')}
diff --git a/go.mod b/go.mod
index 0c475f61..ff13f2bb 100644
--- a/go.mod
+++ b/go.mod
@@ -1,23 +1,26 @@
module github.com/APIParkLab/APIPark
-go 1.21
+go 1.23.4
-//toolchain go1.21.1
+toolchain go1.23.6
require (
github.com/eolinker/ap-account v1.0.15
github.com/eolinker/eosc v0.18.3
github.com/eolinker/go-common v1.1.5
+ github.com/eolinker/go-common v1.1.5
github.com/gabriel-vasile/mimetype v1.4.4
github.com/getkin/kin-openapi v0.127.0
+ github.com/gin-contrib/gzip v1.0.1
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/ollama/ollama v0.5.8
github.com/stretchr/testify v1.9.0
github.com/urfave/cli v1.22.16
- golang.org/x/crypto v0.24.0
+ golang.org/x/crypto v0.31.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/gorm v1.25.5
)
@@ -42,7 +45,6 @@ require (
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/invopop/yaml v0.3.1 // indirect
@@ -60,7 +62,6 @@ require (
github.com/oapi-codegen/runtime v1.0.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
- github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/redis/go-redis/v9 v9.5.3 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
@@ -72,8 +73,9 @@ require (
go.uber.org/zap v1.23.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/net v0.26.0 // indirect
- golang.org/x/sys v0.22.0 // indirect
- golang.org/x/text v0.16.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
+ golang.org/x/term v0.27.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.5.2 // indirect
@@ -83,6 +85,7 @@ require (
// github.com/eolinker/eosc => ../../eolinker/eosc
//)
-//replace github.com/eolinker/ap-account => ../aoaccount
+//replace github.com/eolinker/ap-account => ../../eolinker/ap-account
+
//
//replace github.com/eolinker/go-common => ../../eolinker/go-common
diff --git a/go.sum b/go.sum
index bfec4ac8..922827d0 100644
--- a/go.sum
+++ b/go.sum
@@ -112,6 +112,8 @@ github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE=
github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY=
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
+github.com/ollama/ollama v0.5.8 h1:b2S6YdZ18/ntCsWzoy/HmB3BHGW4GX0Qp7RARrJtJXU=
+github.com/ollama/ollama v0.5.8/go.mod h1:ibdmDvb/TjKY1OArBWIazL3pd1DHTk8eG2MMjEkWhiI=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
@@ -158,16 +160,18 @@ go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
-golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
-golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
-golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/module/ai-api/dto/input.go b/module/ai-api/dto/input.go
index fe71acac..e559dd5f 100644
--- a/module/ai-api/dto/input.go
+++ b/module/ai-api/dto/input.go
@@ -27,6 +27,7 @@ type AiModel struct {
Id string `json:"id"`
Config string `json:"config"`
Provider string `json:"provider"`
+ Type string `json:"type"`
}
type EditAPI struct {
diff --git a/module/ai-api/dto/output.go b/module/ai-api/dto/output.go
index a00c6690..521d8979 100644
--- a/module/ai-api/dto/output.go
+++ b/module/ai-api/dto/output.go
@@ -4,6 +4,39 @@ import (
"github.com/eolinker/go-common/auto"
)
+type ModelType string
+
+const (
+ ModelTypeOnline ModelType = "online"
+ ModelTypeLocal ModelType = "local"
+)
+
+func (m ModelType) String() string {
+ return string(m)
+}
+
+func (m ModelType) Int() int {
+ switch m {
+ case ModelTypeOnline:
+ return 0
+ case ModelTypeLocal:
+ return 1
+ default:
+ return -1
+ }
+}
+
+func FromModelType(m int) ModelType {
+ switch m {
+ case 0:
+ return ModelTypeOnline
+ case 1:
+ return ModelTypeLocal
+ default:
+ return ""
+ }
+}
+
type API struct {
Id string `json:"id"`
Name string `json:"name"`
@@ -19,6 +52,7 @@ type API struct {
type APIItem struct {
Id string `json:"id"`
Name string `json:"name"`
+ ModelType ModelType `json:"model_type"`
RequestPath string `json:"request_path"`
Description string `json:"description"`
Disable bool `json:"disabled"`
diff --git a/module/ai-api/iml.go b/module/ai-api/iml.go
index 12994aa2..58b9d884 100644
--- a/module/ai-api/iml.go
+++ b/module/ai-api/iml.go
@@ -54,7 +54,7 @@ func (i *imlAPIModule) getAPIDoc(ctx context.Context, serviceId string) (*openap
return openapi3Loader.LoadFromData([]byte(doc.Content))
}
-func (i *imlAPIModule) updateAPIDoc(ctx context.Context, serviceId string, serviceName string, path string, summary string, description string, aiPrompt *ai_api_dto.AiPrompt) error {
+func (i *imlAPIModule) updateAPIDoc(ctx context.Context, serviceId, serviceName, orgPath, path, summary, description string, aiPrompt *ai_api_dto.AiPrompt) error {
doc, err := i.getAPIDoc(ctx, serviceId)
if err != nil {
return err
@@ -64,6 +64,10 @@ func (i *imlAPIModule) updateAPIDoc(ctx context.Context, serviceId string, servi
if aiPrompt != nil {
variables = aiPrompt.Variables
}
+ if doc.Paths != nil {
+ doc.Paths.Delete(orgPath)
+ }
+
doc.AddOperation(path, http.MethodPost, genOperation(summary, description, variables))
result, err := doc.MarshalJSON()
if err != nil {
@@ -103,10 +107,11 @@ func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_a
input.Id = uuid.New().String()
}
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
- err = i.updateAPIDoc(ctx, serviceId, info.Name, input.Path, input.Name, input.Description, input.AiPrompt)
+ err = i.updateAPIDoc(ctx, serviceId, info.Name, "", input.Path, input.Name, input.Description, input.AiPrompt)
if err != nil {
return err
}
+
return i.aiAPIService.Create(ctx, &ai_api.Create{
ID: input.Id,
Name: input.Name,
@@ -118,6 +123,7 @@ func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_a
Retry: input.Retry,
Model: input.AiModel.Id,
Provider: input.AiModel.Provider,
+ Type: ai_api_dto.ModelType(input.AiModel.Type).Int(),
AdditionalConfig: map[string]interface{}{
"ai_prompt": input.AiPrompt,
"ai_model": input.AiModel,
@@ -141,13 +147,14 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
if err != nil {
return err
}
+ orgPath := apiInfo.Path
if input.Path != nil {
apiInfo.Path = *input.Path
}
if input.Description != nil {
apiInfo.Description = *input.Description
}
- err = i.updateAPIDoc(ctx, serviceId, info.Name, apiInfo.Path, apiInfo.Name, apiInfo.Description, input.AiPrompt)
+ err = i.updateAPIDoc(ctx, serviceId, info.Name, orgPath, apiInfo.Path, apiInfo.Name, apiInfo.Description, input.AiPrompt)
if err != nil {
return err
}
@@ -163,6 +170,7 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
if input.AiModel != nil {
apiInfo.AdditionalConfig["ai_model"] = input.AiModel
}
+ typ := ai_api_dto.ModelType(input.AiModel.Type).Int()
return i.aiAPIService.Save(ctx, apiId, &ai_api.Edit{
Name: input.Name,
Path: input.Path,
@@ -171,6 +179,7 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string,
Retry: input.Retry,
Model: modelId,
Provider: providerId,
+ Type: &typ,
AdditionalConfig: &apiInfo.AdditionalConfig,
Disable: input.Disable,
})
diff --git a/module/ai-api/schema.go b/module/ai-api/schema.go
index caf72423..5c7c7c5c 100644
--- a/module/ai-api/schema.go
+++ b/module/ai-api/schema.go
@@ -13,12 +13,7 @@ func genOpenAPI3Template(title string, description string) *openapi3.T {
Description: description,
Version: "beta",
}
- //result.Tags = openapi3.Tags{
- // {
- // Name: title,
- // Description: description,
- // },
- //}
+
result.Components = components
result.Paths = new(openapi3.Paths)
return result
@@ -26,7 +21,6 @@ func genOpenAPI3Template(title string, description string) *openapi3.T {
func genOperation(summary string, description string, variables []*ai_api_dto.AiPromptVariable) *openapi3.Operation {
operation := openapi3.NewOperation()
- //operation.Parameters = genRequestParameters(variables)
operation.Summary = summary
operation.Description = description
operation.RequestBody = genRequestBody(variables)
@@ -78,9 +72,13 @@ func genResponse() *openapi3.ResponseRef {
func genRequestBodySchema(variables []*ai_api_dto.AiPromptVariable) *openapi3.Schema {
result := openapi3.NewObjectSchema()
- result.WithProperty("variables", genVariableSchema(variables))
+ if len(variables) > 0 {
+ result.WithProperty("variables", genVariableSchema(variables))
+ result.WithRequired([]string{"variables", "messages"})
+ }
+
result.WithPropertyRef("messages", messagesSchemaRef)
- result.WithRequired([]string{"variables", "messages"})
+
return result
}
@@ -136,10 +134,10 @@ func genMessageSchema() *openapi3.Schema {
result.Description = "Chat Message"
roleSchema := openapi3.NewStringSchema()
roleSchema.Description = "Role of the message sender"
- roleSchema.Example = "assistant"
+ roleSchema.Example = "user"
contentSchema := openapi3.NewStringSchema()
contentSchema.Description = "The message content"
- contentSchema.Example = "Hello, how can I help you?"
+ contentSchema.Example = "Hello, who are you?"
result.WithProperties(map[string]*openapi3.Schema{
"role": roleSchema,
"content": contentSchema,
diff --git a/module/ai-balance/dto/input.go b/module/ai-balance/dto/input.go
new file mode 100644
index 00000000..900a2323
--- /dev/null
+++ b/module/ai-balance/dto/input.go
@@ -0,0 +1,14 @@
+package ai_balance_dto
+
+type Create struct {
+ Id string `json:"id"`
+ Type string `json:"type"`
+ Provider string `json:"provider"`
+ Model string `json:"model"`
+}
+
+type Sort struct {
+ Origin string `json:"origin"`
+ Target string `json:"target"`
+ Sort string `json:"sort"`
+}
diff --git a/module/ai-balance/dto/output.go b/module/ai-balance/dto/output.go
new file mode 100644
index 00000000..c45d25ca
--- /dev/null
+++ b/module/ai-balance/dto/output.go
@@ -0,0 +1,81 @@
+package ai_balance_dto
+
+const (
+ ModelTypeOnline = "online"
+ ModelTypeLocal = "local"
+
+ StateNormal = "normal"
+ StateAbnormal = "abnormal"
+)
+
+type ModelType string
+
+func (m ModelType) String() string {
+ return string(m)
+}
+
+func (m ModelType) Int() int {
+ switch m {
+ case ModelTypeOnline:
+ return 0
+ case ModelTypeLocal:
+ return 1
+ default:
+ return -1
+ }
+}
+
+func ModelTypeFromInt(i int) ModelType {
+ switch i {
+ case 0:
+ return ModelTypeOnline
+ case 1:
+ return ModelTypeLocal
+ default:
+ return "unknown"
+ }
+}
+
+type ModelState string
+
+func (m ModelState) String() string {
+ return string(m)
+}
+
+func (m ModelState) Int() int {
+ switch m {
+ case StateNormal:
+ return 1
+ case StateAbnormal:
+ return 0
+ default:
+ return -1
+ }
+}
+
+func ModelStateFromInt(i int) ModelState {
+ switch i {
+ case 1:
+ return StateNormal
+ case 0:
+ return StateAbnormal
+ default:
+ return "unknown"
+ }
+}
+
+type Item struct {
+ Id string `json:"id"`
+ Priority int `json:"priority"`
+ Provider *BasicItem `json:"provider"`
+ Model *BasicItem `json:"model"`
+ Type ModelType `json:"type"`
+ State ModelState `json:"state"`
+ APICount int64 `json:"api_count"`
+ KeyCount int64 `json:"key_count"`
+}
+
+type BasicItem struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+}
diff --git a/module/ai-balance/iml.go b/module/ai-balance/iml.go
new file mode 100644
index 00000000..5ab66bc8
--- /dev/null
+++ b/module/ai-balance/iml.go
@@ -0,0 +1,329 @@
+package ai_balance
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sort"
+
+ "github.com/APIParkLab/APIPark/service/setting"
+
+ ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
+
+ model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
+
+ "gorm.io/gorm"
+
+ ai_key "github.com/APIParkLab/APIPark/service/ai-key"
+
+ ai_api "github.com/APIParkLab/APIPark/service/ai-api"
+
+ "github.com/google/uuid"
+
+ ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
+
+ "github.com/eolinker/go-common/store"
+
+ "github.com/APIParkLab/APIPark/gateway"
+
+ "github.com/APIParkLab/APIPark/service/cluster"
+
+ ai_balance_dto "github.com/APIParkLab/APIPark/module/ai-balance/dto"
+ "github.com/eolinker/eosc/log"
+)
+
+var _ IBalanceModule = (*imlBalanceModule)(nil)
+
+type imlBalanceModule struct {
+ clusterService cluster.IClusterService `autowired:""`
+ aiAPIService ai_api.IAPIService `autowired:""`
+ aiKeyService ai_key.IKeyService `autowired:""`
+ balanceService ai_balance.IBalanceService `autowired:""`
+ settingService setting.ISettingService `autowired:""`
+ transaction store.ITransaction `autowired:""`
+}
+
+func (i *imlBalanceModule) SyncLocalBalances(ctx context.Context, address string) error {
+ releases, err := i.getLocalBalances(ctx, address)
+ if err != nil {
+ return err
+ }
+ return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
+}
+
+func (i *imlBalanceModule) Create(ctx context.Context, input *ai_balance_dto.Create) error {
+ has, err := i.balanceService.Exist(ctx, input.Provider, input.Model)
+ if err != nil {
+ return err
+ }
+ if has {
+ return fmt.Errorf("model already exists")
+ }
+ priority, err := i.balanceService.MaxPriority(ctx)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+ priority = 0
+ }
+ if input.Id == "" {
+ input.Id = uuid.NewString()
+ }
+ providerName := ""
+ modelName := ""
+ base := ""
+ switch input.Type {
+ case ai_balance_dto.ModelTypeOnline:
+ p, has := model_runtime.GetProvider(input.Provider)
+ if !has {
+ return fmt.Errorf("provider not found")
+ }
+ providerName = p.Name()
+ modelName = input.Model
+ base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
+ case ai_balance_dto.ModelTypeLocal:
+ input.Provider = "ollama"
+ providerName = "Ollama"
+ modelName = input.Model
+ v, has := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if !has {
+ return fmt.Errorf("ollama address not found")
+ }
+ base = v
+ }
+
+ return i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ err = i.balanceService.Create(ctx, &ai_balance.Create{
+ Id: input.Id,
+ Priority: priority + 1,
+ Provider: input.Provider,
+ ProviderName: providerName,
+ Model: input.Model,
+ ModelName: modelName,
+ Type: ai_balance_dto.ModelType(input.Type).Int(),
+ })
+ if err != nil {
+ return err
+ }
+ item, err := i.balanceService.Get(ctx, input.Id)
+ if err != nil {
+ return err
+ }
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{newRelease(item, base)}, true)
+ })
+
+}
+
+func newRelease(item *ai_balance.Balance, base string) *gateway.DynamicRelease {
+
+ cfg := make(map[string]interface{})
+ cfg["provider"] = item.Provider
+ cfg["model"] = item.Model
+ cfg["model_config"] = ai_provider_local.OllamaConfig
+ cfg["base"] = base
+ cfg["priority"] = item.Priority
+ return &gateway.DynamicRelease{
+ BasicItem: &gateway.BasicItem{
+ ID: item.Id,
+ Description: item.ModelName,
+ Resource: "ai-provider",
+ Version: item.UpdateAt.Format("20060102150405"),
+ MatchLabels: map[string]string{
+ "module": "ai-provider",
+ },
+ },
+ Attr: cfg,
+ }
+}
+
+func (i *imlBalanceModule) Sort(ctx context.Context, input *ai_balance_dto.Sort) error {
+ var list []*ai_balance.Balance
+ var err error
+ switch input.Sort {
+ case "after":
+ list, err = i.balanceService.SortAfter(ctx, input.Origin, input.Target)
+ default:
+ list, err = i.balanceService.SortBefore(ctx, input.Origin, input.Target)
+ }
+ if err != nil {
+ return err
+ }
+ v, has := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if !has {
+ return fmt.Errorf("ollama address not found")
+ }
+ releases := make([]*gateway.DynamicRelease, 0, len(list))
+ for _, item := range list {
+ base := v
+ if item.Provider != "ollama" {
+ p, has := model_runtime.GetProvider(item.Provider)
+ if !has {
+ continue
+ }
+ base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
+ }
+
+ releases = append(releases, newRelease(item, base))
+ }
+ err = i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (i *imlBalanceModule) List(ctx context.Context, keyword string) ([]*ai_balance_dto.Item, error) {
+ list, err := i.balanceService.Search(ctx, keyword, nil, "priority asc")
+ if err != nil {
+ return nil, err
+ }
+ sort.Slice(list, func(i, j int) bool {
+ return list[i].Priority < list[j].Priority
+ })
+ aiAPIMap, err := i.aiAPIService.CountMapByModel(ctx, "", nil)
+ if err != nil {
+ return nil, fmt.Errorf("get ai api count error:%v", err)
+ }
+ keyMap, err := i.aiKeyService.CountMapByProvider(ctx, "", nil)
+ if err != nil {
+ return nil, fmt.Errorf("get ai key count error:%v", err)
+ }
+ result := make([]*ai_balance_dto.Item, 0, len(list))
+ for i, item := range list {
+ priority := i + 1
+ result = append(result, &ai_balance_dto.Item{
+ Id: item.Id,
+ Provider: &ai_balance_dto.BasicItem{
+ Id: item.Provider,
+ Name: item.ProviderName,
+ },
+ Model: &ai_balance_dto.BasicItem{
+ Id: item.Model,
+ Name: item.ModelName,
+ },
+ Priority: priority,
+ Type: ai_balance_dto.ModelTypeFromInt(item.Type),
+ State: ai_balance_dto.ModelStateFromInt(item.State),
+ APICount: aiAPIMap[item.Model],
+ KeyCount: keyMap[item.Provider],
+ })
+ }
+ return result, nil
+}
+
+func (i *imlBalanceModule) Delete(ctx context.Context, id string) error {
+ return i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ err := i.balanceService.Delete(ctx, id)
+ if err != nil {
+ return err
+ }
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ {
+ BasicItem: &gateway.BasicItem{
+ ID: id,
+ },
+ },
+ }, false)
+ })
+
+}
+
+func (i *imlBalanceModule) 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 {
+ dynamicClient.Offline(ctx, releaseInfo)
+ }
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (i *imlBalanceModule) getLocalBalances(ctx context.Context, v string) ([]*gateway.DynamicRelease, error) {
+ balances, err := i.balanceService.Search(ctx, "", map[string]interface{}{"provider": "ollama"}, "priority asc")
+ if err != nil {
+ return nil, err
+ }
+ if v == "" {
+ var has bool
+ v, has = i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if !has {
+ return nil, fmt.Errorf("ollama address not found")
+ }
+ }
+
+ releases := make([]*gateway.DynamicRelease, 0, len(balances))
+ for _, item := range balances {
+ base := v
+ if item.Provider != "ollama" {
+ p, has := model_runtime.GetProvider(item.Provider)
+ if !has {
+ continue
+ }
+ base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
+ }
+ releases = append(releases, newRelease(item, base))
+ }
+ return releases, nil
+}
+
+func (i *imlBalanceModule) getBalances(ctx context.Context) ([]*gateway.DynamicRelease, error) {
+ balances, err := i.balanceService.Search(ctx, "", nil, "priority asc")
+ if err != nil {
+ return nil, err
+ }
+ v, has := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if !has {
+ return nil, fmt.Errorf("ollama address not found")
+ }
+ releases := make([]*gateway.DynamicRelease, 0, len(balances))
+ for _, item := range balances {
+ base := v
+ if item.Provider != "ollama" {
+ p, has := model_runtime.GetProvider(item.Provider)
+ if !has {
+ continue
+ }
+ base = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
+ }
+ releases = append(releases, newRelease(item, base))
+ }
+ return releases, nil
+}
+
+func (i *imlBalanceModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
+ releases, err := i.getBalances(ctx)
+ if err != nil {
+ return err
+ }
+ for _, p := range releases {
+ client, err := clientDriver.Dynamic(p.Resource)
+ if err != nil {
+ return err
+ }
+ err = client.Online(ctx, p)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/module/ai-balance/module.go b/module/ai-balance/module.go
new file mode 100644
index 00000000..a72e6b7f
--- /dev/null
+++ b/module/ai-balance/module.go
@@ -0,0 +1,28 @@
+package ai_balance
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/APIParkLab/APIPark/gateway"
+
+ "github.com/eolinker/go-common/autowire"
+
+ ai_balance_dto "github.com/APIParkLab/APIPark/module/ai-balance/dto"
+)
+
+type IBalanceModule interface {
+ Create(ctx context.Context, input *ai_balance_dto.Create) error
+ Sort(ctx context.Context, input *ai_balance_dto.Sort) error
+ List(ctx context.Context, keyword string) ([]*ai_balance_dto.Item, error)
+ Delete(ctx context.Context, id string) error
+ SyncLocalBalances(ctx context.Context, address string) error
+}
+
+func init() {
+ balanceModule := new(imlBalanceModule)
+ autowire.Auto[IBalanceModule](func() reflect.Value {
+ gateway.RegisterInitHandleFunc(balanceModule.initGateway)
+ return reflect.ValueOf(balanceModule)
+ })
+}
diff --git a/module/ai-local/dto/input.go b/module/ai-local/dto/input.go
new file mode 100644
index 00000000..28ac175f
--- /dev/null
+++ b/module/ai-local/dto/input.go
@@ -0,0 +1,15 @@
+package ai_local_dto
+
+type Update struct {
+ Disable bool `json:"disable"`
+}
+
+type CancelDeploy struct {
+ Model string `json:"model"`
+}
+
+type DeployInput struct {
+ Model string `json:"model"`
+ Service string `json:"service"`
+ Team string `json:"team"`
+}
diff --git a/module/ai-local/dto/output.go b/module/ai-local/dto/output.go
new file mode 100644
index 00000000..62550016
--- /dev/null
+++ b/module/ai-local/dto/output.go
@@ -0,0 +1,141 @@
+package ai_local_dto
+
+import "github.com/eolinker/go-common/auto"
+
+type LocalModelState string
+
+const (
+ LocalModelStateNormal LocalModelState = "normal"
+ LocalModelStateDisable LocalModelState = "disabled"
+ LocalModelStateDeployingError LocalModelState = "deploying_error"
+ LocalModelStateError LocalModelState = "error"
+ LocalModelStateDeploying LocalModelState = "deploying"
+
+ DeployStateDownload DeployState = "download"
+ DeployStateDeploy DeployState = "deploy"
+ DeployStateInitializing DeployState = "initializing"
+ DeployStateFinish DeployState = "finish"
+ DeployStateDownloadError DeployState = "download error"
+ DeployStateDeployError DeployState = "deploy error"
+ DeployStateInitializingError DeployState = "initializing error"
+)
+
+func (l LocalModelState) String() string {
+ return string(l)
+}
+
+func (l LocalModelState) Int() int {
+ switch l {
+ case LocalModelStateDisable:
+ return 0
+ case LocalModelStateNormal:
+ return 1
+ case LocalModelStateError:
+ return 2
+ case LocalModelStateDeploying:
+ return 3
+ case LocalModelStateDeployingError:
+ return 4
+ default:
+ return 0
+ }
+}
+
+func FromLocalModelState(state int) LocalModelState {
+ switch state {
+ case 0:
+ return LocalModelStateDisable
+ case 1:
+ return LocalModelStateNormal
+ case 2:
+ return LocalModelStateError
+ case 3:
+ return LocalModelStateDeploying
+ case 4:
+ return LocalModelStateDeployingError
+ default:
+ return LocalModelStateDisable
+ }
+}
+
+type OllamaConfig struct {
+ Address string `json:"address"`
+}
+
+type SimpleItem struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ DefaultConfig string `json:"default_config"`
+ Logo string `json:"logo"`
+}
+
+type LocalModelItem struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ State LocalModelState `json:"state"`
+ APICount int64 `json:"api_count"`
+ Provider string `json:"provider"`
+ UpdateTime auto.TimeLabel `json:"update_time"`
+ CanDelete bool `json:"can_delete"`
+}
+
+type LocalModelPackageItem struct {
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Size string `json:"size"`
+ IsPopular bool `json:"is_popular"`
+}
+
+type DeployState string
+
+func (d DeployState) String() string {
+ return string(d)
+}
+
+func (d DeployState) Int() int {
+ switch d {
+ case DeployStateDownload:
+ return 1
+ case DeployStateDeploy:
+ return 2
+ case DeployStateInitializing:
+ return 3
+ case DeployStateFinish:
+ return 4
+ case DeployStateDownloadError:
+ return 5
+ case DeployStateDeployError:
+ return 6
+ case DeployStateInitializingError:
+ return 7
+ default:
+ return 1
+ }
+}
+
+func FromDeployState(state int) DeployState {
+ switch state {
+ case 1:
+ return DeployStateDownload
+ case 2:
+ return DeployStateDeploy
+ case 3:
+ return DeployStateInitializing
+ case 4:
+ return DeployStateFinish
+ case 5:
+ return DeployStateDownloadError
+ case 6:
+ return DeployStateDeployError
+ case 7:
+ return DeployStateInitializingError
+ default:
+ return DeployStateDownload
+ }
+}
+
+type ModelInfo struct {
+ Current int64 `json:"current"`
+ Total int64 `json:"total"`
+ LastMessage string `json:"last_message"`
+}
diff --git a/module/ai-local/iml.go b/module/ai-local/iml.go
new file mode 100644
index 00000000..28577413
--- /dev/null
+++ b/module/ai-local/iml.go
@@ -0,0 +1,635 @@
+package ai_local
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strings"
+
+ ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
+
+ "github.com/APIParkLab/APIPark/service/setting"
+
+ "github.com/APIParkLab/APIPark/service/api"
+
+ "github.com/APIParkLab/APIPark/gateway"
+ "github.com/eolinker/eosc/log"
+
+ "github.com/APIParkLab/APIPark/service/cluster"
+
+ "github.com/APIParkLab/APIPark/service/service"
+
+ "github.com/eolinker/go-common/auto"
+
+ ai_api "github.com/APIParkLab/APIPark/service/ai-api"
+
+ "github.com/eolinker/go-common/register"
+ "github.com/eolinker/go-common/server"
+
+ "github.com/eolinker/go-common/utils"
+
+ "gorm.io/gorm"
+
+ ai_local "github.com/APIParkLab/APIPark/service/ai-local"
+
+ "github.com/eolinker/go-common/store"
+
+ ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
+ ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
+)
+
+var (
+ _ ILocalModelModule = (*imlLocalModel)(nil)
+)
+
+type imlLocalModel struct {
+ localModelService ai_local.ILocalModelService `autowired:""`
+ localModelPackageService ai_local.ILocalModelPackageService `autowired:""`
+ localModelStateService ai_local.ILocalModelInstallStateService `autowired:""`
+ localModelCacheService ai_local.ILocalModelCacheService `autowired:""`
+ balanceService ai_balance.IBalanceService `autowired:""`
+ clusterService cluster.IClusterService `autowired:""`
+ aiAPIService ai_api.IAPIService `autowired:""`
+ routerService api.IAPIService `autowired:""`
+ serviceService service.IServiceService `autowired:""`
+ settingService setting.ISettingService `autowired:""`
+ transaction store.ITransaction `autowired:""`
+}
+
+func (i *imlLocalModel) SyncLocalModels(ctx context.Context, address string) error {
+ releases, err := i.getLocalModels(ctx, address)
+ if err != nil {
+ return err
+ }
+ return i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
+}
+
+func (i *imlLocalModel) SimpleList(ctx context.Context) ([]*ai_local_dto.SimpleItem, error) {
+ list, err := i.localModelService.List(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return utils.SliceToSlice(list, func(s *ai_local.LocalModel) *ai_local_dto.SimpleItem {
+ return &ai_local_dto.SimpleItem{
+ Id: s.Id,
+ Name: s.Name,
+ DefaultConfig: ai_provider_local.OllamaConfig,
+ Logo: ai_provider_local.OllamaSvg,
+ }
+ }, func(l *ai_local.LocalModel) bool {
+ if l.State != ai_local_dto.LocalModelStateNormal.Int() && l.State != ai_local_dto.LocalModelStateDisable.Int() {
+ return false
+ }
+ return true
+ }), nil
+}
+
+func (i *imlLocalModel) ModelState(ctx context.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error) {
+ info, err := i.localModelStateService.Get(ctx, model)
+ if err != nil {
+ return nil, nil, err
+ }
+ state := ai_local_dto.FromDeployState(info.State)
+ return &state, &ai_local_dto.ModelInfo{
+ Current: info.Complete,
+ Total: info.Total,
+ LastMessage: info.Msg,
+ }, nil
+}
+
+func (i *imlLocalModel) Search(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error) {
+ list, err := i.localModelService.Search(ctx, keyword, nil, "update_at desc")
+ if err != nil {
+ return nil, err
+ }
+ apiCountMap, err := i.aiAPIService.CountMapByModel(ctx, "", map[string]interface{}{
+ "type": 1,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return utils.SliceToSlice(list, func(s *ai_local.LocalModel) *ai_local_dto.LocalModelItem {
+ count := apiCountMap[s.Id]
+ return &ai_local_dto.LocalModelItem{
+ Id: s.Id,
+ Name: s.Name,
+ State: ai_local_dto.FromLocalModelState(s.State),
+ APICount: count,
+ CanDelete: count < 1 && s.State != ai_local_dto.LocalModelStateDeploying.Int(),
+ UpdateTime: auto.TimeLabel(s.UpdateAt),
+ Provider: "ollama",
+ }
+ }), nil
+}
+
+func (i *imlLocalModel) ListCanInstall(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error) {
+
+ if keyword == "" {
+ list, err := i.localModelPackageService.Search(ctx, keyword, nil)
+ if err != nil {
+ return nil, err
+ }
+ return utils.SliceToSlice(list, func(s *ai_local.LocalModelPackage) *ai_local_dto.LocalModelPackageItem {
+ return &ai_local_dto.LocalModelPackageItem{
+ Id: s.Id,
+ Name: s.Name,
+ Size: s.Size,
+ IsPopular: s.IsPopular,
+ }
+ }), nil
+ } else {
+ info, err := i.localModelPackageService.Get(ctx, keyword)
+ if err != nil {
+ return nil, err
+ }
+ result := make([]*ai_local_dto.LocalModelPackageItem, 0)
+
+ //for _, v := range list {
+ models := ai_provider_local.ModelsCanInstallById(info.Id)
+ for _, model := range models {
+ result = append(result, &ai_local_dto.LocalModelPackageItem{
+ Id: model.Id,
+ Name: model.Name,
+ Size: model.Size,
+ IsPopular: model.IsPopular,
+ })
+ }
+ //}
+ return result, nil
+ }
+
+}
+
+func (i *imlLocalModel) pullHook(fn ...func() error) func(msg ai_provider_local.PullMessage) error {
+ return func(msg ai_provider_local.PullMessage) error {
+ return i.transaction.Transaction(context.Background(), func(ctx context.Context) error {
+
+ state := ai_local_dto.DeployStateFinish.Int()
+ modelState := ai_local_dto.LocalModelStateNormal.Int()
+ if msg.Status == "error" {
+ state = ai_local_dto.DeployStateDownloadError.Int()
+ modelState = ai_local_dto.LocalModelStateDeployingError.Int()
+ }
+ err := i.localModelService.Save(ctx, msg.Model, &ai_local.EditLocalModel{State: &modelState})
+ if err != nil {
+ return err
+ }
+ info, err := i.localModelStateService.Get(ctx, msg.Model)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+
+ err = i.localModelStateService.Create(ctx, &ai_local.CreateLocalModelInstallState{
+ Id: msg.Model,
+ Complete: msg.Completed,
+ Total: msg.Total,
+ State: state,
+ Msg: msg.Msg,
+ })
+ if err != nil {
+ return err
+ }
+ info, err = i.localModelStateService.Get(ctx, msg.Model)
+ if err != nil {
+ return err
+ }
+
+ } else {
+ if info.Complete < msg.Completed {
+ info.Complete = msg.Completed
+
+ }
+ if info.Total < msg.Total {
+ info.Total = msg.Total
+ }
+ if msg.Msg != "" {
+ info.Msg = msg.Msg
+ }
+ err = i.localModelStateService.Save(ctx, msg.Model, &ai_local.EditLocalModelInstallState{State: &state, Complete: &info.Complete, Total: &info.Total, Msg: &info.Msg})
+ if err != nil {
+ return err
+ }
+ }
+
+ serviceState := 0
+ if msg.Status == "error" {
+ state = 2
+ }
+ list, err := i.localModelCacheService.List(ctx, msg.Model, ai_local.CacheTypeService)
+ if err != nil {
+ return err
+ }
+ for _, l := range list {
+ serviceInfo, err := i.serviceService.Get(ctx, l.Target)
+ if err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ continue
+ }
+ return err
+ }
+ if serviceInfo.State == serviceState {
+ continue
+ }
+ err = i.serviceService.Save(ctx, l.Target, &service.Edit{State: &serviceState})
+ if err != nil {
+ return err
+ }
+ }
+ if state == ai_local_dto.DeployStateFinish.Int() {
+ for _, f := range fn {
+ err = f()
+ if err != nil {
+ return err
+ }
+ }
+ v, _ := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+
+ cfg := make(map[string]interface{})
+ cfg["provider"] = "ollama"
+ cfg["model"] = msg.Model
+ cfg["model_config"] = ai_provider_local.OllamaConfig
+ cfg["priority"] = 0
+ cfg["base"] = v
+
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ {
+ BasicItem: &gateway.BasicItem{
+ ID: msg.Model,
+ Description: msg.Model,
+ Resource: "ai-provider",
+ Version: info.UpdateAt.Format("20060102150405"),
+ MatchLabels: map[string]string{
+ "module": "ai-provider",
+ },
+ },
+ Attr: cfg,
+ }}, true)
+ }
+ return nil
+ })
+ }
+}
+
+func (i *imlLocalModel) 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 *imlLocalModel) Deploy(ctx context.Context, model string, session string, fn ...func() error) (*ai_provider_local.Pipeline, error) {
+ var p *ai_provider_local.Pipeline
+ err := i.transaction.Transaction(ctx, func(txCtx context.Context) error {
+ item, err := i.localModelCacheService.GetByTarget(ctx, ai_local.CacheTypeService, model)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+
+ } else {
+ model = item.Model
+ }
+ info, err := i.localModelService.Get(ctx, model)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+ err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{
+ Id: model,
+ Name: model,
+ Provider: "ollama",
+ State: ai_local_dto.LocalModelStateDeploying.Int(),
+ })
+
+ } else {
+ if info.State == ai_local_dto.LocalModelStateDeployingError.Int() {
+ state := ai_local_dto.LocalModelStateDeploying.Int()
+ err = i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &state})
+ }
+ }
+ if err != nil {
+ return err
+ }
+ p, err = ai_provider_local.PullModel(model, session, i.pullHook(fn...))
+ if err != nil {
+ return err
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return p, nil
+}
+
+func (i *imlLocalModel) SaveCache(ctx context.Context, model string, target string) error {
+ return i.localModelCacheService.Save(ctx, model, ai_local.CacheTypeService, target)
+}
+
+func (i *imlLocalModel) CancelDeploy(ctx context.Context, model string) error {
+ return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
+ item, err := i.localModelCacheService.GetByTarget(ctx, ai_local.CacheTypeService, model)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+
+ } else {
+ model = item.Model
+ }
+ list, err := i.localModelCacheService.List(ctx, model, ai_local.CacheTypeService)
+ if err != nil {
+ return err
+ }
+ for _, l := range list {
+ info, err := i.serviceService.Get(ctx, l.Target)
+ if err != nil {
+ if errors.Is(err, gorm.ErrRecordNotFound) {
+ continue
+ }
+ return err
+ }
+ if info.State == 0 {
+ continue
+ }
+ err = i.serviceService.Delete(ctx, info.Id)
+ if err != nil {
+ return err
+ }
+ err = i.aiAPIService.DeleteByService(ctx, info.Id)
+ if err != nil {
+ return err
+ }
+ err = i.routerService.DeleteByService(ctx, info.Id)
+ if err != nil {
+ return err
+ }
+ }
+ err = i.localModelCacheService.Delete(ctx, model)
+ if err != nil {
+ return err
+ }
+ // 删除模型
+ err = i.localModelService.Delete(ctx, model)
+ if err != nil {
+ return err
+ }
+ ai_provider_local.StopPull(model)
+ return nil
+ })
+}
+
+func (i *imlLocalModel) RemoveModel(ctx context.Context, model string) error {
+ // 判断是否有api
+ count, err := i.aiAPIService.CountByModel(ctx, model)
+ if err != nil {
+ return err
+ }
+ if count > 0 {
+ return fmt.Errorf("model %s has api, can not remove", model)
+ }
+ info, err := i.localModelService.Get(ctx, model)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+ return ai_provider_local.RemoveModel(model)
+ }
+ if info.State == ai_local_dto.LocalModelStateDeploying.Int() {
+ return fmt.Errorf("model %s is deploying, can not remove", model)
+ }
+ return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
+ err = i.localModelService.Delete(ctx, model)
+ if err != nil {
+ return err
+ }
+ return ai_provider_local.RemoveModel(model)
+ })
+
+}
+
+func (i *imlLocalModel) Enable(ctx context.Context, model string) error {
+ info, err := i.localModelService.Get(ctx, model)
+ if err != nil {
+ return err
+ }
+ if info.State == ai_local_dto.LocalModelStateDisable.Int() || info.State == ai_local_dto.LocalModelStateError.Int() {
+
+ return i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ status := ai_local_dto.LocalModelStateNormal.Int()
+ err = i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &status})
+ if err != nil {
+ return err
+ }
+ v, _ := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ cfg := make(map[string]interface{})
+ cfg["provider"] = "ollama"
+ cfg["model"] = info.Id
+ cfg["model_config"] = ai_provider_local.OllamaConfig
+ cfg["priority"] = 0
+ cfg["base"] = v
+
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ {
+ BasicItem: &gateway.BasicItem{
+ ID: info.Id,
+ Description: info.Id,
+ Resource: "ai-provider",
+ Version: info.UpdateAt.Format("20060102150405"),
+ MatchLabels: map[string]string{
+ "module": "ai-provider",
+ },
+ },
+ Attr: cfg,
+ }}, true)
+ })
+
+ }
+ return fmt.Errorf("model %s is not disabled state,can not enable", model)
+}
+
+func (i *imlLocalModel) Disable(ctx context.Context, model string) error {
+ info, err := i.localModelService.Get(ctx, model)
+ if err != nil {
+ return err
+ }
+ if info.State == ai_local_dto.LocalModelStateNormal.Int() {
+ disable := ai_local_dto.LocalModelStateDisable.Int()
+ return i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ err = i.localModelService.Save(ctx, model, &ai_local.EditLocalModel{State: &disable})
+ if err != nil {
+ return err
+ }
+
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ {
+ BasicItem: &gateway.BasicItem{
+ ID: info.Id,
+ Resource: "ai-provider",
+ },
+ }}, false)
+ })
+
+ }
+ return fmt.Errorf("model %s is not enabled state,can not disable", model)
+}
+
+func (i *imlLocalModel) OnInit() {
+ register.Handle(func(v server.Server) {
+ ctx := context.Background()
+
+ list, err := i.localModelPackageService.List(ctx)
+ if err != nil {
+ return
+ }
+ oldModels := utils.SliceToMapO(list, func(s *ai_local.LocalModelPackage) (string, *ai_local.LocalModelPackage) {
+ return s.Id, s
+ })
+ models, version := ai_provider_local.ModelsCanInstall()
+ for _, model := range models {
+ delete(oldModels, model.Id)
+ if v, ok := oldModels[model.Id]; ok {
+ if v.Version == version {
+ continue
+ }
+ err = i.localModelPackageService.Save(ctx, model.Id, &ai_local.EditLocalModelPackage{
+ Size: &model.Size,
+ Hash: &model.Digest,
+ Description: &model.Description,
+ Version: &version,
+ Popular: &model.IsPopular,
+ })
+ if err != nil {
+ return
+ }
+ } else {
+ err = i.localModelPackageService.Create(ctx, &ai_local.CreateLocalModelPackage{
+ Id: model.Id,
+ Name: model.Name,
+ Size: model.Size,
+ Hash: model.Digest,
+ Description: model.Description,
+ Version: version,
+ Popular: model.IsPopular,
+ })
+ if err != nil {
+ return
+ }
+ }
+ }
+ for id := range oldModels {
+ err = i.localModelPackageService.Delete(ctx, id)
+ if err != nil {
+ return
+ }
+ }
+ installModels, err := ai_provider_local.ModelsInstalled()
+ if err != nil {
+ return
+ }
+ for _, model := range installModels {
+
+ id := strings.TrimSuffix(model.Name, ":latest")
+ name := strings.TrimSuffix(model.Name, ":latest")
+ _, err = i.localModelService.Get(ctx, id)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return
+ }
+ err = i.localModelService.Create(ctx, &ai_local.CreateLocalModel{
+ Id: id,
+ Name: name,
+ State: 1,
+ })
+ if err != nil {
+ return
+ }
+ }
+ }
+ })
+}
+
+func (i *imlLocalModel) getLocalModels(ctx context.Context, v string) ([]*gateway.DynamicRelease, error) {
+ list, err := i.localModelService.List(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if v == "" {
+ var has bool
+ v, has = i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if !has {
+ return nil, errors.New("ollama_address not set")
+ }
+ }
+
+ releases := make([]*gateway.DynamicRelease, 0, len(list))
+ for _, l := range list {
+ if l.State != ai_local_dto.LocalModelStateNormal.Int() {
+ continue
+ }
+ cfg := make(map[string]interface{})
+ cfg["provider"] = "ollama"
+ cfg["model"] = l.Id
+ cfg["model_config"] = ai_provider_local.OllamaConfig
+ cfg["base"] = v
+ 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 releases, nil
+}
+
+func (i *imlLocalModel) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error {
+ releases, err := i.getLocalModels(ctx, "")
+ if err != nil {
+ return err
+ }
+
+ for _, p := range releases {
+ client, err := clientDriver.Dynamic(p.Resource)
+ if err != nil {
+ return err
+ }
+ err = client.Online(ctx, p)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/module/ai-local/module.go b/module/ai-local/module.go
new file mode 100644
index 00000000..131c72ca
--- /dev/null
+++ b/module/ai-local/module.go
@@ -0,0 +1,37 @@
+package ai_local
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/APIParkLab/APIPark/gateway"
+
+ "github.com/eolinker/go-common/autowire"
+
+ ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
+
+ ai_local_dto "github.com/APIParkLab/APIPark/module/ai-local/dto"
+)
+
+type ILocalModelModule interface {
+ Search(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelItem, error)
+ ListCanInstall(ctx context.Context, keyword string) ([]*ai_local_dto.LocalModelPackageItem, error)
+ Deploy(ctx context.Context, model string, session string, fn ...func() error) (*ai_provider_local.Pipeline, error)
+ CancelDeploy(ctx context.Context, model string) error
+ RemoveModel(ctx context.Context, model string) error
+ Enable(ctx context.Context, model string) error
+ Disable(ctx context.Context, model string) error
+ ModelState(ctx context.Context, model string) (*ai_local_dto.DeployState, *ai_local_dto.ModelInfo, error)
+ SimpleList(ctx context.Context) ([]*ai_local_dto.SimpleItem, error)
+ SaveCache(ctx context.Context, model string, target string) error
+
+ SyncLocalModels(ctx context.Context, address string) error
+}
+
+func init() {
+ localModel := new(imlLocalModel)
+ autowire.Auto[ILocalModelModule](func() reflect.Value {
+ gateway.RegisterInitHandleFunc(localModel.initGateway)
+ return reflect.ValueOf(localModel)
+ })
+}
diff --git a/module/ai/dto/output.go b/module/ai/dto/output.go
index 83914ae6..27d07eae 100644
--- a/module/ai/dto/output.go
+++ b/module/ai/dto/output.go
@@ -13,15 +13,15 @@ type SimpleProvider struct {
}
type Provider struct {
- Id string `json:"id"`
- Name string `json:"name"`
- Config string `json:"config"`
- GetAPIKeyUrl string `json:"get_apikey_url"`
- DefaultLLM string `json:"default_llm"`
- DefaultLLMConfig string `json:"-"`
- Priority int `json:"priority"`
- Status ProviderStatus `json:"status"`
- Configured bool `json:"configured"`
+ Id string `json:"id"`
+ Name string `json:"name"`
+ Config string `json:"config"`
+ GetAPIKeyUrl string `json:"get_apikey_url"`
+ DefaultLLM string `json:"default_llm"`
+ DefaultLLMConfig string `json:"-"`
+ //Priority int `json:"priority"`
+ Status ProviderStatus `json:"status"`
+ Configured bool `json:"configured"`
}
type ConfiguredProviderItem struct {
@@ -31,9 +31,8 @@ type ConfiguredProviderItem struct {
DefaultLLM string `json:"default_llm"`
Status ProviderStatus `json:"status"`
APICount int64 `json:"api_count"`
- KeyCount int `json:"key_count"`
- KeyStatus []*KeyStatus `json:"keys"`
- Priority int `json:"priority"`
+ KeyCount int64 `json:"key_count"`
+ CanDelete bool `json:"can_delete"`
}
type KeyStatus struct {
@@ -59,13 +58,14 @@ type SimpleProviderItem struct {
DefaultConfig string `json:"default_config"`
Status ProviderStatus `json:"status"`
Model *BasicInfo `json:"model,omitempty"`
- Priority int `json:"-"`
+ Type string `json:"type"`
}
type BackupProvider struct {
Id string `json:"id"`
Name string `json:"name"`
Model *BasicInfo `json:"model,omitempty"`
+ Type string `json:"type"`
}
type LLMItem struct {
diff --git a/module/ai/iml.go b/module/ai/iml.go
index ba4ee255..d4c06d89 100644
--- a/module/ai/iml.go
+++ b/module/ai/iml.go
@@ -8,9 +8,18 @@ import (
"sort"
"time"
- "github.com/APIParkLab/APIPark/service/service"
+ "github.com/google/uuid"
- ai_key_dto "github.com/APIParkLab/APIPark/module/ai-key/dto"
+ ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
+
+ "github.com/eolinker/go-common/register"
+ "github.com/eolinker/go-common/server"
+
+ ai_local "github.com/APIParkLab/APIPark/service/ai-local"
+
+ ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
+
+ "github.com/APIParkLab/APIPark/service/service"
ai_key "github.com/APIParkLab/APIPark/service/ai-key"
@@ -54,11 +63,105 @@ func newKey(key *ai_key.Key) *gateway.DynamicRelease {
var _ IProviderModule = (*imlProviderModule)(nil)
type imlProviderModule struct {
- providerService ai.IProviderService `autowired:""`
- clusterService cluster.IClusterService `autowired:""`
- aiAPIService ai_api.IAPIService `autowired:""`
- aiKeyService ai_key.IKeyService `autowired:""`
- transaction store.ITransaction `autowired:""`
+ providerService ai.IProviderService `autowired:""`
+ clusterService cluster.IClusterService `autowired:""`
+ aiAPIService ai_api.IAPIService `autowired:""`
+ aiKeyService ai_key.IKeyService `autowired:""`
+ aiBalanceService ai_balance.IBalanceService `autowired:""`
+ transaction store.ITransaction `autowired:""`
+}
+
+func (i *imlProviderModule) OnInit() {
+ register.Handle(func(v server.Server) {
+ ctx := context.Background()
+
+ list, err := i.providerService.List(ctx)
+ if err != nil {
+ return
+ }
+ i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ for _, l := range list {
+ if l.Priority < 1 {
+ continue
+ }
+ has, err := i.aiBalanceService.Exist(ctx, l.Id, l.DefaultLLM)
+ if err != nil {
+ return err
+ }
+ if has {
+ continue
+ }
+
+ p, has := model_runtime.GetProvider(l.Id)
+ if !has {
+ continue
+ }
+ err = i.aiBalanceService.Create(ctx, &ai_balance.Create{
+ Id: uuid.NewString(),
+ Priority: l.Priority,
+ Provider: l.Id,
+ ProviderName: p.Name(),
+ Model: l.DefaultLLM,
+ ModelName: l.DefaultLLM,
+ Type: 0,
+ })
+ if err != nil {
+ return err
+ }
+ priority := 0
+ err = i.providerService.Save(ctx, l.Id, &ai.SetProvider{
+ Priority: &priority,
+ })
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+
+ })
+}
+
+func (i *imlProviderModule) Delete(ctx context.Context, id string) error {
+ return i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ // 判断是否有api
+ count, err := i.aiAPIService.CountByProvider(ctx, id)
+ if err != nil {
+ return err
+ }
+ if count > 0 {
+ return fmt.Errorf("provider has api")
+ }
+ keys, err := i.aiKeyService.KeysByProvider(ctx, id)
+ if err != nil {
+ return err
+ }
+ err = i.aiKeyService.DeleteByProvider(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ err = i.providerService.Delete(ctx, id)
+ if err != nil {
+ return err
+ }
+ releases := make([]*gateway.DynamicRelease, 0, len(keys))
+ for _, key := range keys {
+ releases = append(releases, newKey(key))
+ }
+ err = i.syncGateway(ctx, cluster.DefaultClusterID, releases, false)
+ if err != nil {
+ return err
+ }
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ {
+ BasicItem: &gateway.BasicItem{
+ ID: id,
+ Resource: "ai-provider",
+ },
+ },
+ }, false)
+ })
}
func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_dto.SimpleProvider, error) {
@@ -75,83 +178,19 @@ func (i *imlProviderModule) SimpleProvider(ctx context.Context, id string) (*ai_
}, nil
}
-func (i *imlProviderModule) Sort(ctx context.Context, input *ai_dto.Sort) error {
- return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
- list, err := i.providerService.List(ctx)
- if err != nil {
- return err
- }
- providerMap := utils.SliceToMap(list, func(e *ai.Provider) string {
- return e.Id
- })
- releases := make([]*gateway.DynamicRelease, 0, len(list))
- offlineReleases := make([]*gateway.DynamicRelease, 0, len(list))
- for index, id := range input.Providers {
- p, has := model_runtime.GetProvider(id)
- if !has {
- continue
- }
-
- l, has := providerMap[id]
- if !has {
- continue
- }
- model, has := p.GetModel(l.DefaultLLM)
- if !has {
- continue
- }
- priority := index + 1
- err = i.providerService.Save(txCtx, id, &ai.SetProvider{
- Priority: &priority,
- })
- if err != nil {
- return err
- }
- if ai_dto.ToProviderStatus(l.Status) == ai_dto.ProviderDisabled {
- offlineReleases = append(offlineReleases, &gateway.DynamicRelease{
- BasicItem: &gateway.BasicItem{
- ID: l.Id,
- Resource: "ai-provider",
- }})
- } else {
- cfg := make(map[string]interface{})
- cfg["provider"] = l.Id
- cfg["model"] = l.DefaultLLM
- cfg["model_config"] = model.DefaultConfig()
- cfg["priority"] = l.Priority
- cfg["base"] = fmt.Sprintf("%s://%s", p.URI().Scheme(), p.URI().Host())
- releases = append(releases, &gateway.DynamicRelease{
- BasicItem: &gateway.BasicItem{
- ID: l.Id,
- Description: l.Name,
- Resource: "ai-provider",
- Version: l.UpdateAt.Format("20060102150405"),
- MatchLabels: map[string]string{
- "module": "ai-provider",
- },
- },
- Attr: cfg,
- })
- }
- }
- err = i.syncGateway(ctx, cluster.DefaultClusterID, releases, true)
- if err != nil {
- return err
- }
- return i.syncGateway(ctx, cluster.DefaultClusterID, offlineReleases, false)
-
- })
-}
-
-func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error) {
+func (i *imlProviderModule) ConfiguredProviders(ctx context.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error) {
// 获取已配置的AI服务商
- list, err := i.providerService.List(ctx)
+ list, err := i.providerService.Search(ctx, keyword, nil, "update_at")
if err != nil {
- return nil, nil, fmt.Errorf("get provider list error:%v", err)
+ return nil, fmt.Errorf("get provider list error:%v", err)
}
aiAPIMap, err := i.aiAPIService.CountMapByProvider(ctx, "", nil)
if err != nil {
- return nil, nil, fmt.Errorf("get ai api count error:%v", err)
+ return nil, fmt.Errorf("get ai api count error:%v", err)
+ }
+ keyMap, err := i.aiKeyService.CountMapByProvider(ctx, "", nil)
+ if err != nil {
+ return nil, fmt.Errorf("get ai key count error:%v", err)
}
providers := make([]*ai_dto.ConfiguredProviderItem, 0, len(list))
for _, l := range list {
@@ -159,7 +198,7 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
_, err = i.aiKeyService.DefaultKey(ctx, l.Id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, nil, err
+ return nil, err
}
err = i.aiKeyService.Create(ctx, &ai_key.Create{
ID: l.Id,
@@ -173,7 +212,7 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
Default: true,
})
if err != nil {
- return nil, nil, fmt.Errorf("create default key error:%v", err)
+ return nil, fmt.Errorf("create default key error:%v", err)
}
}
@@ -181,29 +220,7 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
if !has {
continue
}
- keys, err := i.aiKeyService.KeysByProvider(ctx, l.Id)
- if err != nil {
- return nil, nil, fmt.Errorf("get provider keys error:%v", err)
- }
-
- keysStatus := make([]*ai_dto.KeyStatus, 0, len(keys))
- for _, k := range keys {
- status := ai_key_dto.ToKeyStatus(k.Status)
- switch status {
- case ai_key_dto.KeyNormal, ai_key_dto.KeyDisable, ai_key_dto.KeyError:
- default:
- status = ai_key_dto.KeyError
- }
- keysStatus = append(keysStatus, &ai_dto.KeyStatus{
- Id: k.ID,
- Name: k.Name,
- Status: status.String(),
- Priority: k.Priority,
- })
- }
- sort.Slice(keysStatus, func(i, j int) bool {
- return keysStatus[i].Priority < keysStatus[j].Priority
- })
+ apiCount := aiAPIMap[l.Id]
providers = append(providers, &ai_dto.ConfiguredProviderItem{
Id: l.Id,
@@ -211,35 +228,13 @@ func (i *imlProviderModule) ConfiguredProviders(ctx context.Context) ([]*ai_dto.
Logo: p.Logo(),
DefaultLLM: l.DefaultLLM,
Status: ai_dto.ToProviderStatus(l.Status),
- APICount: aiAPIMap[l.Id],
- KeyCount: len(keysStatus),
- KeyStatus: keysStatus,
- Priority: l.Priority,
+ APICount: apiCount,
+ KeyCount: keyMap[l.Id],
+ CanDelete: apiCount < 1,
})
}
- sort.Slice(providers, func(i, j int) bool {
- if providers[i].Priority != providers[j].Priority {
- if providers[i].Priority == 0 {
- return false
- }
- if providers[j].Priority == 0 {
- return true
- }
- return providers[i].Priority < providers[j].Priority
- }
- return providers[i].Name < providers[j].Name
- })
- var backup *ai_dto.BackupProvider
- for _, p := range providers {
- if p.Status == ai_dto.ProviderEnabled {
- backup = &ai_dto.BackupProvider{
- Id: p.Id,
- Name: p.Name,
- }
- break
- }
- }
- return providers, backup, nil
+
+ return providers, nil
}
func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error) {
@@ -252,6 +247,7 @@ func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.Simp
providerMap := utils.SliceToMap(list, func(e *ai.Provider) string {
return e.Id
})
+
items := make([]*ai_dto.SimpleProviderItem, 0, len(providers))
for _, v := range providers {
item := &ai_dto.SimpleProviderItem{
@@ -264,31 +260,35 @@ func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.Simp
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) {
+func (i *imlProviderModule) SimpleConfiguredProviders(ctx context.Context, all bool) ([]*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))
+
+ healthProvider := make(map[string]struct{})
+ if all {
+ healthProvider["ollama"] = struct{}{}
+ items = append(items, &ai_dto.SimpleProviderItem{
+ Id: "ollama",
+ Name: "Ollama",
+ Logo: ai_provider_local.OllamaSvg,
+ Configured: true,
+ DefaultConfig: "",
+ Status: ai_dto.ProviderEnabled,
+ Type: "local",
+ })
+ }
+
var backup *ai_dto.BackupProvider
for _, l := range list {
p, has := model_runtime.GetProvider(l.Id)
@@ -308,34 +308,32 @@ func (i *imlProviderModule) SimpleConfiguredProviders(ctx context.Context) ([]*a
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(),
},
}
-
+ if item.Status == ai_dto.ProviderEnabled {
+ healthProvider[l.Id] = struct{}{}
+ }
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 {
+
+ aiBalanceItems, err := i.aiBalanceService.Search(ctx, "", nil, "priority asc")
+ if err != nil {
+ return nil, nil, err
+ }
+ for _, item := range aiBalanceItems {
+ if _, has := healthProvider[item.Provider]; has {
backup = &ai_dto.BackupProvider{
- Id: item.Id,
- Name: item.Name,
- Model: item.Model,
+ Id: item.Provider,
+ Name: item.Provider,
+ Model: &ai_dto.BasicInfo{
+ Id: item.Model,
+ Name: item.Model,
+ },
+ Type: "local",
}
break
}
@@ -388,13 +386,7 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
if !has {
return nil, fmt.Errorf("ai provider not found")
}
- maxPriority, err := i.providerService.MaxPriority(ctx)
- if err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- return nil, err
- }
- }
- maxPriority = maxPriority + 1
+
info, err := i.providerService.Get(ctx, id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
@@ -412,7 +404,7 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
DefaultLLM: defaultLLM.ID(),
DefaultLLMConfig: defaultLLM.Logo(),
Status: ai_dto.ProviderDisabled,
- Priority: maxPriority,
+ //Priority: maxPriority,
}, nil
}
defaultLLM, has := p.GetModel(info.DefaultLLM)
@@ -423,9 +415,6 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
}
defaultLLM = model
}
- if info.Priority == 0 {
- info.Priority = maxPriority
- }
return &ai_dto.Provider{
Id: info.Id,
@@ -434,9 +423,9 @@ func (i *imlProviderModule) Provider(ctx context.Context, id string) (*ai_dto.Pr
GetAPIKeyUrl: p.HelpUrl(),
DefaultLLM: defaultLLM.ID(),
DefaultLLMConfig: defaultLLM.DefaultConfig(),
- Priority: info.Priority,
- Status: ai_dto.ToProviderStatus(info.Status),
- Configured: true,
+ //Priority: info.Priority,
+ Status: ai_dto.ToProviderStatus(info.Status),
+ Configured: true,
}, nil
}
@@ -492,38 +481,48 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
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 i.transaction.Transaction(ctx, func(ctx context.Context) error {
+ info, err := i.providerService.Get(ctx, id)
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return err
+ }
+ if input.DefaultLLM == "" {
+ defaultLLM, has := p.DefaultModel(model_runtime.ModelTypeLLM)
+ if !has {
+ return fmt.Errorf("ai provider default llm not found")
+ }
+ input.DefaultLLM = defaultLLM.ID()
+ }
+ info = &ai.Provider{
+ Id: id,
+ Name: p.Name(),
+ DefaultLLM: input.DefaultLLM,
+ Config: input.Config,
+ }
+ err = i.providerService.Create(ctx, &ai.CreateProvider{
+ Id: info.Id,
+ Name: info.Name,
+ DefaultLLM: input.DefaultLLM,
+ Config: input.Config,
+ })
+ if err != nil {
+ return err
+ }
+ }
+ 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
}
- if input.DefaultLLM == "" {
- defaultLLM, has := p.DefaultModel(model_runtime.ModelTypeLLM)
- if !has {
- return fmt.Errorf("ai provider default llm not found")
- }
- input.DefaultLLM = defaultLLM.ID()
+ input.Config, err = p.GenConfig(input.Config, info.Config)
+ if err != nil {
+ return err
}
- info = &ai.Provider{
- Id: id,
- Name: p.Name(),
- DefaultLLM: input.DefaultLLM,
- 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
- }
- input.Config, err = p.GenConfig(input.Config, info.Config)
- if err != nil {
- return err
- }
- return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
status := 0
if input.Enable != nil && *input.Enable {
status = 1
@@ -532,15 +531,14 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
Name: &info.Name,
DefaultLLM: &input.DefaultLLM,
Config: &input.Config,
- Priority: input.Priority,
Status: &status,
}
- _, err = i.aiKeyService.DefaultKey(txCtx, id)
+ _, err = i.aiKeyService.DefaultKey(ctx, id)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
- err = i.aiKeyService.Create(txCtx, &ai_key.Create{
+ err = i.aiKeyService.Create(ctx, &ai_key.Create{
ID: id,
Name: info.Name,
Config: input.Config,
@@ -551,7 +549,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
Priority: 1,
})
} else {
- err = i.aiKeyService.Save(txCtx, id, &ai_key.Edit{
+ err = i.aiKeyService.Save(ctx, id, &ai_key.Edit{
Config: &input.Config,
Status: &status,
})
@@ -559,13 +557,13 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
if err != nil {
return err
}
- err = i.providerService.Save(txCtx, id, pInfo)
+ err = i.providerService.Save(ctx, id, pInfo)
if err != nil {
return err
}
if *pInfo.Status == 0 {
- return i.syncGateway(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
{
BasicItem: &gateway.BasicItem{
ID: id,
@@ -575,7 +573,7 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
}, false)
}
// 获取当前供应商默认Key信息
- defaultKey, err := i.aiKeyService.DefaultKey(txCtx, id)
+ defaultKey, err := i.aiKeyService.DefaultKey(ctx, id)
if err != nil {
return err
}
@@ -583,9 +581,8 @@ func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string,
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(txCtx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
+ return i.syncGateway(ctx, cluster.DefaultClusterID, []*gateway.DynamicRelease{
{
BasicItem: &gateway.BasicItem{
ID: id,
@@ -624,7 +621,6 @@ func (i *imlProviderModule) getAiProviders(ctx context.Context) ([]*gateway.Dyna
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: l.Id,
@@ -694,16 +690,38 @@ 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:""`
- serviceService service.IServiceService `autowired:""`
+ aiAPIService ai_api.IAPIService `autowired:""`
+ aiAPIUseService ai_api.IAPIUseService `autowired:""`
+ serviceService service.IServiceService `autowired:""`
+ aiLocalModelService ai_local.ILocalModelService `autowired:""`
}
func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId string, start int64, end int64, page int, pageSize int, sortCondition string, asc bool, models []string, serviceIds []string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error) {
- p, has := model_runtime.GetProvider(providerId)
- if !has {
- return nil, nil, 0, fmt.Errorf("ai provider not found")
+ modelItems := make([]*ai_dto.BasicInfo, 0)
+ if providerId == "ollama" {
+ items, err := i.aiLocalModelService.Search(ctx, "", nil, "update_at desc")
+ if err != nil {
+ return nil, nil, 0, err
+ }
+ modelItems = utils.SliceToSlice(items, func(e *ai_local.LocalModel) *ai_dto.BasicInfo {
+ return &ai_dto.BasicInfo{
+ Id: e.Id,
+ Name: e.Name,
+ }
+ })
+ } else {
+ p, has := model_runtime.GetProvider(providerId)
+ if !has {
+ return nil, nil, 0, fmt.Errorf("ai provider not found")
+ }
+ modelItems = utils.SliceToSlice(p.Models(), func(e model_runtime.IModel) *ai_dto.BasicInfo {
+ return &ai_dto.BasicInfo{
+ Id: e.ID(),
+ Name: e.ID(),
+ }
+ })
}
+
sortRule := "desc"
if asc {
sortRule = "asc"
@@ -723,12 +741,6 @@ func (i *imlAIApiModule) APIs(ctx context.Context, keyword string, providerId st
}
- 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:
diff --git a/module/ai/module.go b/module/ai/module.go
index 99b6a56e..650de0ff 100644
--- a/module/ai/module.go
+++ b/module/ai/module.go
@@ -10,23 +10,24 @@ import (
)
type IProviderModule interface {
- ConfiguredProviders(ctx context.Context) ([]*ai_dto.ConfiguredProviderItem, *ai_dto.BackupProvider, error)
+ ConfiguredProviders(ctx context.Context, keyword string) ([]*ai_dto.ConfiguredProviderItem, error)
UnConfiguredProviders(ctx context.Context) ([]*ai_dto.ProviderItem, error)
SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error)
- SimpleConfiguredProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error)
+ SimpleConfiguredProviders(ctx context.Context, all bool) ([]*ai_dto.SimpleProviderItem, *ai_dto.BackupProvider, error)
Provider(ctx context.Context, id string) (*ai_dto.Provider, error)
SimpleProvider(ctx context.Context, id string) (*ai_dto.SimpleProvider, error)
LLMs(ctx context.Context, driver string) ([]*ai_dto.LLMItem, *ai_dto.ProviderItem, error)
- //UpdateProviderStatus(ctx context.Context, id string, enable bool) error
UpdateProviderConfig(ctx context.Context, id string, input *ai_dto.UpdateConfig) error
- //UpdateProviderDefaultLLM(ctx context.Context, id string, input *ai_dto.UpdateLLM) error
- Sort(ctx context.Context, input *ai_dto.Sort) error
+ Delete(ctx context.Context, id string) error
}
type IAIAPIModule interface {
APIs(ctx context.Context, keyword string, providerId string, start int64, end int64, page int, pageSize int, sortCondition string, asc bool, models []string, services []string) ([]*ai_dto.APIItem, *ai_dto.Condition, int64, error)
}
+type ILocalModelModule interface {
+}
+
func init() {
autowire.Auto[IProviderModule](func() reflect.Value {
module := new(imlProviderModule)
diff --git a/module/catalogue/catalogue.go b/module/catalogue/catalogue.go
index eacddd7f..c665d681 100644
--- a/module/catalogue/catalogue.go
+++ b/module/catalogue/catalogue.go
@@ -2,9 +2,10 @@ package catalogue
import (
"context"
- "github.com/APIParkLab/APIPark/module/system"
"reflect"
+ "github.com/APIParkLab/APIPark/module/system"
+
"github.com/eolinker/go-common/autowire"
catalogue_dto "github.com/APIParkLab/APIPark/module/catalogue/dto"
@@ -28,6 +29,7 @@ type ICatalogueModule interface {
// Subscribe 订阅服务
Subscribe(ctx context.Context, subscribeInfo *catalogue_dto.SubscribeService) error
Sort(ctx context.Context, sorts []*catalogue_dto.SortItem) error
+ DefaultCatalogue(ctx context.Context) (*catalogue_dto.Catalogue, error)
//ExportAll(ctx context.Context) ([]*catalogue_dto.ExportCatalogue, error)
}
diff --git a/module/catalogue/iml.go b/module/catalogue/iml.go
index c43de0c8..f09cf820 100644
--- a/module/catalogue/iml.go
+++ b/module/catalogue/iml.go
@@ -66,6 +66,24 @@ type imlCatalogueModule struct {
root *Root
}
+func (i *imlCatalogueModule) DefaultCatalogue(ctx context.Context) (*catalogue_dto.Catalogue, error) {
+ catalogues, err := i.catalogueService.List(ctx)
+ if err != nil {
+ return nil, err
+ }
+ for _, v := range catalogues {
+ if v.Parent == "" {
+ return &catalogue_dto.Catalogue{
+ Id: v.Id,
+ Name: v.Name,
+ Parent: v.Parent,
+ Sort: v.Sort,
+ }, nil
+ }
+ }
+ return nil, errors.New("no default catalogue")
+}
+
func (i *imlCatalogueModule) onlineSubscriber(ctx context.Context, clusterId string, sub *gateway.SubscribeRelease) error {
client, err := i.clusterService.GatewayClient(ctx, clusterId)
if err != nil {
diff --git a/module/service/dto/input.go b/module/service/dto/input.go
index 979efa46..7569d1b5 100644
--- a/module/service/dto/input.go
+++ b/module/service/dto/input.go
@@ -1,5 +1,12 @@
package service_dto
+type QuickCreateAIService struct {
+ Provider string `json:"provider"`
+ Model string `json:"model"`
+ Config string `json:"config"`
+ Team string `json:"team"`
+}
+
type CreateService struct {
Id string `json:"id"`
Name string `json:"name"`
@@ -11,7 +18,9 @@ type CreateService struct {
Catalogue string `json:"catalogue"`
ApprovalType string `json:"approval_type"`
Kind string `json:"service_kind"`
- Provider *string `json:"provider" aocheck:"ai_provider"`
+ State string `json:"state"`
+ Provider *string `json:"provider"`
+ Model *string `json:"model"`
AsApp *bool `json:"as_app"`
AsServer *bool `json:"as_server"`
ModelMapping string `json:"model_mapping"`
@@ -24,8 +33,10 @@ type EditService struct {
Catalogue *string `json:"catalogue"`
Logo *string `json:"logo"`
Tags *[]string `json:"tags"`
- Provider *string `json:"provider" aocheck:"ai_provider"`
+ Provider *string `json:"provider"`
+ Model *string `json:"model"`
ApprovalType *string `json:"approval_type"`
+ State *string `json:"state"`
ModelMapping string `json:"model_mapping"`
}
diff --git a/module/service/dto/output.go b/module/service/dto/output.go
index b2c073c0..636420f0 100644
--- a/module/service/dto/output.go
+++ b/module/service/dto/output.go
@@ -5,6 +5,44 @@ import (
"github.com/eolinker/go-common/auto"
)
+type ServiceState string
+
+const (
+ ServiceStateNormal ServiceState = "normal"
+ ServiceStateDeploying ServiceState = "deploying"
+ ServiceStateDeployError ServiceState = "error"
+)
+
+func (s ServiceState) String() string {
+ return string(s)
+}
+
+func (s ServiceState) Int() int {
+ switch s {
+ case ServiceStateNormal:
+ return 0
+ case ServiceStateDeploying:
+ return 1
+ case ServiceStateDeployError:
+ return 2
+ default:
+ return 0
+ }
+}
+
+func FromServiceState(s int) ServiceState {
+ switch s {
+ case 0:
+ return ServiceStateNormal
+ case 1:
+ return ServiceStateDeploying
+ case 2:
+ return ServiceStateDeployError
+ default:
+ return ""
+ }
+}
+
type ServiceItem struct {
Id string `json:"id"`
Name string `json:"name"`
@@ -15,6 +53,7 @@ type ServiceItem struct {
CreateTime auto.TimeLabel `json:"create_time"`
UpdateTime auto.TimeLabel `json:"update_time"`
Provider *auto.Label `json:"provider,omitempty" aolabel:"ai_provider"`
+ State string `json:"state"`
CanDelete bool `json:"can_delete"`
}
@@ -58,10 +97,13 @@ type Service struct {
Tags []auto.Label `json:"tags" aolabel:"tag"`
Logo string `json:"logo"`
Provider *auto.Label `json:"provider,omitempty" aolabel:"ai_provider"`
+ ProviderType string `json:"provider_type,omitempty"`
+ Model string `json:"model,omitempty"`
ApprovalType string `json:"approval_type"`
AsServer bool `json:"as_server"`
AsApp bool `json:"as_app"`
ServiceKind string `json:"service_kind"`
+ State string `json:"state"`
ModelMapping string `json:"model_mapping"`
}
@@ -80,6 +122,7 @@ func ToService(model *service.Service) *Service {
if model.Prefix != "" {
prefix = model.Prefix
}
+
s := &Service{
Id: model.Id,
Name: model.Name,
@@ -96,10 +139,25 @@ func ToService(model *service.Service) *Service {
AsApp: model.AsApp,
ServiceKind: model.Kind.String(),
}
+ state := FromServiceState(model.State)
+ if state == ServiceStateNormal {
+ s.State = model.ServiceType.String()
+ } else {
+ s.State = state.String()
+ }
+
switch model.Kind {
case service.AIService:
provider := auto.UUID(model.AdditionalConfig["provider"])
s.Provider = &provider
+ s.ProviderType = "local"
+ if provider.Id != "ollama" {
+ s.ProviderType = "online"
+ }
+ modelId := model.AdditionalConfig["model"]
+ if modelId != "" {
+ s.Model = modelId
+ }
}
return s
}
diff --git a/module/service/iml.go b/module/service/iml.go
index cc63add4..d98c322b 100644
--- a/module/service/iml.go
+++ b/module/service/iml.go
@@ -8,6 +8,10 @@ import (
"strings"
"time"
+ ai_local "github.com/APIParkLab/APIPark/service/ai-local"
+
+ model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime"
+
"github.com/eolinker/eosc/log"
"github.com/APIParkLab/APIPark/resources/access"
@@ -59,6 +63,7 @@ type imlServiceModule struct {
teamService team.ITeamService `autowired:""`
teamMemberService team_member.ITeamMemberService `autowired:""`
tagService tag.ITagService `autowired:""`
+ localModelService ai_local.ILocalModelService `autowired:""`
serviceTagService service_tag.ITagService `autowired:""`
apiService api.IAPIService `autowired:""`
@@ -126,7 +131,7 @@ func (i *imlServiceModule) searchMyServices(ctx context.Context, teamId string,
return nil, err
}
condition["team"] = teamId
- return i.serviceService.Search(ctx, keyword, condition, "update_at desc")
+ return i.serviceService.Search(ctx, keyword, condition, "create_at desc")
} else {
membersForUser, err := i.teamMemberService.FilterMembersForUser(ctx, userID)
if err != nil {
@@ -134,7 +139,7 @@ func (i *imlServiceModule) searchMyServices(ctx context.Context, teamId string,
}
teamIds := membersForUser[userID]
condition["team"] = teamIds
- return i.serviceService.Search(ctx, keyword, condition, "update_at desc")
+ return i.serviceService.Search(ctx, keyword, condition, "create_at desc")
}
}
@@ -220,6 +225,25 @@ func (i *imlServiceModule) Get(ctx context.Context, id string) (*service_dto.Ser
s.Tags = auto.List(utils.SliceToSlice(tags, func(p *service_tag.Tag) string {
return p.Tid
}))
+ if s.Model == "" {
+ switch s.ProviderType {
+ case "online":
+ p, has := model_runtime.GetProvider(s.Provider.Id)
+ if has {
+ m, has := p.DefaultModel(model_runtime.ModelTypeLLM)
+ if has {
+ s.Model = m.ID()
+ }
+ }
+ case "local":
+ info, err := i.localModelService.DefaultModel(ctx)
+ if err != nil {
+ return nil, err
+ }
+ s.Model = info.Id
+
+ }
+ }
serviceModelMapping, err := i.serviceModelMappingService.GetByService(ctx, id)
if err != nil {
@@ -239,9 +263,9 @@ func (i *imlServiceModule) Search(ctx context.Context, teamID string, keyword st
if err != nil {
return nil, err
}
- list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"team": teamID, "as_server": true}, "update_at desc")
+ list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"team": teamID, "as_server": true}, "create_at desc")
} else {
- list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"as_server": true}, "update_at desc")
+ list, err = i.serviceService.Search(ctx, keyword, map[string]interface{}{"as_server": true}, "create_at desc")
}
if err != nil {
return nil, err
@@ -277,8 +301,16 @@ func toServiceItem(model *service.Service) *service_dto.ServiceItem {
Team: auto.UUID(model.Team),
ServiceKind: model.Kind.String(),
}
+ state := service_dto.FromServiceState(model.State)
+ if state == service_dto.ServiceStateNormal {
+ item.State = model.ServiceType.String()
+ } else {
+ item.State = state.String()
+ }
+
switch model.Kind {
case service.RestService:
+ item.State = model.ServiceType.String()
return item
case service.AIService:
provider := auto.UUID(model.AdditionalConfig["provider"])
@@ -293,6 +325,13 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser
if input.Id == "" {
input.Id = uuid.New().String()
}
+ if teamID == "" {
+ item, err := i.teamService.DefaultTeam(ctx)
+ if err != nil {
+ return nil, err
+ }
+ teamID = item.Id
+ }
mo := &service.Create{
Id: input.Id,
Name: input.Name,
@@ -302,6 +341,7 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser
Catalogue: input.Catalogue,
Prefix: input.Prefix,
Logo: input.Logo,
+ State: service_dto.ServiceState(input.State).Int(),
ApprovalType: service.ApprovalType(input.ApprovalType),
AdditionalConfig: make(map[string]string),
Kind: service.Kind(input.Kind),
@@ -315,6 +355,11 @@ func (i *imlServiceModule) Create(ctx context.Context, teamID string, input *ser
return nil, fmt.Errorf("ai service: provider can not be empty")
}
mo.AdditionalConfig["provider"] = *input.Provider
+ if input.Model == nil {
+ return nil, fmt.Errorf("ai service: model can not be empty")
+ }
+ mo.AdditionalConfig["model"] = *input.Model
+
}
if input.AsApp == nil {
// 默认值为false
@@ -374,6 +419,9 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d
if input.Provider != nil {
info.AdditionalConfig["provider"] = *input.Provider
}
+ if input.Model != nil {
+ info.AdditionalConfig["model"] = *input.Model
+ }
}
err = i.transaction.Transaction(ctx, func(ctx context.Context) error {
serviceType := (*service.ServiceType)(input.ServiceType)
@@ -386,8 +434,7 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d
if input.ApprovalType != nil {
approvalType = service.ApprovalType(*input.ApprovalType)
}
-
- err = i.serviceService.Save(ctx, id, &service.Edit{
+ editCfg := &service.Edit{
Name: input.Name,
Description: input.Description,
Logo: input.Logo,
@@ -395,7 +442,13 @@ func (i *imlServiceModule) Edit(ctx context.Context, id string, input *service_d
Catalogue: input.Catalogue,
AdditionalConfig: &info.AdditionalConfig,
ApprovalType: &approvalType,
- })
+ }
+ if input.State != nil {
+ state := service_dto.ServiceState(*input.State).Int()
+ editCfg.State = &state
+ }
+
+ err = i.serviceService.Save(ctx, id, editCfg)
if err != nil {
return err
}
diff --git a/module/system/dto/input.go b/module/system/dto/input.go
index 1b7d8f35..ddb8c615 100644
--- a/module/system/dto/input.go
+++ b/module/system/dto/input.go
@@ -6,14 +6,24 @@ import (
)
type InputSetting struct {
- InvokeAddress string `json:"invoke_address" key:"system.node.invoke_address"`
- SitePrefix string `json:"site_prefix" key:"system.setting.site_prefix"`
+ InvokeAddress *string `json:"invoke_address" key:"system.node.invoke_address"`
+ SitePrefix *string `json:"site_prefix" key:"system.setting.site_prefix"`
+ OllamaAddress *string `json:"ollama_address" key:"system.ai_model.ollama_address"`
}
func (i *InputSetting) Validate() error {
- _, err := url.Parse(i.InvokeAddress)
- if err != nil {
- return err
+ if i.InvokeAddress != nil {
+ _, err := url.Parse(*i.InvokeAddress)
+ if err != nil {
+ return err
+ }
+ }
+
+ if i.OllamaAddress != nil {
+ _, err := url.Parse(*i.OllamaAddress)
+ if err != nil {
+ return err
+ }
}
return nil
}
@@ -31,9 +41,18 @@ func ToKeyMap(i interface{}) map[string]string {
{
for i := 0; i < typ.NumField(); i++ {
f := typ.Field(i)
- if f.Tag.Get("key") != "" {
- result[f.Tag.Get("key")] = val.Field(i).String()
+ v := val.Field(i)
+ if f.Type.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ continue
+ }
+ v = v.Elem()
}
+
+ if f.Tag.Get("key") != "" {
+ result[f.Tag.Get("key")] = v.String()
+ }
+
}
}
}
diff --git a/module/system/dto/input_test.go b/module/system/dto/input_test.go
index e33908ed..1b3838de 100644
--- a/module/system/dto/input_test.go
+++ b/module/system/dto/input_test.go
@@ -6,9 +6,11 @@ import (
)
func TestMap(t *testing.T) {
-
+ invokeAddress := "http://127.0.0.1:8080"
+ ollamaAddress := "http://127.0.0.1:8081"
input := &InputSetting{
- InvokeAddress: "http://127.0.0.1:8080",
+ InvokeAddress: &invokeAddress,
+ OllamaAddress: &ollamaAddress,
}
err := input.Validate()
if err != nil {
diff --git a/module/system/dto/output.go b/module/system/dto/output.go
index d6ea63d3..2185f57a 100644
--- a/module/system/dto/output.go
+++ b/module/system/dto/output.go
@@ -8,6 +8,7 @@ import (
type Setting struct {
InvokeAddress string `json:"invoke_address" key:"system.node.invoke_address"`
SitePrefix string `json:"site_prefix" key:"system.setting.site_prefix"`
+ OllamaAddress string `json:"ollama_address" key:"system.ai_model.ollama_address"`
}
func MapStringToStruct[T any](m map[string]string) *T {
diff --git a/module/system/iml.go b/module/system/iml.go
index b83cefe5..b74a231b 100644
--- a/module/system/iml.go
+++ b/module/system/iml.go
@@ -3,6 +3,11 @@ package system
import (
"context"
+ "github.com/eolinker/go-common/server"
+
+ ai_provider_local "github.com/APIParkLab/APIPark/ai-provider/local"
+ "github.com/eolinker/go-common/register"
+
"github.com/eolinker/go-common/store"
"github.com/eolinker/go-common/utils"
@@ -43,6 +48,21 @@ func (i *imlSettingModule) Set(ctx context.Context, input *system_dto.InputSetti
return err
}
}
+ if input.OllamaAddress != nil {
+ ai_provider_local.ResetOllamaAddress(*input.OllamaAddress)
+ }
return nil
})
}
+
+func (i *imlSettingModule) OnInit() {
+ register.Handle(func(v server.Server) {
+ ctx := context.Background()
+
+ address, has := i.settingService.Get(ctx, "system.ai_model.ollama_address")
+ if has {
+ ai_provider_local.ResetOllamaAddress(address)
+ }
+
+ })
+}
diff --git a/plugins/core/ai-local.go b/plugins/core/ai-local.go
new file mode 100644
index 00000000..3c8f3205
--- /dev/null
+++ b/plugins/core/ai-local.go
@@ -0,0 +1,24 @@
+package core
+
+import (
+ "net/http"
+
+ "github.com/eolinker/go-common/pm3"
+)
+
+func (p *plugin) aiLocalApis() []pm3.Api {
+ return []pm3.Api{
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/can_deploy", []string{"context", "query:keyword"}, []string{"models"}, p.aiLocalController.ListCanInstall),
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/list", []string{"context", "query:keyword"}, []string{"models"}, p.aiLocalController.Search),
+ pm3.CreateApiSimple(http.MethodPost, "/api/v1/model/local/deploy", p.aiLocalController.Deploy),
+ pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/model/local/deploy/start", []string{"context", "body"}, nil, p.aiLocalController.DeployStart),
+ pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/model/local/cancel_deploy", []string{"context", "body"}, nil, p.aiLocalController.CancelDeploy),
+ pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/model/local", []string{"context", "query:model"}, nil, p.aiLocalController.RemoveModel),
+ pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/model/local/info", []string{"context", "query:model", "body"}, nil, p.aiLocalController.Update),
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/state", []string{"context", "query:model"}, []string{"state", "info"}, p.aiLocalController.State),
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/models/local/configured", []string{"context"}, []string{"models"}, p.aiLocalController.SimpleList),
+
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/model/local/source/ollama", []string{"context"}, []string{"config"}, p.aiLocalController.OllamaConfig),
+ pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/model/local/source/ollama", []string{"context", "body"}, nil, p.aiLocalController.OllamaConfigUpdate),
+ }
+}
diff --git a/plugins/core/ai.go b/plugins/core/ai.go
index e42a09db..6bf7af69 100644
--- a/plugins/core/ai.go
+++ b/plugins/core/ai.go
@@ -11,13 +11,13 @@ import (
func (p *plugin) aiAPIs() []pm3.Api {
return []pm3.Api{
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/unconfigured", []string{"context"}, []string{"providers"}, p.aiProviderController.UnConfiguredProviders, access.SystemSettingsAiProviderView),
- pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.ConfiguredProviders, access.SystemSettingsAiProviderView),
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/providers/configured", []string{"context", "query:keyword"}, []string{"providers"}, p.aiProviderController.ConfiguredProviders, access.SystemSettingsAiProviderView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers", []string{"context"}, []string{"providers"}, p.aiProviderController.SimpleProviders),
- pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers/configured", []string{"context"}, []string{"providers", "backup"}, p.aiProviderController.SimpleConfiguredProviders),
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/ai/providers/configured", []string{"context", "query:all"}, []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.MethodDelete, "/api/v1/ai/provider", []string{"context", "query:provider"}, nil, p.aiProviderController.Delete),
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/provider/config", []string{"context", "query:provider", "body"}, nil, p.aiProviderController.UpdateProviderConfig, access.SystemSettingsAiProviderManager),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/apis", []string{"context", "query:keyword", "query:provider", "query:start", "query:end", "query:page", "query:page_size", "query:sort", "query:asc", "query:models", "query:services"}, []string{"apis", "condition", "total"}, p.aiStatisticController.APIs),
@@ -36,3 +36,12 @@ func (p *plugin) aiKeyApis() []pm3.Api {
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/resource/key/sort", []string{"context", "query:provider", "body"}, nil, p.aiKeyController.Sort),
}
}
+
+func (p *plugin) aiBalanceAPIs() []pm3.Api {
+ return []pm3.Api{
+ pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai/balances", []string{"context", "query:keyword"}, []string{"list"}, p.aiBalanceController.List),
+ pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai/balance/sort", []string{"context", "body"}, nil, p.aiBalanceController.Sort),
+ pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/ai/balance", []string{"context", "body"}, nil, p.aiBalanceController.Create),
+ pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/ai/balance", []string{"context", "query:id"}, nil, p.aiBalanceController.Delete),
+ }
+}
diff --git a/plugins/core/core.go b/plugins/core/core.go
index 55d8c3f2..be4e3f00 100644
--- a/plugins/core/core.go
+++ b/plugins/core/core.go
@@ -3,6 +3,10 @@ package core
import (
"net/http"
+ ai_balance "github.com/APIParkLab/APIPark/controller/ai-balance"
+
+ ai_local "github.com/APIParkLab/APIPark/controller/ai-local"
+
ai_key "github.com/APIParkLab/APIPark/controller/ai-key"
"github.com/APIParkLab/APIPark/controller/log"
@@ -78,6 +82,8 @@ type plugin struct {
aiAPIController ai_api.IAPIController `autowired:""`
aiStatisticController ai.IStatisticController `autowired:""`
aiKeyController ai_key.IKeyController `autowired:""`
+ aiBalanceController ai_balance.IBalanceController `autowired:""`
+ aiLocalController ai_local.ILocalModelController `autowired:""`
apiDocController router.IAPIDocController `autowired:""`
subscribeController subscribe.ISubscribeController `autowired:""`
strategyController strategy.IStrategyController `autowired:""`
@@ -118,6 +124,8 @@ func (p *plugin) OnComplete() {
p.apis = append(p.apis, p.aiKeyApis()...)
p.apis = append(p.apis, p.strategyApis()...)
p.apis = append(p.apis, p.logApis()...)
+ p.apis = append(p.apis, p.aiLocalApis()...)
+ p.apis = append(p.apis, p.aiBalanceAPIs()...)
}
func (p *plugin) Name() string {
diff --git a/plugins/core/service.go b/plugins/core/service.go
index e929e6ef..82ac2107 100644
--- a/plugins/core/service.go
+++ b/plugins/core/service.go
@@ -20,6 +20,9 @@ func (p *plugin) ServiceApis() []pm3.Api {
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/services", []string{"context", "query:team", "query:keyword"}, []string{"services"}, p.serviceController.Search, access.SystemWorkspaceServiceViewAll, access.TeamTeamServiceView),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/services", []string{"context"}, []string{"services"}, p.serviceController.Simple),
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/services/mine", []string{"context"}, []string{"services"}, p.serviceController.MySimple),
+ pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/quick/service/rest", []string{"context"}, []string{}, p.serviceController.QuickCreateRestfulService, access.SystemWorkspaceServiceManagerAll, access.TeamTeamServiceManager),
+ pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/quick/service/ai", []string{"context", "body"}, []string{}, p.serviceController.QuickCreateAIService, access.SystemWorkspaceServiceManagerAll, access.TeamTeamServiceManager),
+
// 应用相关
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/app/info", []string{"context", "query:app"}, []string{"app"}, p.appController.GetApp, access.SystemWorkspaceApplicationViewAll, access.TeamTeamConsumerView),
pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/app", []string{"context", "query:app"}, nil, p.appController.DeleteApp, access.SystemWorkspaceApplicationManagerAll, access.TeamTeamConsumerManager),
diff --git a/resources/access/access.yaml b/resources/access/access.yaml
index 504b01d1..64bbd001 100644
--- a/resources/access/access.yaml
+++ b/resources/access/access.yaml
@@ -73,6 +73,17 @@ system:
value: 'manager'
dependents:
- system.settings.account.view
+ - name: ai balance
+ value: 'ai_balance'
+ children:
+ - name: view
+ value: 'view'
+ guest_allow: true
+ - name: manager
+ value: 'manager'
+ dependents:
+ - system.settings.ai_balance.view
+
- name: role
value: 'role'
children:
diff --git a/resources/access/role.yaml b/resources/access/role.yaml
index b9f3ebe4..27c30a2e 100644
--- a/resources/access/role.yaml
+++ b/resources/access/role.yaml
@@ -8,6 +8,8 @@ system:
- system.settings.account.view
- system.settings.ai_api.manager
- system.settings.ai_api.view
+ - system.settings.ai_balance.view
+ - system.settings.ai_balance.manager
- system.settings.ai_key_resource.manager
- system.settings.ai_key_resource.view
- system.settings.ai_log.manager
@@ -47,6 +49,8 @@ system:
- system.api_portal.api_portal.view
- system.settings.ai_api.manager
- system.settings.ai_api.view
+ - system.settings.ai_balance.view
+ - system.settings.ai_balance.manager
- system.settings.ai_key_resource.manager
- system.settings.ai_key_resource.view
- system.settings.ai_log.manager
diff --git a/service/ai-api/iml.go b/service/ai-api/iml.go
index b59a004e..358e99f5 100644
--- a/service/ai-api/iml.go
+++ b/service/ai-api/iml.go
@@ -23,6 +23,26 @@ type imlAPIService struct {
universally.IServiceDelete
}
+func (i *imlAPIService) CountByProvider(ctx context.Context, provider string) (int64, error) {
+ return i.store.Count(ctx, "", map[string]interface{}{"provider": provider})
+}
+
+func (i *imlAPIService) CountByModel(ctx context.Context, model string) (int64, error) {
+ return i.store.Count(ctx, "", map[string]interface{}{"model": model})
+}
+
+func (i *imlAPIService) DeleteByService(ctx context.Context, serviceId string) error {
+ _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"service": serviceId})
+ if err != nil {
+ return nil
+ }
+ return err
+}
+
+func (i *imlAPIService) CountMapByModel(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
+ return i.store.CountByGroup(ctx, keyword, conditions, "model")
+}
+
func (i *imlAPIService) CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
return i.store.CountByGroup(ctx, keyword, conditions, "provider")
}
@@ -30,7 +50,7 @@ func (i *imlAPIService) CountMapByProvider(ctx context.Context, keyword string,
func (i *imlAPIService) OnComplete() {
i.IServiceGet = universally.NewGetSoftDelete[API, api.AiAPIInfo](i.store, FromEntity)
i.IServiceCreate = universally.NewCreatorSoftDelete[Create, api.AiAPIInfo](i.store, "ai_api_info", createEntityHandler, uniquestHandler, labelHandler)
- i.IServiceEdit = universally.NewEdit[Edit, api.AiAPIInfo](i.store, updateHandler)
+ i.IServiceEdit = universally.NewEdit[Edit, api.AiAPIInfo](i.store, updateHandler, labelHandler)
i.IServiceDelete = universally.NewSoftDelete[api.AiAPIInfo](i.store)
}
@@ -54,6 +74,7 @@ func createEntityHandler(i *Create) *api.AiAPIInfo {
Model: i.Model,
Provider: i.Provider,
Disable: i.Disable,
+ Type: i.Type,
CreateAt: now,
UpdateAt: now,
AdditionalConfig: string(cfg),
@@ -91,6 +112,9 @@ func updateHandler(e *api.AiAPIInfo, i *Edit) {
if i.UseToken != nil {
e.UseToken = *i.UseToken
}
+ if i.Type != nil {
+ e.Type = *i.Type
+ }
e.UpdateAt = time.Now()
}
diff --git a/service/ai-api/model.go b/service/ai-api/model.go
index b8ee9cb7..8364c02f 100644
--- a/service/ai-api/model.go
+++ b/service/ai-api/model.go
@@ -22,6 +22,7 @@ type API struct {
UseToken int
Creator string
Updater string
+ Type int
AdditionalConfig map[string]interface{}
Disable bool
}
@@ -37,6 +38,7 @@ type Create struct {
Model string
Provider string
AdditionalConfig map[string]interface{}
+ Type int
Disable bool
}
@@ -50,6 +52,7 @@ type Edit struct {
Model *string
Disable *bool
UseToken *int
+ Type *int
AdditionalConfig *map[string]interface{}
}
@@ -73,6 +76,7 @@ func FromEntity(e *api.AiAPIInfo) *API {
Updater: e.Updater,
Disable: e.Disable,
UseToken: e.UseToken,
+ Type: e.Type,
AdditionalConfig: cfg,
}
}
diff --git a/service/ai-api/service.go b/service/ai-api/service.go
index de841fda..62ef5389 100644
--- a/service/ai-api/service.go
+++ b/service/ai-api/service.go
@@ -14,6 +14,10 @@ type IAPIService interface {
universally.IServiceEdit[Edit]
universally.IServiceDelete
CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
+ CountMapByModel(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
+ CountByModel(ctx context.Context, model string) (int64, error)
+ CountByProvider(ctx context.Context, provider string) (int64, error)
+ DeleteByService(ctx context.Context, serviceId string) error
}
type IAPIUseService interface {
diff --git a/service/ai-balance/iml.go b/service/ai-balance/iml.go
new file mode 100644
index 00000000..6adb1839
--- /dev/null
+++ b/service/ai-balance/iml.go
@@ -0,0 +1,186 @@
+package ai_balance
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "sort"
+ "time"
+
+ "gorm.io/gorm"
+
+ "github.com/eolinker/go-common/store"
+
+ "github.com/APIParkLab/APIPark/service/universally"
+ "github.com/APIParkLab/APIPark/stores/ai"
+)
+
+var _ IBalanceService = (*imlBalanceService)(nil)
+
+type imlBalanceService struct {
+ store ai.IBalanceStore `autowired:""`
+ transaction store.ITransaction `autowired:""`
+ universally.IServiceGet[Balance]
+ universally.IServiceCreate[Create]
+ universally.IServiceEdit[Edit]
+ universally.IServiceDelete
+}
+
+func (i *imlBalanceService) Exist(ctx context.Context, provider string, model string) (bool, error) {
+ _, err := i.store.First(ctx, map[string]interface{}{"provider": provider, "model": model})
+ if err != nil {
+ if !errors.Is(err, gorm.ErrRecordNotFound) {
+ return false, err
+ }
+ return false, nil
+ }
+ return true, nil
+}
+
+func (i *imlBalanceService) OnComplete() {
+ i.IServiceGet = universally.NewGet[Balance, ai.Balance](i.store, FromEntity)
+ i.IServiceCreate = universally.NewCreator[Create, ai.Balance](i.store, "ai_balance", createEntityHandler, uniquestHandler, labelHandler)
+ i.IServiceEdit = universally.NewEdit[Edit, ai.Balance](i.store, updateHandler, labelHandler)
+ i.IServiceDelete = universally.NewDelete[ai.Balance](i.store)
+}
+
+func (i *imlBalanceService) MaxPriority(ctx context.Context) (int, error) {
+ info, err := i.store.First(ctx, nil, "priority desc")
+ if err != nil {
+ return 0, err
+ }
+ return info.Priority, nil
+}
+
+func (i *imlBalanceService) SortBefore(ctx context.Context, originID string, targetID string) ([]*Balance, error) {
+ originKey, err := i.store.GetByUUID(ctx, originID)
+ if err != nil {
+ return nil, fmt.Errorf("get key error: %v,id is %s", err, originID)
+ }
+ targetKey, err := i.store.GetByUUID(ctx, targetID)
+ if err != nil {
+ return nil, fmt.Errorf("get key error: %v,id is %s", err, targetID)
+ }
+ originKeySort, targetKeySort := originKey.Priority, targetKey.Priority
+ // 初始化顺序,假设原始Key在目标Key之后,中间的key往后移动,原始Key移动到`targetKeySort`位置
+ originKey.Priority = targetKeySort
+ fn := func(priority int) int {
+ return priority + 1
+ }
+ sql := "priority < ? and priority >= ?"
+ if originKeySort < targetKeySort {
+ // 如果原始Key在目标Key之前,中间的key往前移动,原始Key移动到`targetKeySort - 1`位置
+ sql = "priority > ? and priority < ?"
+ originKey.Priority = targetKeySort - 1
+ fn = func(priority int) int {
+ return priority - 1
+ }
+ }
+ list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "priority asc")
+ if err != nil {
+ return nil, err
+ }
+ result := make([]*Balance, 0, len(list)+1)
+ err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
+ for _, l := range list {
+ l.Priority = fn(l.Priority)
+ _, err := i.store.Update(ctx, l)
+ if err != nil {
+ return err
+ }
+ result = append(result, FromEntity(l))
+ }
+ _, err = i.store.Update(ctx, originKey)
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, FromEntity(originKey))
+ sort.Slice(list, func(i, j int) bool { return list[i].Priority < list[j].Priority })
+ return result, nil
+}
+
+func (i *imlBalanceService) SortAfter(ctx context.Context, originID string, targetID string) ([]*Balance, error) {
+ originKey, err := i.store.GetByUUID(ctx, originID)
+ if err != nil {
+ return nil, fmt.Errorf("get key error: %v,id is %s", err, originID)
+ }
+ targetKey, err := i.store.GetByUUID(ctx, targetID)
+ if err != nil {
+ return nil, fmt.Errorf("get key error: %v,id is %s", err, targetID)
+ }
+ originKeySort, targetKeySort := originKey.Priority, targetKey.Priority
+ // 初始化顺序,假设原始Key在目标Key之后,中间的Key往后移动,原始Key移动到`targetKeySort + 1`位置
+ originKey.Priority = targetKeySort + 1
+ fn := func(priority int) int {
+ return priority + 1
+ }
+ sql := "priority < ? and priority > ?"
+ if originKeySort < targetKeySort {
+ // 如果原始Key在目标Key之前,中间的Key往前移动,原始Key移动到`targetKeySort`位置
+ sql = "priority > ? and priority <= ?"
+ originKey.Priority = targetKeySort
+ fn = func(priority int) int {
+ return priority - 1
+ }
+ }
+ list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "priority asc")
+ if err != nil {
+ return nil, err
+ }
+ result := make([]*Balance, 0, len(list)+1)
+ err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
+ for _, l := range list {
+ l.Priority = fn(l.Priority)
+ _, err = i.store.Update(ctx, l)
+ if err != nil {
+ return err
+ }
+ result = append(result, FromEntity(l))
+ }
+ _, err = i.store.Update(ctx, originKey)
+ return err
+ })
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, FromEntity(originKey))
+ sort.Slice(list, func(i, j int) bool { return list[i].Priority < list[j].Priority })
+ return result, nil
+}
+
+func createEntityHandler(i *Create) *ai.Balance {
+ now := time.Now()
+ return &ai.Balance{
+ Uuid: i.Id,
+ Provider: i.Provider,
+ ProviderName: i.ProviderName,
+ Model: i.Model,
+ ModelName: i.ModelName,
+ Type: i.Type,
+ Priority: i.Priority,
+ CreateAt: now,
+ UpdateAt: now,
+ }
+}
+
+func uniquestHandler(i *Create) []map[string]interface{} {
+ return []map[string]interface{}{{"uuid": i.Id}}
+}
+
+func labelHandler(e *ai.Balance) []string {
+ return []string{e.ProviderName, e.ModelName}
+}
+
+func updateHandler(e *ai.Balance, i *Edit) {
+ if i.Priority != nil {
+ e.Priority = *i.Priority
+ }
+
+ if i.State != nil {
+ e.State = *i.State
+ }
+
+ e.UpdateAt = time.Now()
+}
diff --git a/service/ai-balance/model.go b/service/ai-balance/model.go
new file mode 100644
index 00000000..0dabaa03
--- /dev/null
+++ b/service/ai-balance/model.go
@@ -0,0 +1,54 @@
+package ai_balance
+
+import (
+ "time"
+
+ "github.com/APIParkLab/APIPark/stores/ai"
+)
+
+type Balance struct {
+ Id string
+ Priority int
+ Provider string
+ ProviderName string
+ Model string
+ ModelName string
+ Type int
+ State int
+ Creator string
+ Updater string
+ CreateAt time.Time
+ UpdateAt time.Time
+}
+
+func FromEntity(e *ai.Balance) *Balance {
+ return &Balance{
+ Id: e.Uuid,
+ Priority: e.Priority,
+ Provider: e.Provider,
+ ProviderName: e.ProviderName,
+ Model: e.Model,
+ ModelName: e.ModelName,
+ Type: e.Type,
+ State: e.State,
+ Creator: e.Creator,
+ Updater: e.Updater,
+ CreateAt: e.CreateAt,
+ UpdateAt: e.UpdateAt,
+ }
+}
+
+type Create struct {
+ Id string
+ Priority int
+ Provider string
+ ProviderName string
+ Model string
+ ModelName string
+ Type int
+}
+
+type Edit struct {
+ Priority *int
+ State *int
+}
diff --git a/service/ai-balance/service.go b/service/ai-balance/service.go
new file mode 100644
index 00000000..b4ea9926
--- /dev/null
+++ b/service/ai-balance/service.go
@@ -0,0 +1,27 @@
+package ai_balance
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/eolinker/go-common/autowire"
+
+ "github.com/APIParkLab/APIPark/service/universally"
+)
+
+type IBalanceService interface {
+ universally.IServiceGet[Balance]
+ universally.IServiceCreate[Create]
+ universally.IServiceEdit[Edit]
+ universally.IServiceDelete
+ MaxPriority(ctx context.Context) (int, error)
+ SortBefore(ctx context.Context, originID string, targetID string) ([]*Balance, error)
+ SortAfter(ctx context.Context, originID string, targetID string) ([]*Balance, error)
+ Exist(ctx context.Context, provider string, model string) (bool, error)
+}
+
+func init() {
+ autowire.Auto[IBalanceService](func() reflect.Value {
+ return reflect.ValueOf(new(imlBalanceService))
+ })
+}
diff --git a/service/ai-key/iml.go b/service/ai-key/iml.go
index 302ec182..29c27214 100644
--- a/service/ai-key/iml.go
+++ b/service/ai-key/iml.go
@@ -23,6 +23,18 @@ type imlAIKeyService struct {
universally.IServiceDelete
}
+func (i *imlAIKeyService) DeleteByProvider(ctx context.Context, providerId string) error {
+ _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"provider": providerId})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (i *imlAIKeyService) CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error) {
+ return i.store.CountByGroup(ctx, keyword, conditions, "provider")
+}
+
func (i *imlAIKeyService) IncrUseToken(ctx context.Context, id string, useToken int) error {
info, err := i.store.GetByUUID(ctx, id)
if err != nil {
@@ -111,16 +123,16 @@ func (i *imlAIKeyService) SortBefore(ctx context.Context, provider string, origi
fn := func(priority int) int {
return priority + 1
}
- sql := "sort < ? and sort >= ?"
+ sql := "provider = ? and sort < ? and sort >= ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的key往前移动,原始Key移动到`targetKeySort - 1`位置
- sql = "sort > ? and sort < ?"
+ sql = "provider = ? and sort > ? and sort < ?"
originKey.Sort = targetKeySort - 1
fn = func(priority int) int {
return priority - 1
}
}
- list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
+ list, err := i.store.ListQuery(ctx, sql, []interface{}{provider, originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
@@ -160,16 +172,16 @@ func (i *imlAIKeyService) SortAfter(ctx context.Context, provider string, origin
fn := func(priority int) int {
return priority + 1
}
- sql := "sort < ? and sort > ?"
+ sql := "provider = ? and sort < ? and sort > ?"
if originKeySort < targetKeySort {
// 如果原始Key在目标Key之前,中间的Key往前移动,原始Key移动到`targetKeySort`位置
- sql = "sort > ? and sort <= ?"
+ sql = "provider = ? and sort > ? and sort <= ?"
originKey.Sort = targetKeySort
fn = func(priority int) int {
return priority - 1
}
}
- list, err := i.store.ListQuery(ctx, sql, []interface{}{originKeySort, targetKeySort}, "sort asc")
+ list, err := i.store.ListQuery(ctx, sql, []interface{}{provider, originKeySort, targetKeySort}, "sort asc")
if err != nil {
return nil, err
}
@@ -196,8 +208,8 @@ func (i *imlAIKeyService) SortAfter(ctx context.Context, provider string, origin
func (i *imlAIKeyService) OnComplete() {
i.IServiceGet = universally.NewGet[Key, ai.Key](i.store, FromEntity)
- i.IServiceCreate = universally.NewCreator[Create, ai.Key](i.store, "ai_api_info", createEntityHandler, uniquestHandler, labelHandler)
- i.IServiceEdit = universally.NewEdit[Edit, ai.Key](i.store, updateHandler)
+ i.IServiceCreate = universally.NewCreator[Create, ai.Key](i.store, "ai_key", createEntityHandler, uniquestHandler, labelHandler)
+ i.IServiceEdit = universally.NewEdit[Edit, ai.Key](i.store, updateHandler, labelHandler)
i.IServiceDelete = universally.NewDelete[ai.Key](i.store)
}
diff --git a/service/ai-key/service.go b/service/ai-key/service.go
index 8bbe6d83..aac33499 100644
--- a/service/ai-key/service.go
+++ b/service/ai-key/service.go
@@ -14,8 +14,10 @@ type IKeyService interface {
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
universally.IServiceDelete
+ DeleteByProvider(ctx context.Context, providerId string) error
DefaultKey(ctx context.Context, providerId string) (*Key, error)
KeysByProvider(ctx context.Context, providerId string) ([]*Key, error)
+ CountMapByProvider(ctx context.Context, keyword string, conditions map[string]interface{}) (map[string]int64, error)
MaxPriority(ctx context.Context, providerId string) (int, error)
SortBefore(ctx context.Context, provider string, originID string, targetID string) ([]*Key, error)
SortAfter(ctx context.Context, provider string, originID string, targetID string) ([]*Key, error)
diff --git a/service/ai-local/iml.go b/service/ai-local/iml.go
new file mode 100644
index 00000000..5d21cc2b
--- /dev/null
+++ b/service/ai-local/iml.go
@@ -0,0 +1,252 @@
+package ai_local
+
+import (
+ "context"
+ "time"
+
+ "github.com/eolinker/go-common/utils"
+
+ "github.com/APIParkLab/APIPark/service/universally"
+ "github.com/APIParkLab/APIPark/stores/ai"
+)
+
+var _ ILocalModelService = &imlLocalModelService{}
+
+type imlLocalModelService struct {
+ store ai.ILocalModelStore `autowired:""`
+ universally.IServiceGet[LocalModel]
+ universally.IServiceCreate[CreateLocalModel]
+ universally.IServiceEdit[EditLocalModel]
+ universally.IServiceDelete
+}
+
+func (i *imlLocalModelService) DefaultModel(ctx context.Context) (*LocalModel, error) {
+ info, err := i.store.First(ctx, map[string]interface{}{"state": 1})
+ if err != nil {
+ return nil, err
+ }
+ return i.fromEntity(info), nil
+}
+
+func (i *imlLocalModelService) OnComplete() {
+ i.IServiceGet = universally.NewGet[LocalModel, ai.LocalModel](i.store, i.fromEntity)
+ i.IServiceCreate = universally.NewCreator[CreateLocalModel, ai.LocalModel](i.store, "ai_local_model", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
+ i.IServiceEdit = universally.NewEdit[EditLocalModel, ai.LocalModel](i.store, i.updateHandler, i.labelHandler)
+ i.IServiceDelete = universally.NewDelete[ai.LocalModel](i.store)
+}
+
+func (i *imlLocalModelService) labelHandler(e *ai.LocalModel) []string {
+ return []string{e.Name}
+}
+func (i *imlLocalModelService) uniquestHandler(c *CreateLocalModel) []map[string]interface{} {
+ return []map[string]interface{}{{"uuid": c.Id}}
+}
+func (i *imlLocalModelService) createEntityHandler(c *CreateLocalModel) *ai.LocalModel {
+ now := time.Now()
+ return &ai.LocalModel{
+ Uuid: c.Id,
+ Name: c.Name,
+ Provider: c.Provider,
+ State: c.State,
+ CreateAt: now,
+ UpdateAt: now,
+ }
+}
+
+func (i *imlLocalModelService) updateHandler(e *ai.LocalModel, c *EditLocalModel) {
+ if c.State != nil {
+ e.State = *c.State
+ }
+ e.UpdateAt = time.Now()
+
+}
+
+func (i *imlLocalModelService) fromEntity(e *ai.LocalModel) *LocalModel {
+ return &LocalModel{
+ Id: e.Uuid,
+ Name: e.Name,
+ Provider: e.Provider,
+ State: e.State,
+ CreateAt: e.CreateAt,
+ UpdateAt: e.UpdateAt,
+ Creator: e.Creator,
+ Updater: e.Updater,
+ }
+}
+
+var (
+ _ ILocalModelPackageService = &imlLocalModelPackageService{}
+)
+
+type imlLocalModelPackageService struct {
+ store ai.ILocalModelPackageStore `autowired:""`
+ universally.IServiceGet[LocalModelPackage]
+ universally.IServiceCreate[CreateLocalModelPackage]
+ universally.IServiceEdit[EditLocalModelPackage]
+ universally.IServiceDelete
+}
+
+func (i *imlLocalModelPackageService) OnComplete() {
+ i.IServiceGet = universally.NewGet[LocalModelPackage, ai.LocalModelPackage](i.store, i.fromEntity)
+ i.IServiceCreate = universally.NewCreator[CreateLocalModelPackage, ai.LocalModelPackage](i.store, "ai_local_model_package", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
+ i.IServiceEdit = universally.NewEdit[EditLocalModelPackage, ai.LocalModelPackage](i.store, i.updateHandler, i.labelHandler)
+ i.IServiceDelete = universally.NewDelete[ai.LocalModelPackage](i.store)
+}
+
+func (i *imlLocalModelPackageService) labelHandler(e *ai.LocalModelPackage) []string {
+ return []string{e.Uuid}
+}
+
+func (i *imlLocalModelPackageService) uniquestHandler(c *CreateLocalModelPackage) []map[string]interface{} {
+ return []map[string]interface{}{{"uuid": c.Id}}
+}
+
+func (i *imlLocalModelPackageService) createEntityHandler(c *CreateLocalModelPackage) *ai.LocalModelPackage {
+ return &ai.LocalModelPackage{
+ Uuid: c.Id,
+ Name: c.Name,
+ Size: c.Size,
+ Hash: c.Hash,
+ Description: c.Description,
+ IsPopular: c.Popular,
+ }
+}
+
+func (i *imlLocalModelPackageService) updateHandler(e *ai.LocalModelPackage, c *EditLocalModelPackage) {
+ if c.Size != nil {
+ e.Size = *c.Size
+ }
+ if c.Hash != nil {
+ e.Hash = *c.Hash
+ }
+ if c.Description != nil {
+ e.Description = *c.Description
+ }
+ if c.Popular != nil {
+ e.IsPopular = *c.Popular
+ }
+ if c.Version != nil {
+ e.Version = *c.Version
+ }
+}
+
+func (i *imlLocalModelPackageService) fromEntity(e *ai.LocalModelPackage) *LocalModelPackage {
+ return &LocalModelPackage{
+ Id: e.Uuid,
+ Name: e.Name,
+ Size: e.Size,
+ Hash: e.Hash,
+ Description: e.Description,
+ Version: e.Version,
+ IsPopular: e.IsPopular,
+ }
+}
+
+type imlLocalModelInstallStateService struct {
+ store ai.ILocalModelInstallStateStore `autowired:""`
+ universally.IServiceGet[LocalModelInstallState]
+ universally.IServiceCreate[CreateLocalModelInstallState]
+ universally.IServiceEdit[EditLocalModelInstallState]
+ universally.IServiceDelete
+}
+
+func (i *imlLocalModelInstallStateService) OnComplete() {
+ i.IServiceGet = universally.NewGet[LocalModelInstallState, ai.LocalModelInstallState](i.store, i.fromEntity)
+ i.IServiceCreate = universally.NewCreator[CreateLocalModelInstallState, ai.LocalModelInstallState](i.store, "ai_local_model_install_state", i.createEntityHandler, i.uniquestHandler, i.labelHandler)
+ i.IServiceEdit = universally.NewEdit[EditLocalModelInstallState, ai.LocalModelInstallState](i.store, i.updateHandler, i.labelHandler)
+ i.IServiceDelete = universally.NewDelete[ai.LocalModelInstallState](i.store)
+}
+
+func (i *imlLocalModelInstallStateService) fromEntity(e *ai.LocalModelInstallState) *LocalModelInstallState {
+ return &LocalModelInstallState{
+ Id: e.Uuid,
+ Complete: e.Complete,
+ Total: e.Total,
+ State: e.State,
+ Msg: e.LastMsg,
+ UpdateAt: e.UpdateAt,
+ }
+}
+
+func (i *imlLocalModelInstallStateService) labelHandler(e *ai.LocalModelInstallState) []string {
+ return []string{e.Uuid}
+}
+
+func (i *imlLocalModelInstallStateService) uniquestHandler(c *CreateLocalModelInstallState) []map[string]interface{} {
+ return []map[string]interface{}{{"uuid": c.Id}}
+}
+
+func (i *imlLocalModelInstallStateService) createEntityHandler(c *CreateLocalModelInstallState) *ai.LocalModelInstallState {
+ return &ai.LocalModelInstallState{
+ Uuid: c.Id,
+ Complete: c.Complete,
+ Total: c.Total,
+ State: c.State,
+ LastMsg: c.Msg,
+ UpdateAt: time.Now(),
+ }
+}
+
+func (i *imlLocalModelInstallStateService) updateHandler(e *ai.LocalModelInstallState, c *EditLocalModelInstallState) {
+ if c.Complete != nil {
+ e.Complete = *c.Complete
+ }
+ if c.Total != nil {
+ e.Total = *c.Total
+ }
+ if c.State != nil {
+ e.State = *c.State
+ }
+ if c.Msg != nil {
+ e.LastMsg = *c.Msg
+ }
+ e.UpdateAt = time.Now()
+}
+
+var _ ILocalModelCacheService = &imlLocalModelCacheService{}
+
+type imlLocalModelCacheService struct {
+ store ai.ILocalModelCacheStore `autowired:""`
+}
+
+func (i *imlLocalModelCacheService) GetByTarget(ctx context.Context, typ CacheType, target string) (*LocalModelCache, error) {
+ item, err := i.store.First(ctx, map[string]interface{}{"target": target, "type": typ.Int()})
+ if err != nil {
+ return nil, err
+ }
+ return &LocalModelCache{
+ Model: item.Model,
+ Target: item.Target,
+ Type: CacheType(item.Type),
+ }, nil
+}
+
+func (i *imlLocalModelCacheService) List(ctx context.Context, model string, typ CacheType) ([]*LocalModelCache, error) {
+ list, err := i.store.List(ctx, map[string]interface{}{"model": model, "type": typ.Int()})
+ if err != nil {
+ return nil, err
+ }
+ return utils.SliceToSlice(list, func(s *ai.LocalModelCache) *LocalModelCache {
+ return &LocalModelCache{
+ Model: s.Model,
+ Target: s.Target,
+ Type: CacheType(s.Type),
+ }
+ }), nil
+}
+
+func (i *imlLocalModelCacheService) Delete(ctx context.Context, model string) error {
+ _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"model": model})
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func (i *imlLocalModelCacheService) Save(ctx context.Context, model string, typ CacheType, target string) error {
+ return i.store.Insert(ctx, &ai.LocalModelCache{
+ Model: model,
+ Target: target,
+ Type: typ.Int(),
+ })
+}
diff --git a/service/ai-local/model.go b/service/ai-local/model.go
new file mode 100644
index 00000000..fc51508f
--- /dev/null
+++ b/service/ai-local/model.go
@@ -0,0 +1,111 @@
+package ai_local
+
+import "time"
+
+type LocalModel struct {
+ Id string
+ Name string
+ Provider string
+ State int
+ CreateAt time.Time
+ UpdateAt time.Time
+ Creator string
+ Updater string
+}
+
+type CreateLocalModel struct {
+ Id string
+ Name string
+ Provider string
+ State int
+}
+
+type EditLocalModel struct {
+ State *int
+}
+
+type LocalModelPackage struct {
+ Id string
+ Name string
+ Size string
+ Hash string
+ Description string
+ Version string
+ IsPopular bool
+}
+
+type CreateLocalModelPackage struct {
+ Id string
+ Name string
+ Size string
+ Hash string
+ Description string
+ Version string
+ Popular bool
+}
+
+type EditLocalModelPackage struct {
+ Size *string
+ Hash *string
+ Description *string
+ Version *string
+ Popular *bool
+}
+
+type LocalModelInstallState struct {
+ Id string
+ Complete int64
+ Total int64
+ State int
+ Msg string
+ UpdateAt time.Time
+}
+
+type CreateLocalModelInstallState struct {
+ Id string
+ Complete int64
+ Total int64
+ State int
+ Msg string
+}
+
+type EditLocalModelInstallState struct {
+ Complete *int64
+ Total *int64
+ State *int
+ Msg *string
+}
+
+type LocalModelCache struct {
+ Model string
+ Target string
+ Type CacheType
+}
+
+type CacheType string
+
+func (c CacheType) String() string {
+ return string(c)
+}
+
+func (c CacheType) Int() int {
+ switch c {
+ case CacheTypeService:
+ return 0
+ default:
+ return 0
+ }
+}
+
+func FromCacheType(s int) CacheType {
+ switch s {
+ case 0:
+ return CacheTypeService
+ default:
+ return CacheTypeService
+ }
+}
+
+const (
+ CacheTypeService CacheType = "service"
+)
diff --git a/service/ai-local/service.go b/service/ai-local/service.go
new file mode 100644
index 00000000..2c493b36
--- /dev/null
+++ b/service/ai-local/service.go
@@ -0,0 +1,56 @@
+package ai_local
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/APIParkLab/APIPark/service/universally"
+ "github.com/eolinker/go-common/autowire"
+)
+
+type ILocalModelService interface {
+ universally.IServiceGet[LocalModel]
+ universally.IServiceCreate[CreateLocalModel]
+ universally.IServiceEdit[EditLocalModel]
+ universally.IServiceDelete
+ DefaultModel(ctx context.Context) (*LocalModel, error)
+}
+
+type ILocalModelPackageService interface {
+ universally.IServiceGet[LocalModelPackage]
+ universally.IServiceCreate[CreateLocalModelPackage]
+ universally.IServiceEdit[EditLocalModelPackage]
+ universally.IServiceDelete
+ //SearchByModel(ctx context.Context, model string) ([]*LocalModelPackage, error)
+}
+
+type ILocalModelInstallStateService interface {
+ universally.IServiceGet[LocalModelInstallState]
+ universally.IServiceCreate[CreateLocalModelInstallState]
+ universally.IServiceEdit[EditLocalModelInstallState]
+ universally.IServiceDelete
+}
+
+type ILocalModelCacheService interface {
+ List(ctx context.Context, model string, typ CacheType) ([]*LocalModelCache, error)
+ Delete(ctx context.Context, model string) error
+ Save(ctx context.Context, model string, typ CacheType, target string) error
+ GetByTarget(ctx context.Context, typ CacheType, target string) (*LocalModelCache, error)
+}
+
+func init() {
+ autowire.Auto[ILocalModelService](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelService))
+ })
+ autowire.Auto[ILocalModelPackageService](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelPackageService))
+ })
+
+ autowire.Auto[ILocalModelInstallStateService](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelInstallStateService))
+ })
+
+ autowire.Auto[ILocalModelCacheService](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelCacheService))
+ })
+}
diff --git a/service/ai/iml.go b/service/ai/iml.go
index 453d63a7..6eda08a9 100644
--- a/service/ai/iml.go
+++ b/service/ai/iml.go
@@ -2,90 +2,73 @@ package ai
import (
"context"
- "encoding/base64"
- "errors"
"time"
"github.com/APIParkLab/APIPark/service/universally"
"github.com/APIParkLab/APIPark/stores/ai"
"github.com/eolinker/go-common/auto"
"github.com/eolinker/go-common/utils"
- "gorm.io/gorm"
)
var _ IProviderService = (*imlProviderService)(nil)
type imlProviderService struct {
universally.IServiceGet[Provider]
+ universally.IServiceCreate[CreateProvider]
+ universally.IServiceEdit[SetProvider]
+ universally.IServiceDelete
store ai.IProviderStore `autowired:""`
}
-func (i *imlProviderService) MaxPriority(ctx context.Context) (int, error) {
- t, err := i.store.First(ctx, nil, "priority desc")
- if err != nil {
- return 0, err
- }
- return t.Priority, nil
-}
-
-func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvider) error {
- userId := utils.UserId(ctx)
- now := time.Now()
- info, err := i.store.First(ctx, map[string]interface{}{"uuid": id})
- if err != nil {
- if !errors.Is(err, gorm.ErrRecordNotFound) {
- return err
- }
- if cfg.Name == nil || cfg.Config == nil || cfg.DefaultLLM == nil {
- return errors.New("invalid params")
- }
- status := 1
- if cfg.Status != nil {
- status = *cfg.Status
- }
- priority := 1
- if cfg.Priority == nil {
- count, err := i.store.Count(ctx, "", nil)
- if err != nil {
- return err
- }
- priority = int(count) + 1
- } else {
- priority = *cfg.Priority
- }
- info = &ai.Provider{
- UUID: id,
- Name: *cfg.Name,
- DefaultLLM: *cfg.DefaultLLM,
- Config: base64.RawStdEncoding.EncodeToString([]byte(*cfg.Config)),
- Status: status,
- Creator: userId,
- Updater: userId,
- Priority: priority,
- CreateAt: now,
- UpdateAt: now,
- }
- } else {
- if cfg.Name != nil {
- info.Name = *cfg.Name
- }
- if cfg.Config != nil {
- info.Config = base64.RawStdEncoding.EncodeToString([]byte(*cfg.Config))
- }
- if cfg.DefaultLLM != nil {
- info.DefaultLLM = *cfg.DefaultLLM
- }
- if cfg.Status != nil {
- info.Status = *cfg.Status
- }
- if cfg.Priority != nil {
- info.Priority = *cfg.Priority
- }
- info.Updater = userId
- info.UpdateAt = now
- }
- return i.store.Save(ctx, info)
-}
+//func (i *imlProviderService) Save(ctx context.Context, id string, cfg *SetProvider) error {
+// userId := utils.UserId(ctx)
+// now := time.Now()
+// info, err := i.store.First(ctx, map[string]interface{}{"uuid": id})
+// if err != nil {
+// if !errors.Is(err, gorm.ErrRecordNotFound) {
+// return err
+// }
+// if cfg.Name == nil || cfg.Config == nil || cfg.DefaultLLM == nil {
+// return errors.New("invalid params")
+// }
+// status := 1
+// if cfg.Status != nil {
+// status = *cfg.Status
+// }
+//
+// info = &ai.Provider{
+// UUID: id,
+// Name: *cfg.Name,
+// DefaultLLM: *cfg.DefaultLLM,
+// Config: base64.RawStdEncoding.EncodeToString([]byte(*cfg.Config)),
+// Status: status,
+// Creator: userId,
+// Updater: userId,
+// //Priority: priority,
+// CreateAt: now,
+// UpdateAt: now,
+// }
+// } else {
+// if cfg.Name != nil {
+// info.Name = *cfg.Name
+// }
+// if cfg.Config != nil {
+// info.Config = base64.RawStdEncoding.EncodeToString([]byte(*cfg.Config))
+// }
+// if cfg.DefaultLLM != nil {
+// info.DefaultLLM = *cfg.DefaultLLM
+// }
+// if cfg.Status != nil {
+// info.Status = *cfg.Status
+// }
+// //if cfg.Priority != nil {
+// // info.Priority = *cfg.Priority
+// //}
+// info.Updater = userId
+// info.UpdateAt = now
+// }
+// return i.store.Save(ctx, info)
+//}
func (i *imlProviderService) GetLabels(ctx context.Context, ids ...string) map[string]string {
if len(ids) == 0 {
@@ -102,5 +85,49 @@ func (i *imlProviderService) GetLabels(ctx context.Context, ids ...string) map[s
func (i *imlProviderService) OnComplete() {
i.IServiceGet = universally.NewGet[Provider, ai.Provider](i.store, FromEntity)
+ i.IServiceCreate = universally.NewCreator[CreateProvider, ai.Provider](i.store, "ai_provider", createEntityHandler, uniquestHandler, labelHandler)
+ i.IServiceEdit = universally.NewEdit[SetProvider, ai.Provider](i.store, updateHandler, labelHandler)
+ i.IServiceDelete = universally.NewDelete[ai.Provider](i.store)
auto.RegisterService("ai_provider", i)
}
+
+func labelHandler(e *ai.Provider) []string {
+ return []string{e.Name, e.UUID}
+}
+
+func uniquestHandler(i *CreateProvider) []map[string]interface{} {
+ return []map[string]interface{}{{"uuid": i.Id}}
+}
+
+func createEntityHandler(i *CreateProvider) *ai.Provider {
+ //cfg, _ := json.Marshal(i.Config)
+ now := time.Now()
+ return &ai.Provider{
+ UUID: i.Id,
+ Name: i.Name,
+ DefaultLLM: i.DefaultLLM,
+ Config: i.Config,
+ Status: i.Status,
+ CreateAt: now,
+ UpdateAt: now,
+ }
+}
+
+func updateHandler(e *ai.Provider, i *SetProvider) {
+ if i.Name != nil {
+ e.Name = *i.Name
+ }
+ if i.DefaultLLM != nil {
+ e.DefaultLLM = *i.DefaultLLM
+ }
+ if i.Config != nil {
+ e.Config = *i.Config
+ }
+ if i.Status != nil {
+ e.Status = *i.Status
+ }
+ if i.Priority != nil {
+ e.Priority = *i.Priority
+ }
+ e.UpdateAt = time.Now()
+}
diff --git a/service/ai/model.go b/service/ai/model.go
index 80c6b43f..263a79fd 100644
--- a/service/ai/model.go
+++ b/service/ai/model.go
@@ -20,12 +20,20 @@ type Provider struct {
UpdateAt time.Time
}
+type CreateProvider struct {
+ Id string
+ Name string
+ DefaultLLM string
+ Config string
+ Status int
+}
+
type SetProvider struct {
Name *string
DefaultLLM *string
Config *string
- Status *int
Priority *int
+ Status *int
}
func FromEntity(e *ai.Provider) *Provider {
diff --git a/service/ai/service.go b/service/ai/service.go
index 83760314..c4201db6 100644
--- a/service/ai/service.go
+++ b/service/ai/service.go
@@ -1,7 +1,6 @@
package ai
import (
- "context"
"reflect"
"github.com/APIParkLab/APIPark/service/universally"
@@ -10,8 +9,11 @@ import (
type IProviderService interface {
universally.IServiceGet[Provider]
- Save(ctx context.Context, id string, cfg *SetProvider) error
- MaxPriority(ctx context.Context) (int, error)
+ universally.IServiceCreate[CreateProvider]
+ universally.IServiceEdit[SetProvider]
+ universally.IServiceDelete
+ //Save(ctx context.Context, id string, cfg *SetProvider) error
+ //MaxPriority(ctx context.Context) (int, error)
}
func init() {
diff --git a/service/api/iml.go b/service/api/iml.go
index 5e81c236..a784fed5 100644
--- a/service/api/iml.go
+++ b/service/api/iml.go
@@ -41,6 +41,12 @@ type imlAPIService struct {
universally.IServiceDelete
}
+func (i *imlAPIService) DeleteByService(ctx context.Context, serviceId string) error {
+ return i.store.SoftDelete(ctx, map[string]interface{}{
+ "service": serviceId,
+ })
+}
+
func (i *imlAPIService) ListForServices(ctx context.Context, serviceIds ...string) ([]*API, error) {
w := map[string]interface{}{}
if len(serviceIds) > 0 {
diff --git a/service/api/model.go b/service/api/model.go
index e4d5e662..4254f94a 100644
--- a/service/api/model.go
+++ b/service/api/model.go
@@ -48,7 +48,7 @@ func FromEntity(e *api.API) *API {
return &API{
UUID: e.UUID,
CreateAt: e.CreateAt,
- IsDelete: e.IsDelete != 0,
+ IsDelete: e.IsDelete,
Service: e.Service,
Team: e.Team,
Creator: e.Creator,
diff --git a/service/api/service.go b/service/api/service.go
index 9c58c335..7e2a758f 100644
--- a/service/api/service.go
+++ b/service/api/service.go
@@ -43,6 +43,7 @@ type IAPIService interface {
Save(ctx context.Context, id string, model *Edit) error
Create(ctx context.Context, input *Create) (err error)
+ DeleteByService(ctx context.Context, serviceId string) error
}
var (
diff --git a/service/service/iml.go b/service/service/iml.go
index be5ea44f..8e42fea6 100644
--- a/service/service/iml.go
+++ b/service/service/iml.go
@@ -18,12 +18,18 @@ var _ IServiceService = (*imlServiceService)(nil)
type imlServiceService struct {
store service.IServiceStore `autowired:""`
+
universally.IServiceGet[Service]
universally.IServiceDelete
universally.IServiceCreate[Create]
universally.IServiceEdit[Edit]
}
+func (i *imlServiceService) ForceDelete(ctx context.Context, id string) error {
+ _, err := i.store.DeleteWhere(ctx, map[string]interface{}{"uuid": id})
+ return err
+}
+
func (i *imlServiceService) ServiceList(ctx context.Context, serviceIds ...string) ([]*Service, error) {
w := make(map[string]interface{})
if len(serviceIds) > 0 {
@@ -168,6 +174,7 @@ func createEntityHandler(i *Create) *service.Service {
ApprovalType: i.ApprovalType.Int(),
Kind: i.Kind.Int(),
AdditionalConfig: string(cfg),
+ State: i.State,
Catalogue: i.Catalogue,
AsServer: i.AsServer,
AsApp: i.AsApp,
@@ -199,4 +206,8 @@ func updateHandler(e *service.Service, i *Edit) {
if i.ApprovalType != nil {
e.ApprovalType = (*i.ApprovalType).Int()
}
+ if i.State != nil {
+ e.State = *i.State
+ }
+ e.UpdateAt = time.Now()
}
diff --git a/service/service/model.go b/service/service/model.go
index 03e19fab..bd8a1b3b 100644
--- a/service/service/model.go
+++ b/service/service/model.go
@@ -119,6 +119,7 @@ type Service struct {
Kind Kind
Catalogue string
AdditionalConfig map[string]string
+ State int
AsServer bool
AsApp bool
ApprovalType ApprovalType
@@ -144,6 +145,7 @@ func FromEntity(e *service.Service) *Service {
ApprovalType: ToApprovalType(e.ApprovalType),
AsServer: e.AsServer,
AsApp: e.AsApp,
+ State: e.State,
CreateTime: e.CreateAt,
UpdateTime: e.UpdateAt,
AdditionalConfig: additionalConfig,
@@ -162,6 +164,7 @@ type Create struct {
Catalogue string
AdditionalConfig map[string]string
ApprovalType ApprovalType
+ State int
AsServer bool
AsApp bool
}
@@ -174,6 +177,7 @@ type Edit struct {
Catalogue *string
Logo *string
AdditionalConfig *map[string]string
+ State *int
ApprovalType *ApprovalType
}
diff --git a/service/service/service.go b/service/service/service.go
index d91e2d89..a9f67ada 100644
--- a/service/service/service.go
+++ b/service/service/service.go
@@ -20,6 +20,7 @@ type IServiceService interface {
ServiceList(ctx context.Context, serviceIds ...string) ([]*Service, error)
ServiceListByKind(ctx context.Context, kind Kind, serviceIds ...string) ([]*Service, error)
AppList(ctx context.Context, appIds ...string) ([]*Service, error)
+ ForceDelete(ctx context.Context, id string) error
}
func init() {
diff --git a/service/team/iml.go b/service/team/iml.go
index 0f469fc6..cba876c6 100644
--- a/service/team/iml.go
+++ b/service/team/iml.go
@@ -24,6 +24,15 @@ type imlTeamService struct {
universally.IServiceEdit[EditTeam]
}
+func (s *imlTeamService) DefaultTeam(ctx context.Context) (*Team, error) {
+ item, err := s.teamStore.First(ctx, nil, "id asc")
+ if err != nil {
+ return nil, err
+
+ }
+ return FromEntity(item), nil
+}
+
func (s *imlTeamService) OnComplete() {
s.IServiceGet = universally.NewGetSoftDelete[Team, team.Team](s.teamStore, FromEntity)
diff --git a/service/team/service.go b/service/team/service.go
index 1408733e..e5278d94 100644
--- a/service/team/service.go
+++ b/service/team/service.go
@@ -1,8 +1,9 @@
package team
import (
+ "context"
"reflect"
-
+
"github.com/APIParkLab/APIPark/service/universally"
"github.com/eolinker/go-common/autowire"
)
@@ -12,6 +13,7 @@ type ITeamService interface {
universally.IServiceDelete
universally.IServiceCreate[CreateTeam]
universally.IServiceEdit[EditTeam]
+ DefaultTeam(ctx context.Context) (*Team, error)
}
func init() {
diff --git a/service/universally/create-softDelete.go b/service/universally/create-softDelete.go
index d790cd8f..807bb6f8 100644
--- a/service/universally/create-softDelete.go
+++ b/service/universally/create-softDelete.go
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
+
"github.com/eolinker/go-common/utils"
"github.com/eolinker/go-common/auto"
diff --git a/stores/ai/model.go b/stores/ai/model.go
index b25f4c29..6d607d16 100644
--- a/stores/ai/model.go
+++ b/stores/ai/model.go
@@ -68,3 +68,99 @@ func (i *Key) TableName() string {
func (i *Key) IdValue() int64 {
return i.Id
}
+
+type Balance struct {
+ Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
+ Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
+ Provider string `gorm:"type:varchar(100);not null;column:provider;comment:供应商ID;uniqueIndex:provider_model" aovalue:"provider`
+ ProviderName string `gorm:"type:varchar(100);not null;column:provider_name;comment:供应商名称"`
+ Model string `gorm:"type:varchar(100);not null;column:model;comment:模型ID;uniqueIndex:provider_model"`
+ ModelName string `gorm:"type:varchar(100);not null;column:model_name;comment:模型名称"`
+ Type int `gorm:"type:tinyint(1);not null;column:type;comment:类型,0:online,1:local"`
+ State int `gorm:"type:tinyint(1);not null;column:state;comment:状态,0:异常,1:正常;default:1"`
+ Priority int `gorm:"type:int;not null;column:priority;comment:优先级,数字越小优先级越大"`
+ Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
+ Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人
+ CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
+ UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
+}
+
+func (i *Balance) TableName() string {
+ return "ai_balance"
+}
+
+func (i *Balance) IdValue() int64 {
+ return i.Id
+}
+
+type LocalModel struct {
+ Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
+ Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
+ Name string `gorm:"type:varchar(100);not null;column:name;comment:名称"`
+ Provider string `gorm:"type:varchar(100);not null;column:provider;comment:供应商ID"`
+ State int `gorm:"type:tinyint(1);not null;column:state;comment:状态,0:关闭,1:正常;default:1"`
+ Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
+ Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"` // 更新人
+ CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
+ UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
+}
+
+func (i *LocalModel) TableName() string {
+ return "ai_local_model"
+}
+
+func (i *LocalModel) IdValue() int64 {
+ return i.Id
+}
+
+type LocalModelInstallState struct {
+ Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
+ Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
+ Complete int64 `gorm:"type:BIGINT(20);not null;column:complete;comment:已下载大小"`
+ Total int64 `gorm:"type:BIGINT(20);not null;column:total;comment:总大小"`
+ State int `gorm:"type:tinyint(1);not null;column:state;comment:状态"`
+ LastMsg string `gorm:"type:text;not null;column:last_msg;comment:最后一次消息"`
+ UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
+}
+
+func (i *LocalModelInstallState) TableName() string {
+ return "ai_local_model_install_state"
+}
+
+func (i *LocalModelInstallState) IdValue() int64 {
+ return i.Id
+}
+
+type LocalModelPackage struct {
+ Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
+ Uuid string `gorm:"type:varchar(100);not null;column:uuid;uniqueIndex:uuid;comment:UUID;"`
+ Name string `gorm:"type:varchar(100);not null;column:name;comment:名称"`
+ Size string `gorm:"type:varchar(100);not null;column:size;comment:模型大小"`
+ Hash string `gorm:"type:varchar(100);not null;column:hash;comment:模型hash"`
+ Description string `gorm:"type:varchar(255);not null;column:description;comment:描述"`
+ Version string `gorm:"type:varchar(100);not null;column:version;comment:版本"`
+ IsPopular bool `gorm:"type:tinyint(1);not null;column:is_popular;comment:是否热门"`
+}
+
+func (i *LocalModelPackage) TableName() string {
+ return "ai_local_model_package"
+}
+
+func (i *LocalModelPackage) IdValue() int64 {
+ return i.Id
+}
+
+type LocalModelCache struct {
+ Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
+ Model string `gorm:"type:varchar(100);not null;column:model;comment:模型ID"`
+ Target string `gorm:"type:varchar(100);not null;column:target;comment:目标"`
+ Type int `gorm:"type:tinyint(1);not null;column:type;comment:类型,0: 服务"`
+}
+
+func (i *LocalModelCache) TableName() string {
+ return "ai_local_model_cache"
+}
+
+func (i *LocalModelCache) IdValue() int64 {
+ return i.Id
+}
diff --git a/stores/ai/store.go b/stores/ai/store.go
index 4afa193f..8c462f42 100644
--- a/stores/ai/store.go
+++ b/stores/ai/store.go
@@ -31,6 +31,46 @@ type imlKeyStore struct {
store.SearchStore[Key]
}
+type IBalanceStore interface {
+ store.ISearchStore[Balance]
+}
+
+type imlBalanceStore struct {
+ store.SearchStore[Balance]
+}
+
+type ILocalModelStore interface {
+ store.ISearchStore[LocalModel]
+}
+
+type imlLocalModelStore struct {
+ store.SearchStore[LocalModel]
+}
+
+type ILocalModelPackageStore interface {
+ store.ISearchStore[LocalModelPackage]
+}
+
+type imlLocalModelPackageStore struct {
+ store.SearchStore[LocalModelPackage]
+}
+
+type ILocalModelInstallStateStore interface {
+ store.ISearchStore[LocalModelInstallState]
+}
+
+type imlLocalModelInstallStateStore struct {
+ store.SearchStore[LocalModelInstallState]
+}
+
+type ILocalModelCacheStore interface {
+ store.IBaseStore[LocalModelCache]
+}
+
+type imlLocalModelCacheStore struct {
+ store.Store[LocalModelCache]
+}
+
func init() {
autowire.Auto[IProviderStore](func() reflect.Value {
return reflect.ValueOf(new(imlProviderStore))
@@ -40,7 +80,27 @@ func init() {
return reflect.ValueOf(new(imlLogMetricsStore))
})
+ autowire.Auto[IBalanceStore](func() reflect.Value {
+ return reflect.ValueOf(new(imlBalanceStore))
+ })
+
+ autowire.Auto[ILocalModelStore](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelStore))
+ })
+
+ autowire.Auto[ILocalModelPackageStore](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelPackageStore))
+ })
+
autowire.Auto[IKeyStore](func() reflect.Value {
return reflect.ValueOf(new(imlKeyStore))
})
+
+ autowire.Auto[ILocalModelInstallStateStore](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelInstallStateStore))
+ })
+
+ autowire.Auto[ILocalModelCacheStore](func() reflect.Value {
+ return reflect.ValueOf(new(imlLocalModelCacheStore))
+ })
}
diff --git a/stores/api/model.go b/stores/api/model.go
index d530cf3f..9447da32 100644
--- a/stores/api/model.go
+++ b/stores/api/model.go
@@ -12,10 +12,10 @@ type API struct {
Upstream string `gorm:"size:36;not null;column:upstream;comment:上游;index:upstream"` // 上游ID
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"` // 创建人
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
- IsDelete int `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:未删除 1:已删除"`
Method []string `gorm:"size:255;not null;column:method;comment:请求方法;serializer:json"`
Protocol []string `gorm:"type:text;not null;column:protocol;comment:协议;serializer:json"`
Path string `gorm:"size:255;not null;column:path;comment:请求路径"`
+ IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:未删除 1:已删除"`
}
type Info struct {
Id int64 `gorm:"column:id;type:BIGINT(20);NOT NULL;comment:id;primary_key;comment:主键ID;"`
@@ -86,6 +86,7 @@ type AiAPIInfo struct {
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
AdditionalConfig string `gorm:"type:text;null;column:additional_config;comment:额外配置"`
UseToken int `gorm:"type:int(11);not null;column:use_token;comment:使用token"`
+ Type int `gorm:"type:tinyint(1);not null;column:type;comment:类型 0:online api 1:local api"`
Disable bool `gorm:"type:tinyint(1);not null;column:disable;comment:是否禁用 0:否 1:是"`
IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:否 1:是"`
}
diff --git a/stores/service/model.go b/stores/service/model.go
index 22b0fb5a..d0d42d30 100644
--- a/stores/service/model.go
+++ b/stores/service/model.go
@@ -15,8 +15,9 @@ type Service struct {
Catalogue string `gorm:"type:text;not null;column:catalogue;comment:目录"`
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;column:update_at;comment:修改时间"`
- IsDelete int `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除"`
+ IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除"`
Kind int `gorm:"type:tinyint(4);not null;column:kind;comment:服务种类,0:Rest服务,1:AI服务"`
+ State int `gorm:"type:tinyint(4);not null;column:state;comment:状态"`
AdditionalConfig string `gorm:"type:text;not null;column:additional_config;comment:额外配置"`
ApprovalType int `gorm:"type:tinyint(4);not null;column:approval_type;comment:审核类型"`
AsServer bool `gorm:"type:tinyint(1);not null;column:as_server;comment:是否为服务端项目"`