[add] openai.* process

This commit is contained in:
Max 2023-05-12 21:47:18 +08:00
parent a0d5ca35b3
commit 538c6c063a
2 changed files with 160 additions and 3 deletions

View file

@ -1,12 +1,20 @@
package openai
import (
"context"
"github.com/yaoapp/gou/http"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/gou/runtime/v8/bridge"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/kun/log"
)
func init() {
process.Register("yao.openai.tiktoken", ProcessTiktoken)
process.RegisterGroup("openai", map[string]process.Handler{
"tiktoken": ProcessTiktoken,
"chat.completions": ProcessChatCompletions,
})
}
// ProcessTiktoken get number of tokens
@ -20,3 +28,88 @@ func ProcessTiktoken(process *process.Process) interface{} {
}
return nums
}
// ProcessChatCompletions get number of tokens
func ProcessChatCompletions(process *process.Process) interface{} {
process.ValidateArgNums(2)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
model := process.ArgsString(0)
messages := []map[string]interface{}{}
intput := process.ArgsArray(1)
for idx, v := range intput {
message, ok := v.(map[string]interface{})
if !ok {
exception.New("ChatCompletions input must be array of map, index %d", 400, idx).Throw()
}
messages = append(messages, message)
}
ai, err := New(model)
if err != nil {
exception.New("ChatCompletions error: %s", 400, err).Throw()
}
options := map[string]interface{}{}
if process.NumOfArgs() > 2 {
if opts, ok := process.Args[2].(map[string]interface{}); ok {
options = opts
}
}
if process.NumOfArgs() == 3 {
data, ex := ai.ChatCompletionsWith(ctx, messages, options, nil)
if ex != nil {
ex.Throw()
}
return data
}
if process.NumOfArgs() == 4 {
switch cb := process.Args[3].(type) {
case func(data []byte) int:
res, ex := ai.ChatCompletionsWith(ctx, messages, options, cb)
if ex != nil {
ex.Throw()
}
return res
case bridge.FunctionT:
res, ex := ai.ChatCompletionsWith(ctx, messages, options, func(data []byte) int {
v, err := cb.Call(string(data))
if err != nil {
log.Error("Call callback function error: %s", err.Error())
return http.HandlerReturnError
}
ret, ok := v.(int)
if !ok {
log.Error("Callback function must return int")
return http.HandlerReturnError
}
return ret
})
if ex != nil {
ex.Throw()
}
return res
default:
exception.New("ChatCompletions error: invalid callback arguments", 400).Throw()
return nil
}
}
res, ex := ai.ChatCompletionsWith(ctx, messages, options, nil)
if ex != nil {
ex.Throw()
}
return res
}

View file

@ -5,15 +5,79 @@ import (
"github.com/stretchr/testify/assert"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/test"
)
func TestProcessTiktoken(t *testing.T) {
// Hash
args := []interface{}{"gpt-3.5-turbo", "hello world"}
res := process.New("yao.openai.Tiktoken", args...).Run()
res := process.New("openai.Tiktoken", args...).Run()
assert.Equal(t, 2, res)
args = []interface{}{"gpt-3.5-turbo", "你好世界!"}
res = process.New("yao.openai.Tiktoken", args...).Run()
res = process.New("openai.Tiktoken", args...).Run()
assert.Equal(t, 6, res)
}
func TestProcessChatCompletions(t *testing.T) {
test.Prepare(t, config.Conf)
defer test.Clean()
args := []interface{}{"gpt-3_5-turbo", []map[string]interface{}{{"role": "user", "content": "hello"}}}
res := process.New("openai.chat.Completions", args...).Run()
data, ok := res.(map[string]interface{})
if !ok {
t.Fatalf("ChatCompletions return type error")
}
assert.NotEmpty(t, data["id"])
// With options
args = []interface{}{
"gpt-3_5-turbo",
[]map[string]interface{}{{"role": "user", "content": "hello"}},
map[string]interface{}{"max_tokens": 2},
}
res = process.New("openai.chat.Completions", args...).Run()
data, ok = res.(map[string]interface{})
if !ok {
t.Fatalf("ChatCompletions return type error")
}
usage, ok := data["usage"].(map[string]interface{})
if !ok {
t.Fatalf("ChatCompletions return type error")
}
assert.Equal(t, 2, int(usage["completion_tokens"].(float64)))
// With callback
content := []byte{}
args = []interface{}{
"gpt-3_5-turbo",
[]map[string]interface{}{{"role": "user", "content": "hello"}},
nil,
func(data []byte) int {
content = append(content, data...)
if len(data) == 0 {
res = append(content, []byte("\n")...)
}
if string(data) == "data: [DONE]" {
return 0
}
return 1
},
}
res = process.New("openai.chat.Completions", args...).Run()
assert.Contains(t, string(content), "[DONE]")
// With JS Callback
res, err := process.New("scripts.openai.TestProcessChatCompletions").Exec()
if err != nil {
t.Fatal(err)
}
assert.Contains(t, res, "[DONE]")
}