mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
apinto对接完成
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -37,3 +37,4 @@ provider_credential_schema:
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 API URL
|
||||
en_US: Enter your API URL
|
||||
address: https://api.openai.com
|
||||
|
||||
@@ -29,3 +29,4 @@ provider_credential_schema:
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 API Key
|
||||
en_US: Enter your API Key
|
||||
address: https://api.openai.com
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+146
-4
@@ -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) {
|
||||
|
||||
+94
-32
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
+52
-60
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
+192
-46
@@ -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)
|
||||
}
|
||||
|
||||
+4
-1
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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: 订阅方管理
|
||||
|
||||
@@ -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
|
||||
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
|
||||
@@ -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"]}}
|
||||
@@ -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"]}}
|
||||
@@ -1 +1 @@
|
||||
{"group":"eolinker.com","project":"apinto","name":"plugin_app","version":"innert","render":{"type":"object","eo:type":"object"}}
|
||||
{"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"]}}
|
||||
@@ -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
|
||||
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
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
|
||||
+10
-2
@@ -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))
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user