Merge pull request #854 from trheyi/main
Add Azure OpenAI support and improve message parsing
This commit is contained in:
commit
321f1c22e1
2 changed files with 56 additions and 27 deletions
|
|
@ -2,6 +2,7 @@ package message
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
|
|
@ -184,7 +185,16 @@ func NewAny(content interface{}) (*Message, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOpenAI create a new message from OpenAI response
|
// NewOpenAI create a new message from OpenAI response
|
||||||
|
// @todo:
|
||||||
|
//
|
||||||
|
// this function need to be refactored
|
||||||
func NewOpenAI(data []byte, isThinking bool) *Message {
|
func NewOpenAI(data []byte, isThinking bool) *Message {
|
||||||
|
|
||||||
|
// For Debug
|
||||||
|
if os.Getenv("YAO_AGENT_DEBUG") == "true" {
|
||||||
|
fmt.Printf("%s\n", string(data))
|
||||||
|
}
|
||||||
|
|
||||||
if data == nil || len(data) == 0 {
|
if data == nil || len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +204,7 @@ func NewOpenAI(data []byte, isThinking bool) *Message {
|
||||||
data = []byte(strings.TrimPrefix(text, "data: "))
|
data = []byte(strings.TrimPrefix(text, "data: "))
|
||||||
switch {
|
switch {
|
||||||
|
|
||||||
case strings.Contains(text, `"delta":{`) && strings.Contains(text, `"tool_calls"`):
|
case strings.Contains(text, `"delta":{`) && strings.Contains(text, `"tool_calls"`) && !strings.Contains(text, `"tool_calls":null`):
|
||||||
var toolCalls openai.ToolCalls
|
var toolCalls openai.ToolCalls
|
||||||
if err := jsoniter.Unmarshal(data, &toolCalls); err != nil {
|
if err := jsoniter.Unmarshal(data, &toolCalls); err != nil {
|
||||||
color.Red("JSON parse error: %s", err.Error())
|
color.Red("JSON parse error: %s", err.Error())
|
||||||
|
|
@ -247,7 +257,7 @@ func NewOpenAI(data []byte, isThinking bool) *Message {
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
case strings.Index(text, `{"code":`) == 0:
|
case strings.Index(text, `{"code":`) == 0 || strings.Index(text, `"statusCode":`) > 0:
|
||||||
var errorMessage openai.Error
|
var errorMessage openai.Error
|
||||||
if err := jsoniter.UnmarshalFromString(text, &errorMessage); err != nil {
|
if err := jsoniter.UnmarshalFromString(text, &errorMessage); err != nil {
|
||||||
color.Red("JSON parse error: %s", err.Error())
|
color.Red("JSON parse error: %s", err.Error())
|
||||||
|
|
@ -277,6 +287,10 @@ func NewOpenAI(data []byte, isThinking bool) *Message {
|
||||||
msg.IsDone = true
|
msg.IsDone = true
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case strings.Contains(text, `"usage":`) && !strings.Contains(text, `"chat.completion.chunk`):
|
||||||
|
msg.IsDone = true
|
||||||
|
break
|
||||||
|
|
||||||
case strings.Contains(text, `[DONE]`):
|
case strings.Contains(text, `[DONE]`):
|
||||||
msg.IsDone = true
|
msg.IsDone = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ type OpenAI struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
organization string
|
organization string
|
||||||
maxToken int
|
maxToken int
|
||||||
|
azure bool // Azure Credentials, "true" or "false" or ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// New create a new OpenAI instance by connector id
|
// New create a new OpenAI instance by connector id
|
||||||
|
|
@ -94,6 +95,11 @@ func NewOpenAI(setting map[string]interface{}) (*OpenAI, error) {
|
||||||
maxToken = v
|
maxToken = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
azure := false
|
||||||
|
if v, ok := setting["azure"].(string); ok {
|
||||||
|
azure = v == "true" || v == "1"
|
||||||
|
}
|
||||||
|
|
||||||
return &OpenAI{
|
return &OpenAI{
|
||||||
key: key,
|
key: key,
|
||||||
model: model,
|
model: model,
|
||||||
|
|
@ -101,6 +107,7 @@ func NewOpenAI(setting map[string]interface{}) (*OpenAI, error) {
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
organization: organization,
|
organization: organization,
|
||||||
maxToken: maxToken,
|
maxToken: maxToken,
|
||||||
|
azure: azure,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,11 +373,14 @@ func (openai OpenAI) Stream(ctx context.Context, path string, payload map[string
|
||||||
func (openai OpenAI) post(path string, payload map[string]interface{}) (interface{}, *exception.Exception) {
|
func (openai OpenAI) post(path string, payload map[string]interface{}) (interface{}, *exception.Exception) {
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s", openai.host, path)
|
url := fmt.Sprintf("%s%s", openai.host, path)
|
||||||
key := fmt.Sprintf("Bearer %s", openai.key)
|
|
||||||
payload["model"] = openai.model
|
payload["model"] = openai.model
|
||||||
|
|
||||||
req := http.New(url).
|
req := http.New(url)
|
||||||
WithHeader(map[string][]string{"Authorization": {key}})
|
if openai.azure {
|
||||||
|
req.WithHeader(map[string][]string{"api-key": {openai.key}})
|
||||||
|
} else {
|
||||||
|
req.WithHeader(map[string][]string{"Authorization": {fmt.Sprintf("Bearer %s", openai.key)}})
|
||||||
|
}
|
||||||
|
|
||||||
res := req.Post(payload)
|
res := req.Post(payload)
|
||||||
if err := openai.isError(res); err != nil {
|
if err := openai.isError(res); err != nil {
|
||||||
|
|
@ -383,10 +393,12 @@ func (openai OpenAI) post(path string, payload map[string]interface{}) (interfac
|
||||||
func (openai OpenAI) postWithoutModel(path string, payload map[string]interface{}) (interface{}, *exception.Exception) {
|
func (openai OpenAI) postWithoutModel(path string, payload map[string]interface{}) (interface{}, *exception.Exception) {
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s", openai.host, path)
|
url := fmt.Sprintf("%s%s", openai.host, path)
|
||||||
key := fmt.Sprintf("Bearer %s", openai.key)
|
req := http.New(url)
|
||||||
|
if openai.azure {
|
||||||
req := http.New(url).
|
req.WithHeader(map[string][]string{"api-key": {openai.key}})
|
||||||
WithHeader(map[string][]string{"Authorization": {key}})
|
} else {
|
||||||
|
req.WithHeader(map[string][]string{"Authorization": {fmt.Sprintf("Bearer %s", openai.key)}})
|
||||||
|
}
|
||||||
|
|
||||||
res := req.Post(payload)
|
res := req.Post(payload)
|
||||||
if err := openai.isError(res); err != nil {
|
if err := openai.isError(res); err != nil {
|
||||||
|
|
@ -399,14 +411,15 @@ func (openai OpenAI) postWithoutModel(path string, payload map[string]interface{
|
||||||
func (openai OpenAI) postFile(path string, files map[string][]byte, option map[string]interface{}) (interface{}, *exception.Exception) {
|
func (openai OpenAI) postFile(path string, files map[string][]byte, option map[string]interface{}) (interface{}, *exception.Exception) {
|
||||||
|
|
||||||
url := fmt.Sprintf("%s%s", openai.host, path)
|
url := fmt.Sprintf("%s%s", openai.host, path)
|
||||||
key := fmt.Sprintf("Bearer %s", openai.key)
|
|
||||||
option["model"] = openai.model
|
option["model"] = openai.model
|
||||||
|
|
||||||
req := http.New(url).
|
req := http.New(url).WithHeader(map[string][]string{"Content-Type": {"multipart/form-data"}})
|
||||||
WithHeader(map[string][]string{
|
|
||||||
"Authorization": {key},
|
if openai.azure {
|
||||||
"Content-Type": {"multipart/form-data"},
|
req.WithHeader(map[string][]string{"api-key": {openai.key}})
|
||||||
})
|
} else {
|
||||||
|
req.WithHeader(map[string][]string{"Authorization": {fmt.Sprintf("Bearer %s", openai.key)}})
|
||||||
|
}
|
||||||
|
|
||||||
for name, data := range files {
|
for name, data := range files {
|
||||||
req.AddFileBytes(name, fmt.Sprintf("%s.mp3", name), data)
|
req.AddFileBytes(name, fmt.Sprintf("%s.mp3", name), data)
|
||||||
|
|
@ -425,11 +438,12 @@ func (openai OpenAI) postFileWithoutModel(path string, files map[string][]byte,
|
||||||
url := fmt.Sprintf("%s%s", openai.host, path)
|
url := fmt.Sprintf("%s%s", openai.host, path)
|
||||||
key := fmt.Sprintf("Bearer %s", openai.key)
|
key := fmt.Sprintf("Bearer %s", openai.key)
|
||||||
|
|
||||||
req := http.New(url).
|
req := http.New(url).WithHeader(map[string][]string{"Authorization": {key}})
|
||||||
WithHeader(map[string][]string{
|
if openai.azure {
|
||||||
"Authorization": {key},
|
req.WithHeader(map[string][]string{"api-key": {openai.key}})
|
||||||
"Content-Type": {"multipart/form-data"},
|
} else {
|
||||||
})
|
req.WithHeader(map[string][]string{"Authorization": {fmt.Sprintf("Bearer %s", openai.key)}})
|
||||||
|
}
|
||||||
|
|
||||||
for name, data := range files {
|
for name, data := range files {
|
||||||
req.AddFileBytes(name, fmt.Sprintf("%s.mp3", name), data)
|
req.AddFileBytes(name, fmt.Sprintf("%s.mp3", name), data)
|
||||||
|
|
@ -445,16 +459,17 @@ func (openai OpenAI) postFileWithoutModel(path string, files map[string][]byte,
|
||||||
// stream post request
|
// stream post request
|
||||||
func (openai OpenAI) stream(ctx context.Context, path string, payload map[string]interface{}, cb func(data []byte) int) *exception.Exception {
|
func (openai OpenAI) stream(ctx context.Context, path string, payload map[string]interface{}, cb func(data []byte) int) *exception.Exception {
|
||||||
url := fmt.Sprintf("%s%s", openai.host, path)
|
url := fmt.Sprintf("%s%s", openai.host, path)
|
||||||
key := fmt.Sprintf("Bearer %s", openai.key)
|
|
||||||
payload["model"] = openai.model
|
payload["model"] = openai.model
|
||||||
req := http.New(url)
|
req := http.New(url)
|
||||||
err := req.
|
req.WithHeader(map[string][]string{"Content-Type": {"application/json; charset=utf-8"}})
|
||||||
WithHeader(map[string][]string{
|
|
||||||
"Content-Type": {"application/json; charset=utf-8"},
|
|
||||||
"Authorization": {key},
|
|
||||||
}).
|
|
||||||
Stream(ctx, "POST", payload, cb)
|
|
||||||
|
|
||||||
|
if openai.azure {
|
||||||
|
req.WithHeader(map[string][]string{"api-key": {openai.key}})
|
||||||
|
} else {
|
||||||
|
req.WithHeader(map[string][]string{"Authorization": {fmt.Sprintf("Bearer %s", openai.key)}})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := req.Stream(ctx, "POST", payload, cb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exception.New(err.Error(), 500)
|
return exception.New(err.Error(), 500)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue