Merge pull request #828 from trheyi/main

Implement assistant call functionality in Neo API
This commit is contained in:
Max 2025-01-23 16:38:43 +08:00 committed by GitHub
commit eec3a8a285
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 0 deletions

View file

@ -15,6 +15,7 @@ import (
"github.com/yaoapp/gou/connector"
"github.com/yaoapp/gou/process"
"github.com/yaoapp/yao/helper"
"github.com/yaoapp/yao/neo/assistant"
chatctx "github.com/yaoapp/yao/neo/context"
"github.com/yaoapp/yao/neo/message"
"github.com/yaoapp/yao/neo/store"
@ -44,7 +45,9 @@ func (neo *DSL) API(router *gin.Engine, path string) error {
router.OPTIONS(path+"/dangerous/clear_chats", neo.optionsHandler)
router.OPTIONS(path+"/assistants", neo.optionsHandler)
router.OPTIONS(path+"/assistants/:id", neo.optionsHandler)
router.OPTIONS(path+"/assistants/:id/call", neo.optionsHandler)
// Chat endpoint
// Chat endpoint
// Example:
// curl -X GET 'http://localhost:5099/api/__yao/neo?content=Hello&chat_id=chat_123&context=previous_context&token=xxx'
@ -71,6 +74,12 @@ func (neo *DSL) API(router *gin.Engine, path string) error {
// curl -X GET 'http://localhost:5099/api/__yao/neo/assistants/assistant_123?token=xxx'
router.GET(path+"/assistants/:id", append(middlewares, neo.handleAssistantDetail)...)
// Execute assistant API example:
// curl -X POST 'http://localhost:5099/api/__yao/neo/assistants/assistant_123/api' \
// -H 'Content-Type: application/json' \
// -d '{"name": "Test", "payload": {"name": "yao", "age": 18}}'
router.POST(path+"/assistants/:id/call", append(middlewares, neo.handleAssistantCall)...)
// Create/Update assistant example:
// curl -X POST 'http://localhost:5099/api/__yao/neo/assistants' \
// -H 'Content-Type: application/json' \
@ -938,6 +947,46 @@ func parseBoolValue(value string) *bool {
}
}
// handleAssistantAPI handles the assistant API
func (neo *DSL) handleAssistantCall(c *gin.Context) {
assistantID := c.Param("id")
if assistantID == "" {
c.JSON(400, gin.H{"message": "assistant id is required", "code": 400})
c.Done()
return
}
ast, err := assistant.Get(assistantID)
if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done()
return
}
sid := c.GetString("__sid")
if sid == "" {
c.JSON(400, gin.H{"message": "sid is required", "code": 400})
c.Done()
return
}
payload := assistant.APIPayload{Sid: sid}
if err := c.BindJSON(&payload); err != nil {
c.JSON(400, gin.H{"message": "invalid request body", "code": 400})
c.Done()
return
}
result, err := ast.Call(c, payload)
if err != nil {
c.JSON(500, gin.H{"message": err.Error(), "code": 500})
c.Done()
return
}
c.JSON(200, result)
c.Done()
}
// handleAssistantDetail handles getting a single assistant's details
func (neo *DSL) handleAssistantDetail(c *gin.Context) {
assistantID := c.Param("id")

View file

@ -170,6 +170,25 @@ func (next *NextAction) Execute(c *gin.Context, ctx chatctx.Context) error {
}
}
// Call implements the call functionality
func (ast *Assistant) Call(c *gin.Context, payload APIPayload) (interface{}, error) {
scriptCtx, err := ast.Script.NewContext(payload.Sid, nil)
if err != nil {
return nil, err
}
defer scriptCtx.Close()
ctx := c.Request.Context()
method := fmt.Sprintf("%sAPI", payload.Name)
// Check if the method exists
if !scriptCtx.Global().Has(method) {
return nil, fmt.Errorf(HookErrorMethodNotFound)
}
return scriptCtx.CallWith(ctx, method, payload.Payload)
}
// handleChatStream manages the streaming chat interaction with the AI
func (ast *Assistant) handleChatStream(c *gin.Context, ctx chatctx.Context, messages []chatMessage.Message, options map[string]interface{}, contents *chatMessage.Contents) error {
clientBreak := make(chan bool, 1)

View file

@ -25,6 +25,14 @@ type API interface {
Download(ctx context.Context, fileID string) (*FileResponse, error)
ReadBase64(ctx context.Context, fileID string) (string, error)
Execute(c *gin.Context, ctx chatctx.Context, input string, options map[string]interface{}) error
Call(c *gin.Context, payload APIPayload) (interface{}, error)
}
// APIPayload the API payload
type APIPayload struct {
Sid string `json:"sid"`
Name string `json:"name"`
Payload map[string]interface{} `json:"payload"`
}
// ResHookInit the response of the init hook