Merge pull request #854 from trheyi/main

Add Azure OpenAI support and improve message parsing
This commit is contained in:
Max 2025-02-08 11:18:57 +08:00 committed by GitHub
commit 321f1c22e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 56 additions and 27 deletions

View file

@ -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

View file

@ -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)
} }