mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
ai路由完成
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"github.com/gin-gonic/gin"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type IAPIController interface {
|
||||
Create(ctx *gin.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error)
|
||||
Edit(ctx *gin.Context, serviceId string, apiId string, input *ai_api_dto.EditAPI) (*ai_api_dto.API, error)
|
||||
Delete(ctx *gin.Context, serviceId string, apiId string) error
|
||||
List(ctx *gin.Context, keyword string, serviceId string) ([]*ai_api_dto.APIItem, error)
|
||||
Get(ctx *gin.Context, serviceId string, apiId string) (*ai_api_dto.API, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IAPIController](func() reflect.Value {
|
||||
m := new(imlAPIController)
|
||||
return reflect.ValueOf(m)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
ai_api "github.com/APIParkLab/APIPark/module/ai-api"
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var _ IAPIController = (*imlAPIController)(nil)
|
||||
|
||||
type imlAPIController struct {
|
||||
module ai_api.IAPIModule `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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
func (i *imlAPIController) Delete(ctx *gin.Context, serviceId string, apiId string) error {
|
||||
return i.module.Delete(ctx, serviceId, apiId)
|
||||
}
|
||||
|
||||
func (i *imlAPIController) List(ctx *gin.Context, keyword string, serviceId string) ([]*ai_api_dto.APIItem, error) {
|
||||
return i.module.List(ctx, keyword, serviceId)
|
||||
}
|
||||
|
||||
func (i *imlAPIController) Get(ctx *gin.Context, serviceId string, apiId string) (*ai_api_dto.API, error) {
|
||||
return i.module.Get(ctx, serviceId, apiId)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package ai_api_dto
|
||||
|
||||
type CreateAPI struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout int `json:"timeout"`
|
||||
Retry int `json:"retry"`
|
||||
}
|
||||
|
||||
type AiPrompt struct {
|
||||
Variables []*AiPromptVariable `json:"variables"`
|
||||
Prompt string `json:"prompt"`
|
||||
}
|
||||
|
||||
type AiPromptVariable struct {
|
||||
Key string `json:"key"`
|
||||
Description string `json:"description"`
|
||||
Require bool `json:"require"`
|
||||
}
|
||||
|
||||
type AiModel struct {
|
||||
Id string `json:"id"`
|
||||
Config string `json:"config"`
|
||||
}
|
||||
|
||||
type EditAPI struct {
|
||||
Name *string `json:"name"`
|
||||
Path *string `json:"path"`
|
||||
Description *string `json:"description"`
|
||||
Disable *bool `json:"disable"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout *int `json:"timeout"`
|
||||
Retry *int `json:"retry"`
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ai_api_dto
|
||||
|
||||
import (
|
||||
"github.com/eolinker/go-common/auto"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
AiPrompt *AiPrompt `json:"ai_prompt"`
|
||||
AiModel *AiModel `json:"ai_model"`
|
||||
Timeout int `json:"timeout"`
|
||||
Retry int `json:"retry"`
|
||||
}
|
||||
|
||||
type APIItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RequestPath string `json:"request_path"`
|
||||
Description string `json:"description"`
|
||||
Disable bool `json:"disable"`
|
||||
Creator auto.Label `json:"creator" aolabel:"user"`
|
||||
Updater auto.Label `json:"updater" aolabel:"user"`
|
||||
CreateTime auto.TimeLabel `json:"create_time"`
|
||||
UpdateTime auto.TimeLabel `json:"update_time"`
|
||||
Model ModelItem `json:"model"`
|
||||
}
|
||||
|
||||
type ModelItem struct {
|
||||
Id string `json:"id"`
|
||||
Logo string `json:"logo"`
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/APIParkLab/APIPark/module/ai/provider"
|
||||
ai_api "github.com/APIParkLab/APIPark/service/ai-api"
|
||||
api_doc "github.com/APIParkLab/APIPark/service/api-doc"
|
||||
"github.com/APIParkLab/APIPark/service/service"
|
||||
"github.com/eolinker/go-common/auto"
|
||||
"github.com/eolinker/go-common/store"
|
||||
"github.com/eolinker/go-common/utils"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
var _ IAPIModule = (*imlAPIModule)(nil)
|
||||
|
||||
var (
|
||||
openapi3Loader = openapi3.NewLoader()
|
||||
)
|
||||
|
||||
type imlAPIModule struct {
|
||||
serviceService service.IServiceService `autowired:""`
|
||||
apiDocService api_doc.IAPIDocService `autowired:""`
|
||||
aiAPIService ai_api.IAPIService `autowired:""`
|
||||
transaction store.ITransaction `autowired:""`
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) getAPIDoc(ctx context.Context, serviceId string) (*openapi3.T, error) {
|
||||
doc, err := i.apiDocService.GetDoc(ctx, serviceId)
|
||||
if err != nil {
|
||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get service info error:%v", err)
|
||||
}
|
||||
return genOpenAPI3Template(info.Name, info.Description), nil
|
||||
}
|
||||
return openapi3Loader.LoadFromData([]byte(doc.Content))
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) updateAPIDoc(ctx context.Context, serviceId string, path string, description string, aiPrompt *ai_api_dto.AiPrompt) error {
|
||||
doc, err := i.getAPIDoc(ctx, serviceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var variables []*ai_api_dto.AiPromptVariable
|
||||
if aiPrompt != nil {
|
||||
variables = aiPrompt.Variables
|
||||
}
|
||||
doc.AddOperation(path, http.MethodPost, genOperation(description, variables))
|
||||
result, err := doc.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.apiDocService.UpdateDoc(ctx, serviceId, &api_doc.UpdateDoc{
|
||||
ID: uuid.New().String(),
|
||||
Content: string(result),
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) deleteAPIDoc(ctx context.Context, serviceId string, path string) error {
|
||||
doc, err := i.getAPIDoc(ctx, serviceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
doc.Paths.Delete(path)
|
||||
result, err := doc.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.apiDocService.UpdateDoc(ctx, serviceId, &api_doc.UpdateDoc{
|
||||
ID: uuid.New().String(),
|
||||
Content: string(result),
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) Create(ctx context.Context, serviceId string, input *ai_api_dto.CreateAPI) (*ai_api_dto.API, error) {
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Kind != service.AIService {
|
||||
return nil, 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.updateAPIDoc(ctx, serviceId, input.Path, input.Description, input.AiPrompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.aiAPIService.Create(ctx, &ai_api.Create{
|
||||
ID: input.Id,
|
||||
Name: input.Name,
|
||||
Service: serviceId,
|
||||
Path: input.Path,
|
||||
Description: input.Description,
|
||||
Timeout: input.Timeout,
|
||||
Retry: input.Retry,
|
||||
Model: input.AiModel.Id,
|
||||
AdditionalConfig: map[string]interface{}{
|
||||
"ai_prompt": input.AiPrompt,
|
||||
"ai_model": input.AiModel,
|
||||
},
|
||||
})
|
||||
})
|
||||
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) {
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Kind != service.AIService {
|
||||
return nil, fmt.Errorf("service kind is not ai service")
|
||||
}
|
||||
|
||||
err = 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
|
||||
}
|
||||
if input.Description != nil {
|
||||
apiInfo.Description = *input.Description
|
||||
}
|
||||
err = i.updateAPIDoc(ctx, serviceId, apiInfo.Path, apiInfo.Description, input.AiPrompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var modelId *string
|
||||
if input.AiModel != nil {
|
||||
modelId = &input.AiModel.Id
|
||||
}
|
||||
if input.AiPrompt != nil {
|
||||
apiInfo.AdditionalConfig["ai_prompt"] = input.AiPrompt
|
||||
}
|
||||
if input.AiModel != nil {
|
||||
apiInfo.AdditionalConfig["ai_model"] = input.AiModel
|
||||
}
|
||||
return i.aiAPIService.Save(ctx, apiId, &ai_api.Edit{
|
||||
Name: input.Name,
|
||||
Path: input.Path,
|
||||
Description: input.Description,
|
||||
Timeout: input.Timeout,
|
||||
Retry: input.Retry,
|
||||
Model: modelId,
|
||||
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 {
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Kind != service.AIService {
|
||||
return fmt.Errorf("service kind is not ai service")
|
||||
}
|
||||
return i.transaction.Transaction(ctx, func(txCtx context.Context) error {
|
||||
err = i.deleteAPIDoc(ctx, serviceId, apiId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.aiAPIService.Delete(ctx, apiId)
|
||||
})
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) List(ctx context.Context, keyword string, serviceId string) ([]*ai_api_dto.APIItem, error) {
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Kind != service.AIService {
|
||||
return nil, fmt.Errorf("service kind is not ai service")
|
||||
}
|
||||
apis, err := i.aiAPIService.Search(ctx, keyword, map[string]interface{}{
|
||||
"service": serviceId,
|
||||
}, "update_at desc")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, has := provider.GetProvider(info.AdditionalConfig["provider"])
|
||||
if !has {
|
||||
return nil, fmt.Errorf("provider not found")
|
||||
}
|
||||
return utils.SliceToSlice(apis, func(t *ai_api.API) *ai_api_dto.APIItem {
|
||||
modelItem := ai_api_dto.ModelItem{
|
||||
Id: t.Model,
|
||||
}
|
||||
model, has := p.LLM(t.Model)
|
||||
if has {
|
||||
modelItem.Logo = model.Logo
|
||||
}
|
||||
return &ai_api_dto.APIItem{
|
||||
Id: t.ID,
|
||||
Name: t.Name,
|
||||
RequestPath: t.Path,
|
||||
Description: t.Description,
|
||||
Disable: t.Disable,
|
||||
Creator: auto.UUID(t.Creator),
|
||||
Updater: auto.UUID(t.Updater),
|
||||
CreateTime: auto.TimeLabel(t.CreateAt),
|
||||
UpdateTime: auto.TimeLabel(t.UpdateAt),
|
||||
Model: modelItem,
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (i *imlAPIModule) Get(ctx context.Context, serviceId string, apiId string) (*ai_api_dto.API, error) {
|
||||
info, err := i.serviceService.Get(ctx, serviceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Kind != service.AIService {
|
||||
return nil, fmt.Errorf("service kind is not ai service")
|
||||
}
|
||||
apiInfo, err := i.aiAPIService.Get(ctx, apiId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prompt, err := ConvertStruct[ai_api_dto.AiPrompt](apiInfo.AdditionalConfig["ai_prompt"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aiModel, err := ConvertStruct[ai_api_dto.AiModel](apiInfo.AdditionalConfig["ai_model"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ai_api_dto.API{
|
||||
Id: apiInfo.ID,
|
||||
Name: apiInfo.Name,
|
||||
Path: apiInfo.Path,
|
||||
Description: apiInfo.Description,
|
||||
Disable: apiInfo.Disable,
|
||||
AiPrompt: prompt,
|
||||
AiModel: aiModel,
|
||||
Timeout: apiInfo.Timeout,
|
||||
Retry: apiInfo.Retry,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ConvertStruct[T any](data interface{}) (*T, error) {
|
||||
b, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var t T
|
||||
err = json.Unmarshal(b, &t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"context"
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IAPIModule](func() reflect.Value {
|
||||
m := new(imlAPIModule)
|
||||
return reflect.ValueOf(m)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
ai_api_dto "github.com/APIParkLab/APIPark/module/ai-api/dto"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
)
|
||||
|
||||
func genOpenAPI3Template(title string, description string) *openapi3.T {
|
||||
result := new(openapi3.T)
|
||||
result.OpenAPI = "3.1.0"
|
||||
result.Info = &openapi3.Info{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Version: "beta",
|
||||
}
|
||||
result.Components = genComponents()
|
||||
result.Paths = new(openapi3.Paths)
|
||||
return result
|
||||
}
|
||||
|
||||
func genOperation(description string, variables []*ai_api_dto.AiPromptVariable) *openapi3.Operation {
|
||||
operation := openapi3.NewOperation()
|
||||
operation.RequestBody = genRequestBody(variables)
|
||||
operation.Responses = &openapi3.Responses{}
|
||||
operation.Responses.Set("200", genResponse())
|
||||
operation.Description = description
|
||||
return operation
|
||||
}
|
||||
|
||||
func genRequestBody(variables []*ai_api_dto.AiPromptVariable) *openapi3.RequestBodyRef {
|
||||
requestBody := openapi3.NewRequestBody()
|
||||
requestBody.Content = openapi3.NewContentWithSchema(genRequestBodySchema(variables), []string{"application/json"})
|
||||
return &openapi3.RequestBodyRef{
|
||||
Value: requestBody,
|
||||
}
|
||||
}
|
||||
|
||||
func genResponse() *openapi3.ResponseRef {
|
||||
response := openapi3.NewResponse()
|
||||
response.Content = openapi3.NewContentWithSchema(genResponseSchema(), []string{"application/json"})
|
||||
description := "Response from the server"
|
||||
response.Description = &description
|
||||
return &openapi3.ResponseRef{
|
||||
Value: response,
|
||||
}
|
||||
}
|
||||
|
||||
func genRequestBodySchema(variables []*ai_api_dto.AiPromptVariable) *openapi3.Schema {
|
||||
result := openapi3.NewObjectSchema()
|
||||
variableSchema := openapi3.NewObjectSchema()
|
||||
required := make([]string, 0, len(variables))
|
||||
for _, v := range variables {
|
||||
val := openapi3.NewStringSchema()
|
||||
val.Description = v.Description
|
||||
if v.Require {
|
||||
required = append(required, v.Key)
|
||||
}
|
||||
variableSchema.WithProperty(v.Key, val)
|
||||
}
|
||||
result.WithProperty("variables", variableSchema.WithRequired(required))
|
||||
result.WithProperty("messages", genMessageSchema())
|
||||
result.WithRequired([]string{"variables", "messages"})
|
||||
return result
|
||||
}
|
||||
|
||||
func genComponents() *openapi3.Components {
|
||||
components := openapi3.NewComponents()
|
||||
components.Schemas = make(openapi3.Schemas)
|
||||
components.Schemas["Message"] = genMessageSchema().NewRef()
|
||||
components.Schemas["Response"] = genResponseSchema().NewRef()
|
||||
return &components
|
||||
}
|
||||
|
||||
func genMessageSchema() *openapi3.Schema {
|
||||
messageSchema := openapi3.NewObjectSchema()
|
||||
messageSchema.Title = "Message"
|
||||
messageSchema.Description = "Chat Message"
|
||||
roleSchema := openapi3.NewStringSchema()
|
||||
roleSchema.Description = "Role of the message sender"
|
||||
roleSchema.Example = "assistant"
|
||||
contentSchema := openapi3.NewStringSchema()
|
||||
contentSchema.Description = "The message content"
|
||||
contentSchema.Example = "Hello, how can I help you?"
|
||||
messageSchema.WithProperties(map[string]*openapi3.Schema{
|
||||
"role": roleSchema,
|
||||
"content": contentSchema,
|
||||
})
|
||||
return messageSchema
|
||||
}
|
||||
|
||||
func genResponseSchema() *openapi3.Schema {
|
||||
responseSchema := openapi3.NewObjectSchema()
|
||||
responseSchema.Description = "Response from the server"
|
||||
responseSchema.WithPropertyRef("message", openapi3.NewSchemaRef("#/components/schemas/Message", genMessageSchema()))
|
||||
responseSchema.WithProperty("code", openapi3.NewInt32Schema().WithMin(0))
|
||||
responseSchema.WithProperty("error", openapi3.NewStringSchema())
|
||||
responseSchema.WithProperty("finish_reason", openapi3.NewStringSchema().WithEnum([]string{
|
||||
"stop",
|
||||
"length",
|
||||
"function_call",
|
||||
"content_filter",
|
||||
"null",
|
||||
}))
|
||||
return responseSchema
|
||||
}
|
||||
@@ -8,11 +8,12 @@ type Provider struct {
|
||||
}
|
||||
|
||||
type ProviderItem struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DefaultLLM string `json:"default_llm"`
|
||||
Logo string `json:"logo"`
|
||||
Configured bool `json:"configured"`
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
DefaultLLM string `json:"default_llm"`
|
||||
DefaultLLMLogo string `json:"default_llm_logo"`
|
||||
Logo string `json:"logo"`
|
||||
Configured bool `json:"configured"`
|
||||
}
|
||||
|
||||
type SimpleProviderItem struct {
|
||||
|
||||
@@ -69,11 +69,16 @@ func (i *imlProviderModule) Providers(ctx context.Context) ([]*ai_dto.ProviderIt
|
||||
DefaultLLM: v.Info().DefaultLLM,
|
||||
}
|
||||
if info, has := providerMap[v.Info().Id]; has {
|
||||
llm, has := v.LLM(info.DefaultLLM)
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
err = v.GlobalConfig().CheckConfig(info.Config)
|
||||
if err == nil {
|
||||
item.Configured = true
|
||||
}
|
||||
item.DefaultLLM = info.DefaultLLM
|
||||
item.DefaultLLMLogo = llm.Logo
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"github.com/APIParkLab/APIPark/module/ai/provider"
|
||||
"github.com/eolinker/eosc"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -27,19 +28,23 @@ type ModelData struct {
|
||||
type Model struct {
|
||||
globalConfig provider.IAIConfig
|
||||
invokeConfig provider.IAIConfig
|
||||
models []*ModelData
|
||||
models eosc.Untyped[string, *ModelData]
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func NewModel() provider.IAIProvider {
|
||||
modelData := make([]*ModelData, 0)
|
||||
json.Unmarshal(modelJson, &modelData)
|
||||
return &Model{
|
||||
m := &Model{
|
||||
globalConfig: NewGlobalConfigDriver(),
|
||||
invokeConfig: NewInvokeConfigDriver(),
|
||||
models: modelData,
|
||||
models: eosc.BuildUntyped[string, *ModelData](),
|
||||
locker: sync.RWMutex{},
|
||||
}
|
||||
for _, v := range modelData {
|
||||
m.models.Set(v.Id, v)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Model) Index() int {
|
||||
@@ -83,11 +88,9 @@ func (m *Model) UpdateLLMs() error {
|
||||
}
|
||||
|
||||
func (m *Model) LLMs() []*provider.LLM {
|
||||
m.locker.RLock()
|
||||
models := m.models
|
||||
m.locker.RUnlock()
|
||||
result := make([]*provider.LLM, 0, len(models))
|
||||
for _, model := range models {
|
||||
result := make([]*provider.LLM, 0, models.Count())
|
||||
for _, model := range models.List() {
|
||||
llm := &provider.LLM{
|
||||
Id: model.Id,
|
||||
Logo: model.Logo,
|
||||
@@ -97,3 +100,15 @@ func (m *Model) LLMs() []*provider.LLM {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Model) LLM(id string) (*provider.LLM, bool) {
|
||||
model, has := m.models.Get(id)
|
||||
if !has {
|
||||
return nil, false
|
||||
}
|
||||
return &provider.LLM{
|
||||
Id: model.Id,
|
||||
Logo: model.Logo,
|
||||
Scopes: model.Scopes,
|
||||
}, true
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ type IAIProvider interface {
|
||||
InvokeConfig() IAIConfig
|
||||
UpdateLLMs() error
|
||||
LLMs() []*LLM
|
||||
LLM(id string) (*LLM, bool)
|
||||
}
|
||||
|
||||
type IAIConfig interface {
|
||||
|
||||
@@ -29,7 +29,6 @@ func (i *imlAPIDocModule) UpdateDoc(ctx context.Context, serviceId string, input
|
||||
}
|
||||
err = i.apiDocService.UpdateDoc(ctx, serviceId, &api_doc.UpdateDoc{
|
||||
ID: input.Id,
|
||||
Service: serviceId,
|
||||
Content: input.Content,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -19,5 +19,11 @@ func (p *plugin) apiApis() []pm3.Api {
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/api_doc", []string{"context", "query:service"}, []string{"doc"}, p.apiDocController.GetDoc),
|
||||
|
||||
pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/api_doc/upload", []string{"context", "query:service"}, []string{"doc"}, p.apiDocController.UploadDoc),
|
||||
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/ai-router", []string{"context", "query:service", "query:router"}, []string{"api"}, p.aiAPIController.Get),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/service/ai-routers", []string{"context", "query:keyword", "query:service"}, []string{"apis"}, p.aiAPIController.List),
|
||||
pm3.CreateApiWidthDoc(http.MethodPut, "/api/v1/service/ai-router", []string{"context", "query:service", "query:router", "body"}, []string{"api"}, p.aiAPIController.Edit),
|
||||
pm3.CreateApiWidthDoc(http.MethodPost, "/api/v1/service/ai-router", []string{"context", "query:service", "body"}, []string{"api"}, p.aiAPIController.Create),
|
||||
pm3.CreateApiWidthDoc(http.MethodDelete, "/api/v1/service/ai-router", []string{"context", "query:service", "query:router"}, nil, p.aiAPIController.Delete),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"github.com/APIParkLab/APIPark/controller/ai"
|
||||
ai_api "github.com/APIParkLab/APIPark/controller/ai-api"
|
||||
"github.com/APIParkLab/APIPark/controller/monitor"
|
||||
"github.com/APIParkLab/APIPark/controller/router"
|
||||
"github.com/APIParkLab/APIPark/controller/system"
|
||||
@@ -67,6 +68,7 @@ type plugin struct {
|
||||
catalogueController catalogue.ICatalogueController `autowired:""`
|
||||
upstreamController upstream.IUpstreamController `autowired:""`
|
||||
routerController router.IRouterController `autowired:""`
|
||||
aiAPIController ai_api.IAPIController `autowired:""`
|
||||
apiDocController router.IAPIDocController `autowired:""`
|
||||
subscribeController subscribe.ISubscribeController `autowired:""`
|
||||
appAuthorizationController application_authorization.IAuthorizationController `autowired:""`
|
||||
|
||||
@@ -20,12 +20,12 @@ func (p *plugin) ServiceApis() []pm3.Api {
|
||||
//pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/simple/services", []string{"context", "query:keyword"}, []string{"services"}, p.serviceController.Simple),
|
||||
|
||||
// AI服务
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai-services", []string{"context", "query:service", "query:keyword"}, []string{"service"}, p.serviceController.SearchAIServices),
|
||||
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.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{"services"}, p.serviceController.Get),
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/ai-service/info", []string{"context", "query:service"}, []string{"service"}, p.serviceController.Get),
|
||||
|
||||
// 应用相关
|
||||
pm3.CreateApiWidthDoc(http.MethodGet, "/api/v1/app/info", []string{"context", "query:app"}, []string{"app"}, p.appController.GetApp),
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/APIParkLab/APIPark/service/universally"
|
||||
"github.com/APIParkLab/APIPark/stores/api"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ IAPIService = (*imlAPIService)(nil)
|
||||
|
||||
type imlAPIService struct {
|
||||
store api.IAiAPIInfoStore `autowired:""`
|
||||
|
||||
universally.IServiceGet[API]
|
||||
universally.IServiceCreate[Create]
|
||||
universally.IServiceEdit[Edit]
|
||||
universally.IServiceDelete
|
||||
}
|
||||
|
||||
func (i *imlAPIService) OnComplete() {
|
||||
i.IServiceGet = universally.NewGetSoftDelete[API, api.AiAPIInfo](i.store, FromEntity)
|
||||
i.IServiceCreate = universally.NewCreatorSoftDelete[Create, api.AiAPIInfo](i.store, "ai_api_info", createEntityHandler, uniquestHandler, labelHandler)
|
||||
i.IServiceEdit = universally.NewEdit[Edit, api.AiAPIInfo](i.store, updateHandler)
|
||||
i.IServiceDelete = universally.NewSoftDelete[api.AiAPIInfo](i.store)
|
||||
}
|
||||
|
||||
func labelHandler(e *api.AiAPIInfo) []string {
|
||||
return []string{e.Name, e.Uuid}
|
||||
}
|
||||
func uniquestHandler(i *Create) []map[string]interface{} {
|
||||
return []map[string]interface{}{{"uuid": i.ID}}
|
||||
}
|
||||
func createEntityHandler(i *Create) *api.AiAPIInfo {
|
||||
now := time.Now()
|
||||
cfg, _ := json.Marshal(i.AdditionalConfig)
|
||||
return &api.AiAPIInfo{
|
||||
Uuid: i.ID,
|
||||
Name: i.Name,
|
||||
Service: i.Service,
|
||||
Path: i.Path,
|
||||
Description: i.Description,
|
||||
Timeout: i.Timeout,
|
||||
Retry: i.Retry,
|
||||
Model: i.Model,
|
||||
CreateAt: now,
|
||||
UpdateAt: now,
|
||||
AdditionalConfig: string(cfg),
|
||||
}
|
||||
}
|
||||
func updateHandler(e *api.AiAPIInfo, i *Edit) {
|
||||
if i.Name != nil {
|
||||
e.Name = *i.Name
|
||||
}
|
||||
if i.Path != nil {
|
||||
e.Path = *i.Path
|
||||
}
|
||||
if i.Description != nil {
|
||||
e.Description = *i.Description
|
||||
}
|
||||
if i.Timeout != nil {
|
||||
e.Timeout = *i.Timeout
|
||||
}
|
||||
if i.Retry != nil {
|
||||
e.Retry = *i.Retry
|
||||
}
|
||||
if i.Model != nil {
|
||||
e.Model = *i.Model
|
||||
}
|
||||
if i.AdditionalConfig != nil {
|
||||
cfg, _ := json.Marshal(i.AdditionalConfig)
|
||||
e.AdditionalConfig = string(cfg)
|
||||
}
|
||||
e.UpdateAt = time.Now()
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/APIParkLab/APIPark/stores/api"
|
||||
"time"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
ID string
|
||||
Name string
|
||||
Service string
|
||||
Path string
|
||||
Description string
|
||||
Timeout int
|
||||
Retry int
|
||||
Model string
|
||||
CreateAt time.Time
|
||||
UpdateAt time.Time
|
||||
Creator string
|
||||
Updater string
|
||||
AdditionalConfig map[string]interface{}
|
||||
Disable bool
|
||||
}
|
||||
|
||||
type Create struct {
|
||||
ID string
|
||||
Name string
|
||||
Service string
|
||||
Path string
|
||||
Description string
|
||||
Timeout int
|
||||
Retry int
|
||||
Model string
|
||||
AdditionalConfig map[string]interface{}
|
||||
}
|
||||
|
||||
type Edit struct {
|
||||
Name *string
|
||||
Path *string
|
||||
Description *string
|
||||
Timeout *int
|
||||
Retry *int
|
||||
Model *string
|
||||
AdditionalConfig *map[string]interface{}
|
||||
}
|
||||
|
||||
func FromEntity(e *api.AiAPIInfo) *API {
|
||||
cfg := make(map[string]interface{})
|
||||
if e.AdditionalConfig != "" {
|
||||
_ = json.Unmarshal([]byte(e.AdditionalConfig), &cfg)
|
||||
}
|
||||
return &API{
|
||||
ID: e.Uuid,
|
||||
Name: e.Name,
|
||||
Service: e.Service,
|
||||
Path: e.Path,
|
||||
Description: e.Description,
|
||||
Timeout: e.Timeout,
|
||||
Retry: e.Retry,
|
||||
Model: e.Model,
|
||||
CreateAt: e.CreateAt,
|
||||
UpdateAt: e.UpdateAt,
|
||||
Creator: e.Creator,
|
||||
Updater: e.Updater,
|
||||
AdditionalConfig: cfg,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ai_api
|
||||
|
||||
import (
|
||||
"github.com/APIParkLab/APIPark/service/universally"
|
||||
"github.com/eolinker/go-common/autowire"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type IAPIService interface {
|
||||
universally.IServiceGet[API]
|
||||
universally.IServiceCreate[Create]
|
||||
universally.IServiceEdit[Edit]
|
||||
universally.IServiceDelete
|
||||
|
||||
//ListByServices(ctx context.Context, serviceIds ...string) ([]*API, error)
|
||||
}
|
||||
|
||||
func init() {
|
||||
autowire.Auto[IAPIService](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAPIService))
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import "time"
|
||||
|
||||
type UpdateDoc struct {
|
||||
ID string
|
||||
Service string
|
||||
Content string
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,29 @@ type IAPIDocStore interface {
|
||||
store.IBaseStore[Doc]
|
||||
}
|
||||
|
||||
type IAiAPIInfoStore interface {
|
||||
store.ISearchStore[AiAPIInfo]
|
||||
}
|
||||
|
||||
type imlAiAPIInfoStore struct {
|
||||
store.SearchStoreSoftDelete[AiAPIInfo]
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
autowire.Auto[IApiBaseStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlApiBaseStore))
|
||||
})
|
||||
|
||||
autowire.Auto[IAPIInfoStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(store.Store[Info]))
|
||||
})
|
||||
|
||||
autowire.Auto[IAPIDocStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAPIDocStore))
|
||||
})
|
||||
|
||||
autowire.Auto[IAiAPIInfoStore](func() reflect.Value {
|
||||
return reflect.ValueOf(new(imlAiAPIInfoStore))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,3 +66,29 @@ func (i *Doc) TableName() string {
|
||||
func (i *Doc) IdValue() int64 {
|
||||
return i.Id
|
||||
}
|
||||
|
||||
type AiAPIInfo struct {
|
||||
Id int64 `gorm:"column:id;type:BIGINT(20);AUTO_INCREMENT;NOT NULL;comment:id;primary_key;comment:主键ID;"`
|
||||
Uuid string `gorm:"type:varchar(36);not null;column:uuid;uniqueIndex:uuid;comment:UUID"`
|
||||
Name string `gorm:"type:varchar(100);not null;column:name;comment:name"`
|
||||
Service string `gorm:"size:36;not null;column:service;comment:服务;index:service"`
|
||||
Path string `gorm:"size:512;not null;column:path;comment:请求路径"`
|
||||
Description string `gorm:"size:255;not null;column:description;comment:description"`
|
||||
Timeout int `gorm:"type:int(11);not null;column:timeout;comment:超时时间"`
|
||||
Retry int `gorm:"type:int(11);not null;column:retry;comment:重试次数"`
|
||||
Model string `gorm:"size:36;not null;column:model;comment:模型"`
|
||||
Creator string `gorm:"size:36;not null;column:creator;comment:创建人;index:creator" aovalue:"creator"`
|
||||
CreateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:create_at;comment:创建时间"`
|
||||
Updater string `gorm:"size:36;not null;column:updater;comment:更新人;index:updater" aovalue:"updater"`
|
||||
UpdateAt time.Time `gorm:"type:timestamp;NOT NULL;DEFAULT:CURRENT_TIMESTAMP;column:update_at;comment:更新时间"`
|
||||
AdditionalConfig string `gorm:"type:text;null;column:additional_config;comment:额外配置"`
|
||||
IsDelete bool `gorm:"type:tinyint(1);not null;column:is_delete;comment:是否删除 0:否 1:是"`
|
||||
}
|
||||
|
||||
func (a *AiAPIInfo) TableName() string {
|
||||
return "ai_api_info"
|
||||
}
|
||||
|
||||
func (a *AiAPIInfo) IdValue() int64 {
|
||||
return a.Id
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user