mirror of
https://github.com/alibaba/higress.git
synced 2026-03-05 00:50:53 +08:00
AI proxy plugin (#420)
This commit is contained in:
21
plugins/wasm-go/extensions/chatgpt-proxy/README.md
Normal file
21
plugins/wasm-go/extensions/chatgpt-proxy/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 功能说明
|
||||
`chatgpt-proxy`插件实现了代理请求AI大语言模型服务的功能。
|
||||
|
||||
# 配置字段
|
||||
|
||||
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
|
||||
| -------- | -------- | -------- | -------- | -------- |
|
||||
| model | string | 选填 | text-davinci-003 | 配置使用的模型模型名称 |
|
||||
| apiKey | string | 必填 | - | 配置使用的OpenAI API密钥 |
|
||||
| promptParam | string | 选填 | prompt | 配置prompt的来源字段名称,URL参数 |
|
||||
| chatgptUri | string | 选填 | api.openai.com/v1/completions | 配置调用AI模型服务的URL路径,默认值为OPENAI的API调用路径 |
|
||||
# 配置示例
|
||||
|
||||
## 进行OpenAI curie模型的调用
|
||||
```yaml
|
||||
apiKey: "xxxxxxxxxxxxxx",
|
||||
promptParam: "text",
|
||||
model: "curie"
|
||||
```
|
||||
|
||||
|
||||
15
plugins/wasm-go/extensions/chatgpt-proxy/go.mod
Normal file
15
plugins/wasm-go/extensions/chatgpt-proxy/go.mod
Normal file
@@ -0,0 +1,15 @@
|
||||
module chatgpt-proxy
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
|
||||
github.com/tidwall/gjson v1.14.4
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
)
|
||||
22
plugins/wasm-go/extensions/chatgpt-proxy/go.sum
Normal file
22
plugins/wasm-go/extensions/chatgpt-proxy/go.sum
Normal file
@@ -0,0 +1,22 @@
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230619024848-7d2a05ef1c35 h1:9xBMl8mJ5CGE1ebqtx9s11zrKJHs0559Yf/QWFh0WIQ=
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230619024848-7d2a05ef1c35/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d h1:PyBnIfssGRMKvBf6wKH11Z1t+X1Z7qegD1pAO60sFrk=
|
||||
github.com/alibaba/higress/plugins/wasm-go v0.0.0-20230629030002-81e467b6242d/go.mod h1:AzSnkuon5c26nIePTiJQIAFsKdhkNdncLcTuahpGtQs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.19.1-0.20220822060051-f9d179a57f8c h1:OCUFXVGixHLfNjg6/QYEhv+jHJ5mRGhpEUVFv9eWPJE=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.19.1-0.20220822060051-f9d179a57f8c/go.mod h1:5t/pWFNJ9eMyu/K/Z+OeGhDJ9sN9eCo8fc2pyM/Qjg4=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
|
||||
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
128
plugins/wasm-go/extensions/chatgpt-proxy/main.go
Normal file
128
plugins/wasm-go/extensions/chatgpt-proxy/main.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wrapper.SetCtx(
|
||||
"chatgpt-proxy",
|
||||
wrapper.ParseConfigBy(parseConfig),
|
||||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
|
||||
)
|
||||
}
|
||||
|
||||
type MyConfig struct {
|
||||
Model string
|
||||
ApiKey string
|
||||
PromptParam string
|
||||
ChatgptPath string
|
||||
HumainId string
|
||||
AIId string
|
||||
client wrapper.HttpClient
|
||||
}
|
||||
|
||||
func parseConfig(json gjson.Result, config *MyConfig, log wrapper.Log) error {
|
||||
chatgptUri := json.Get("chatgptUri").String()
|
||||
var chatgptHost string
|
||||
if chatgptUri == "" {
|
||||
config.ChatgptPath = "/v1/completions"
|
||||
chatgptHost = "api.openai.com"
|
||||
} else {
|
||||
cp, err := url.Parse(chatgptUri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.ChatgptPath = cp.Path
|
||||
chatgptHost = cp.Host
|
||||
}
|
||||
if config.ChatgptPath == "" {
|
||||
return errors.New("not found path in chatgptUri")
|
||||
}
|
||||
if chatgptHost == "" {
|
||||
return errors.New("not found host in chatgptUri")
|
||||
}
|
||||
config.client = wrapper.NewClusterClient(wrapper.RouteCluster{
|
||||
Host: chatgptHost,
|
||||
})
|
||||
config.Model = json.Get("model").String()
|
||||
if config.Model == "" {
|
||||
config.Model = "text-davinci-003"
|
||||
}
|
||||
config.ApiKey = json.Get("apiKey").String()
|
||||
if config.ApiKey == "" {
|
||||
return errors.New("no apiKey found in config")
|
||||
}
|
||||
config.PromptParam = json.Get("promptParam").String()
|
||||
if config.PromptParam == "" {
|
||||
config.PromptParam = "prompt"
|
||||
}
|
||||
config.HumainId = json.Get("HumainId").String()
|
||||
if config.HumainId == "" {
|
||||
config.HumainId = "Humain:"
|
||||
}
|
||||
config.AIId = json.Get("AIId").String()
|
||||
if config.AIId == "" {
|
||||
config.AIId = "AI:"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const bodyTemplate string = `
|
||||
{
|
||||
"model":"%s",
|
||||
"prompt":"%s",
|
||||
"temperature":0.9,
|
||||
"max_tokens": 150,
|
||||
"top_p": 1,
|
||||
"frequency_penalty": 0.0,
|
||||
"presence_penalty": 0.6,
|
||||
"stop": [" %s", " %s"]
|
||||
}
|
||||
`
|
||||
|
||||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config MyConfig, log wrapper.Log) types.Action {
|
||||
pairs := strings.SplitN(ctx.Path(), "?", 2)
|
||||
|
||||
if len(pairs) < 2 {
|
||||
proxywasm.SendHttpResponse(400, nil, []byte("1-need prompt param"), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
querys, err := url.ParseQuery(pairs[1])
|
||||
if err != nil {
|
||||
proxywasm.SendHttpResponse(400, nil, []byte("2-need prompt param"), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
var prompt []string
|
||||
var ok bool
|
||||
if prompt, ok = querys[config.PromptParam]; !ok || len(prompt) == 0 {
|
||||
proxywasm.SendHttpResponse(400, nil, []byte("3-need prompt param"), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
body := fmt.Sprintf(bodyTemplate, config.Model, prompt[0], config.HumainId, config.AIId)
|
||||
err = config.client.Post(config.ChatgptPath, [][2]string{
|
||||
{"Content-Type", "application/json"},
|
||||
{"Authorization", "Bearer " + config.ApiKey},
|
||||
}, []byte(body),
|
||||
func(statusCode int, responseHeaders http.Header, responseBody []byte) {
|
||||
var headers [][2]string
|
||||
for key, value := range responseHeaders {
|
||||
headers = append(headers, [2]string{key, value[0]})
|
||||
}
|
||||
proxywasm.SendHttpResponse(uint32(statusCode), headers, responseBody, -1)
|
||||
}, 10000)
|
||||
if err != nil {
|
||||
proxywasm.SendHttpResponse(500, nil, []byte("Internel Error: "+err.Error()), -1)
|
||||
return types.ActionContinue
|
||||
}
|
||||
return types.ActionPause
|
||||
}
|
||||
BIN
plugins/wasm-go/extensions/chatgpt-proxy/main.wasm
Normal file
BIN
plugins/wasm-go/extensions/chatgpt-proxy/main.wasm
Normal file
Binary file not shown.
Reference in New Issue
Block a user