finish:quick create service

This commit is contained in:
Liujian
2025-02-14 23:27:24 +08:00
parent 0dc5439726
commit 3127cc6780
13 changed files with 370 additions and 41 deletions
+1
View File
@@ -20,6 +20,7 @@ type IProviderController interface {
Disable(ctx *gin.Context, id string) error
UpdateProviderConfig(ctx *gin.Context, id string, input *ai_dto.UpdateConfig) error
UpdateProviderDefaultLLM(ctx *gin.Context, id string, input *ai_dto.UpdateLLM) error
Delete(ctx *gin.Context, id string) error
//Sort(ctx *gin.Context, input *ai_dto.Sort) error
}
+4
View File
@@ -17,6 +17,10 @@ type imlProviderController struct {
module ai.IProviderModule `autowired:""`
}
func (i *imlProviderController) Delete(ctx *gin.Context, id string) error {
return i.module.Delete(ctx, id)
}
//func (i *imlProviderController) Sort(ctx *gin.Context, input *ai_dto.Sort) error {
// return i.module.Sort(ctx, input)
//}
+269 -23
View File
@@ -3,10 +3,21 @@ package service
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
ai_local "github.com/APIParkLab/APIPark/module/ai-local"
ai_dto "github.com/APIParkLab/APIPark/module/ai/dto"
api_doc_dto "github.com/APIParkLab/APIPark/module/api-doc/dto"
"github.com/APIParkLab/APIPark/module/catalogue"
"github.com/APIParkLab/APIPark/module/team"
"github.com/eolinker/go-common/pm3"
"github.com/APIParkLab/APIPark/module/system"
@@ -40,6 +51,10 @@ import (
"github.com/google/uuid"
)
var (
ollamaConfig = "{\n \"mirostat\": 0,\n \"mirostat_eta\": 0.1,\n \"mirostat_tau\": 5.0,\n \"num_ctx\": 4096,\n \"repeat_last_n\":64,\n \"repeat_penalty\": 1.1,\n \"temperature\": 0.7,\n \"seed\": 42,\n \"num_predict\": 42,\n \"top_k\": 40,\n \"top_p\": 0.9,\n \"min_p\": 0.5\n}\n"
)
var (
_ IServiceController = (*imlServiceController)(nil)
@@ -47,15 +62,226 @@ var (
)
type imlServiceController struct {
module service.IServiceModule `autowired:""`
docModule service.IServiceDocModule `autowired:""`
aiAPIModule ai_api.IAPIModule `autowired:""`
routerModule router.IRouterModule `autowired:""`
apiDocModule api_doc.IAPIDocModule `autowired:""`
providerModule ai.IProviderModule `autowired:""`
upstreamModule upstream.IUpstreamModule `autowired:""`
settingModule system.ISettingModule `autowired:""`
transaction store.ITransaction `autowired:""`
module service.IServiceModule `autowired:""`
docModule service.IServiceDocModule `autowired:""`
aiAPIModule ai_api.IAPIModule `autowired:""`
routerModule router.IRouterModule `autowired:""`
apiDocModule api_doc.IAPIDocModule `autowired:""`
providerModule ai.IProviderModule `autowired:""`
aiLocalModel ai_local.ILocalModelModule `autowired:""`
upstreamModule upstream.IUpstreamModule `autowired:""`
settingModule system.ISettingModule `autowired:""`
teamModule team.ITeamModule `autowired:""`
catalogueModule catalogue.ICatalogueModule `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlServiceController) QuickCreateAIService(ctx *gin.Context, input *service_dto.QuickCreateAIService) error {
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
enable := true
err := i.providerModule.UpdateProviderConfig(ctx, input.Provider, &ai_dto.UpdateConfig{
Config: input.Config,
Enable: &enable,
})
if err != nil {
return err
}
pv, err := i.providerModule.Provider(ctx, input.Provider)
if err != nil {
return err
}
p, has := model_runtime.GetProvider(input.Provider)
if !has {
return fmt.Errorf("provider not found")
}
m, has := p.GetModel(pv.DefaultLLM)
if !has {
return fmt.Errorf("model %s not found", pv.DefaultLLM)
}
var info *service_dto.Service
id := uuid.NewString()
prefix := fmt.Sprintf("/%s", id[:8])
catalogueInfo, err := i.catalogueModule.DefaultCatalogue(ctx)
if err != nil {
return err
}
info, err = i.module.Create(ctx, input.Team, &service_dto.CreateService{
Id: uuid.NewString(),
Name: input.Provider + " AI Service",
Prefix: prefix,
Description: "Quick create by AI provider",
ServiceType: "public",
State: "normal",
Catalogue: catalogueInfo.Id,
ApprovalType: "auto",
Provider: &input.Provider,
Kind: "ai",
})
if err != nil {
return err
}
path := fmt.Sprintf("%s/demo_translation_api", prefix)
timeout := 300000
retry := 0
aiPrompt := &ai_api_dto.AiPrompt{
Variables: []*ai_api_dto.AiPromptVariable{
{
Key: "source_lang",
Description: "",
Require: true,
},
{
Key: "target_lang",
Description: "",
Require: true,
},
{
Key: "text",
Description: "",
Require: true,
},
},
Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}",
}
aiModel := &ai_api_dto.AiModel{
Id: m.ID(),
Config: m.DefaultConfig(),
Provider: input.Provider,
}
name := "Demo Translation API"
description := "A demo that shows you how to use a prompt to create a Translation API."
apiId := uuid.New().String()
err = i.aiAPIModule.Create(
ctx,
info.Id,
&ai_api_dto.CreateAPI{
Id: apiId,
Name: name,
Path: path,
Description: description,
Disable: false,
AiPrompt: aiPrompt,
AiModel: aiModel,
Timeout: timeout,
Retry: retry,
},
)
if err != nil {
return err
}
plugins := make(map[string]api.PluginSetting)
plugins["ai_prompt"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"prompt": aiPrompt.Prompt,
"variables": aiPrompt.Variables,
},
}
plugins["ai_formatter"] = api.PluginSetting{
Config: plugin_model.ConfigType{
"model": aiModel.Id,
"provider": info.Provider.Id,
"config": aiModel.Config,
},
}
_, err = i.routerModule.Create(ctx, info.Id, &router_dto.Create{
Id: apiId,
Name: name,
Path: path,
Methods: []string{
http.MethodPost,
},
Description: description,
Protocols: []string{"http", "https"},
MatchRules: nil,
Proxy: &router_dto.InputProxy{
Path: path,
Timeout: timeout,
Retry: retry,
Plugins: plugins,
},
Disable: false,
})
if err != nil {
return err
}
return i.docModule.SaveServiceDoc(ctx, info.Id, &service_dto.SaveServiceDoc{
Doc: "The Translation API allows developers to translate text from one language to another. It supports multiple languages and enables easy integration of high-quality translation features into applications. With simple API requests, you can quickly translate content into different target languages.",
})
})
}
func (i *imlServiceController) QuickCreateRestfulService(ctx *gin.Context) error {
fileHeader, err := ctx.FormFile("file")
if err != nil {
return err
}
file, err := fileHeader.Open()
if err != nil {
return err
}
content, err := io.ReadAll(file)
if err != nil {
return err
}
typ := ctx.PostForm("type")
switch typ {
case "swagger", "":
default:
return fmt.Errorf("type %s not support", typ)
}
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
teamId := ctx.PostForm("team")
id := uuid.NewString()
prefix := fmt.Sprintf("/%s", id[:8])
catalogueInfo, err := i.catalogueModule.DefaultCatalogue(ctx)
if err != nil {
return err
}
s, err := i.module.Create(ctx, teamId, &service_dto.CreateService{
Id: uuid.NewString(),
Name: "Restful Service By Swagger",
Prefix: prefix,
Description: "Auto create by upload swagger",
ServiceType: "public",
State: "normal",
Catalogue: catalogueInfo.Id,
ApprovalType: "auto",
Kind: "rest",
})
if err != nil {
return err
}
_, err = i.apiDocModule.UpdateDoc(ctx, s.Id, &api_doc_dto.UpdateDoc{
Id: s.Id,
Content: string(content),
})
if err != nil {
return err
}
path := prefix + "/"
_, err = i.routerModule.Create(ctx, s.Id, &router_dto.Create{
Id: uuid.NewString(),
Name: "",
Path: path + "*",
Methods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodPatch, http.MethodOptions},
Description: "auto create by create service",
Protocols: []string{"http", "https"},
Proxy: &router_dto.InputProxy{
Path: path,
Timeout: 30000,
Retry: 0,
},
Disable: false,
})
if err != nil {
return err
}
return nil
})
}
var (
@@ -192,21 +418,40 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string,
input.Prefix = input.Id[:8]
}
}
pv, err := i.providerModule.Provider(ctx, *input.Provider)
if err != nil {
return nil, err
}
p, has := model_runtime.GetProvider(*input.Provider)
if !has {
return nil, fmt.Errorf("provider not found")
}
m, has := p.GetModel(pv.DefaultLLM)
if !has {
return nil, fmt.Errorf("model %s not found", pv.DefaultLLM)
modelId := ""
modelCfg := ""
modelType := "online"
if *input.Provider == "ollama" {
modelType = "local"
list, err := i.aiLocalModel.SimpleList(ctx)
if err != nil {
return nil, err
}
if len(list) == 0 {
return nil, fmt.Errorf("no local model")
}
modelId = list[0].Id
modelCfg = ollamaConfig
} else {
pv, err := i.providerModule.Provider(ctx, *input.Provider)
if err != nil {
return nil, err
}
p, has := model_runtime.GetProvider(*input.Provider)
if !has {
return nil, fmt.Errorf("provider not found")
}
m, has := p.GetModel(pv.DefaultLLM)
if !has {
return nil, fmt.Errorf("model %s not found", pv.DefaultLLM)
}
modelId = m.ID()
modelCfg = m.DefaultConfig()
}
var info *service_dto.Service
err = i.transaction.Transaction(ctx, func(txCtx context.Context) error {
err := i.transaction.Transaction(ctx, func(txCtx context.Context) error {
var err error
info, err = i.module.Create(ctx, teamID, input)
if err != nil {
@@ -236,9 +481,10 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string,
Prompt: "You need to translate {{source_lang}} into {{target_lang}}, and the following is the content that needs to be translated.\n---\n{{text}}",
}
aiModel := &ai_api_dto.AiModel{
Id: m.ID(),
Config: m.DefaultConfig(),
Id: modelId,
Config: modelCfg,
Provider: *input.Provider,
Type: modelType,
}
name := "Demo Translation API"
description := "A demo that shows you how to use a prompt to create a Translation API."
+3
View File
@@ -16,6 +16,9 @@ type IServiceController interface {
// SearchMyServices 搜索服务
SearchMyServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error)
Search(ctx *gin.Context, teamIDs string, keyword string) ([]*service_dto.ServiceItem, error)
QuickCreateRestfulService(ctx *gin.Context) error
QuickCreateAIService(ctx *gin.Context, input *service_dto.QuickCreateAIService) error
// Create 创建
Create(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error)
// Edit 编辑
+9 -1
View File
@@ -169,7 +169,15 @@ func (i *imlLocalModel) pullHook() func(msg ai_provider_local.PullMessage) error
if msg.Msg != "" {
info.Msg = msg.Msg
}
return i.localModelStateService.Save(ctx, msg.Model, &ai_local.EditLocalModelInstallState{State: &state, Complete: &info.Complete, Total: &info.Total, Msg: &info.Msg})
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
}
return i.serviceService.Save(ctx, msg.Model, &service.Edit{State: &serviceState})
})
}
}
+60 -15
View File
@@ -8,6 +8,8 @@ import (
"sort"
"time"
ai_local "github.com/APIParkLab/APIPark/service/ai-local"
ai_balance "github.com/APIParkLab/APIPark/service/ai-balance"
"github.com/APIParkLab/APIPark/service/service"
@@ -63,9 +65,36 @@ type imlProviderModule struct {
}
func (i *imlProviderModule) Delete(ctx context.Context, id string) error {
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
// TODO: implement Delete
return nil
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
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)
})
}
@@ -670,16 +699,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"
@@ -699,12 +750,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:
+8 -1
View File
@@ -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"`
@@ -12,7 +19,7 @@ type CreateService struct {
ApprovalType string `json:"approval_type"`
Kind string `json:"service_kind"`
State string `json:"state"`
Provider *string `json:"provider" aocheck:"ai_provider"`
Provider *string `json:"provider"`
AsApp *bool `json:"as_app"`
AsServer *bool `json:"as_server"`
}
+1 -1
View File
@@ -17,7 +17,7 @@ func (p *plugin) aiAPIs() []pm3.Api {
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),
+3
View File
@@ -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),
+8
View File
@@ -23,6 +23,14 @@ 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")
}
+1
View File
@@ -14,6 +14,7 @@ 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)
+2
View File
@@ -17,6 +17,7 @@ var _ IProviderService = (*imlProviderService)(nil)
type imlProviderService struct {
universally.IServiceGet[Provider]
universally.IServiceDelete
store ai.IProviderStore `autowired:""`
}
@@ -102,5 +103,6 @@ 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.IServiceDelete = universally.NewDelete[ai.Provider](i.store)
auto.RegisterService("ai_provider", i)
}
+1
View File
@@ -10,6 +10,7 @@ import (
type IProviderService interface {
universally.IServiceGet[Provider]
universally.IServiceDelete
Save(ctx context.Context, id string, cfg *SetProvider) error
MaxPriority(ctx context.Context) (int, error)
}