Skip to content

Commit

Permalink
AI proxy plugin (#420)
Browse files Browse the repository at this point in the history
  • Loading branch information
vikizhao156 committed Jul 7, 2023
1 parent d4dbaba commit 2a97921
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
21 changes: 21 additions & 0 deletions plugins/wasm-go/extensions/chatgpt-proxy/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions plugins/wasm-go/extensions/chatgpt-proxy/go.mod
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions plugins/wasm-go/extensions/chatgpt-proxy/go.sum
Original file line number Diff line number Diff line change
@@ -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 changes: 128 additions & 0 deletions plugins/wasm-go/extensions/chatgpt-proxy/main.go
Original file line number Diff line number Diff line change
@@ -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
}
Binary file not shown.

0 comments on commit 2a97921

Please sign in to comment.