Merge pull request #819 from trheyi/main
# Neo API: Enhance Message Handling and Vision Support
This commit is contained in:
commit
a77cc6cf87
7 changed files with 185 additions and 87 deletions
|
|
@ -7,10 +7,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/yaoapp/gou/fs"
|
"github.com/yaoapp/gou/fs"
|
||||||
"github.com/yaoapp/gou/process"
|
"github.com/yaoapp/gou/process"
|
||||||
|
"github.com/yaoapp/kun/utils"
|
||||||
chatctx "github.com/yaoapp/yao/neo/context"
|
chatctx "github.com/yaoapp/yao/neo/context"
|
||||||
"github.com/yaoapp/yao/neo/message"
|
|
||||||
chatMessage "github.com/yaoapp/yao/neo/message"
|
chatMessage "github.com/yaoapp/yao/neo/message"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -160,10 +161,10 @@ func (next *NextAction) Execute(c *gin.Context, ctx chatctx.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleChatStream manages the streaming chat interaction with the AI
|
// handleChatStream manages the streaming chat interaction with the AI
|
||||||
func (ast *Assistant) handleChatStream(c *gin.Context, ctx chatctx.Context, messages []message.Message, options map[string]interface{}) error {
|
func (ast *Assistant) handleChatStream(c *gin.Context, ctx chatctx.Context, messages []chatMessage.Message, options map[string]interface{}) error {
|
||||||
clientBreak := make(chan bool, 1)
|
clientBreak := make(chan bool, 1)
|
||||||
done := make(chan bool, 1)
|
done := make(chan bool, 1)
|
||||||
content := message.NewContent("text")
|
content := chatMessage.NewContent("text")
|
||||||
|
|
||||||
// Chat with AI in background
|
// Chat with AI in background
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -190,11 +191,11 @@ func (ast *Assistant) handleChatStream(c *gin.Context, ctx chatctx.Context, mess
|
||||||
func (ast *Assistant) streamChat(
|
func (ast *Assistant) streamChat(
|
||||||
c *gin.Context,
|
c *gin.Context,
|
||||||
ctx chatctx.Context,
|
ctx chatctx.Context,
|
||||||
messages []message.Message,
|
messages []chatMessage.Message,
|
||||||
options map[string]interface{},
|
options map[string]interface{},
|
||||||
clientBreak chan bool,
|
clientBreak chan bool,
|
||||||
done chan bool,
|
done chan bool,
|
||||||
content *message.Content) error {
|
content *chatMessage.Content) error {
|
||||||
|
|
||||||
return ast.Chat(c.Request.Context(), messages, options, func(data []byte) int {
|
return ast.Chat(c.Request.Context(), messages, options, func(data []byte) int {
|
||||||
select {
|
select {
|
||||||
|
|
@ -240,7 +241,7 @@ func (ast *Assistant) streamChat(
|
||||||
content.Append(value)
|
content.Append(value)
|
||||||
if value != "" {
|
if value != "" {
|
||||||
// Handle stream
|
// Handle stream
|
||||||
res, err := ast.HookStream(c, ctx, messages, content.String(), msg.Type == "tool_calls")
|
res, err := ast.HookStream(c, ctx, messages, content.String(), content.Type == "function")
|
||||||
if err == nil && res != nil {
|
if err == nil && res != nil {
|
||||||
if res.Output != "" {
|
if res.Output != "" {
|
||||||
value = res.Output
|
value = res.Output
|
||||||
|
|
@ -276,8 +277,8 @@ func (ast *Assistant) streamChat(
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Call HookDone
|
// Call HookDone
|
||||||
content.SetStatus(message.ContentStatusDone)
|
content.SetStatus(chatMessage.ContentStatusDone)
|
||||||
res, hookErr := ast.HookDone(c, ctx, messages, content.String(), msg.Type == "tool_calls")
|
res, hookErr := ast.HookDone(c, ctx, messages, content.String(), content.Type == "function")
|
||||||
if hookErr == nil && res != nil {
|
if hookErr == nil && res != nil {
|
||||||
if res.Output != "" {
|
if res.Output != "" {
|
||||||
chatMessage.New().
|
chatMessage.New().
|
||||||
|
|
@ -316,7 +317,7 @@ func (ast *Assistant) streamChat(
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveChatHistory saves the chat history if storage is available
|
// saveChatHistory saves the chat history if storage is available
|
||||||
func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []message.Message, content *message.Content) {
|
func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []chatMessage.Message, content *chatMessage.Content) {
|
||||||
if len(content.Bytes) > 0 && ctx.Sid != "" && len(messages) > 0 {
|
if len(content.Bytes) > 0 && ctx.Sid != "" && len(messages) > 0 {
|
||||||
storage.SaveHistory(
|
storage.SaveHistory(
|
||||||
ctx.Sid,
|
ctx.Sid,
|
||||||
|
|
@ -352,21 +353,21 @@ func (ast *Assistant) withOptions(options map[string]interface{}) map[string]int
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *Assistant) withPrompts(messages []message.Message) []message.Message {
|
func (ast *Assistant) withPrompts(messages []chatMessage.Message) []chatMessage.Message {
|
||||||
if ast.Prompts != nil {
|
if ast.Prompts != nil {
|
||||||
for _, prompt := range ast.Prompts {
|
for _, prompt := range ast.Prompts {
|
||||||
name := ast.Name
|
name := ast.Name
|
||||||
if prompt.Name != "" {
|
if prompt.Name != "" {
|
||||||
name = prompt.Name
|
name = prompt.Name
|
||||||
}
|
}
|
||||||
messages = append(messages, *message.New().Map(map[string]interface{}{"role": prompt.Role, "content": prompt.Content, "name": name}))
|
messages = append(messages, *chatMessage.New().Map(map[string]interface{}{"role": prompt.Role, "content": prompt.Content, "name": name}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *Assistant) withHistory(ctx chatctx.Context, input string) ([]message.Message, error) {
|
func (ast *Assistant) withHistory(ctx chatctx.Context, input string) ([]chatMessage.Message, error) {
|
||||||
messages := []message.Message{}
|
messages := []chatMessage.Message{}
|
||||||
messages = ast.withPrompts(messages)
|
messages = ast.withPrompts(messages)
|
||||||
if storage != nil {
|
if storage != nil {
|
||||||
history, err := storage.GetHistory(ctx.Sid, ctx.ChatID)
|
history, err := storage.GetHistory(ctx.Sid, ctx.ChatID)
|
||||||
|
|
@ -376,17 +377,17 @@ func (ast *Assistant) withHistory(ctx chatctx.Context, input string) ([]message.
|
||||||
|
|
||||||
// Add history messages
|
// Add history messages
|
||||||
for _, h := range history {
|
for _, h := range history {
|
||||||
messages = append(messages, *message.New().Map(h))
|
messages = append(messages, *chatMessage.New().Map(h))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add user message
|
// Add user message
|
||||||
messages = append(messages, *message.New().Map(map[string]interface{}{"role": "user", "content": input, "name": ctx.Sid}))
|
messages = append(messages, *chatMessage.New().Map(map[string]interface{}{"role": "user", "content": input, "name": ctx.Sid}))
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat implements the chat functionality
|
// Chat implements the chat functionality
|
||||||
func (ast *Assistant) Chat(ctx context.Context, messages []message.Message, option map[string]interface{}, cb func(data []byte) int) error {
|
func (ast *Assistant) Chat(ctx context.Context, messages []chatMessage.Message, option map[string]interface{}, cb func(data []byte) int) error {
|
||||||
if ast.openai == nil {
|
if ast.openai == nil {
|
||||||
return fmt.Errorf("openai is not initialized")
|
return fmt.Errorf("openai is not initialized")
|
||||||
}
|
}
|
||||||
|
|
@ -404,27 +405,10 @@ func (ast *Assistant) Chat(ctx context.Context, messages []message.Message, opti
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *Assistant) requestMessages(ctx context.Context, messages []message.Message) ([]map[string]interface{}, error) {
|
func (ast *Assistant) requestMessages(ctx context.Context, messages []chatMessage.Message) ([]map[string]interface{}, error) {
|
||||||
newMessages := []map[string]interface{}{}
|
newMessages := []map[string]interface{}{}
|
||||||
// With Prompts
|
|
||||||
if ast.Prompts != nil {
|
|
||||||
for _, prompt := range ast.Prompts {
|
|
||||||
msg := map[string]interface{}{
|
|
||||||
"role": prompt.Role,
|
|
||||||
"content": prompt.Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
name := ast.Name
|
|
||||||
if prompt.Name != "" {
|
|
||||||
name = prompt.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
msg["name"] = name
|
|
||||||
newMessages = append(newMessages, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
length := len(messages)
|
length := len(messages)
|
||||||
|
|
||||||
for index, message := range messages {
|
for index, message := range messages {
|
||||||
role := message.Role
|
role := message.Role
|
||||||
if role == "" {
|
if role == "" {
|
||||||
|
|
@ -454,12 +438,24 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []message.Me
|
||||||
}
|
}
|
||||||
|
|
||||||
newMessage["content"] = msg.Text
|
newMessage["content"] = msg.Text
|
||||||
if msg.Attachments != nil {
|
if message.Attachments != nil {
|
||||||
content, err := ast.withAttachments(ctx, msg)
|
contents, err := ast.withAttachments(ctx, &message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("with attachments error: %s", err.Error())
|
return nil, fmt.Errorf("with attachments error: %s", err.Error())
|
||||||
}
|
}
|
||||||
newMessage["content"] = content
|
|
||||||
|
// if current assistant is vision capable, add the contents directly
|
||||||
|
if ast.vision {
|
||||||
|
newMessage["content"] = contents
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If current assistant is not vision capable, add the description of the image
|
||||||
|
if contents != nil {
|
||||||
|
for _, content := range contents {
|
||||||
|
newMessages = append(newMessages, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,10 +466,27 @@ func (ast *Assistant) requestMessages(ctx context.Context, messages []message.Me
|
||||||
|
|
||||||
func (ast *Assistant) withAttachments(ctx context.Context, msg *chatMessage.Message) ([]map[string]interface{}, error) {
|
func (ast *Assistant) withAttachments(ctx context.Context, msg *chatMessage.Message) ([]map[string]interface{}, error) {
|
||||||
contents := []map[string]interface{}{{"type": "text", "text": msg.Text}}
|
contents := []map[string]interface{}{{"type": "text", "text": msg.Text}}
|
||||||
|
if !ast.vision {
|
||||||
|
contents = []map[string]interface{}{{"role": "user", "content": msg.Text}}
|
||||||
|
}
|
||||||
|
|
||||||
images := []string{}
|
images := []string{}
|
||||||
for _, attachment := range msg.Attachments {
|
for _, attachment := range msg.Attachments {
|
||||||
if strings.HasPrefix(attachment.ContentType, "image/") {
|
if strings.HasPrefix(attachment.ContentType, "image/") {
|
||||||
images = append(images, attachment.FileID)
|
if ast.vision {
|
||||||
|
images = append(images, attachment.URL)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the current assistant is not vision capable, add the description of the image
|
||||||
|
raw, err := jsoniter.MarshalToString(attachment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("marshal attachment error: %s", err.Error())
|
||||||
|
}
|
||||||
|
contents = append(contents, map[string]interface{}{
|
||||||
|
"role": "system",
|
||||||
|
"content": raw,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -481,20 +494,40 @@ func (ast *Assistant) withAttachments(ctx context.Context, msg *chatMessage.Mess
|
||||||
return contents, nil
|
return contents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, image := range images {
|
// If the current assistant is vision capable, add the image to the contents directly
|
||||||
bytes64, err := ast.ReadBase64(ctx, image)
|
if ast.vision {
|
||||||
if err != nil {
|
for _, url := range images {
|
||||||
return nil, fmt.Errorf("read base64 error: %s", err.Error())
|
|
||||||
|
// If the image is already a URL, add it directly
|
||||||
|
if strings.HasPrefix(url, "http") {
|
||||||
|
contents = append(contents, map[string]interface{}{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": map[string]string{
|
||||||
|
"url": url,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read base64
|
||||||
|
bytes64, err := ast.ReadBase64(ctx, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("read base64 error: %s", err.Error())
|
||||||
|
}
|
||||||
|
contents = append(contents, map[string]interface{}{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": map[string]string{
|
||||||
|
"url": fmt.Sprintf("data:image/jpeg;base64,%s", bytes64),
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
contents = append(contents, map[string]interface{}{
|
utils.Dump(contents)
|
||||||
"type": "image_url",
|
return contents, nil
|
||||||
"image_url": map[string]string{
|
|
||||||
"url": fmt.Sprintf("data:image/jpeg;base64,%s", bytes64),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the current assistant is not vision capable, add the description of the image
|
||||||
|
|
||||||
return contents, nil
|
return contents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -197,12 +197,22 @@ func (ast *Assistant) handleRAG(ctx context.Context, file *File, reader io.Reade
|
||||||
|
|
||||||
// handleVision handles the file with Vision if available
|
// handleVision handles the file with Vision if available
|
||||||
func (ast *Assistant) handleVision(ctx context.Context, file *File, option map[string]interface{}) error {
|
func (ast *Assistant) handleVision(ctx context.Context, file *File, option map[string]interface{}) error {
|
||||||
|
|
||||||
if vision == nil {
|
if vision == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if Vision processing is enabled
|
handleVision := false
|
||||||
if option, ok := option["vision"].(bool); !ok || !option {
|
if vv, has := option["vision"]; has {
|
||||||
|
switch v := vv.(type) {
|
||||||
|
case bool:
|
||||||
|
handleVision = v
|
||||||
|
case string:
|
||||||
|
handleVision = v == "true" || v == "1" || v == "yes" || v == "on" || v == "enable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !handleVision {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,12 +221,6 @@ func (ast *Assistant) handleVision(ctx context.Context, file *File, option map[s
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get model from options
|
|
||||||
model := ""
|
|
||||||
if v, ok := option["model"].(string); ok {
|
|
||||||
model = v
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset reader for vision service
|
// Reset reader for vision service
|
||||||
data, err := fs.Get("data")
|
data, err := fs.Get("data")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -237,43 +241,48 @@ func (ast *Assistant) handleVision(ctx context.Context, file *File, option map[s
|
||||||
return fmt.Errorf("read file error: %s", err.Error())
|
return fmt.Errorf("read file error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if VisionCapableModels[model] {
|
// The model is vision capable
|
||||||
|
if ast.vision {
|
||||||
// For vision-capable models, upload to vision service to get URL
|
// For vision-capable models, upload to vision service to get URL
|
||||||
resp, err := vision.Upload(ctx, file.Filename, bytes.NewReader(imgData), file.ContentType)
|
resp, err := vision.Upload(ctx, file.Filename, bytes.NewReader(imgData), file.ContentType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("vision upload error: %s", err.Error())
|
return fmt.Errorf("vision upload error: %s", err.Error())
|
||||||
}
|
}
|
||||||
file.URL = resp.URL // Store the URL for vision-capable models to use
|
file.URL = resp.URL // Store the URL for vision-capable models to use
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-vision models, get image description
|
||||||
|
prompt := "Describe this image in detail."
|
||||||
|
if v, ok := option["vision_prompt"].(string); ok {
|
||||||
|
prompt = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload to vision service first Compress image
|
||||||
|
resp, err := vision.Upload(ctx, file.Filename, bytes.NewReader(imgData), file.ContentType)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vision upload error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze using base64 data
|
||||||
|
result, err := vision.Analyze(ctx, resp.FileID, prompt)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("vision analyze error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract description text from response
|
||||||
|
if desc, ok := result.Description["description"].(string); ok {
|
||||||
|
file.Description = desc
|
||||||
|
} else if desc, ok := result.Description["text"].(string); ok {
|
||||||
|
file.Description = desc
|
||||||
} else {
|
} else {
|
||||||
// For non-vision models, get image description
|
// Convert the entire description to JSON string as fallback
|
||||||
prompt := "Describe this image in detail."
|
bytes, err := jsoniter.Marshal(result.Description)
|
||||||
if v, ok := option["vision_prompt"].(string); ok {
|
if err == nil {
|
||||||
prompt = v
|
file.Description = string(bytes)
|
||||||
}
|
|
||||||
|
|
||||||
// Upload to vision service first Compress image
|
|
||||||
resp, err := vision.Upload(ctx, file.Filename, bytes.NewReader(imgData), file.ContentType)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("vision upload error: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Analyze using base64 data
|
|
||||||
result, err := vision.Analyze(ctx, resp.FileID, prompt)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("vision analyze error: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract description text from response
|
|
||||||
if desc, ok := result.Description["text"].(string); ok {
|
|
||||||
file.Description = desc
|
|
||||||
} else {
|
|
||||||
// Convert the entire description to JSON string as fallback
|
|
||||||
bytes, err := jsoniter.Marshal(result.Description)
|
|
||||||
if err == nil {
|
|
||||||
file.Description = string(bytes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -517,5 +517,25 @@ func (ast *Assistant) initialize() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ast.openai = api
|
ast.openai = api
|
||||||
|
|
||||||
|
// Check if the assistant supports vision
|
||||||
|
model := api.Model()
|
||||||
|
if v, ok := ast.Options["model"].(string); ok {
|
||||||
|
model = strings.TrimLeft(v, "moapi:")
|
||||||
|
}
|
||||||
|
if _, ok := VisionCapableModels[model]; ok {
|
||||||
|
ast.vision = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the assistant has an init hook
|
||||||
|
if ast.Script != nil {
|
||||||
|
scriptCtx, err := ast.Script.NewContext("", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer scriptCtx.Close()
|
||||||
|
ast.initHook = scriptCtx.Global().Has("init")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,8 @@ type Assistant struct {
|
||||||
CreatedAt int64 `json:"created_at"` // Creation timestamp
|
CreatedAt int64 `json:"created_at"` // Creation timestamp
|
||||||
UpdatedAt int64 `json:"updated_at"` // Last update timestamp
|
UpdatedAt int64 `json:"updated_at"` // Last update timestamp
|
||||||
openai *api.OpenAI // OpenAI API
|
openai *api.OpenAI // OpenAI API
|
||||||
|
vision bool // Whether this assistant supports vision
|
||||||
|
initHook bool // Whether this assistant has an init hook
|
||||||
}
|
}
|
||||||
|
|
||||||
// VisionCapableModels list of LLM models that support vision capabilities
|
// VisionCapableModels list of LLM models that support vision capabilities
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package message
|
package message
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ContentStatusPending the content status pending
|
// ContentStatusPending the content status pending
|
||||||
|
|
@ -36,7 +38,32 @@ func NewContent(typ string) *Content {
|
||||||
// String the content string
|
// String the content string
|
||||||
func (c *Content) String() string {
|
func (c *Content) String() string {
|
||||||
if c.Type == "function" {
|
if c.Type == "function" {
|
||||||
return fmt.Sprintf(`{"id":"%s","type": "function", "function": {"name": "%s", "arguments": "%s"}}`, c.ID, c.Name, c.Bytes)
|
|
||||||
|
var arguments interface{} = string(c.Bytes)
|
||||||
|
if c.Status == ContentStatusDone {
|
||||||
|
var vv interface{} = nil
|
||||||
|
err := jsoniter.Unmarshal(c.Bytes, &vv)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
arguments = vv
|
||||||
|
}
|
||||||
|
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"id": c.ID,
|
||||||
|
"type": "function",
|
||||||
|
"function": map[string]interface{}{
|
||||||
|
"name": c.Name,
|
||||||
|
"arguments": arguments,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := jsoniter.MarshalToString(data)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return raw
|
||||||
}
|
}
|
||||||
return string(c.Bytes)
|
return string(c.Bytes)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ type Message struct {
|
||||||
type Attachment struct {
|
type Attachment struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
ContentType string `json:"content_type,omitempty"`
|
ContentType string `json:"content_type,omitempty"`
|
||||||
Bytes int64 `json:"bytes,omitempty"`
|
Bytes int64 `json:"bytes,omitempty"`
|
||||||
|
|
@ -97,6 +98,7 @@ func NewOpenAI(data []byte) *Message {
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.Type = "text"
|
||||||
if len(message.Choices) > 0 {
|
if len(message.Choices) > 0 {
|
||||||
msg.Text = message.Choices[0].Delta.Content
|
msg.Text = message.Choices[0].Delta.Content
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,11 @@ func NewMoapi(model string) (*OpenAI, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Model get the model
|
||||||
|
func (openai OpenAI) Model() string {
|
||||||
|
return openai.model
|
||||||
|
}
|
||||||
|
|
||||||
// Completions Creates a completion for the provided prompt and parameters.
|
// Completions Creates a completion for the provided prompt and parameters.
|
||||||
// https://platform.openai.com/docs/api-reference/completions/create
|
// https://platform.openai.com/docs/api-reference/completions/create
|
||||||
func (openai OpenAI) Completions(prompt interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
func (openai OpenAI) Completions(prompt interface{}, option map[string]interface{}, cb func(data []byte) int) (interface{}, *exception.Exception) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue