mirror of
https://github.com/alibaba/higress.git
synced 2026-06-10 05:07:30 +08:00
feat: Support configuring a global provider list in ai-proxy plugin (#1334)
This commit is contained in:
@@ -607,6 +607,7 @@ provider:
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 使用original协议代理百炼智能体应用
|
### 使用original协议代理百炼智能体应用
|
||||||
|
|
||||||
**配置信息**
|
**配置信息**
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -832,6 +833,7 @@ provider:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 使用 OpenAI 协议代理混元服务
|
### 使用 OpenAI 协议代理混元服务
|
||||||
|
|
||||||
**配置信息**
|
**配置信息**
|
||||||
@@ -849,9 +851,10 @@ provider:
|
|||||||
```
|
```
|
||||||
|
|
||||||
**请求示例**
|
**请求示例**
|
||||||
请求脚本:
|
|
||||||
```sh
|
|
||||||
|
|
||||||
|
请求脚本:
|
||||||
|
|
||||||
|
```shell
|
||||||
curl --location 'http://<your higress domain>/v1/chat/completions' \
|
curl --location 'http://<your higress domain>/v1/chat/completions' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data '{
|
--data '{
|
||||||
|
|||||||
@@ -139,9 +139,9 @@ For 360 Brain, the corresponding `type` is `ai360`. It has no unique configurati
|
|||||||
|
|
||||||
For Mistral, the corresponding `type` is `mistral`. It has no unique configuration fields.
|
For Mistral, the corresponding `type` is `mistral`. It has no unique configuration fields.
|
||||||
|
|
||||||
#### Minimax
|
#### MiniMax
|
||||||
|
|
||||||
For Minimax, the corresponding `type` is `minimax`. Its unique configuration field is:
|
For MiniMax, the corresponding `type` is `minimax`. Its unique configuration field is:
|
||||||
|
|
||||||
| Name | Data Type | Filling Requirements | Default Value | Description |
|
| Name | Data Type | Filling Requirements | Default Value | Description |
|
||||||
| ---------------- | -------- | --------------------- |---------------|------------------------------------------------------------------------------------------------------------|
|
| ---------------- | -------- | --------------------- |---------------|------------------------------------------------------------------------------------------------------------|
|
||||||
@@ -593,6 +593,69 @@ provider:
|
|||||||
"request_id": "187e99ba-5b64-9ffe-8f69-01dafbaf6ed7"
|
"request_id": "187e99ba-5b64-9ffe-8f69-01dafbaf6ed7"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Forwards requests to AliCloud Bailian with the "original" protocol
|
||||||
|
|
||||||
|
**Configuration Information**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
activeProviderId: my-qwen
|
||||||
|
providers:
|
||||||
|
- id: my-qwen
|
||||||
|
type: qwen
|
||||||
|
apiTokens:
|
||||||
|
- "YOUR_DASHSCOPE_API_TOKEN"
|
||||||
|
protocol: original
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Request**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"prompt": "What is Dubbo?"
|
||||||
|
},
|
||||||
|
"parameters": {},
|
||||||
|
"debug": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example Response**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"output": {
|
||||||
|
"finish_reason": "stop",
|
||||||
|
"session_id": "677e7e8fbb874e1b84792b65042e1599",
|
||||||
|
"text": "Apache Dubbo is a..."
|
||||||
|
},
|
||||||
|
"usage": {
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"output_tokens": 449,
|
||||||
|
"model_id": "qwen-max",
|
||||||
|
"input_tokens": 282
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"request_id": "b59e45e3-5af4-91df-b7c6-9d746fd3297c"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using OpenAI Protocol Proxy for Doubao Service
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
activeProviderId: my-doubao
|
||||||
|
providers:
|
||||||
|
- id: my-doubao
|
||||||
|
type: doubao
|
||||||
|
apiTokens:
|
||||||
|
- YOUR_DOUBAO_API_KEY
|
||||||
|
modelMapping:
|
||||||
|
'*': YOUR_DOUBAO_ENDPOINT
|
||||||
|
timeout: 1200000
|
||||||
|
```
|
||||||
|
|
||||||
### Utilizing Moonshot with its Native File Context
|
### Utilizing Moonshot with its Native File Context
|
||||||
|
|
||||||
Upload files to Moonshot in advance and use its AI services based on file content.
|
Upload files to Moonshot in advance and use its AI services based on file content.
|
||||||
@@ -782,8 +845,7 @@ provider:
|
|||||||
|
|
||||||
Request script:
|
Request script:
|
||||||
|
|
||||||
```sh
|
```shell
|
||||||
|
|
||||||
curl --location 'http://<your higress domain>/v1/chat/completions' \
|
curl --location 'http://<your higress domain>/v1/chat/completions' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data '{
|
--data '{
|
||||||
@@ -955,7 +1017,7 @@ provider:
|
|||||||
provider:
|
provider:
|
||||||
type: ai360
|
type: ai360
|
||||||
apiTokens:
|
apiTokens:
|
||||||
- "YOUR_MINIMAX_API_TOKEN"
|
- "YOUR_AI360_API_TOKEN"
|
||||||
modelMapping:
|
modelMapping:
|
||||||
"gpt-4o": "360gpt-turbo-responsibility-8k"
|
"gpt-4o": "360gpt-turbo-responsibility-8k"
|
||||||
"gpt-4": "360gpt2-pro"
|
"gpt-4": "360gpt2-pro"
|
||||||
@@ -1264,6 +1326,7 @@ Here, `model` denotes the service tier of DeepL and can only be either `Free` or
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Response Example**
|
**Response Example**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"choices": [
|
"choices": [
|
||||||
|
|||||||
@@ -25,32 +25,70 @@ import (
|
|||||||
type PluginConfig struct {
|
type PluginConfig struct {
|
||||||
// @Title zh-CN AI服务提供商配置
|
// @Title zh-CN AI服务提供商配置
|
||||||
// @Description zh-CN AI服务提供商配置,包含API接口、模型和知识库文件等信息
|
// @Description zh-CN AI服务提供商配置,包含API接口、模型和知识库文件等信息
|
||||||
providerConfig provider.ProviderConfig `required:"true" yaml:"provider"`
|
providerConfigs []provider.ProviderConfig `required:"true" yaml:"providers"`
|
||||||
|
|
||||||
provider provider.Provider `yaml:"-"`
|
activeProviderConfig *provider.ProviderConfig `yaml:"-"`
|
||||||
|
activeProvider provider.Provider `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PluginConfig) FromJson(json gjson.Result) {
|
func (c *PluginConfig) FromJson(json gjson.Result) {
|
||||||
c.providerConfig.FromJson(json.Get("provider"))
|
if providersJson := json.Get("providers"); providersJson.Exists() && providersJson.IsArray() {
|
||||||
|
c.providerConfigs = make([]provider.ProviderConfig, 0)
|
||||||
|
for _, providerJson := range providersJson.Array() {
|
||||||
|
providerConfig := provider.ProviderConfig{}
|
||||||
|
providerConfig.FromJson(providerJson)
|
||||||
|
c.providerConfigs = append(c.providerConfigs, providerConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if providerJson := json.Get("provider"); providerJson.Exists() && providerJson.IsObject() {
|
||||||
|
// TODO: For legacy config support. To be removed later.
|
||||||
|
providerConfig := provider.ProviderConfig{}
|
||||||
|
providerConfig.FromJson(providerJson)
|
||||||
|
c.providerConfigs = []provider.ProviderConfig{providerConfig}
|
||||||
|
c.activeProviderConfig = &providerConfig
|
||||||
|
// Legacy configuration is used and the active provider is determined.
|
||||||
|
// We don't need to continue with the new configuration style.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.activeProviderConfig = nil
|
||||||
|
|
||||||
|
activeProviderId := json.Get("activeProviderId").String()
|
||||||
|
if activeProviderId != "" {
|
||||||
|
for _, providerConfig := range c.providerConfigs {
|
||||||
|
if providerConfig.GetId() == activeProviderId {
|
||||||
|
c.activeProviderConfig = &providerConfig
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PluginConfig) Validate() error {
|
func (c *PluginConfig) Validate() error {
|
||||||
if err := c.providerConfig.Validate(); err != nil {
|
if c.activeProviderConfig == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := c.activeProviderConfig.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PluginConfig) Complete() error {
|
func (c *PluginConfig) Complete() error {
|
||||||
|
if c.activeProviderConfig == nil {
|
||||||
|
c.activeProvider = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var err error
|
var err error
|
||||||
c.provider, err = provider.CreateProvider(c.providerConfig)
|
c.activeProvider, err = provider.CreateProvider(*c.activeProviderConfig)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PluginConfig) GetProvider() provider.Provider {
|
func (c *PluginConfig) GetProvider() provider.Provider {
|
||||||
return c.provider
|
return c.activeProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *PluginConfig) GetProviderConfig() provider.ProviderConfig {
|
func (c *PluginConfig) GetProviderConfig() *provider.ProviderConfig {
|
||||||
return c.providerConfig
|
return c.activeProviderConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ static_resources:
|
|||||||
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
"@type": "type.googleapis.com/google.protobuf.StringValue"
|
||||||
value: |
|
value: |
|
||||||
{
|
{
|
||||||
"provider": {
|
"activeProviderId": "moonshot",
|
||||||
|
"providers": [
|
||||||
|
{
|
||||||
"type": "moonshot",
|
"type": "moonshot",
|
||||||
"domain": "api.moonshot.cn",
|
"domain": "api.moonshot.cn",
|
||||||
"apiTokens": [
|
"apiTokens": [
|
||||||
@@ -71,6 +73,7 @@ static_resources:
|
|||||||
"*": "moonshot-v1-8k"
|
"*": "moonshot-v1-8k"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
- name: envoy.filters.http.router
|
- name: envoy.filters.http.router
|
||||||
clusters:
|
clusters:
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
pluginName = "ai-proxy"
|
pluginName = "ai-proxy"
|
||||||
|
|
||||||
ctxKeyApiName = "apiKey"
|
ctxKeyApiName = "apiName"
|
||||||
|
|
||||||
defaultMaxBodyBytes uint32 = 10 * 1024 * 1024
|
defaultMaxBodyBytes uint32 = 10 * 1024 * 1024
|
||||||
)
|
)
|
||||||
@@ -28,7 +28,7 @@ const (
|
|||||||
func main() {
|
func main() {
|
||||||
wrapper.SetCtx(
|
wrapper.SetCtx(
|
||||||
pluginName,
|
pluginName,
|
||||||
wrapper.ParseConfigBy(parseConfig),
|
wrapper.ParseOverrideConfigBy(parseGlobalConfig, parseOverrideRuleConfig),
|
||||||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeader),
|
wrapper.ProcessRequestHeadersBy(onHttpRequestHeader),
|
||||||
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
|
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
|
||||||
wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
|
wrapper.ProcessResponseHeadersBy(onHttpResponseHeaders),
|
||||||
@@ -37,8 +37,23 @@ func main() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseConfig(json gjson.Result, pluginConfig *config.PluginConfig, log wrapper.Log) error {
|
func parseGlobalConfig(json gjson.Result, pluginConfig *config.PluginConfig, log wrapper.Log) error {
|
||||||
// log.Debugf("loading config: %s", json.String())
|
//log.Debugf("loading global config: %s", json.String())
|
||||||
|
|
||||||
|
pluginConfig.FromJson(json)
|
||||||
|
if err := pluginConfig.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := pluginConfig.Complete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseOverrideRuleConfig(json gjson.Result, global config.PluginConfig, pluginConfig *config.PluginConfig, log wrapper.Log) error {
|
||||||
|
//log.Debugf("loading override rule config: %s", json.String())
|
||||||
|
|
||||||
|
*pluginConfig = global
|
||||||
|
|
||||||
pluginConfig.FromJson(json)
|
pluginConfig.FromJson(json)
|
||||||
if err := pluginConfig.Validate(); err != nil {
|
if err := pluginConfig.Validate(); err != nil {
|
||||||
|
|||||||
@@ -126,7 +126,10 @@ type ResponseBodyHandler interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProviderConfig struct {
|
type ProviderConfig struct {
|
||||||
// @Title zh-CN AI服务提供商
|
// @Title zh-CN ID
|
||||||
|
// @Description zh-CN AI服务提供商标识
|
||||||
|
id string `required:"true" yaml:"id" json:"id"`
|
||||||
|
// @Title zh-CN 类型
|
||||||
// @Description zh-CN AI服务提供商类型
|
// @Description zh-CN AI服务提供商类型
|
||||||
typ string `required:"true" yaml:"type" json:"type"`
|
typ string `required:"true" yaml:"type" json:"type"`
|
||||||
// @Title zh-CN API Tokens
|
// @Title zh-CN API Tokens
|
||||||
@@ -197,7 +200,20 @@ type ProviderConfig struct {
|
|||||||
customSettings []CustomSetting
|
customSettings []CustomSetting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ProviderConfig) GetId() string {
|
||||||
|
return c.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProviderConfig) GetType() string {
|
||||||
|
return c.typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProviderConfig) GetProtocol() string {
|
||||||
|
return c.protocol
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ProviderConfig) FromJson(json gjson.Result) {
|
func (c *ProviderConfig) FromJson(json gjson.Result) {
|
||||||
|
c.id = json.Get("id").String()
|
||||||
c.typ = json.Get("type").String()
|
c.typ = json.Get("type").String()
|
||||||
c.apiTokens = make([]string, 0)
|
c.apiTokens = make([]string, 0)
|
||||||
for _, token := range json.Get("apiTokens").Array() {
|
for _, token := range json.Get("apiTokens").Array() {
|
||||||
@@ -322,6 +338,10 @@ func (c *ProviderConfig) IsOriginal() bool {
|
|||||||
return c.protocol == protocolOriginal
|
return c.protocol == protocolOriginal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *ProviderConfig) ReplaceByCustomSettings(body []byte) ([]byte, error) {
|
||||||
|
return ReplaceByCustomSettings(body, c.customSettings)
|
||||||
|
}
|
||||||
|
|
||||||
func CreateProvider(pc ProviderConfig) (Provider, error) {
|
func CreateProvider(pc ProviderConfig) (Provider, error) {
|
||||||
initializer, has := providerInitializers[pc.typ]
|
initializer, has := providerInitializers[pc.typ]
|
||||||
if !has {
|
if !has {
|
||||||
@@ -366,7 +386,3 @@ func doGetMappedModel(model string, modelMapping map[string]string, log wrapper.
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c ProviderConfig) ReplaceByCustomSettings(body []byte) ([]byte, error) {
|
|
||||||
return ReplaceByCustomSettings(body, c.customSettings)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user