diff --git a/ai-provider/model-runtime/entity/provider.go b/ai-provider/model-runtime/entity/provider.go index 56bf29d7..195da52a 100644 --- a/ai-provider/model-runtime/entity/provider.go +++ b/ai-provider/model-runtime/entity/provider.go @@ -22,6 +22,7 @@ type Provider struct { SupportedModelTypes []string `json:"supported_model_types" yaml:"supported_model_types"` ProviderCredentialSchema ProviderCredentialSchema `json:"provider_credential_schema" yaml:"provider_credential_schema"` Default map[string]string `json:"default"` + Address string `json:"address"` } type ProviderCredentialSchema struct { diff --git a/ai-provider/model-runtime/loader.go b/ai-provider/model-runtime/loader.go index 9892af35..d6d7d314 100644 --- a/ai-provider/model-runtime/loader.go +++ b/ai-provider/model-runtime/loader.go @@ -2,6 +2,7 @@ package model_runtime import ( "embed" + "encoding/json" "fmt" "github.com/eolinker/eosc" "strings" @@ -13,9 +14,46 @@ func init() { type IConfig interface { Check(cfg string) error + GenConfig(target string, origin string) (string, error) DefaultConfig() string } +func NewConfig(cfg string, validator IParamValidator) *Config { + return &Config{cfg: cfg, validator: validator} +} + +type Config struct { + cfg string + validator IParamValidator +} + +func (c *Config) Check(cfg string) error { + data := make(map[string]interface{}) + err := json.Unmarshal([]byte(cfg), &data) + if err != nil { + return err + } + return c.validator.Valid(data) +} + +func (c *Config) GenConfig(target string, origin string) (string, error) { + var targetData map[string]interface{} + err := json.Unmarshal([]byte(target), &targetData) + if err != nil { + return "", err + } + var originData map[string]interface{} + err = json.Unmarshal([]byte(origin), &originData) + if err != nil { + return "", err + } + return c.validator.GenConfig(targetData, originData) +} + +func (c *Config) DefaultConfig() string { + return c.cfg +} + const ( DirAssets = "assets" ) diff --git a/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml b/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml index cf41f544..c6ee0dba 100644 --- a/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml +++ b/ai-provider/model-runtime/model-providers/authropic/anthropic.yaml @@ -37,3 +37,4 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API URL en_US: Enter your API URL +address: https://api.openai.com diff --git a/ai-provider/model-runtime/model-providers/google/google.yaml b/ai-provider/model-runtime/model-providers/google/google.yaml index 69d4e371..030dbe2b 100644 --- a/ai-provider/model-runtime/model-providers/google/google.yaml +++ b/ai-provider/model-runtime/model-providers/google/google.yaml @@ -29,3 +29,4 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Key en_US: Enter your API Key +address: https://api.openai.com \ No newline at end of file diff --git a/ai-provider/model-runtime/model-providers/openAI/openai.yaml b/ai-provider/model-runtime/model-providers/openAI/openai.yaml index b4dc8fd4..8b04aaef 100644 --- a/ai-provider/model-runtime/model-providers/openAI/openai.yaml +++ b/ai-provider/model-runtime/model-providers/openAI/openai.yaml @@ -87,3 +87,4 @@ provider_credential_schema: placeholder: zh_Hans: 在此输入您的 API Base, 如:https://api.openai.com en_US: Enter your API Base, e.g. https://api.openai.com +address: https://api.openai.com diff --git a/ai-provider/model-runtime/model.go b/ai-provider/model-runtime/model.go index 9247c596..b1d0e235 100644 --- a/ai-provider/model-runtime/model.go +++ b/ai-provider/model-runtime/model.go @@ -14,10 +14,11 @@ type IModel interface { } type Model struct { - id string - logo string - defaultConfig string - validator IParamValidator + id string + logo string + //defaultConfig string + IConfig + //validator IParamValidator } func (m *Model) ID() string { @@ -28,19 +29,6 @@ func (m *Model) Logo() string { return m.logo } -func (m *Model) Check(cfg string) error { - data := make(map[string]interface{}) - err := json.Unmarshal([]byte(cfg), &data) - if err != nil { - return err - } - return m.validator.Valid(data) -} - -func (m *Model) DefaultConfig() string { - return m.defaultConfig -} - func NewModel(data string, logo string) (IModel, error) { var cfg entity.AIModel err := yaml.Unmarshal([]byte(data), &cfg) @@ -112,9 +100,8 @@ func NewModel(data string, logo string) (IModel, error) { return nil, err } return &Model{ - id: cfg.Model, - logo: logo, - defaultConfig: string(dCfg), - validator: params, + id: cfg.Model, + logo: logo, + IConfig: NewConfig(string(dCfg), params), }, nil } diff --git a/ai-provider/model-runtime/param.go b/ai-provider/model-runtime/param.go index 22e504e5..684e3d31 100644 --- a/ai-provider/model-runtime/param.go +++ b/ai-provider/model-runtime/param.go @@ -1,6 +1,9 @@ package model_runtime -import "fmt" +import ( + "encoding/json" + "fmt" +) const ( ParameterTypeInt = "int" @@ -11,10 +14,33 @@ const ( type IParamValidator interface { Valid(map[string]interface{}) error + GenConfig(target map[string]interface{}, origin map[string]interface{}) (string, error) } type ParamValidator []Param +func (p ParamValidator) GenConfig(target map[string]interface{}, origin map[string]interface{}) (string, error) { + for _, rule := range p { + if !rule.Secret { + continue + } + vv, ok := origin[rule.Name] + if !ok { + continue + } + v, ok := vv.(string) + if !ok || v == "******" { + continue + } + target[rule.Name] = origin[rule.Name] + } + data, err := json.Marshal(target) + if err != nil { + return "", err + } + return string(data), nil +} + type Param struct { Name string `json:"name" yaml:"name"` Default interface{} `json:"default" yaml:"default"` @@ -22,6 +48,7 @@ type Param struct { Min float64 `json:"min" yaml:"min"` Max float64 `json:"max" yaml:"max"` Required bool `json:"required" yaml:"required"` + Secret bool `json:"secret" yaml:"secret"` } func (p ParamValidator) Valid(params map[string]interface{}) error { @@ -52,10 +79,13 @@ func (p ParamValidator) Valid(params map[string]interface{}) error { return fmt.Errorf("invalid parameter %s: %v", rule.Name, v) } case ParameterTypeStr: - _, ok = v.(string) + vv, ok := v.(string) if !ok { return fmt.Errorf("invalid parameter %s: %v", rule.Name, v) } + if rule.Required && len(vv) == 0 { + return fmt.Errorf("parameter %s is empty", rule.Name) + } case ParameterTypeBool: _, ok = v.(bool) if !ok { @@ -64,4 +94,5 @@ func (p ParamValidator) Valid(params map[string]interface{}) error { } } return nil + } diff --git a/ai-provider/model-runtime/provider.go b/ai-provider/model-runtime/provider.go index aa463c5d..c9d9c94c 100644 --- a/ai-provider/model-runtime/provider.go +++ b/ai-provider/model-runtime/provider.go @@ -6,6 +6,7 @@ import ( "github.com/APIParkLab/APIPark/ai-provider/model-runtime/entity" "github.com/eolinker/eosc" "gopkg.in/yaml.v3" + "net/url" "strings" ) @@ -22,12 +23,19 @@ type IProvider interface { MaskConfig(cfg string) string } +type IProviderURI interface { + Scheme() string + Host() string + Path() string +} + type IProviderInfo interface { ID() string Name() string DefaultModel(modelType string) (IModel, bool) HelpUrl() string Logo() string + URI() IProviderURI } func NewProvider(providerData string, modelContents map[string]eosc.Untyped[string, string]) (IProvider, error) { @@ -36,11 +44,15 @@ func NewProvider(providerData string, modelContents map[string]eosc.Untyped[stri if err != nil { return nil, err } - + uri, err := newProviderUri(providerCfg.Address) + if err != nil { + return nil, err + } assetsFiles, ok := modelContents[DirAssets] if !ok { return nil, fmt.Errorf("assets not found") } + delete(modelContents, DirAssets) providerLogo, _ := assetsFiles.Get(providerCfg.IconLarge[entity.LanguageEnglish]) modelLogo, _ := assetsFiles.Get(providerCfg.IconSmall[entity.LanguageEnglish]) @@ -53,24 +65,27 @@ func NewProvider(providerData string, modelContents map[string]eosc.Untyped[stri defaultModels: eosc.BuildUntyped[string, IModel](), modelsByType: eosc.BuildUntyped[string, []IModel](), maskKeys: make([]string, 0), + uri: uri, } defaultCfg := make(map[string]string) params := make(ParamValidator, 0, len(providerCfg.ProviderCredentialSchema.CredentialFormSchemas)) for _, v := range providerCfg.ProviderCredentialSchema.CredentialFormSchemas { - params = append(params, Param{ + param := Param{ Name: v.Variable, Default: v.Label[entity.LanguageEnglish], Type: ParameterTypeStr, Required: v.Required, - }) + } + if v.Type == "secret-input" { provider.maskKeys = append(provider.maskKeys, v.Variable) + param.Secret = true } + params = append(params, param) defaultCfg[v.Variable] = v.Label[entity.LanguageEnglish] } defaultCfgByte, _ := json.MarshalIndent(defaultCfg, "", " ") - provider.defaultConfig = string(defaultCfgByte) - provider.paramValidator = params + provider.IConfig = NewConfig(string(defaultCfgByte), params) for name, f := range modelContents { models := make([]IModel, 0, f.Count()) defaultModel := providerCfg.Default[name] @@ -90,20 +105,25 @@ func NewProvider(providerData string, modelContents map[string]eosc.Untyped[stri } provider.SetModelsByType(name, models) } + return provider, nil } type Provider struct { - id string - name string - logo string - helpUrl string - defaultConfig string - paramValidator IParamValidator - models eosc.Untyped[string, IModel] - defaultModels eosc.Untyped[string, IModel] - modelsByType eosc.Untyped[string, []IModel] - maskKeys []string + id string + name string + logo string + helpUrl string + models eosc.Untyped[string, IModel] + defaultModels eosc.Untyped[string, IModel] + modelsByType eosc.Untyped[string, []IModel] + maskKeys []string + uri IProviderURI + IConfig +} + +func (p *Provider) URI() IProviderURI { + return p.uri } func (p *Provider) ID() string { @@ -138,19 +158,6 @@ func (p *Provider) ModelsByType(modelType string) ([]IModel, bool) { return p.modelsByType.Get(modelType) } -func (p *Provider) Check(cfg string) error { - data := make(map[string]interface{}) - err := json.Unmarshal([]byte(cfg), &data) - if err != nil { - return err - } - return p.paramValidator.Valid(data) -} - -func (p *Provider) DefaultConfig() string { - return p.defaultConfig -} - func (p *Provider) MaskConfig(cfg string) string { var data map[string]string err := json.Unmarshal([]byte(cfg), &data) @@ -158,8 +165,8 @@ func (p *Provider) MaskConfig(cfg string) string { return cfg } for _, key := range p.maskKeys { - if v, ok := data[key]; ok { - data[key] = PartialMasking(v, 4, -1) + if _, ok := data[key]; ok { + data[key] = "******" } } result, _ := json.Marshal(data) @@ -178,6 +185,43 @@ func (p *Provider) SetModelsByType(modelType string, models []IModel) { p.modelsByType.Set(modelType, models) } +type providerUri struct { + scheme string + host string + path string +} + +func newProviderUri(addr string) (IProviderURI, error) { + uri, err := url.Parse(addr) + if err != nil { + return nil, err + } + if uri.Host == "" { + return nil, fmt.Errorf("host is empty") + } + if uri.Scheme == "" { + return nil, fmt.Errorf("scheme is empty") + } + + return &providerUri{ + scheme: uri.Scheme, + host: uri.Host, + path: uri.Path, + }, nil +} + +func (p *providerUri) Scheme() string { + return p.scheme +} + +func (p *providerUri) Host() string { + return p.host +} + +func (p *providerUri) Path() string { + return p.path +} + func PartialMasking(origin string, begin int, length int) string { target := strings.Builder{} runes := []rune(origin) diff --git a/controller/ai-api/iml.go b/controller/ai-api/iml.go index 9a8b7ff3..8d28adf2 100644 --- a/controller/ai-api/iml.go +++ b/controller/ai-api/iml.go @@ -1,27 +1,169 @@ package ai_api import ( + "context" + "fmt" + "github.com/APIParkLab/APIPark/model/plugin_model" ai_api "github.com/APIParkLab/APIPark/module/ai-api" ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto" + "github.com/APIParkLab/APIPark/module/router" + router_dto "github.com/APIParkLab/APIPark/module/router/dto" + "github.com/APIParkLab/APIPark/module/service" + "github.com/APIParkLab/APIPark/service/api" + "github.com/eolinker/go-common/store" "github.com/gin-gonic/gin" + "github.com/google/uuid" + "net/http" + "strings" ) var _ IAPIController = (*imlAPIController)(nil) type imlAPIController struct { - module ai_api.IAPIModule `autowired:""` + module ai_api.IAPIModule `autowired:""` + routerModule router.IRouterModule `autowired:""` + serviceModule service.IServiceModule `autowired:""` + transaction store.ITransaction `autowired:""` } func (i *imlAPIController) Create(ctx *gin.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) { - return i.module.Create(ctx, serviceId, input) + info, err := i.serviceModule.Get(ctx, serviceId) + if err != nil { + return nil, err + } + if input.Id == "" { + input.Id = uuid.New().String() + } + err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { + err = i.module.Create(ctx, serviceId, input) + if err != nil { + return err + } + plugins := make(map[string]api.PluginSetting) + if input.AiPrompt != nil { + plugins["ai_prompt"] = api.PluginSetting{ + Config: plugin_model.ConfigType{ + "prompt": input.AiPrompt.Prompt, + "variables": input.AiPrompt.Variables, + }, + } + } + if input.AiModel != nil { + plugins["ai_formatter"] = api.PluginSetting{ + Config: plugin_model.ConfigType{ + "model": input.AiModel.Id, + "provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id), + "config": input.AiModel.Config, + }, + } + } + + _, err = i.routerModule.Create(ctx, serviceId, &router_dto.Create{ + Id: input.Id, + Path: input.Path, + Methods: []string{ + http.MethodPost, + }, + Description: input.Description, + Protocols: []string{"http", "https"}, + MatchRules: nil, + Proxy: &router_dto.InputProxy{ + Path: input.Path, + Timeout: input.Timeout, + Retry: input.Retry, + Plugins: plugins, + }, + Disable: false, + }) + + return err + }) + if err != nil { + return nil, err + } + return i.module.Get(ctx, serviceId, input.Id) } func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) { - return i.module.Edit(ctx, serviceId, apiId, input) + info, err := i.serviceModule.Get(ctx, serviceId) + if err != nil { + return nil, err + } + err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { + apiInfo, err := i.routerModule.Detail(ctx, serviceId, apiId) + if err != nil { + return err + } + proxy := &router_dto.InputProxy{ + Path: apiInfo.Proxy.Path, + Timeout: apiInfo.Proxy.Timeout, + Retry: apiInfo.Proxy.Retry, + Plugins: apiInfo.Proxy.Plugins, + } + if input.AiModel != nil { + proxy.Plugins["ai_formatter"] = api.PluginSetting{ + Config: plugin_model.ConfigType{ + "model": input.AiModel.Id, + "provider": fmt.Sprintf("%s@ai-provider", info.Provider.Id), + "config": input.AiModel.Config, + }, + } + } + if input.AiPrompt != nil { + proxy.Plugins["ai_prompt"] = api.PluginSetting{ + Config: plugin_model.ConfigType{ + "prompt": input.AiPrompt.Prompt, + "variables": input.AiPrompt.Variables, + }, + } + } + path := apiInfo.Path + if input.Path != nil { + inputPath := *input.Path + prefix, err := i.module.Prefix(ctx, serviceId) + if err != nil { + return err + } + if !strings.HasSuffix(inputPath, prefix) { + if inputPath[0] != '/' { + inputPath = fmt.Sprintf("/%s", inputPath) + } + path = fmt.Sprintf("%s%s", prefix, inputPath) + } else { + path = inputPath + } + } + proxy.Path = path + input.Path = &path + _, err = i.routerModule.Edit(ctx, serviceId, apiId, &router_dto.Edit{ + Description: input.Description, + Proxy: proxy, + Path: input.Path, + Disable: input.Disable, + Methods: &apiInfo.Methods, + }) + if err != nil { + return err + } + + return i.module.Edit(ctx, serviceId, apiId, input) + }) + if err != nil { + return nil, err + } + + return i.module.Get(ctx, serviceId, apiId) } func (i *imlAPIController) Delete(ctx *gin.Context, serviceId string, apiId string) error { - return i.module.Delete(ctx, serviceId, apiId) + return i.transaction.Transaction(ctx, func(txCtx context.Context) error { + err := i.routerModule.Delete(ctx, serviceId, apiId) + if err != nil { + return err + } + return i.module.Delete(ctx, serviceId, apiId) + }) + } func (i *imlAPIController) List(ctx *gin.Context, keyword string, serviceId string) ([]*ai_api_dto.APIItem, error) { diff --git a/controller/service/iml.go b/controller/service/iml.go index 65b4454f..0fcd2721 100644 --- a/controller/service/iml.go +++ b/controller/service/iml.go @@ -1,12 +1,17 @@ package service import ( + "context" "fmt" + model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime" "github.com/APIParkLab/APIPark/module/ai" ai_api "github.com/APIParkLab/APIPark/module/ai-api" ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto" "github.com/APIParkLab/APIPark/module/service" service_dto "github.com/APIParkLab/APIPark/module/service/dto" + "github.com/APIParkLab/APIPark/module/upstream" + upstream_dto "github.com/APIParkLab/APIPark/module/upstream/dto" + "github.com/eolinker/go-common/store" "github.com/gin-gonic/gin" "github.com/google/uuid" "strings" @@ -23,6 +28,56 @@ type imlServiceController struct { docModule service.IServiceDocModule `autowired:""` aiAPIModule ai_api.IAPIModule `autowired:""` providerModule ai.IProviderModule `autowired:""` + upstreamModule upstream.IUpstreamModule `autowired:""` + transaction store.ITransaction `autowired:""` +} + +func newAIUpstream(id string, provider string, uri model_runtime.IProviderURI) *upstream_dto.Upstream { + return &upstream_dto.Upstream{ + Type: "http", + Balance: "round-robin", + Timeout: 300000, + Retry: 0, + Remark: fmt.Sprintf("auto create by ai service %s,provider is %s", id, provider), + LimitPeerSecond: 0, + ProxyHeaders: nil, + Scheme: uri.Scheme(), + PassHost: "node", + Nodes: []*upstream_dto.NodeConfig{ + { + Address: uri.Host(), + Weight: 100, + }, + }, + } +} + +func (i *imlServiceController) EditAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) { + + if input.Provider == nil { + return nil, fmt.Errorf("provider is required") + } + p, has := model_runtime.GetProvider(*input.Provider) + if !has { + return nil, fmt.Errorf("provider not found") + } + info, err := i.module.Get(ctx, id) + if err != nil { + + } + err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { + info, err = i.module.Edit(ctx, id, input) + if err != nil { + return err + } + _, err = i.upstreamModule.Save(ctx, id, newAIUpstream(id, *input.Provider, p.URI())) + return err + }) + if err != nil { + return nil, err + } + + return info, nil } func (i *imlServiceController) CreateAIService(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) { @@ -31,7 +86,7 @@ func (i *imlServiceController) CreateAIService(ctx *gin.Context, teamID string, if input.Provider == nil { return nil, fmt.Errorf("provider is required") } - p, err := i.providerModule.Provider(ctx, *input.Provider) + pv, err := i.providerModule.Provider(ctx, *input.Provider) if err != nil { return nil, err } @@ -45,44 +100,51 @@ func (i *imlServiceController) CreateAIService(ctx *gin.Context, teamID string, input.Prefix = input.Id[:8] } } - info, err := i.module.Create(ctx, teamID, input) - if err != nil { - return nil, err + p, has := model_runtime.GetProvider(*input.Provider) + if !has { + return nil, fmt.Errorf("provider not found") } - _, err = i.aiAPIModule.Create( - ctx, - info.Id, - &ai_api_dto.CreateAPI{ - Name: "Default API", - Path: fmt.Sprintf("/%s", strings.Trim(input.Prefix, "/")), - Description: "Default API for service", - Disable: false, - AiPrompt: &ai_api_dto.AiPrompt{ - Variables: []*ai_api_dto.AiPromptVariable{ - { - Key: "Query", - Description: "", - Require: true, + var info *service_dto.Service + err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { + info, err = i.module.Create(ctx, teamID, input) + if err != nil { + return err + } + err = i.aiAPIModule.Create( + ctx, + info.Id, + &ai_api_dto.CreateAPI{ + Name: "Default API", + Path: fmt.Sprintf("/%s", strings.Trim(input.Prefix, "/")), + Description: "Default API for service", + Disable: false, + AiPrompt: &ai_api_dto.AiPrompt{ + Variables: []*ai_api_dto.AiPromptVariable{ + { + Key: "Query", + Description: "", + Require: true, + }, }, + Prompt: "{{Query}}", }, - Prompt: "{{Query}}", + AiModel: &ai_api_dto.AiModel{ + Id: pv.DefaultLLM, + Config: pv.DefaultLLMConfig, + }, + Timeout: 300000, + Retry: 0, }, - AiModel: &ai_api_dto.AiModel{ - Id: p.DefaultLLM, - Config: p.DefaultLLMConfig, - }, - Timeout: 300000, - Retry: 0, - }, - ) - if err != nil { - i.module.Delete(ctx, info.Id, "ai") - return nil, err - } - return info, nil + ) + _, err = i.upstreamModule.Save(ctx, info.Id, newAIUpstream(info.Id, *input.Provider, p.URI())) + return err + }) + + return info, err } func (i *imlServiceController) DeleteAIService(ctx *gin.Context, id string) error { + // TODO: 检查是否有发布过版本,若发布,则不允许删除 return i.module.Delete(ctx, id, "ai") } diff --git a/controller/service/service.go b/controller/service/service.go index 13bbee95..f1529302 100644 --- a/controller/service/service.go +++ b/controller/service/service.go @@ -22,15 +22,11 @@ type IServiceController interface { Edit(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) // Delete 删除 Delete(ctx *gin.Context, id string) error - // Simple 获取简易列表 - //Simple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) - //// MySimple 获取我的简易列表 - //MySimple(ctx *gin.Context, keyword string) ([]*service_dto.SimpleServiceItem, error) ServiceDoc(ctx *gin.Context, id string) (*service_dto.ServiceDoc, error) SaveServiceDoc(ctx *gin.Context, id string, input *service_dto.SaveServiceDoc) error CreateAIService(ctx *gin.Context, teamID string, input *service_dto.CreateService) (*service_dto.Service, error) - //EditAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) + EditAIService(ctx *gin.Context, id string, input *service_dto.EditService) (*service_dto.Service, error) DeleteAIService(ctx *gin.Context, id string) error SearchMyAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) SearchAIServices(ctx *gin.Context, teamID string, keyword string) ([]*service_dto.ServiceItem, error) diff --git a/gateway/apinto/entity/router.go b/gateway/apinto/entity/router.go index 5b82feb9..c7328985 100644 --- a/gateway/apinto/entity/router.go +++ b/gateway/apinto/entity/router.go @@ -4,9 +4,9 @@ import ( "fmt" "net/textproto" "strings" - + "github.com/APIParkLab/APIPark/common" - + "github.com/APIParkLab/APIPark/common/enum" "github.com/APIParkLab/APIPark/gateway" ) @@ -79,7 +79,7 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) //若请求路径包含restful参数 if common.IsRestfulPath(r.Path) { rewritePlugin.PathType = "regex" //正则替换 - + //如果转发路径包含restful参数 if common.IsRestfulPath(r.ProxyPath) { r.ProxyPath = formatProxyPath(r.Path, r.ProxyPath) @@ -100,7 +100,7 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) }, } } - + rules := make([]*Rule, 0, len(r.Rules)) for _, m := range r.Rules { rule := &Rule{ @@ -108,11 +108,11 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) Name: m.Key, Value: "", } - + if m.Position == enum.MatchPositionHeader { rule.Name = textproto.CanonicalMIMEHeaderKey(rule.Name) } - + switch m.MatchType { case enum.MatchTypeEqual: rule.Value = m.Pattern @@ -137,7 +137,7 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) case enum.MatchTypeAny: rule.Value = "*" } - + rules = append(rules, rule) } plugin := map[string]*Plugin{ @@ -148,8 +148,8 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) } for k, v := range r.Plugins { plugin[k] = &Plugin{ - Disable: false, - Config: v, + Disable: v.Disable, + Config: v.Config, } } hosts := make([]string, 0, len(r.Host)) @@ -160,7 +160,7 @@ func ToRouter(r *gateway.ApiRelease, version string, matches map[string]string) if r.Labels != nil { labels = r.Labels } - + return &Router{ BasicInfo: &BasicInfo{ ID: fmt.Sprintf("%s@router", r.ID), @@ -195,7 +195,7 @@ func formatProxyPath(requestPath, proxyPath string) string { i += 1 } } - + for param, order := range restfulSet { newProxyPath = strings.ReplaceAll(newProxyPath, param, order) } diff --git a/gateway/apinto/plugin/apinto_plugin.yml b/gateway/apinto/plugin/apinto_plugin.yml index f82c811d..629bddb5 100644 --- a/gateway/apinto/plugin/apinto_plugin.yml +++ b/gateway/apinto/plugin/apinto_plugin.yml @@ -31,6 +31,7 @@ content_typ: "text/plan" charset: "utf-8" body: "Forbidden" + - id: eolinker.com:apinto:strategy-plugin-visit name: strategy_visit @@ -62,5 +63,11 @@ rely: eolinker.com:apinto:plugin_app config: cache: redis@output - - +- + id: eolinker.com:apinto:ai_prompt + name: ai_prompt + status: enable +- + id: eolinker.com:apinto:ai_formatter + name: ai_formatter + status: enable \ No newline at end of file diff --git a/gateway/apinto/profession.go b/gateway/apinto/profession.go index b75a9918..e2827871 100644 --- a/gateway/apinto/profession.go +++ b/gateway/apinto/profession.go @@ -6,6 +6,7 @@ const ( ProfessionRouter = "router" ProfessionApplication = "app" ProfessionService = "service" + ProfessionAIProvider = "ai-provider" ) var dynamicResourceMap = map[string]Worker{ @@ -42,6 +43,10 @@ var dynamicResourceMap = map[string]Worker{ Profession: ProfessionCertificate, Driver: "server", }, + "openai": { + Profession: ProfessionAIProvider, + Driver: "openai", + }, } type Worker struct { diff --git a/module/ai-api/iml.go b/module/ai-api/iml.go index b442980e..9ff16112 100644 --- a/module/ai-api/iml.go +++ b/module/ai-api/iml.go @@ -86,41 +86,41 @@ func (i *imlAPIModule) deleteAPIDoc(ctx context.Context, serviceId string, path }) } -func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) { +func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_api_dto.CreateAPI) error { info, err := i.serviceService.Get(ctx, serviceId) if err != nil { - return nil, err + return err } if info.Kind != service.AIService { - return nil, fmt.Errorf("service kind is not ai service") + return fmt.Errorf("service kind is not ai service") } if input.Id == "" { input.Id = uuid.New().String() } - err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { - err = i.apiService.Exist(ctx, "", &api.Exist{Path: input.Path, Methods: []string{http.MethodPost}}) - if err != nil { - return err - } - err = i.apiService.Create(ctx, &api.Create{ - UUID: input.Id, - Description: input.Description, - Service: serviceId, - Team: info.Team, - Methods: []string{ - http.MethodPost, - }, - Protocols: []string{ - "http", - "https", - }, - Disable: false, - Path: input.Path, - Match: "{}", - }) - if err != nil { - return err - } + return i.transaction.Transaction(ctx, func(txCtx context.Context) error { + //err = i.apiService.Exist(ctx, "", &api.Exist{Path: input.Path, Methods: []string{http.MethodPost}}) + //if err != nil { + // return err + //} + //err = i.apiService.Create(ctx, &api.Create{ + // UUID: input.Id, + // Description: input.Description, + // Service: serviceId, + // Team: info.Team, + // Methods: []string{ + // http.MethodPost, + // }, + // Protocols: []string{ + // "http", + // "https", + // }, + // Disable: false, + // Path: input.Path, + // Match: "{}", + //}) + //if err != nil { + // return err + //} err = i.updateAPIDoc(ctx, serviceId, input.Path, input.Description, input.AiPrompt) if err != nil { return err @@ -140,50 +140,47 @@ func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_a }, }) }) - if err != nil { - return nil, err - } - return i.Get(ctx, serviceId, input.Id) } -func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) { +func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) error { info, err := i.serviceService.Get(ctx, serviceId) if err != nil { - return nil, err + return err } if info.Kind != service.AIService { - return nil, fmt.Errorf("service kind is not ai service") + return fmt.Errorf("service kind is not ai service") } - err = i.transaction.Transaction(ctx, func(txCtx context.Context) error { + return i.transaction.Transaction(ctx, func(txCtx context.Context) error { apiInfo, err := i.aiAPIService.Get(ctx, apiId) if err != nil { return err } if input.Path != nil { apiInfo.Path = *input.Path - prefix, err := i.Prefix(ctx, serviceId) - if err != nil { - return err - } - if !strings.HasSuffix(apiInfo.Path, prefix) { - if apiInfo.Path[0] != '/' { - apiInfo.Path = fmt.Sprintf("/%s", apiInfo.Path) - } - apiInfo.Path = fmt.Sprintf("%s%s", prefix, apiInfo.Path) - err := i.apiService.Exist(ctx, apiId, &api.Exist{Path: apiInfo.Path, Methods: []string{http.MethodPost}}) - if err != nil { - return err - } - err = i.apiService.Save(ctx, apiId, &api.Edit{ - Path: &apiInfo.Path, - }) - if err != nil { - return err - } - } } + // prefix, err := i.Prefix(ctx, serviceId) + // if err != nil { + // return err + // } + // if !strings.HasSuffix(apiInfo.Path, prefix) { + // if apiInfo.Path[0] != '/' { + // apiInfo.Path = fmt.Sprintf("/%s", apiInfo.Path) + // } + // apiInfo.Path = fmt.Sprintf("%s%s", prefix, apiInfo.Path) + // err := i.apiService.Exist(ctx, apiId, &api.Exist{Path: apiInfo.Path, Methods: []string{http.MethodPost}}) + // if err != nil { + // return err + // } + // err = i.apiService.Save(ctx, apiId, &api.Edit{ + // Path: &apiInfo.Path, + // }) + // if err != nil { + // return err + // } + // } + //} if input.Description != nil { apiInfo.Description = *input.Description } @@ -211,11 +208,6 @@ func (i *imlAPIModule) Edit(ctx context.Context, serviceId string, apiId string, AdditionalConfig: &apiInfo.AdditionalConfig, }) }) - if err != nil { - return nil, err - } - - return i.Get(ctx, serviceId, apiId) } func (i *imlAPIModule) Delete(ctx context.Context, serviceId string, apiId string) error { diff --git a/module/ai-api/module.go b/module/ai-api/module.go index fb9b250c..1119a2e0 100644 --- a/module/ai-api/module.go +++ b/module/ai-api/module.go @@ -8,11 +8,12 @@ import ( ) type IAPIModule interface { - Create(ctx context.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) - Edit(ctx context.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error) + Create(ctx context.Context, serviceId string, input *ai_api_dto.CreateAPI) error + Edit(ctx context.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) error Delete(ctx context.Context, serviceId string, apiId string) error List(ctx context.Context, keyword, serviceId string) ([]*ai_api_dto.APIItem, error) Get(ctx context.Context, serviceId string, apiId string) (*ai_api_dto.API, error) + Prefix(ctx context.Context, serviceId string) (string, error) } func init() { diff --git a/module/ai/iml.go b/module/ai/iml.go index 3ddbf569..b372f3fc 100644 --- a/module/ai/iml.go +++ b/module/ai/iml.go @@ -2,11 +2,16 @@ package ai import ( "context" + "encoding/json" "errors" "fmt" model_runtime "github.com/APIParkLab/APIPark/ai-provider/model-runtime" + "github.com/APIParkLab/APIPark/gateway" ai_dto "github.com/APIParkLab/APIPark/module/ai/dto" "github.com/APIParkLab/APIPark/service/ai" + "github.com/APIParkLab/APIPark/service/cluster" + "github.com/eolinker/eosc/log" + "github.com/eolinker/go-common/store" "github.com/eolinker/go-common/utils" "gorm.io/gorm" "sort" @@ -15,7 +20,9 @@ import ( var _ IProviderModule = (*imlProviderModule)(nil) type imlProviderModule struct { - providerService ai.IProviderService `autowired:""` + providerService ai.IProviderService `autowired:""` + clusterService cluster.IClusterService `autowired:""` + transaction store.ITransaction `autowired:""` } func (i *imlProviderModule) SimpleProviders(ctx context.Context) ([]*ai_dto.SimpleProviderItem, error) { @@ -71,7 +78,7 @@ func (i *imlProviderModule) Providers(ctx context.Context) ([]*ai_dto.ProviderIt } items = append(items, item) } - sort.SliceIsSorted(items, func(i, j int) bool { + sort.Slice(items, func(i, j int) bool { return items[i].UpdateTime.After(items[j].UpdateTime) }) @@ -160,59 +167,124 @@ func (i *imlProviderModule) LLMs(ctx context.Context, driver string) ([]*ai_dto. } func (i *imlProviderModule) UpdateProviderStatus(ctx context.Context, id string, enable bool) error { - _, err := i.providerService.Get(ctx, id) + info, err := i.providerService.Get(ctx, id) if err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return err } - p, has := model_runtime.GetProvider(id) - if !has { - return fmt.Errorf("ai provider not found") - } - cfg := p.DefaultConfig() - name := p.Name() - defaultLLm, ok := p.DefaultModel(model_runtime.ModelTypeLLM) - if !ok { - return fmt.Errorf("ai provider default llm not found") - } - defaultLLmId := defaultLLm.ID() - return i.providerService.Save(ctx, id, &ai.SetProvider{ - Name: &name, - DefaultLLM: &defaultLLmId, - Config: &cfg, - Status: &enable, - }) + return fmt.Errorf("ai provider not found") + //p, has := model_runtime.GetProvider(id) + //if !has { + // return fmt.Errorf("ai provider not found") + //} + //cfg := p.DefaultConfig() + //name := p.Name() + //defaultLLm, ok := p.DefaultModel(model_runtime.ModelTypeLLM) + //if !ok { + // return fmt.Errorf("ai provider default llm not found") + //} + //defaultLLmId := defaultLLm.ID() + //return i.providerService.Save(ctx, id, &ai.SetProvider{ + // Name: &name, + // DefaultLLM: &defaultLLmId, + // Config: &cfg, + // Status: &enable, + //}) } - return i.providerService.Save(ctx, id, &ai.SetProvider{ - Status: &enable, + + return i.transaction.Transaction(ctx, func(txCtx context.Context) error { + err = i.providerService.Save(txCtx, id, &ai.SetProvider{ + Status: &enable, + }) + if err != nil { + return err + } + if enable { + cfg := make(map[string]interface{}) + err = json.Unmarshal([]byte(info.Config), &cfg) + if err != nil { + log.Errorf("unmarshal ai provider config error,id is %s,err is %v", info.Id, err) + return err + } + cfg["driver"] = info.Id + return i.syncGateway(txCtx, cluster.DefaultClusterID, &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: info.Id, + Description: info.Name, + Version: info.UpdateAt.Format("20060102150405"), + MatchLabels: map[string]string{ + "module": "ai-provider", + }, + }, + Attr: cfg, + }, enable) + } else { + return i.syncGateway(txCtx, cluster.DefaultClusterID, &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: info.Id, + }, + }, enable) + } + }) } func (i *imlProviderModule) UpdateProviderConfig(ctx context.Context, id string, input *ai_dto.UpdateConfig) error { - _, err := i.providerService.Get(ctx, id) + p, has := model_runtime.GetProvider(id) + if !has { + return fmt.Errorf("ai provider not found") + } + info, err := i.providerService.Get(ctx, id) if err != nil { if !errors.Is(err, gorm.ErrRecordNotFound) { return err } - p, has := model_runtime.GetProvider(id) - if !has { - return fmt.Errorf("ai provider not found") - } - name := p.Name() defaultLLm, ok := p.DefaultModel(model_runtime.ModelTypeLLM) if !ok { return fmt.Errorf("ai provider default llm not found") } - defaultLLmId := defaultLLm.ID() - return i.providerService.Save(ctx, id, &ai.SetProvider{ - Name: &name, - DefaultLLM: &defaultLLmId, + info = &ai.Provider{ + Id: id, + Name: p.Name(), + DefaultLLM: defaultLLm.ID(), + Config: input.Config, + } + } + 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 { + err = i.providerService.Save(ctx, id, &ai.SetProvider{ + Name: &info.Name, + DefaultLLM: &info.DefaultLLM, Config: &input.Config, }) - } - return i.providerService.Save(ctx, id, &ai.SetProvider{ - Config: &input.Config, + if err != nil { + return err + } + cfg := make(map[string]interface{}) + err = json.Unmarshal([]byte(input.Config), &cfg) + if err != nil { + log.Errorf("unmarshal ai provider config error,id is %s,err is %v", id, err) + return err + } + return i.syncGateway(ctx, cluster.DefaultClusterID, &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: id, + Description: info.Name, + Version: info.UpdateAt.Format("20060102150405"), + MatchLabels: map[string]string{ + "module": "ai-provider", + }, + }, + Attr: cfg, + }, true) }) } @@ -222,19 +294,93 @@ func (i *imlProviderModule) UpdateProviderDefaultLLM(ctx context.Context, id str if !errors.Is(err, gorm.ErrRecordNotFound) { return err } - p, has := model_runtime.GetProvider(id) - if !has { - return fmt.Errorf("ai provider not found") - } - name := p.Name() - cfg := p.DefaultConfig() - return i.providerService.Save(ctx, id, &ai.SetProvider{ - Name: &name, - DefaultLLM: &input.LLM, - Config: &cfg, - }) + return fmt.Errorf("ai provider not found") + //p, has := model_runtime.GetProvider(id) + //if !has { + // return fmt.Errorf("ai provider not found") + //} + //name := p.Name() + //cfg := p.DefaultConfig() + //return i.providerService.Save(ctx, id, &ai.SetProvider{ + // Name: &name, + // DefaultLLM: &input.LLM, + // Config: &cfg, + //}) } return i.providerService.Save(ctx, id, &ai.SetProvider{ DefaultLLM: &input.LLM, }) } + +func (i *imlProviderModule) getAiProviders(ctx context.Context, clusterId string) ([]*gateway.DynamicRelease, error) { + list, err := i.providerService.List(ctx, clusterId) + if err != nil { + return nil, err + } + if err != nil { + return nil, err + } + providers := make([]*gateway.DynamicRelease, 0, len(list)) + for _, p := range list { + if !p.Status { + // 关闭 + continue + } + cfg := make(map[string]interface{}) + err = json.Unmarshal([]byte(p.Config), &cfg) + if err != nil { + log.Errorf("unmarshal ai provider config error,id is %s,err is %v", p.Id, err) + continue + } + providers = append(providers, &gateway.DynamicRelease{ + BasicItem: &gateway.BasicItem{ + ID: p.Id, + Description: p.Name, + Version: p.UpdateAt.Format("20060102150405"), + MatchLabels: map[string]string{ + "module": "ai-provider", + }, + }, + Attr: cfg, + }) + } + return providers, nil +} +func (i *imlProviderModule) initGateway(ctx context.Context, clusterId string, clientDriver gateway.IClientDriver) error { + providers, err := i.getAiProviders(ctx, clusterId) + if err != nil { + return err + } + for _, p := range providers { + client, err := clientDriver.Dynamic(p.ID) + if err != nil { + return err + } + err = client.Online(ctx, providers...) + if err != nil { + return err + } + } + return nil +} + +func (i *imlProviderModule) syncGateway(ctx context.Context, clusterId string, releaseInfo *gateway.DynamicRelease, online bool) error { + client, err := i.clusterService.GatewayClient(ctx, clusterId) + if err != nil { + return err + } + defer func() { + err := client.Close(ctx) + if err != nil { + log.Warn("close apinto client:", err) + } + }() + dynamicClient, err := client.Dynamic(releaseInfo.ID) + if err != nil { + return err + } + if online { + return dynamicClient.Online(ctx, releaseInfo) + } + return dynamicClient.Offline(ctx, releaseInfo) +} diff --git a/module/ai/module.go b/module/ai/module.go index c81db942..4bc67358 100644 --- a/module/ai/module.go +++ b/module/ai/module.go @@ -2,6 +2,7 @@ package ai import ( "context" + "github.com/APIParkLab/APIPark/gateway" ai_dto "github.com/APIParkLab/APIPark/module/ai/dto" "github.com/eolinker/go-common/autowire" "reflect" @@ -19,6 +20,8 @@ type IProviderModule interface { func init() { autowire.Auto[IProviderModule](func() reflect.Value { - return reflect.ValueOf(&imlProviderModule{}) + module := new(imlProviderModule) + gateway.RegisterInitHandleFunc(module.initGateway) + return reflect.ValueOf(module) }) } diff --git a/module/publish/iml.go b/module/publish/iml.go index 0b050e59..7e915c92 100644 --- a/module/publish/iml.go +++ b/module/publish/iml.go @@ -50,7 +50,7 @@ type imlPublishModule struct { } func (m *imlPublishModule) initGateway(ctx context.Context, partitionId string, clientDriver gateway.IClientDriver) error { - + return nil projects, err := m.serviceService.List(ctx) if err != nil { return err diff --git a/module/router/iml.go b/module/router/iml.go index 46ff5752..7515d5c9 100644 --- a/module/router/iml.go +++ b/module/router/iml.go @@ -280,7 +280,7 @@ func (i *imlRouterModule) Edit(ctx context.Context, serviceId string, apiId stri err = i.transaction.Transaction(ctx, func(ctx context.Context) error { if dto.Path != nil { - err = i.apiService.Exist(ctx, "", &api.Exist{Path: *dto.Path, Methods: *dto.Methods}) + err = i.apiService.Exist(ctx, apiId, &api.Exist{Path: *dto.Path, Methods: *dto.Methods}) if err != nil { return err } diff --git a/plugins/core/service.go b/plugins/core/service.go index ff0e7c4c..e447e4d9 100644 --- a/plugins/core/service.go +++ b/plugins/core/service.go @@ -22,7 +22,7 @@ func (p *plugin) ServiceApis() []pm3.Api { // AI服务 pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai-services", []string{"context", "query:service", "query:keyword"}, []string{"services"}, p.serviceController.SearchAIServices), pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/team/ai-service", []string{"context", "query:team", "body"}, []string{"service"}, p.serviceController.CreateAIService), - pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai-service/info", []string{"context", "query:service", "body"}, []string{"service"}, p.serviceController.Edit), + pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/ai-service/info", []string{"context", "query:service", "body"}, []string{"service"}, p.serviceController.EditAIService), pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/team/ai-service", []string{"context", "query:service"}, nil, p.serviceController.DeleteAIService), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/my_ai_services", []string{"context", "query:team", "query:keyword"}, []string{"services"}, p.serviceController.SearchMyAIServices), pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai-service/info", []string{"context", "query:service"}, []string{"service"}, p.serviceController.Get), diff --git a/resources/access/access.yaml b/resources/access/access.yaml index db1bfff2..0d9bed52 100644 --- a/resources/access/access.yaml +++ b/resources/access/access.yaml @@ -339,7 +339,7 @@ team: value: 'manager' apis: - "POST:/api/v1/service/publish/release/do" - - "PUT:/api/v1/service/publish/execute" +# - "PUT:/api/v1/service/publish/execute" - "DELETE:/api/v1/service/release" - name: subscription management cname: 订阅方管理 diff --git a/resources/plugin/plugin.yml b/resources/plugin/plugin.yml index 70a07ffc..d1996727 100644 --- a/resources/plugin/plugin.yml +++ b/resources/plugin/plugin.yml @@ -1,4 +1,4 @@ -version: v5 +version: v6 sort: - "access_log" - "monitor" @@ -74,4 +74,12 @@ plugin: name: strategy_cache status: global config: - cache: redis@output \ No newline at end of file + cache: redis@output + ai_prompt: + id: eolinker.com:apinto:ai_prompt + name: ai_prompt + status: enable + ai_formatter: + id: eolinker.com:apinto:ai_formatter + name: ai_formatter + status: enable \ No newline at end of file diff --git a/resources/plugin/render/eolinker_com_apinto_ai_formatter.json b/resources/plugin/render/eolinker_com_apinto_ai_formatter.json new file mode 100644 index 00000000..e50929f3 --- /dev/null +++ b/resources/plugin/render/eolinker_com_apinto_ai_formatter.json @@ -0,0 +1 @@ +{"group":"eolinker.com","project":"apinto","name":"ai_formatter","version":"innert","render":{"type":"object","eo:type":"object","properties":{"config":{"type":"string","eo:type":"string"},"model":{"type":"string","eo:type":"string"},"provider":{"type":"string","eo:type":"require","skill":"github.com/eolinker/apinto/convert.convert.IConverterDriver"}},"ui:sort":["provider","model","config"]}} \ No newline at end of file diff --git a/resources/plugin/render/eolinker_com_apinto_ai_prompt.json b/resources/plugin/render/eolinker_com_apinto_ai_prompt.json new file mode 100644 index 00000000..3b6c2033 --- /dev/null +++ b/resources/plugin/render/eolinker_com_apinto_ai_prompt.json @@ -0,0 +1 @@ +{"group":"eolinker.com","project":"apinto","name":"ai_prompt","version":"innert","render":{"type":"object","eo:type":"object","properties":{"prompt":{"type":"string","eo:type":"string"},"variables":{"type":"array","eo:type":"array","items":{"type":"object","eo:type":"object","properties":{"key":{"type":"string","eo:type":"string"},"require":{"type":"boolean","eo:type":"boolean"}},"ui:sort":["key","require"]}}},"ui:sort":["prompt","variables"]}} \ No newline at end of file diff --git a/resources/plugin/render/eolinker_com_apinto_plugin_app.json b/resources/plugin/render/eolinker_com_apinto_plugin_app.json index a9b2d2a8..5409f762 100644 --- a/resources/plugin/render/eolinker_com_apinto_plugin_app.json +++ b/resources/plugin/render/eolinker_com_apinto_plugin_app.json @@ -1 +1 @@ -{"group":"eolinker.com","project":"apinto","name":"plugin_app","version":"innert","render":{"type":"object","eo:type":"object"}} \ No newline at end of file +{"group":"eolinker.com","project":"apinto","name":"plugin_app","version":"innert","render":{"type":"object","eo:type":"object","properties":{"force_auth":{"type":"boolean","eo:type":"boolean","label":"是否强制认证"}},"ui:sort":["force_auth"]}} \ No newline at end of file diff --git a/resources/plugin/render/render.init.sh b/resources/plugin/render/render.init.sh index 4af324e1..8ada333f 100644 --- a/resources/plugin/render/render.init.sh +++ b/resources/plugin/render/render.init.sh @@ -40,4 +40,6 @@ curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/proxy_mirror > eolink curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/oauth2 > eolinker_com_apinto_oauth2.json curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/strategy-plugin-grey > eolinker_com_apinto_strategy-plugin-grey.json curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/strategy-plugin-fuse > eolinker_com_apinto_strategy-plugin-fuse.json -curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/response_file_parse > eolinker_com_apinto_response_file_parse.json \ No newline at end of file +curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/response_file_parse > eolinker_com_apinto_response_file_parse.json +curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/ai_formatter > eolinker_com_apinto_ai_formatter.json +curl -s http://127.0.0.1:9400/extender/eolinker.com:apinto/ai_prompt > eolinker_com_apinto_ai_prompt.json \ No newline at end of file diff --git a/service/api/iml.go b/service/api/iml.go index 12f42c25..6b2a37e1 100644 --- a/service/api/iml.go +++ b/service/api/iml.go @@ -29,6 +29,7 @@ type HistoryType string const ( HistoryRequest HistoryType = "request" HistoryProxy HistoryType = "proxy" + HistoryPlugin HistoryType = "plugin" ) type imlAPIService struct { @@ -36,10 +37,31 @@ type imlAPIService struct { apiInfoStore api.IAPIInfoStore `autowired:""` requestCommitService commit.ICommitWithKeyService[Request] `autowired:""` proxyCommitService commit.ICommitWithKeyService[Proxy] `autowired:""` + //pluginCommitService commit.ICommitWithKeyService[Plugin] `autowired:""` universally.IServiceGet[API] universally.IServiceDelete } +//func (i *imlAPIService) ListLatestCommitPlugin(ctx context.Context, aid ...string) ([]*commit.Commit[Plugin], error) { +// return i.pluginCommitService.ListLatest(ctx, aid...) +//} +// +//func (i *imlAPIService) GetPluginCommit(ctx context.Context, commitId string) (*commit.Commit[Plugin], error) { +// return i.pluginCommitService.Get(ctx, commitId) +//} +// +//func (i *imlAPIService) ListPluginCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Plugin], error) { +// return i.pluginCommitService.List(ctx, commitId...) +//} +// +//func (i *imlAPIService) SavePlugin(ctx context.Context, aid string, data *Plugin) error { +// return i.pluginCommitService.Save(ctx, aid, data) +//} +// +//func (i *imlAPIService) LatestPlugin(ctx context.Context, aid string) (*commit.Commit[Plugin], error) { +// return i.pluginCommitService.Latest(ctx, aid) +//} + func (i *imlAPIService) ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) { return i.requestCommitService.ListLatest(ctx, aid...) } @@ -228,12 +250,18 @@ func (i *imlAPIService) Exist(ctx context.Context, aid string, a *Exist) error { } continue } + if t.UUID == aid { + continue + } + for _, m := range t.Method { + existMethodMap[m] = struct{}{} } } for _, m := range a.Methods { if _, ok := existMethodMap[m]; ok { + return fmt.Errorf("method(%s),path(%s) is exist", m, a.Path) } } diff --git a/service/api/model.go b/service/api/model.go index 5fa6265b..b71059be 100644 --- a/service/api/model.go +++ b/service/api/model.go @@ -108,7 +108,6 @@ type PluginSetting struct { } type Request struct { - //ID string `json:"id"` Path string `json:"path"` Methods []string `json:"methods"` Protocols []string `json:"protocols"` @@ -143,3 +142,5 @@ type Match struct { Key string `json:"key"` Pattern string `json:"pattern"` } + +type Plugin map[string]interface{} diff --git a/service/api/service.go b/service/api/service.go index 8504887d..09911b0e 100644 --- a/service/api/service.go +++ b/service/api/service.go @@ -20,17 +20,24 @@ type IAPIService interface { GetInfo(ctx context.Context, aid string) (*Info, error) ListInfo(ctx context.Context, aids ...string) ([]*Info, error) ListInfoForService(ctx context.Context, serviceId string) ([]*Info, error) - ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) + ListLatestCommitProxy(ctx context.Context, aid ...string) ([]*commit.Commit[Proxy], error) - LatestRequest(ctx context.Context, aid string) (*commit.Commit[Request], error) LatestProxy(ctx context.Context, aid string) (*commit.Commit[Proxy], error) GetProxyCommit(ctx context.Context, commitId string) (*commit.Commit[Proxy], error) ListProxyCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Proxy], error) SaveProxy(ctx context.Context, aid string, data *Proxy) error + ListLatestCommitRequest(ctx context.Context, aid ...string) ([]*commit.Commit[Request], error) GetRequestCommit(ctx context.Context, commitId string) (*commit.Commit[Request], error) ListRequestCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Request], error) SaveRequest(ctx context.Context, aid string, data *Request) error + LatestRequest(ctx context.Context, aid string) (*commit.Commit[Request], error) + + //ListLatestCommitPlugin(ctx context.Context, aid ...string) ([]*commit.Commit[Plugin], error) + //GetPluginCommit(ctx context.Context, commitId string) (*commit.Commit[Plugin], error) + //ListPluginCommit(ctx context.Context, commitId ...string) ([]*commit.Commit[Plugin], error) + //SavePlugin(ctx context.Context, aid string, data *Plugin) error + //LatestPlugin(ctx context.Context, aid string) (*commit.Commit[Plugin], error) Save(ctx context.Context, id string, model *Edit) error Create(ctx context.Context, input *Create) (err error) @@ -47,5 +54,6 @@ func init() { commit.InitCommitWithKeyService[Proxy]("api", string(HistoryProxy)) commit.InitCommitWithKeyService[Request]("api", string(HistoryRequest)) + commit.InitCommitWithKeyService[Plugin]("api", string(HistoryPlugin)) }