Refactor Assistant methods and context handling for improved functionality

- Updated the Stream method to return a structured response, including create, done, and completion hooks.
- Introduced new methods for managing message history and building LLM requests, enhancing modularity.
- Refactored the Script type to utilize a new hook structure, improving code organization.
- Removed the obsolete hooks file to streamline the codebase.
- Enhanced context types with additional fields for better integration with LLM models.
This commit is contained in:
Max 2025-11-13 09:26:45 +08:00
parent 27d4cd9555
commit 9c97035cc6
15 changed files with 197 additions and 42 deletions

View file

@ -1,9 +1,14 @@
package assistant
import "github.com/yaoapp/yao/agent/context"
import (
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/llm"
)
// Stream stream the agent
func (ast *Assistant) Stream(ctx *context.Context, messages []context.Message, handler context.StreamFunc) error {
func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Message, handler context.StreamFunc) (*context.Response, error) {
var err error
// Initialize stack and auto-handle completion/failure/restore
_, traceID, done := context.EnterStack(ctx, ast.ID, ctx.Referer)
@ -11,23 +16,85 @@ func (ast *Assistant) Stream(ctx *context.Context, messages []context.Message, h
_ = traceID // traceID is available for trace logging
// Full input messages with chat history
fullMessages, err := ast.WithHistory(ctx, inputMessages)
if err != nil {
return nil, err
}
// Request Create hook ( Optional )
var createResponse *context.ResponseHookCreate
if ast.Script != nil {
var err error
createResponse, err = ast.Script.Create(ctx, fullMessages)
if err != nil {
return nil, err
}
}
_ = createResponse // createResponse is available for further processing
var completionOptions *llm.CompletionOptions // default is nil
// LLM Call Stream ( Optional )
var completionMessages []context.Message
var completionResponse *context.ResponseCompletion
if ast.Prompts != nil || ast.MCP != nil {
llm, err := llm.New(ast.GetConnector(ctx))
if err != nil {
return nil, err
}
// Build the LLM request
completionMessages, completionOptions, err = ast.BuildLLMRequest(ctx, inputMessages, createResponse)
if err != nil {
return nil, err
}
// Call the LLM Completion Stream
completionResponse, err = llm.Stream(ctx, completionMessages, completionOptions, handler)
if err != nil {
return nil, err
}
}
// Request MCP hook ( Optional )
var mcpResponse *context.ResponseHookMCP
if ast.MCP != nil {
_ = mcpResponse // mcpResponse is available for further processing
// MCP Execution Loop
}
// Request Done hook ( Optional )
var doneResponse *context.ResponseHookDone
if ast.Script != nil {
var err error
doneResponse, err = ast.Script.Done(ctx, fullMessages, completionResponse, mcpResponse)
if err != nil {
return nil, err
}
}
return nil
_ = doneResponse // doneResponse is available for further processing
return &context.Response{Create: createResponse, Done: doneResponse, Completion: completionResponse}, nil
}
// Run run the agent
func (ast *Assistant) Run(ctx *context.Context, messages []context.Message) (*context.Response, error) {
// Initialize stack and auto-handle completion/failure/restore
_, traceID, done := context.EnterStack(ctx, ast.ID, ctx.Referer)
defer done()
_ = traceID // traceID is available for trace logging
return &context.Response{}, nil
// GetConnector get the connector from the context
func (ast *Assistant) GetConnector(ctx *context.Context) string {
if ctx.Connector != "" {
return ctx.Connector
}
return ast.Connector
}
// BuildLLMRequest build the LLM request
func (ast *Assistant) BuildLLMRequest(ctx *context.Context, messages []context.Message, createResponse *context.ResponseHookCreate) ([]context.Message, *llm.CompletionOptions, error) {
return messages, nil, nil
}
// WithHistory with the history messages
func (ast *Assistant) WithHistory(ctx *context.Context, messages []context.Message) ([]context.Message, error) {
return messages, nil
}

View file

@ -0,0 +1,8 @@
package hook
import "github.com/yaoapp/yao/agent/context"
// Create create a new assistant
func (s *Script) Create(ctx *context.Context, messages []context.Message) (*context.ResponseHookCreate, error) {
return &context.ResponseHookCreate{}, nil
}

View file

@ -0,0 +1,10 @@
package hook
import (
"github.com/yaoapp/yao/agent/context"
)
// Done done hook
func (s *Script) Done(ctx *context.Context, inputMessages []context.Message, completionResponse *context.ResponseCompletion, mcpResponse *context.ResponseHookMCP) (*context.ResponseHookDone, error) {
return &context.ResponseHookDone{}, nil
}

View file

@ -0,0 +1,8 @@
package hook
import "github.com/yaoapp/yao/agent/context"
// Failback failback hook
func (s *Script) Failback(ctx *context.Context, inputMessages []context.Message, completionResponse *context.ResponseCompletion) (*context.ResponseHookFailback, error) {
return &context.ResponseHookFailback{}, nil
}

View file

@ -0,0 +1 @@
package hook

View file

@ -0,0 +1,8 @@
package hook
import "github.com/yaoapp/yao/agent/context"
// MCP MCP hook
func (s *Script) MCP(ctx *context.Context, messages []context.Message) (*context.ResponseHookMCP, error) {
return &context.ResponseHookMCP{}, nil
}

View file

@ -0,0 +1 @@
package hook

View file

@ -0,0 +1,10 @@
package hook
import (
v8 "github.com/yaoapp/gou/runtime/v8"
)
// Script the script hook align
type Script struct {
*v8.Script
}

View file

@ -1 +0,0 @@
package hooks

View file

@ -13,6 +13,7 @@ import (
"github.com/yaoapp/gou/application"
"github.com/yaoapp/gou/fs"
v8 "github.com/yaoapp/gou/runtime/v8"
"github.com/yaoapp/yao/agent/assistant/hook"
"github.com/yaoapp/yao/agent/i18n"
store "github.com/yaoapp/yao/agent/store/types"
agentvision "github.com/yaoapp/yao/agent/vision"
@ -106,9 +107,7 @@ func LoadBuiltIn() error {
loaded.Put(assistant)
// Remove the built-in assistant from the store
if _, ok := deletedBuiltIn[assistant.ID]; ok {
delete(deletedBuiltIn, assistant.ID)
}
delete(deletedBuiltIn, assistant.ID)
}
// Remove deleted built-in assistants
@ -290,16 +289,16 @@ func LoadPath(path string) (*Assistant, error) {
data["updated_at"] = max(updatedAt, ts)
}
// load tools
toolsfile := filepath.Join(path, "tools.yao")
if has, _ := app.Exists(toolsfile); has {
tools, ts, err := loadTools(toolsfile)
if err != nil {
return nil, err
}
data["tools"] = tools
updatedAt = max(updatedAt, ts)
}
// load tools, deprecated, use mcp instead
// toolsfile := filepath.Join(path, "tools.yao")
// if has, _ := app.Exists(toolsfile); has {
// tools, ts, err := loadTools(toolsfile)
// if err != nil {
// return nil, err
// }
// data["tools"] = tools
// updatedAt = max(updatedAt, ts)
// }
// i18ns
locales, err := i18n.GetLocales(path)
@ -430,6 +429,9 @@ func loadMap(data map[string]interface{}) (*Assistant, error) {
}
assistant.Tags = tags
case string:
assistant.Tags = []string{vv}
case interface{}:
raw, err := jsoniter.Marshal(vv)
if err != nil {
@ -442,8 +444,6 @@ func loadMap(data map[string]interface{}) (*Assistant, error) {
}
assistant.Tags = tags
case string:
assistant.Tags = []string{vv}
}
}
@ -571,9 +571,11 @@ func loadMap(data map[string]interface{}) (*Assistant, error) {
if err != nil {
return nil, err
}
assistant.Script = script
case *v8.Script:
assistant.Script = &hook.Script{Script: script}
case *hook.Script:
assistant.Script = v
case *v8.Script:
assistant.Script = &hook.Script{Script: v}
}
}
@ -641,7 +643,7 @@ func loadPrompts(file string, root string) (string, int64, error) {
return string(prompts), ts.UnixNano(), nil
}
func loadScript(file string, root string) (*v8.Script, int64, error) {
func loadScript(file string, root string) (*hook.Script, int64, error) {
app, err := fs.Get("app")
if err != nil {
@ -658,7 +660,7 @@ func loadScript(file string, root string) (*v8.Script, int64, error) {
return nil, 0, err
}
return script, ts.UnixNano(), nil
return &hook.Script{Script: script}, ts.UnixNano(), nil
}
func loadScriptSource(source string, file string) (*v8.Script, error) {

View file

@ -5,7 +5,7 @@ import (
"io"
"github.com/gin-gonic/gin"
v8 "github.com/yaoapp/gou/runtime/v8"
"github.com/yaoapp/yao/agent/assistant/hook"
chatctx "github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/message"
store "github.com/yaoapp/yao/agent/store/types"
@ -96,16 +96,16 @@ type QueryParam struct {
type Assistant struct {
store.AssistantModel
Search *SearchOption `json:"search,omitempty" yaml:"search,omitempty"` // Whether this assistant supports search
Script *v8.Script `json:"-" yaml:"-"` // Assistant Script
Script *hook.Script `json:"-" yaml:"-"` // Assistant Script
// Internal
// ===============================
openai *api.OpenAI // OpenAI API
search bool // Whether this assistant supports search
vision bool // Whether this assistant supports vision
toolCalls bool // Whether this assistant supports tool_calls
initHook bool // Whether this assistant has an init hook
runtimeTools []Tool // Converted tools for business logic (OpenAI format)
openai *api.OpenAI // OpenAI API
search bool // Whether this assistant supports search
vision bool // Whether this assistant supports vision
// toolCalls bool // Whether this assistant supports tool_calls
initHook bool // Whether this assistant has an init hook
runtimeTools []Tool // Converted tools for business logic (OpenAI format)
}
// ConnectorSetting the connector setting

View file

@ -131,6 +131,8 @@ type Context struct {
ChatID string `json:"chat_id,omitempty"` // Chat ID, use to select chat
AssistantID string `json:"assistant_id,omitempty"` // Assistant ID, use to select assistant
Sid string `json:"sid" yaml:"-"` // Session ID (Deprecated, use Authorized instead)
Connector string `json:"connector,omitempty"` // Connector, use to select the connector of the LLM Model, Default is Assistant.Connector
Search *bool `json:"search,omitempty"` // Search mode, default is true
// Arguments for call
Args []interface{} `json:"args,omitempty"` // Arguments for call, it will be used to pass data to the call
@ -181,7 +183,28 @@ type Stack struct {
// Response the response
// 100% compatible with the OpenAI API
type Response struct{}
type Response struct {
Create *ResponseHookCreate `json:"create,omitempty"`
MCP *ResponseHookMCP `json:"mcp,omitempty"`
Done *ResponseHookDone `json:"done,omitempty"`
Failback *ResponseHookFailback `json:"failback,omitempty"`
Completion *ResponseCompletion `json:"completion,omitempty"`
}
// ResponseHookCreate the response of the create hook
type ResponseHookCreate struct{}
// ResponseHookDone the response of the done hook
type ResponseHookDone struct{}
// ResponseHookMCP the response of the mcp hook
type ResponseHookMCP struct{}
// ResponseHookFailback the response of the failback hook
type ResponseHookFailback struct{}
// ResponseCompletion the response of the completion
type ResponseCompletion struct{}
// Message Structure ( OpenAI Chat Completion Input Message Structure, https://platform.openai.com/docs/api-reference/chat/create#chat/create-messages )
// ===============================

9
agent/llm/interfaces.go Normal file
View file

@ -0,0 +1,9 @@
package llm
import "github.com/yaoapp/yao/agent/context"
// LLM the LLM interface
type LLM interface {
Stream(ctx *context.Context, messages []context.Message, options *CompletionOptions, handler context.StreamFunc) (*context.ResponseCompletion, error)
Post(ctx *context.Context, messages []context.Message, options *CompletionOptions) (*context.ResponseCompletion, error)
}

View file

@ -1 +1,6 @@
package llm
// New create a new LLM instance
func New(connector string) (LLM, error) {
return nil, nil
}

4
agent/llm/types.go Normal file
View file

@ -0,0 +1,4 @@
package llm
// CompletionOptions the completion request
type CompletionOptions struct{}