Refactor Assistant context handling to improve options management
- Updated the Assistant methods to accept an Options parameter, enhancing flexibility in context management. - Removed the Connector field from the context and related structures, transitioning to a more streamlined options-based approach. - Adjusted various tests to accommodate the new options handling, ensuring comprehensive coverage of the updated functionality. - Enhanced the Create and Next hooks to return options alongside responses, improving the overall usability of the API. - Cleaned up deprecated fields and improved context initialization for better maintainability.
This commit is contained in:
parent
beb63b77de
commit
a96946d8bb
42 changed files with 425 additions and 332 deletions
|
|
@ -18,7 +18,7 @@ import (
|
||||||
|
|
||||||
// Stream stream the agent
|
// Stream stream the agent
|
||||||
// handler is optional, if not provided, a default handler will be used
|
// handler is optional, if not provided, a default handler will be used
|
||||||
func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Message, handler ...message.StreamFunc) (interface{}, error) {
|
func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Message, options ...*context.Options) (interface{}, error) {
|
||||||
|
|
||||||
log.Trace("[AGENT] Stream started: assistant=%s, contextID=%s", ast.ID, ctx.ID)
|
log.Trace("[AGENT] Stream started: assistant=%s, contextID=%s", ast.ID, ctx.ID)
|
||||||
defer log.Trace("[AGENT] Stream ended: assistant=%s, contextID=%s", ast.ID, ctx.ID)
|
defer log.Trace("[AGENT] Stream ended: assistant=%s, contextID=%s", ast.ID, ctx.ID)
|
||||||
|
|
@ -39,8 +39,16 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
||||||
// Initialize
|
// Initialize
|
||||||
// ================================================
|
// ================================================
|
||||||
|
|
||||||
|
// Get or create options
|
||||||
|
var opts *context.Options
|
||||||
|
if len(options) > 0 && options[0] != nil {
|
||||||
|
opts = options[0]
|
||||||
|
} else {
|
||||||
|
opts = &context.Options{}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize stack and auto-handle completion/failure/restore
|
// Initialize stack and auto-handle completion/failure/restore
|
||||||
_, _, done := context.EnterStack(ctx, ast.ID, ctx.Referer)
|
_, _, done := context.EnterStack(ctx, ast.ID, opts)
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
fmt.Println("--- Stack debug ---")
|
fmt.Println("--- Stack debug ---")
|
||||||
|
|
@ -51,11 +59,11 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
||||||
fmt.Println("------ end stack debug ------")
|
fmt.Println("------ end stack debug ------")
|
||||||
|
|
||||||
// Determine stream handler
|
// Determine stream handler
|
||||||
streamHandler := ast.getStreamHandler(ctx, handler...)
|
streamHandler := ast.getStreamHandler(ctx, opts)
|
||||||
|
|
||||||
// Get connector and capabilities early (before sending stream_start)
|
// Get connector and capabilities early (before sending stream_start)
|
||||||
// so that output adapters can use them when converting stream_start event
|
// so that output adapters can use them when converting stream_start event
|
||||||
err = ast.initializeCapabilities(ctx)
|
err = ast.initializeCapabilities(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ast.sendStreamEndOnError(ctx, streamHandler, streamStartTime, err)
|
ast.sendStreamEndOnError(ctx, streamHandler, streamStartTime, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -85,7 +93,7 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
||||||
var createResponse *context.HookCreateResponse
|
var createResponse *context.HookCreateResponse
|
||||||
if ast.Script != nil {
|
if ast.Script != nil {
|
||||||
var err error
|
var err error
|
||||||
createResponse, err = ast.Script.Create(ctx, fullMessages)
|
createResponse, opts, err = ast.Script.Create(ctx, fullMessages, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ast.traceAgentFail(agentNode, err)
|
ast.traceAgentFail(agentNode, err)
|
||||||
// Send error stream_end for root stack
|
// Send error stream_end for root stack
|
||||||
|
|
@ -235,11 +243,11 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
||||||
|
|
||||||
if ast.Script != nil {
|
if ast.Script != nil {
|
||||||
var err error
|
var err error
|
||||||
nextResponse, err = ast.Script.Next(ctx, &context.NextHookPayload{
|
nextResponse, opts, err = ast.Script.Next(ctx, &context.NextHookPayload{
|
||||||
Messages: fullMessages,
|
Messages: fullMessages,
|
||||||
Completion: completionResponse,
|
Completion: completionResponse,
|
||||||
Tools: toolCallResponses,
|
Tools: toolCallResponses,
|
||||||
})
|
}, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ast.traceAgentFail(agentNode, err)
|
ast.traceAgentFail(agentNode, err)
|
||||||
ast.sendStreamEndOnError(ctx, streamHandler, streamStartTime, err)
|
ast.sendStreamEndOnError(ctx, streamHandler, streamStartTime, err)
|
||||||
|
|
@ -318,14 +326,14 @@ func (ast *Assistant) Stream(ctx *context.Context, inputMessages []context.Messa
|
||||||
return finalResponse, nil
|
return finalResponse, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConnector get the connector object, capabilities, and error with priority: createResponse > ctx > ast
|
// GetConnector get the connector object, capabilities, and error with priority: opts.Connector > ast.Connector
|
||||||
// Note: createResponse.Connector is already applied to ctx.Connector by applyContextAdjustments in create.go
|
// Note: opts.Connector may be set by Create hook's applyOptionsAdjustments
|
||||||
// Returns: (connector, capabilities, error)
|
// Returns: (connector, capabilities, error)
|
||||||
func (ast *Assistant) GetConnector(ctx *context.Context) (connector.Connector, *openai.Capabilities, error) {
|
func (ast *Assistant) GetConnector(ctx *context.Context, opts ...*context.Options) (connector.Connector, *openai.Capabilities, error) {
|
||||||
// Determine connector ID with priority
|
// Determine connector ID with priority: opts.Connector > ast.Connector
|
||||||
connectorID := ast.Connector
|
connectorID := ast.Connector
|
||||||
if ctx.Connector != "" {
|
if len(opts) > 0 && opts[0] != nil && opts[0].Connector != "" {
|
||||||
connectorID = ctx.Connector
|
connectorID = opts[0].Connector
|
||||||
}
|
}
|
||||||
|
|
||||||
// If empty, return error
|
// If empty, return error
|
||||||
|
|
@ -361,10 +369,11 @@ func (ast *Assistant) Info(locale ...string) *message.AssistantInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStreamHandler returns the stream handler from the provided handlers or a default one
|
// getStreamHandler returns the stream handler from options or a default one
|
||||||
func (ast *Assistant) getStreamHandler(ctx *context.Context, handler ...message.StreamFunc) message.StreamFunc {
|
func (ast *Assistant) getStreamHandler(ctx *context.Context, opts ...*context.Options) message.StreamFunc {
|
||||||
if len(handler) > 0 && handler[0] != nil {
|
// Check if handler is provided in options
|
||||||
return handler[0]
|
if len(opts) > 0 && opts[0] != nil && opts[0].Writer != nil {
|
||||||
|
return handlers.DefaultStreamHandler(ctx)
|
||||||
}
|
}
|
||||||
return handlers.DefaultStreamHandler(ctx)
|
return handlers.DefaultStreamHandler(ctx)
|
||||||
}
|
}
|
||||||
|
|
@ -460,12 +469,12 @@ func (ast *Assistant) handleInterrupt(ctx *context.Context, signal *context.Inte
|
||||||
// initializeCapabilities gets connector and capabilities, then sets them in context
|
// initializeCapabilities gets connector and capabilities, then sets them in context
|
||||||
// This should be called early (before sending stream_start) so that output adapters
|
// This should be called early (before sending stream_start) so that output adapters
|
||||||
// can use capabilities when converting stream_start event
|
// can use capabilities when converting stream_start event
|
||||||
func (ast *Assistant) initializeCapabilities(ctx *context.Context) error {
|
func (ast *Assistant) initializeCapabilities(ctx *context.Context, opts *context.Options) error {
|
||||||
if ast.Prompts == nil && ast.MCP == nil {
|
if ast.Prompts == nil && ast.MCP == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, capabilities, err := ast.GetConnector(ctx)
|
_, capabilities, err := ast.GetConnector(ctx, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ func newTestContextWithInterrupt(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ type agentCallerWrapper struct {
|
||||||
ast *Assistant
|
ast *Assistant
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *agentCallerWrapper) Stream(ctx *agentContext.Context, messages []agentContext.Message) (interface{}, error) {
|
func (w *agentCallerWrapper) Stream(ctx *agentContext.Context, messages []agentContext.Message, options ...*agentContext.Options) (interface{}, error) {
|
||||||
return w.ast.Stream(ctx, messages)
|
return w.ast.Stream(ctx, messages, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get get the assistant by id
|
// Get get the assistant by id
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func TestBuildRequest_MCP(t *testing.T) {
|
||||||
// Call create hook to get createResponse
|
// Call create hook to get createResponse
|
||||||
var createResponse *context.HookCreateResponse
|
var createResponse *context.HookCreateResponse
|
||||||
if hookAgent.Script != nil {
|
if hookAgent.Script != nil {
|
||||||
createResponse, err = hookAgent.Script.Create(hookCtx, inputMessages)
|
createResponse, _, err = hookAgent.Script.Create(hookCtx, inputMessages, &context.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to call create hook: %s", err.Error())
|
t.Fatalf("Failed to call create hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -650,7 +650,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, createResponse)
|
require.NotNil(t, createResponse)
|
||||||
assert.Equal(t, "mode.friendly", createResponse.PromptPreset)
|
assert.Equal(t, "mode.friendly", createResponse.PromptPreset)
|
||||||
|
|
@ -681,7 +681,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, createResponse)
|
require.NotNil(t, createResponse)
|
||||||
assert.Equal(t, "mode.professional", createResponse.PromptPreset)
|
assert.Equal(t, "mode.professional", createResponse.PromptPreset)
|
||||||
|
|
@ -718,7 +718,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, createResponse)
|
require.NotNil(t, createResponse)
|
||||||
require.NotNil(t, createResponse.DisableGlobalPrompts)
|
require.NotNil(t, createResponse.DisableGlobalPrompts)
|
||||||
|
|
@ -753,7 +753,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, createResponse)
|
require.NotNil(t, createResponse)
|
||||||
assert.Equal(t, "mode.friendly", createResponse.PromptPreset)
|
assert.Equal(t, "mode.friendly", createResponse.PromptPreset)
|
||||||
|
|
@ -788,7 +788,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, createResponse)
|
require.NotNil(t, createResponse)
|
||||||
assert.Equal(t, "non.existent.preset", createResponse.PromptPreset)
|
assert.Equal(t, "non.existent.preset", createResponse.PromptPreset)
|
||||||
|
|
@ -819,7 +819,7 @@ func TestPromptPresetAssistant(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Create hook - should return nil
|
// Call Create hook - should return nil
|
||||||
createResponse, err := ast.Script.Create(ctx, messages)
|
createResponse, _, err := ast.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, createResponse)
|
assert.Nil(t, createResponse)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ func newTestContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
@ -64,7 +63,7 @@ func TestBuildRequest(t *testing.T) {
|
||||||
inputMessages := []context.Message{{Role: "user", Content: "no_override"}}
|
inputMessages := []context.Message{{Role: "user", Content: "no_override"}}
|
||||||
|
|
||||||
// Call Create hook
|
// Call Create hook
|
||||||
createResponse, err := agent.Script.Create(ctx, inputMessages)
|
createResponse, _, err := agent.Script.Create(ctx, inputMessages, &context.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -112,7 +111,7 @@ func TestBuildRequest(t *testing.T) {
|
||||||
t.Run("OverrideTemperature", func(t *testing.T) {
|
t.Run("OverrideTemperature", func(t *testing.T) {
|
||||||
inputMessages := []context.Message{{Role: "user", Content: "override_temperature"}}
|
inputMessages := []context.Message{{Role: "user", Content: "override_temperature"}}
|
||||||
|
|
||||||
createResponse, err := agent.Script.Create(ctx, inputMessages)
|
createResponse, _, err := agent.Script.Create(ctx, inputMessages, &context.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -143,7 +142,7 @@ func TestBuildRequest(t *testing.T) {
|
||||||
t.Run("OverrideAll", func(t *testing.T) {
|
t.Run("OverrideAll", func(t *testing.T) {
|
||||||
inputMessages := []context.Message{{Role: "user", Content: "override_all"}}
|
inputMessages := []context.Message{{Role: "user", Content: "override_all"}}
|
||||||
|
|
||||||
createResponse, err := agent.Script.Create(ctx, inputMessages)
|
createResponse, _, err := agent.Script.Create(ctx, inputMessages, &context.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +195,7 @@ func TestBuildRequest(t *testing.T) {
|
||||||
t.Run("OverrideRouteMetadata", func(t *testing.T) {
|
t.Run("OverrideRouteMetadata", func(t *testing.T) {
|
||||||
inputMessages := []context.Message{{Role: "user", Content: "override_route_metadata"}}
|
inputMessages := []context.Message{{Role: "user", Content: "override_route_metadata"}}
|
||||||
|
|
||||||
createResponse, err := agent.Script.Create(ctx, inputMessages)
|
createResponse, _, err := agent.Script.Create(ctx, inputMessages, &context.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
t.Fatalf("Failed to call Create hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,53 +9,57 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create create a new assistant
|
// Create create a new assistant
|
||||||
func (s *Script) Create(ctx *context.Context, messages []context.Message) (*context.HookCreateResponse, error) {
|
// opts is optional - if provided, will be adjusted based on hook response
|
||||||
res, err := s.Execute(ctx, "Create", messages)
|
func (s *Script) Create(ctx *context.Context, messages []context.Message, opts ...*context.Options) (*context.HookCreateResponse, *context.Options, error) {
|
||||||
|
// Get or create options
|
||||||
|
var options *context.Options
|
||||||
|
if len(opts) > 0 && opts[0] != nil {
|
||||||
|
options = opts[0]
|
||||||
|
} else {
|
||||||
|
options = &context.Options{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute hook with ctx, messages, and options (convert options to map for JS)
|
||||||
|
optionsMap := options.ToMap()
|
||||||
|
res, err := s.Execute(ctx, "Create", messages, optionsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := s.getHookCreateResponse(res)
|
response, err := s.getHookCreateResponse(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply context adjustments from the response back to the context
|
// Apply adjustments from the response
|
||||||
if response != nil {
|
if response != nil {
|
||||||
s.applyContextAdjustments(ctx, response)
|
s.applyContextAdjustments(ctx, response)
|
||||||
|
s.applyOptionsAdjustments(options, response)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyContextAdjustments applies context field overrides from the hook response back to the context
|
// applyContextAdjustments applies session-level field overrides from the hook response back to the context
|
||||||
func (s *Script) applyContextAdjustments(ctx *context.Context, response *context.HookCreateResponse) {
|
func (s *Script) applyContextAdjustments(ctx *context.Context, response *context.HookCreateResponse) {
|
||||||
// Override assistant ID if provided
|
// Note: AssistantID cannot be overridden - it's set at initialization and immutable
|
||||||
if response.AssistantID != "" {
|
|
||||||
ctx.AssistantID = response.AssistantID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override connector if provided
|
// Override locale if provided (session-level)
|
||||||
if response.Connector != "" {
|
|
||||||
ctx.Connector = response.Connector
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override locale if provided
|
|
||||||
if response.Locale != "" {
|
if response.Locale != "" {
|
||||||
ctx.Locale = response.Locale
|
ctx.Locale = response.Locale
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override theme if provided
|
// Override theme if provided (session-level)
|
||||||
if response.Theme != "" {
|
if response.Theme != "" {
|
||||||
ctx.Theme = response.Theme
|
ctx.Theme = response.Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override route if provided
|
// Override route if provided (session-level)
|
||||||
if response.Route != "" {
|
if response.Route != "" {
|
||||||
ctx.Route = response.Route
|
ctx.Route = response.Route
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge or override metadata if provided
|
// Merge or override metadata if provided (session-level)
|
||||||
if len(response.Metadata) > 0 {
|
if len(response.Metadata) > 0 {
|
||||||
if ctx.Metadata == nil {
|
if ctx.Metadata == nil {
|
||||||
ctx.Metadata = make(map[string]interface{})
|
ctx.Metadata = make(map[string]interface{})
|
||||||
|
|
@ -67,6 +71,14 @@ func (s *Script) applyContextAdjustments(ctx *context.Context, response *context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyOptionsAdjustments applies call-level field overrides from the hook response to options
|
||||||
|
func (s *Script) applyOptionsAdjustments(opts *context.Options, response *context.HookCreateResponse) {
|
||||||
|
// Override connector if provided (call-level parameter)
|
||||||
|
if response.Connector != "" {
|
||||||
|
opts.Connector = response.Connector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getHookCreateResponse convert the result to a HookCreateResponse
|
// getHookCreateResponse convert the result to a HookCreateResponse
|
||||||
func (s *Script) getHookCreateResponse(res interface{}) (*context.HookCreateResponse, error) {
|
func (s *Script) getHookCreateResponse(res interface{}) (*context.HookCreateResponse, error) {
|
||||||
// Handle nil result
|
// Handle nil result
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ func BenchmarkSimpleStandardMode(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ctx := newBenchContext("bench-simple-standard", "tests.create")
|
ctx := newBenchContext("bench-simple-standard", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -61,7 +61,7 @@ func BenchmarkSimplePerformanceMode(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ctx := newBenchContext("bench-simple-performance", "tests.create")
|
ctx := newBenchContext("bench-simple-performance", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -95,7 +95,7 @@ func BenchmarkBusinessStandardMode(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
scenario := scenarios[i%len(scenarios)]
|
scenario := scenarios[i%len(scenarios)]
|
||||||
ctx := newBenchContext("bench-business-standard", "tests.create")
|
ctx := newBenchContext("bench-business-standard", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: scenario.content},
|
{Role: "user", Content: scenario.content},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -125,7 +125,7 @@ func BenchmarkBusinessPerformanceMode(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
scenario := scenarios[i%len(scenarios)]
|
scenario := scenarios[i%len(scenarios)]
|
||||||
ctx := newBenchContext("bench-business-performance", "tests.create")
|
ctx := newBenchContext("bench-business-performance", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: scenario.content},
|
{Role: "user", Content: scenario.content},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -159,7 +159,7 @@ func BenchmarkConcurrentSimpleStandardMode(b *testing.B) {
|
||||||
i := 0
|
i := 0
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
ctx := newBenchContext("bench-concurrent-simple-standard", "tests.create")
|
ctx := newBenchContext("bench-concurrent-simple-standard", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -191,7 +191,7 @@ func BenchmarkConcurrentSimplePerformanceMode(b *testing.B) {
|
||||||
i := 0
|
i := 0
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
ctx := newBenchContext("bench-concurrent-simple", "tests.create")
|
ctx := newBenchContext("bench-concurrent-simple", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -226,7 +226,7 @@ func BenchmarkConcurrentBusinessStandardMode(b *testing.B) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
scenario := scenarios[i%len(scenarios)]
|
scenario := scenarios[i%len(scenarios)]
|
||||||
ctx := newBenchContext("bench-concurrent-business-standard", "tests.create")
|
ctx := newBenchContext("bench-concurrent-business-standard", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: scenario.content},
|
{Role: "user", Content: scenario.content},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -261,7 +261,7 @@ func BenchmarkConcurrentBusinessPerformanceMode(b *testing.B) {
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
scenario := scenarios[i%len(scenarios)]
|
scenario := scenarios[i%len(scenarios)]
|
||||||
ctx := newBenchContext("bench-concurrent-business", "tests.create")
|
ctx := newBenchContext("bench-concurrent-business", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: scenario.content},
|
{Role: "user", Content: scenario.content},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -301,7 +301,6 @@ func newBenchContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func TestMemoryLeakStandardMode(t *testing.T) {
|
||||||
// Warm up - execute a few times to stabilize memory
|
// Warm up - execute a few times to stabilize memory
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -52,7 +52,7 @@ func TestMemoryLeakStandardMode(t *testing.T) {
|
||||||
iterations := 1000
|
iterations := 1000
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newMemTestContext("mem-test-standard", "tests.create")
|
ctx := newMemTestContext("mem-test-standard", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -124,7 +124,7 @@ func TestMemoryLeakPerformanceMode(t *testing.T) {
|
||||||
// Warm up - execute a few times to stabilize memory and fill isolate pool
|
// Warm up - execute a few times to stabilize memory and fill isolate pool
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -140,7 +140,7 @@ func TestMemoryLeakPerformanceMode(t *testing.T) {
|
||||||
iterations := 1000
|
iterations := 1000
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newMemTestContext("mem-test-performance", "tests.create")
|
ctx := newMemTestContext("mem-test-performance", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -221,7 +221,7 @@ func TestMemoryLeakBusinessScenarios(t *testing.T) {
|
||||||
// Warm up
|
// Warm up
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "return_full"},
|
{Role: "user", Content: "return_full"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -240,7 +240,7 @@ func TestMemoryLeakBusinessScenarios(t *testing.T) {
|
||||||
iterations := 200
|
iterations := 200
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newMemTestContext("mem-test-business", "tests.create")
|
ctx := newMemTestContext("mem-test-business", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: scenario.content},
|
{Role: "user", Content: scenario.content},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -298,7 +298,7 @@ func TestMemoryLeakConcurrent(t *testing.T) {
|
||||||
// Warm up
|
// Warm up
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -321,7 +321,7 @@ func TestMemoryLeakConcurrent(t *testing.T) {
|
||||||
defer func() { done <- true }()
|
defer func() { done <- true }()
|
||||||
for i := 0; i < iterPerGoroutine; i++ {
|
for i := 0; i < iterPerGoroutine; i++ {
|
||||||
ctx := newMemTestContext("mem-test-concurrent", "tests.create")
|
ctx := newMemTestContext("mem-test-concurrent", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -383,7 +383,7 @@ func TestMemoryLeakNestedCalls(t *testing.T) {
|
||||||
// Warm up
|
// Warm up
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "nested_script_call"},
|
{Role: "user", Content: "nested_script_call"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -400,7 +400,7 @@ func TestMemoryLeakNestedCalls(t *testing.T) {
|
||||||
iterations := 200
|
iterations := 200
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newMemTestContext("mem-test-nested", "tests.create")
|
ctx := newMemTestContext("mem-test-nested", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "deep_nested_call"},
|
{Role: "user", Content: "deep_nested_call"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -459,7 +459,7 @@ func TestMemoryLeakNestedConcurrent(t *testing.T) {
|
||||||
// Warm up
|
// Warm up
|
||||||
for i := 0; i < 20; i++ {
|
for i := 0; i < 20; i++ {
|
||||||
ctx := newMemTestContext("warmup", "tests.create")
|
ctx := newMemTestContext("warmup", "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "nested_script_call"},
|
{Role: "user", Content: "nested_script_call"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -482,7 +482,7 @@ func TestMemoryLeakNestedConcurrent(t *testing.T) {
|
||||||
defer func() { done <- true }()
|
defer func() { done <- true }()
|
||||||
for i := 0; i < iterPerGoroutine; i++ {
|
for i := 0; i < iterPerGoroutine; i++ {
|
||||||
ctx := newMemTestContext("mem-test-nested-concurrent", "tests.create")
|
ctx := newMemTestContext("mem-test-nested-concurrent", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "deep_nested_call"},
|
{Role: "user", Content: "deep_nested_call"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -549,7 +549,7 @@ func TestIsolateDisposal(t *testing.T) {
|
||||||
iterations := 100
|
iterations := 100
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newMemTestContext("disposal-test", "tests.create")
|
ctx := newMemTestContext("disposal-test", "tests.create")
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -615,7 +615,6 @@ func newMemTestContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ func TestNestedScriptCall(t *testing.T) {
|
||||||
|
|
||||||
// Call with deep_nested_call scenario
|
// Call with deep_nested_call scenario
|
||||||
// This will: hook -> scripts.tests.create.NestedCall -> GetRoles -> model
|
// This will: hook -> scripts.tests.create.NestedCall -> GetRoles -> model
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{
|
res, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "deep_nested_call"},
|
{Role: "user", Content: "deep_nested_call"},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -86,7 +86,7 @@ func TestNestedScriptCallConcurrent(t *testing.T) {
|
||||||
for j := 0; j < iterations; j++ {
|
for j := 0; j < iterations; j++ {
|
||||||
ctx := newTestContext("test-concurrent", "tests.create")
|
ctx := newTestContext("test-concurrent", "tests.create")
|
||||||
|
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "deep_nested_call"},
|
{Role: "user", Content: "deep_nested_call"},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ func newTestContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
@ -74,7 +73,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 1: Return null (should get nil response)
|
// Test scenario 1: Return null (should get nil response)
|
||||||
t.Run("ReturnNull", func(t *testing.T) {
|
t.Run("ReturnNull", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_null"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_null"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with null return: %s", err.Error())
|
t.Fatalf("Failed to create with null return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +84,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 2: Return undefined (should get nil response)
|
// Test scenario 2: Return undefined (should get nil response)
|
||||||
t.Run("ReturnUndefined", func(t *testing.T) {
|
t.Run("ReturnUndefined", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_undefined"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_undefined"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with undefined return: %s", err.Error())
|
t.Fatalf("Failed to create with undefined return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -96,7 +95,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 3: Return empty object (should get empty HookCreateResponse)
|
// Test scenario 3: Return empty object (should get empty HookCreateResponse)
|
||||||
t.Run("ReturnEmpty", func(t *testing.T) {
|
t.Run("ReturnEmpty", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_empty"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_empty"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with empty return: %s", err.Error())
|
t.Fatalf("Failed to create with empty return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +109,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 4: Return full response with all fields
|
// Test scenario 4: Return full response with all fields
|
||||||
t.Run("ReturnFull", func(t *testing.T) {
|
t.Run("ReturnFull", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_full"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_full"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with full return: %s", err.Error())
|
t.Fatalf("Failed to create with full return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +165,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 5: Return partial response
|
// Test scenario 5: Return partial response
|
||||||
t.Run("ReturnPartial", func(t *testing.T) {
|
t.Run("ReturnPartial", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_partial"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_partial"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with partial return: %s", err.Error())
|
t.Fatalf("Failed to create with partial return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +196,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 6: Process call - calls models.__yao.role.Get and adds to messages
|
// Test scenario 6: Process call - calls models.__yao.role.Get and adds to messages
|
||||||
t.Run("ReturnProcess", func(t *testing.T) {
|
t.Run("ReturnProcess", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_process"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "return_process"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with process return: %s", err.Error())
|
t.Fatalf("Failed to create with process return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -225,7 +224,7 @@ func TestCreate(t *testing.T) {
|
||||||
// Test scenario 7: Default response
|
// Test scenario 7: Default response
|
||||||
t.Run("ReturnDefault", func(t *testing.T) {
|
t.Run("ReturnDefault", func(t *testing.T) {
|
||||||
testContent := "Hello, how are you?"
|
testContent := "Hello, how are you?"
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: testContent}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: testContent}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with default return: %s", err.Error())
|
t.Fatalf("Failed to create with default return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +251,7 @@ func TestCreate(t *testing.T) {
|
||||||
|
|
||||||
// Test scenario 8: Verify context fields - validates all context fields in JavaScript
|
// Test scenario 8: Verify context fields - validates all context fields in JavaScript
|
||||||
t.Run("VerifyContext", func(t *testing.T) {
|
t.Run("VerifyContext", func(t *testing.T) {
|
||||||
res, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "verify_context"}})
|
res, _, err := agent.Script.Create(ctx, []context.Message{{Role: "user", Content: "verify_context"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with verify_context: %s", err.Error())
|
t.Fatalf("Failed to create with verify_context: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -304,7 +303,7 @@ func TestCreate(t *testing.T) {
|
||||||
adjustCtx := newTestContext("chat-test-adjust", "tests.create")
|
adjustCtx := newTestContext("chat-test-adjust", "tests.create")
|
||||||
|
|
||||||
// Call the hook which should adjust context fields
|
// Call the hook which should adjust context fields
|
||||||
res, err := agent.Script.Create(adjustCtx, []context.Message{{Role: "user", Content: "adjust_context"}})
|
res, _, err := agent.Script.Create(adjustCtx, []context.Message{{Role: "user", Content: "adjust_context"}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create with adjust_context: %s", err.Error())
|
t.Fatalf("Failed to create with adjust_context: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -313,9 +312,7 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the response contains adjusted fields
|
// Verify the response contains adjusted fields
|
||||||
if res.AssistantID != "adjusted.assistant" {
|
// Note: AssistantID cannot be overridden by hooks, removed from HookCreateResponse
|
||||||
t.Errorf("Expected adjusted assistant_id 'adjusted.assistant', got: %s", res.AssistantID)
|
|
||||||
}
|
|
||||||
if res.Connector != "adjusted-connector" {
|
if res.Connector != "adjusted-connector" {
|
||||||
t.Errorf("Expected adjusted connector 'adjusted-connector', got: %s", res.Connector)
|
t.Errorf("Expected adjusted connector 'adjusted-connector', got: %s", res.Connector)
|
||||||
}
|
}
|
||||||
|
|
@ -338,12 +335,8 @@ func TestCreate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify context fields were actually updated
|
// Verify context fields were actually updated
|
||||||
if adjustCtx.AssistantID != "adjusted.assistant" {
|
// Note: AssistantID is immutable and cannot be overridden
|
||||||
t.Errorf("Context assistant_id not updated. Expected 'adjusted.assistant', got: %s", adjustCtx.AssistantID)
|
// Note: Connector is now in Options, not in Context
|
||||||
}
|
|
||||||
if adjustCtx.Connector != "adjusted-connector" {
|
|
||||||
t.Errorf("Context connector not updated. Expected 'adjusted-connector', got: %s", adjustCtx.Connector)
|
|
||||||
}
|
|
||||||
if adjustCtx.Locale != "zh-cn" {
|
if adjustCtx.Locale != "zh-cn" {
|
||||||
t.Errorf("Context locale not updated. Expected 'zh-cn', got: %s", adjustCtx.Locale)
|
t.Errorf("Context locale not updated. Expected 'zh-cn', got: %s", adjustCtx.Locale)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func TestGoroutineLeakDetailed(t *testing.T) {
|
||||||
for i := 0; i < iterations; i++ {
|
for i := 0; i < iterations; i++ {
|
||||||
ctx := newLeakTestContext(fmt.Sprintf("leak-test-%d", i), "tests.create")
|
ctx := newLeakTestContext(fmt.Sprintf("leak-test-%d", i), "tests.create")
|
||||||
|
|
||||||
_, err := agent.Script.Create(ctx, []context.Message{
|
_, _, err := agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -126,7 +126,7 @@ func TestGoroutineLeakByComponent(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newLeakTestContext(fmt.Sprintf("test-%d", i), "tests.create")
|
ctx := newLeakTestContext(fmt.Sprintf("test-%d", i), "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
ctx.Release()
|
ctx.Release()
|
||||||
|
|
@ -184,7 +184,7 @@ func TestGoroutineLeakWithoutRelease(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newLeakTestContext(fmt.Sprintf("no-release-%d", i), "tests.create")
|
ctx := newLeakTestContext(fmt.Sprintf("no-release-%d", i), "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
// Intentionally NOT calling ctx.Release()
|
// Intentionally NOT calling ctx.Release()
|
||||||
|
|
@ -205,7 +205,7 @@ func TestGoroutineLeakWithoutRelease(t *testing.T) {
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
ctx := newLeakTestContext(fmt.Sprintf("with-release-%d", i), "tests.create")
|
ctx := newLeakTestContext(fmt.Sprintf("with-release-%d", i), "tests.create")
|
||||||
_, _ = agent.Script.Create(ctx, []context.Message{
|
_, _, _ = agent.Script.Create(ctx, []context.Message{
|
||||||
{Role: "user", Content: "Hello"},
|
{Role: "user", Content: "Hello"},
|
||||||
})
|
})
|
||||||
ctx.Release() // WITH Release
|
ctx.Release() // WITH Release
|
||||||
|
|
@ -299,7 +299,6 @@ func newLeakTestContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Next next hook for the next action after the completion
|
// Next next hook for the next action after the completion
|
||||||
func (s *Script) Next(ctx *context.Context, payload *context.NextHookPayload) (*context.NextHookResponse, error) {
|
// opts is optional - if provided, will be passed to the hook
|
||||||
|
func (s *Script) Next(ctx *context.Context, payload *context.NextHookPayload, opts ...*context.Options) (*context.NextHookResponse, *context.Options, error) {
|
||||||
|
// Get or create options
|
||||||
|
var options *context.Options
|
||||||
|
if len(opts) > 0 && opts[0] != nil {
|
||||||
|
options = opts[0]
|
||||||
|
} else {
|
||||||
|
options = &context.Options{}
|
||||||
|
}
|
||||||
|
|
||||||
// Convert payload to map for JS (use JSON tag names)
|
// Convert payload to map for JS (use JSON tag names)
|
||||||
payloadMap := map[string]interface{}{
|
payloadMap := map[string]interface{}{
|
||||||
"messages": payload.Messages,
|
"messages": payload.Messages,
|
||||||
|
|
@ -18,12 +27,19 @@ func (s *Script) Next(ctx *context.Context, payload *context.NextHookPayload) (*
|
||||||
"error": payload.Error,
|
"error": payload.Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := s.Execute(ctx, "Next", payloadMap)
|
// Execute hook with ctx, payload, and options (convert options to map for JS)
|
||||||
|
optionsMap := options.ToMap()
|
||||||
|
res, err := s.Execute(ctx, "Next", payloadMap, optionsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.getNextHookResponse(res)
|
response, err := s.getNextHookResponse(res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNextHookResponse convert the result to a NextHookResponse
|
// getNextHookResponse convert the result to a NextHookResponse
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ func newTestContextForNext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
@ -86,7 +85,7 @@ func TestNext(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook with null return: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook with null return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +105,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook with undefined return: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook with undefined return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -126,7 +125,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook with empty return: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook with empty return: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +151,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook with custom data: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook with custom data: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +198,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +244,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook with delegate: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook with delegate: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +306,7 @@ func TestNext(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +365,7 @@ func TestNext(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -410,7 +409,7 @@ func TestNext(t *testing.T) {
|
||||||
Error: "Tool execution failed: timeout",
|
Error: "Tool execution failed: timeout",
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := agent.Script.Next(ctx, payload)
|
res, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
t.Fatalf("Failed to execute Next hook: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ func newRealWorldNextContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
@ -76,7 +75,7 @@ func TestRealWorldNextStandard(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +117,7 @@ func TestRealWorldNextCustomData(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +164,7 @@ func TestRealWorldNextDelegate(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +226,7 @@ func TestRealWorldNextProcessTools(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -280,7 +279,7 @@ func TestRealWorldNextErrorRecovery(t *testing.T) {
|
||||||
Error: "System error: Database connection timeout",
|
Error: "System error: Database connection timeout",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +328,7 @@ func TestRealWorldNextConditional(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +361,7 @@ func TestRealWorldNextConditional(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -406,7 +405,7 @@ func TestRealWorldNextDefault(t *testing.T) {
|
||||||
Error: "",
|
Error: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Next(ctx, payload)
|
response, _, err := agent.Script.Next(ctx, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Next hook failed: %v", err)
|
t.Fatalf("Next hook failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func TestRealWorldSimpleScenario(t *testing.T) {
|
||||||
{Role: "user", Content: "simple"},
|
{Role: "user", Content: "simple"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create failed: %v", err)
|
t.Fatalf("Create failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ func TestRealWorldMCPScenarios(t *testing.T) {
|
||||||
{Role: "user", Content: "mcp_health"},
|
{Role: "user", Content: "mcp_health"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create failed: %v", err)
|
t.Fatalf("Create failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ func TestRealWorldMCPScenarios(t *testing.T) {
|
||||||
{Role: "user", Content: "mcp_tools"},
|
{Role: "user", Content: "mcp_tools"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create failed: %v", err)
|
t.Fatalf("Create failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +162,7 @@ func TestRealWorldMCPScenarios(t *testing.T) {
|
||||||
ctx := newRealWorldContext("test-full-workflow", "tests.realworld")
|
ctx := newRealWorldContext("test-full-workflow", "tests.realworld")
|
||||||
|
|
||||||
// Initialize stack for trace
|
// Initialize stack for trace
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
defer done()
|
defer done()
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
|
|
@ -170,7 +170,7 @@ func TestRealWorldMCPScenarios(t *testing.T) {
|
||||||
{Role: "user", Content: "full_workflow"},
|
{Role: "user", Content: "full_workflow"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create failed: %v", err)
|
t.Fatalf("Create failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +237,7 @@ func TestRealWorldTraceIntensive(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := newRealWorldContext("test-trace-intensive", "tests.realworld")
|
ctx := newRealWorldContext("test-trace-intensive", "tests.realworld")
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
defer done()
|
defer done()
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
|
|
@ -245,7 +245,7 @@ func TestRealWorldTraceIntensive(t *testing.T) {
|
||||||
{Role: "user", Content: "trace_intensive"},
|
{Role: "user", Content: "trace_intensive"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Create failed: %v", err)
|
t.Fatalf("Create failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +279,7 @@ func TestRealWorldStressSimple(t *testing.T) {
|
||||||
{Role: "user", Content: "simple"},
|
{Role: "user", Content: "simple"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Iteration %d failed: %v", i, err)
|
t.Fatalf("Iteration %d failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -338,14 +338,14 @@ func TestRealWorldStressMCP(t *testing.T) {
|
||||||
ctx := newRealWorldContext(fmt.Sprintf("stress-mcp-%d", i), "tests.realworld")
|
ctx := newRealWorldContext(fmt.Sprintf("stress-mcp-%d", i), "tests.realworld")
|
||||||
|
|
||||||
// Initialize stack for trace
|
// Initialize stack for trace
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
messages := []context.Message{
|
messages := []context.Message{
|
||||||
{Role: "user", Content: scenario},
|
{Role: "user", Content: scenario},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Iteration %d (%s) failed: %v", i, scenario, err)
|
t.Fatalf("Iteration %d (%s) failed: %v", i, scenario, err)
|
||||||
}
|
}
|
||||||
|
|
@ -427,14 +427,14 @@ func TestRealWorldStressFullWorkflow(t *testing.T) {
|
||||||
ctx := newRealWorldContext(fmt.Sprintf("stress-workflow-%d", i), "tests.realworld")
|
ctx := newRealWorldContext(fmt.Sprintf("stress-workflow-%d", i), "tests.realworld")
|
||||||
|
|
||||||
// Initialize stack for trace
|
// Initialize stack for trace
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
messages := []context.Message{
|
messages := []context.Message{
|
||||||
{Role: "user", Content: "full_workflow"},
|
{Role: "user", Content: "full_workflow"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Iteration %d failed: %v", i, err)
|
t.Fatalf("Iteration %d failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -531,14 +531,14 @@ func TestRealWorldStressConcurrent(t *testing.T) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Initialize stack for trace
|
// Initialize stack for trace
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
messages := []context.Message{
|
messages := []context.Message{
|
||||||
{Role: "user", Content: scenario},
|
{Role: "user", Content: scenario},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors <- fmt.Errorf("goroutine %d iteration %d (%s): %v", goroutineID, i, scenario, err)
|
errors <- fmt.Errorf("goroutine %d iteration %d (%s): %v", goroutineID, i, scenario, err)
|
||||||
done()
|
done()
|
||||||
|
|
@ -658,14 +658,14 @@ func TestRealWorldStressResourceHeavy(t *testing.T) {
|
||||||
ctx := newRealWorldContext(fmt.Sprintf("stress-heavy-%d", i), "tests.realworld")
|
ctx := newRealWorldContext(fmt.Sprintf("stress-heavy-%d", i), "tests.realworld")
|
||||||
|
|
||||||
// Initialize stack for trace
|
// Initialize stack for trace
|
||||||
stack, _, done := context.EnterStack(ctx, "tests.realworld", context.RefererAPI)
|
stack, _, done := context.EnterStack(ctx, "tests.realworld", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
messages := []context.Message{
|
messages := []context.Message{
|
||||||
{Role: "user", Content: "resource_heavy"},
|
{Role: "user", Content: "resource_heavy"},
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := agent.Script.Create(ctx, messages)
|
response, _, err := agent.Script.Create(ctx, messages)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Iteration %d failed: %v", i, err)
|
t.Fatalf("Iteration %d failed: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -726,7 +726,6 @@ func newRealWorldContext(chatID, assistantID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "gpt-4o",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,6 @@ func newStoreTestContext(chatID, assistantID string) *context.Context {
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
@ -269,7 +268,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("test-chat-id", assistantID)
|
ctx := newStoreTestContext("test-chat-id", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err, "Create hook should execute without error")
|
require.NoError(t, err, "Create hook should execute without error")
|
||||||
require.NotNil(t, res, "Create hook should return a response")
|
require.NotNil(t, res, "Create hook should return a response")
|
||||||
|
|
||||||
|
|
@ -576,7 +575,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("test-chat-all-fields", assistantID)
|
ctx := newStoreTestContext("test-chat-all-fields", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Test message"}}
|
messages := []context.Message{{Role: "user", Content: "Test message"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err, "Create hook should execute without error")
|
require.NoError(t, err, "Create hook should execute without error")
|
||||||
require.NotNil(t, res, "Create hook should return a response")
|
require.NotNil(t, res, "Create hook should return a response")
|
||||||
|
|
||||||
|
|
@ -691,7 +690,7 @@ function Create(ctx: CreateContext, messages: Message[]): CreateResponse | null
|
||||||
{Role: "user", Content: "How are you?"},
|
{Role: "user", Content: "How are you?"},
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err, "TypeScript Create hook should execute without error")
|
require.NoError(t, err, "TypeScript Create hook should execute without error")
|
||||||
require.NotNil(t, res, "Create hook should return a response")
|
require.NotNil(t, res, "Create hook should return a response")
|
||||||
|
|
||||||
|
|
@ -759,7 +758,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("null-test-chat", assistantID)
|
ctx := newStoreTestContext("null-test-chat", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err, "Hook returning null should not error")
|
require.NoError(t, err, "Hook returning null should not error")
|
||||||
assert.Nil(t, res, "Hook returning null should return nil response")
|
assert.Nil(t, res, "Hook returning null should return nil response")
|
||||||
}
|
}
|
||||||
|
|
@ -832,7 +831,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("preset-test-1", assistantID)
|
ctx := newStoreTestContext("preset-test-1", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Be friendly please"}}
|
messages := []context.Message{{Role: "user", Content: "Be friendly please"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
assert.Equal(t, "friendly", res.PromptPreset)
|
assert.Equal(t, "friendly", res.PromptPreset)
|
||||||
|
|
@ -843,7 +842,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("preset-test-2", assistantID)
|
ctx := newStoreTestContext("preset-test-2", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Be professional"}}
|
messages := []context.Message{{Role: "user", Content: "Be professional"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
assert.Equal(t, "professional", res.PromptPreset)
|
assert.Equal(t, "professional", res.PromptPreset)
|
||||||
|
|
@ -854,7 +853,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("preset-test-3", assistantID)
|
ctx := newStoreTestContext("preset-test-3", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
messages := []context.Message{{Role: "user", Content: "Hello"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, res)
|
assert.Nil(t, res)
|
||||||
})
|
})
|
||||||
|
|
@ -916,7 +915,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("disable-test-1", assistantID)
|
ctx := newStoreTestContext("disable-test-1", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "disable_global prompts"}}
|
messages := []context.Message{{Role: "user", Content: "disable_global prompts"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
require.NotNil(t, res.DisableGlobalPrompts)
|
require.NotNil(t, res.DisableGlobalPrompts)
|
||||||
|
|
@ -928,7 +927,7 @@ function Create(ctx: any, messages: any[]): any {
|
||||||
ctx := newStoreTestContext("disable-test-2", assistantID)
|
ctx := newStoreTestContext("disable-test-2", assistantID)
|
||||||
messages := []context.Message{{Role: "user", Content: "enable_global prompts"}}
|
messages := []context.Message{{Role: "user", Content: "enable_global prompts"}}
|
||||||
|
|
||||||
res, err := loaded.Script.Create(ctx, messages)
|
res, _, err := loaded.Script.Create(ctx, messages, &context.Options{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res)
|
require.NotNil(t, res)
|
||||||
require.NotNil(t, res.DisableGlobalPrompts)
|
require.NotNil(t, res.DisableGlobalPrompts)
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,10 @@ func (ast *Assistant) handleDelegation(
|
||||||
// 2. Execute with the same Context (preserving ID, Space, Writer, etc.)
|
// 2. Execute with the same Context (preserving ID, Space, Writer, etc.)
|
||||||
// 3. Call done() to pop from Stack when finished
|
// 3. Call done() to pop from Stack when finished
|
||||||
// This ensures proper Stack tracing: parent assistant -> delegated assistant
|
// This ensures proper Stack tracing: parent assistant -> delegated assistant
|
||||||
return targetAssistant.Stream(ctx, delegate.Messages, streamHandler)
|
|
||||||
|
// Convert options map from delegate config to Options struct
|
||||||
|
delegateOpts := agentContext.OptionsFromMap(delegate.Options)
|
||||||
|
return targetAssistant.Stream(ctx, delegate.Messages, delegateOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildStandardResponse builds the standard agent response when no custom Next hook processing is needed
|
// buildStandardResponse builds the standard agent response when no custom Next hook processing is needed
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ func newTestContext(capabilities *openai.Capabilities) *agentContext.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: "test-chat",
|
ChatID: "test-chat",
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: "openai",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: agentContext.Client{
|
Client: agentContext.Client{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
|
|
||||||
// AgentCaller interface for calling agents (to avoid circular dependency)
|
// AgentCaller interface for calling agents (to avoid circular dependency)
|
||||||
type AgentCaller interface {
|
type AgentCaller interface {
|
||||||
Stream(ctx *agentContext.Context, messages []agentContext.Message) (interface{}, error)
|
Stream(ctx *agentContext.Context, messages []agentContext.Message, options ...*agentContext.Options) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AgentGetterFunc is a function type that gets an agent by ID
|
// AgentGetterFunc is a function type that gets an agent by ID
|
||||||
|
|
@ -35,12 +35,10 @@ func CallAgent(ctx *agentContext.Context, agentID string, message agentContext.M
|
||||||
// Call the agent with the message
|
// Call the agent with the message
|
||||||
messages := []agentContext.Message{message}
|
messages := []agentContext.Message{message}
|
||||||
|
|
||||||
connectorBackup := ctx.Connector
|
// Note: Connector is now in Options (call-level parameter), not Context
|
||||||
ctx.Connector = ""
|
// For A2A calls, we use an empty Connector to let the agent use its default
|
||||||
defer func() {
|
opts := &agentContext.Options{Skip: &agentContext.Skip{History: true}, Writer: nil} // Skip history and output to the caller
|
||||||
ctx.Connector = connectorBackup
|
response, err := agent.Stream(ctx, messages, opts)
|
||||||
}()
|
|
||||||
response, err := agent.Stream(ctx, messages)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to call agent %s: %w", agentID, err)
|
return "", fmt.Errorf("failed to call agent %s: %w", agentID, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -259,23 +259,6 @@ func (ctx *Context) Map() map[string]interface{} {
|
||||||
if ctx.AssistantID != "" {
|
if ctx.AssistantID != "" {
|
||||||
data["assistant_id"] = ctx.AssistantID
|
data["assistant_id"] = ctx.AssistantID
|
||||||
}
|
}
|
||||||
if ctx.Connector != "" {
|
|
||||||
data["connector"] = ctx.Connector
|
|
||||||
}
|
|
||||||
if ctx.Search != nil {
|
|
||||||
data["search"] = *ctx.Search
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arguments for call
|
|
||||||
if len(ctx.Args) > 0 {
|
|
||||||
data["args"] = ctx.Args
|
|
||||||
}
|
|
||||||
if ctx.Retry {
|
|
||||||
data["retry"] = ctx.Retry
|
|
||||||
}
|
|
||||||
if ctx.RetryTimes > 0 {
|
|
||||||
data["retry_times"] = ctx.RetryTimes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locale information
|
// Locale information
|
||||||
if ctx.Locale != "" {
|
if ctx.Locale != "" {
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func TestGetCompletionRequest(t *testing.T) {
|
||||||
c.Request = req
|
c.Request = req
|
||||||
|
|
||||||
// Call GetCompletionRequest
|
// Call GetCompletionRequest
|
||||||
completionReq, ctx, err := GetCompletionRequest(c, cache)
|
completionReq, ctx, opts, err := GetCompletionRequest(c, cache)
|
||||||
|
|
||||||
if tt.expectError {
|
if tt.expectError {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
@ -189,6 +189,7 @@ func TestGetCompletionRequest(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, completionReq)
|
assert.NotNil(t, completionReq)
|
||||||
assert.NotNil(t, ctx)
|
assert.NotNil(t, ctx)
|
||||||
|
assert.NotNil(t, opts)
|
||||||
|
|
||||||
// Verify CompletionRequest
|
// Verify CompletionRequest
|
||||||
assert.Equal(t, tt.expectedModel, completionReq.Model)
|
assert.Equal(t, tt.expectedModel, completionReq.Model)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ func newTestContextWithInterrupt(chatID, assistantID string) *Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: assistantID,
|
AssistantID: assistantID,
|
||||||
Connector: "",
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: Client{
|
Client: Client{
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,6 @@ func (ctx *Context) NewObject(v8ctx *v8go.Context) (*v8go.Value, error) {
|
||||||
// Set primitive fields in template
|
// Set primitive fields in template
|
||||||
jsObject.Set("chat_id", ctx.ChatID)
|
jsObject.Set("chat_id", ctx.ChatID)
|
||||||
jsObject.Set("assistant_id", ctx.AssistantID)
|
jsObject.Set("assistant_id", ctx.AssistantID)
|
||||||
jsObject.Set("connector", ctx.Connector)
|
|
||||||
if ctx.Search != nil {
|
|
||||||
jsObject.Set("search", *ctx.Search)
|
|
||||||
}
|
|
||||||
|
|
||||||
jsObject.Set("retry", ctx.Retry)
|
|
||||||
jsObject.Set("retry_times", uint32(ctx.RetryTimes))
|
|
||||||
jsObject.Set("locale", ctx.Locale)
|
jsObject.Set("locale", ctx.Locale)
|
||||||
jsObject.Set("theme", ctx.Theme)
|
jsObject.Set("theme", ctx.Theme)
|
||||||
jsObject.Set("referer", ctx.Referer)
|
jsObject.Set("referer", ctx.Referer)
|
||||||
|
|
@ -97,15 +90,6 @@ func (ctx *Context) NewObject(v8ctx *v8go.Context) (*v8go.Value, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set complex objects (maps, arrays) after instance creation using bridge
|
// Set complex objects (maps, arrays) after instance creation using bridge
|
||||||
// Args array
|
|
||||||
if ctx.Args != nil {
|
|
||||||
argsVal, err := bridge.JsValue(v8ctx, ctx.Args)
|
|
||||||
if err == nil {
|
|
||||||
obj.Set("args", argsVal)
|
|
||||||
argsVal.Release() // Release Go-side Persistent handle, V8 internal reference remains
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client object
|
// Client object
|
||||||
clientData := map[string]interface{}{
|
clientData := map[string]interface{}{
|
||||||
"type": ctx.Client.Type,
|
"type": ctx.Client.Type,
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,9 @@ func TestMCPListResources(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -66,8 +67,9 @@ func TestMCPReadResource(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -108,8 +110,9 @@ func TestMCPListTools(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -154,8 +157,9 @@ func TestMCPCallTool(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -196,8 +200,9 @@ func TestMCPCallTools(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -243,8 +248,9 @@ func TestMCPCallToolsParallel(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -290,8 +296,9 @@ func TestMCPListPrompts(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -334,8 +341,9 @@ func TestMCPGetPrompt(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -376,8 +384,9 @@ func TestMCPListSamples(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -418,8 +427,9 @@ func TestMCPGetSample(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -462,8 +472,9 @@ func TestMCPJsApiWithTrace(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
stack, _, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ func TestContextRelease(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -75,10 +76,11 @@ func TestTraceRelease(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -137,10 +139,11 @@ func TestContextReleaseWithTrace(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -185,10 +188,11 @@ func TestTryFinallyPattern(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
res, err := v8.Call(v8.CallOptions{}, `
|
res, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -287,10 +291,11 @@ func TestTryFinallyPatternWithError(t *testing.T) {
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,8 @@ func TestStressContextCreationAndRelease(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
cxt.Referer = context.RefererAPI
|
||||||
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -111,10 +112,11 @@ func TestStressTraceOperations(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
_, err := v8.Call(v8.CallOptions{}, fmt.Sprintf(`
|
_, err := v8.Call(v8.CallOptions{}, fmt.Sprintf(`
|
||||||
function test(ctx) {
|
function test(ctx) {
|
||||||
|
|
@ -188,9 +190,10 @@ func TestStressMCPOperations(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
startMemory := getMemStats()
|
startMemory := getMemStats()
|
||||||
|
|
@ -271,9 +274,10 @@ func TestStressConcurrentContexts(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -421,9 +425,10 @@ func TestStressReleasePatterns(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -459,9 +464,10 @@ func TestStressReleasePatterns(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -499,9 +505,10 @@ func TestStressReleasePatterns(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
_, err := v8.Call(v8.CallOptions{}, `
|
_, err := v8.Call(v8.CallOptions{}, `
|
||||||
|
|
@ -544,9 +551,10 @@ func TestStressLongRunningTrace(t *testing.T) {
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, _ := context.EnterStack(cxt, "test-assistant", context.RefererAPI)
|
stack, _, _ := context.EnterStack(cxt, "test-assistant", &context.Options{})
|
||||||
cxt.Stack = stack
|
cxt.Stack = stack
|
||||||
|
|
||||||
startMemory := getMemStats()
|
startMemory := getMemStats()
|
||||||
|
|
|
||||||
|
|
@ -219,15 +219,9 @@ func TestJsValueAllFields(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
searchTrue := true
|
|
||||||
cxt := &context.Context{
|
cxt := &context.Context{
|
||||||
ChatID: "test-chat-id",
|
ChatID: "test-chat-id",
|
||||||
AssistantID: "test-assistant-id",
|
AssistantID: "test-assistant-id",
|
||||||
Connector: "test-connector",
|
|
||||||
Search: &searchTrue,
|
|
||||||
Args: []interface{}{"arg1", "arg2", 123},
|
|
||||||
Retry: true,
|
|
||||||
RetryTimes: 3,
|
|
||||||
Locale: "zh-cn",
|
Locale: "zh-cn",
|
||||||
Theme: "dark",
|
Theme: "dark",
|
||||||
Context: stdContext.Background(),
|
Context: stdContext.Background(),
|
||||||
|
|
@ -279,21 +273,12 @@ func TestJsValueAllFields(t *testing.T) {
|
||||||
// Verify all fields
|
// Verify all fields
|
||||||
assert.Equal(t, "test-chat-id", result["chat_id"], "chat_id mismatch")
|
assert.Equal(t, "test-chat-id", result["chat_id"], "chat_id mismatch")
|
||||||
assert.Equal(t, "test-assistant-id", result["assistant_id"], "assistant_id mismatch")
|
assert.Equal(t, "test-assistant-id", result["assistant_id"], "assistant_id mismatch")
|
||||||
assert.Equal(t, "test-connector", result["connector"], "connector mismatch")
|
|
||||||
assert.Equal(t, true, result["search"], "search mismatch")
|
|
||||||
assert.Equal(t, true, result["retry"], "retry mismatch")
|
|
||||||
assert.Equal(t, float64(3), result["retry_times"], "retry_times mismatch")
|
|
||||||
assert.Equal(t, "zh-cn", result["locale"], "locale mismatch")
|
assert.Equal(t, "zh-cn", result["locale"], "locale mismatch")
|
||||||
assert.Equal(t, "dark", result["theme"], "theme mismatch")
|
assert.Equal(t, "dark", result["theme"], "theme mismatch")
|
||||||
assert.Equal(t, "api", result["referer"], "referer mismatch")
|
assert.Equal(t, "api", result["referer"], "referer mismatch")
|
||||||
assert.Equal(t, "cui-web", result["accept"], "accept mismatch")
|
assert.Equal(t, "cui-web", result["accept"], "accept mismatch")
|
||||||
assert.Equal(t, "/dashboard/home", result["route"], "route mismatch")
|
assert.Equal(t, "/dashboard/home", result["route"], "route mismatch")
|
||||||
|
|
||||||
// Verify args array
|
|
||||||
args, ok := result["args"].([]interface{})
|
|
||||||
assert.True(t, ok, "args should be an array")
|
|
||||||
assert.Equal(t, 3, len(args), "args length mismatch")
|
|
||||||
|
|
||||||
// Verify client object
|
// Verify client object
|
||||||
client, ok := result["client"].(map[string]interface{})
|
client, ok := result["client"].(map[string]interface{})
|
||||||
assert.True(t, ok, "client should be an object")
|
assert.True(t, ok, "client should be an object")
|
||||||
|
|
@ -373,21 +358,6 @@ func testAllFieldsFunction(info *v8go.FunctionCallbackInfo) *v8go.Value {
|
||||||
if val, ok := getField("assistant_id"); ok {
|
if val, ok := getField("assistant_id"); ok {
|
||||||
result["assistant_id"] = val
|
result["assistant_id"] = val
|
||||||
}
|
}
|
||||||
if val, ok := getField("connector"); ok {
|
|
||||||
result["connector"] = val
|
|
||||||
}
|
|
||||||
if val, ok := getField("search"); ok {
|
|
||||||
result["search"] = val
|
|
||||||
}
|
|
||||||
if val, ok := getField("args"); ok {
|
|
||||||
result["args"] = val
|
|
||||||
}
|
|
||||||
if val, ok := getField("retry"); ok {
|
|
||||||
result["retry"] = val
|
|
||||||
}
|
|
||||||
if val, ok := getField("retry_times"); ok {
|
|
||||||
result["retry_times"] = val
|
|
||||||
}
|
|
||||||
if val, ok := getField("locale"); ok {
|
if val, ok := getField("locale"); ok {
|
||||||
result["locale"] = val
|
result["locale"] = val
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,11 @@ func newTestMCPContext() *context.Context {
|
||||||
ChatID: "test-chat",
|
ChatID: "test-chat",
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Locale: "en",
|
Locale: "en",
|
||||||
|
Referer: context.RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize stack and trace
|
// Initialize stack and trace
|
||||||
stack, traceID, _ := context.EnterStack(ctx, "test-assistant", context.RefererAPI)
|
stack, traceID, _ := context.EnterStack(ctx, "test-assistant", &context.Options{})
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
_ = traceID // traceID is set in stack
|
_ = traceID // traceID is set in stack
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCompletionRequest parse completion request and create context from openapi request
|
// GetCompletionRequest parse completion request and create context from openapi request
|
||||||
// Returns: *CompletionRequest, *Context, error
|
// Returns: *CompletionRequest, *Context, *Options, error
|
||||||
func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest, *Context, error) {
|
func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest, *Context, *Options, error) {
|
||||||
// Get authorized information
|
// Get authorized information
|
||||||
authInfo := authorized.GetInfo(c)
|
authInfo := authorized.GetInfo(c)
|
||||||
|
|
||||||
// Parse completion request from payload or query first
|
// Parse completion request from payload or query first
|
||||||
completionReq, err := parseCompletionRequestData(c)
|
completionReq, err := parseCompletionRequestData(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to parse completion request: %w", err)
|
return nil, nil, nil, fmt.Errorf("failed to parse completion request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract assistant ID using completionReq (can extract from model field)
|
// Extract assistant ID using completionReq (can extract from model field)
|
||||||
assistantID, err := GetAssistantID(c, completionReq)
|
assistantID, err := GetAssistantID(c, completionReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to get assistant ID: %w", err)
|
return nil, nil, nil, fmt.Errorf("failed to get assistant ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract chat ID (may generate from messages if not provided)
|
// Extract chat ID (may generate from messages if not provided)
|
||||||
|
|
@ -47,26 +47,10 @@ func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest
|
||||||
// Create context with unique ID using New() to ensure proper initialization
|
// Create context with unique ID using New() to ensure proper initialization
|
||||||
ctx := New(c.Request.Context(), authInfo, chatID)
|
ctx := New(c.Request.Context(), authInfo, chatID)
|
||||||
|
|
||||||
// Set additional fields
|
// Set context fields (session-level state)
|
||||||
ctx.Cache = cache
|
ctx.Cache = cache
|
||||||
ctx.Writer = c.Writer
|
ctx.Writer = c.Writer
|
||||||
ctx.AssistantID = assistantID
|
ctx.AssistantID = assistantID
|
||||||
|
|
||||||
// Try to extract custom connector from model field
|
|
||||||
// If model is a valid connector ID, set it to ctx.Connector
|
|
||||||
// Otherwise, keep the standard OpenAI-compatible behavior (model as assistant ID)
|
|
||||||
if completionReq != nil && completionReq.Model != "" {
|
|
||||||
// Check if model is a valid connector (not containing "-yao_" which indicates assistant ID format)
|
|
||||||
if !strings.Contains(completionReq.Model, "-yao_") {
|
|
||||||
// Try to validate if it's a real connector
|
|
||||||
if _, err := connector.Select(completionReq.Model); err == nil {
|
|
||||||
// It's a valid connector, use it
|
|
||||||
ctx.Connector = completionReq.Model
|
|
||||||
}
|
|
||||||
// If not a valid connector, ignore it (keep ctx.Connector empty to use assistant's default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Locale = GetLocale(c, completionReq)
|
ctx.Locale = GetLocale(c, completionReq)
|
||||||
ctx.Theme = GetTheme(c, completionReq)
|
ctx.Theme = GetTheme(c, completionReq)
|
||||||
ctx.Referer = GetReferer(c, completionReq)
|
ctx.Referer = GetReferer(c, completionReq)
|
||||||
|
|
@ -78,14 +62,34 @@ func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest
|
||||||
}
|
}
|
||||||
ctx.Route = GetRoute(c, completionReq)
|
ctx.Route = GetRoute(c, completionReq)
|
||||||
ctx.Metadata = GetMetadata(c, completionReq)
|
ctx.Metadata = GetMetadata(c, completionReq)
|
||||||
ctx.Skip = GetSkip(c, completionReq)
|
|
||||||
|
// Create Options (call-level parameters)
|
||||||
|
opts := &Options{
|
||||||
|
Context: c.Request.Context(),
|
||||||
|
Skip: GetSkip(c, completionReq),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to extract custom connector from model field
|
||||||
|
// If model is a valid connector ID, set it to opts.Connector
|
||||||
|
// Otherwise, keep the standard OpenAI-compatible behavior (model as assistant ID)
|
||||||
|
if completionReq != nil && completionReq.Model != "" {
|
||||||
|
// Check if model is a valid connector (not containing "-yao_" which indicates assistant ID format)
|
||||||
|
if !strings.Contains(completionReq.Model, "-yao_") {
|
||||||
|
// Try to validate if it's a real connector
|
||||||
|
if _, err := connector.Select(completionReq.Model); err == nil {
|
||||||
|
// It's a valid connector, use it
|
||||||
|
opts.Connector = completionReq.Model
|
||||||
|
}
|
||||||
|
// If not a valid connector, ignore it (keep opts.Connector empty to use assistant's default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize interrupt controller
|
// Initialize interrupt controller
|
||||||
ctx.Interrupt = NewInterruptController()
|
ctx.Interrupt = NewInterruptController()
|
||||||
|
|
||||||
// Register context to global registry first (required for interrupt handler callback)
|
// Register context to global registry first (required for interrupt handler callback)
|
||||||
if err := Register(ctx); err != nil {
|
if err := Register(ctx); err != nil {
|
||||||
return nil, nil, fmt.Errorf("failed to register context: %w", err)
|
return nil, nil, nil, fmt.Errorf("failed to register context: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start interrupt listener after registration
|
// Start interrupt listener after registration
|
||||||
|
|
@ -93,7 +97,7 @@ func GetCompletionRequest(c *gin.Context, cache store.Store) (*CompletionRequest
|
||||||
// HTTP context cancellation is handled by LLM/Agent layers naturally
|
// HTTP context cancellation is handled by LLM/Agent layers naturally
|
||||||
ctx.Interrupt.Start(ctx.ID)
|
ctx.Interrupt.Start(ctx.ID)
|
||||||
|
|
||||||
return completionReq, ctx, nil
|
return completionReq, ctx, opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getClientType parses the client type from User-Agent header
|
// getClientType parses the client type from User-Agent header
|
||||||
|
|
|
||||||
|
|
@ -851,7 +851,7 @@ func TestGetCompletionRequest_WriterInitialized(t *testing.T) {
|
||||||
c, _ := gin.CreateTestContext(w)
|
c, _ := gin.CreateTestContext(w)
|
||||||
c.Request = req
|
c.Request = req
|
||||||
|
|
||||||
completionReq, ctx, err := GetCompletionRequest(c, cache)
|
completionReq, ctx, opts, err := GetCompletionRequest(c, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get completion request: %v", err)
|
t.Fatalf("Failed to get completion request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -867,6 +867,11 @@ func TestGetCompletionRequest_WriterInitialized(t *testing.T) {
|
||||||
t.Error("Expected ctx.Writer to be the same as gin context writer")
|
t.Error("Expected ctx.Writer to be the same as gin context writer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that Options is initialized
|
||||||
|
if opts == nil {
|
||||||
|
t.Error("Expected opts to be initialized, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
// Check other fields
|
// Check other fields
|
||||||
if completionReq.Model != "gpt-4-yao_test" {
|
if completionReq.Model != "gpt-4-yao_test" {
|
||||||
t.Errorf("Expected model 'gpt-4-yao_test', got '%s'", completionReq.Model)
|
t.Errorf("Expected model 'gpt-4-yao_test', got '%s'", completionReq.Model)
|
||||||
|
|
@ -914,12 +919,17 @@ func TestGetCompletionRequest_ChatIDFallback(t *testing.T) {
|
||||||
c, _ := gin.CreateTestContext(w)
|
c, _ := gin.CreateTestContext(w)
|
||||||
c.Request = req
|
c.Request = req
|
||||||
|
|
||||||
_, ctx, err := GetCompletionRequest(c, cache)
|
_, ctx, opts, err := GetCompletionRequest(c, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to get completion request: %v", err)
|
t.Fatalf("Failed to get completion request: %v", err)
|
||||||
}
|
}
|
||||||
defer ctx.Release()
|
defer ctx.Release()
|
||||||
|
|
||||||
|
// Check that Options is initialized
|
||||||
|
if opts == nil {
|
||||||
|
t.Error("Expected opts to be initialized, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
// ChatID should be generated (not empty)
|
// ChatID should be generated (not empty)
|
||||||
if ctx.ChatID == "" {
|
if ctx.ChatID == "" {
|
||||||
t.Error("Expected ChatID to be generated via fallback, got empty string")
|
t.Error("Expected ChatID to be generated via fallback, got empty string")
|
||||||
|
|
|
||||||
71
agent/context/options.go
Normal file
71
agent/context/options.go
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
package context
|
||||||
|
|
||||||
|
// ToMap converts Options struct to map for JSON serialization
|
||||||
|
func (opts *Options) ToMap() map[string]interface{} {
|
||||||
|
if opts == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
|
// Add configurable fields (with json tags)
|
||||||
|
if opts.Connector != "" {
|
||||||
|
result["connector"] = opts.Connector
|
||||||
|
}
|
||||||
|
if opts.Mode != "" {
|
||||||
|
result["mode"] = opts.Mode
|
||||||
|
}
|
||||||
|
if opts.Search != nil {
|
||||||
|
result["search"] = *opts.Search
|
||||||
|
}
|
||||||
|
if opts.Skip != nil {
|
||||||
|
result["skip"] = opts.Skip
|
||||||
|
}
|
||||||
|
// Only add DisableGlobalPrompts if true (avoid false values in map)
|
||||||
|
if opts.DisableGlobalPrompts {
|
||||||
|
result["disable_global_prompts"] = opts.DisableGlobalPrompts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Runtime fields (Context, Writer) are not serialized (json:"-")
|
||||||
|
// They should not be included in the map
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionsFromMap creates Options struct from map (e.g., from JS Hook)
|
||||||
|
func OptionsFromMap(m map[string]interface{}) *Options {
|
||||||
|
if m == nil {
|
||||||
|
return &Options{}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &Options{}
|
||||||
|
|
||||||
|
// Extract configurable fields
|
||||||
|
if connector, ok := m["connector"].(string); ok {
|
||||||
|
opts.Connector = connector
|
||||||
|
}
|
||||||
|
if mode, ok := m["mode"].(string); ok {
|
||||||
|
opts.Mode = mode
|
||||||
|
}
|
||||||
|
if search, ok := m["search"].(bool); ok {
|
||||||
|
opts.Search = &search
|
||||||
|
}
|
||||||
|
if skipMap, ok := m["skip"].(map[string]interface{}); ok {
|
||||||
|
skip := &Skip{}
|
||||||
|
if history, ok := skipMap["history"].(bool); ok {
|
||||||
|
skip.History = history
|
||||||
|
}
|
||||||
|
if trace, ok := skipMap["trace"].(bool); ok {
|
||||||
|
skip.Trace = trace
|
||||||
|
}
|
||||||
|
opts.Skip = skip
|
||||||
|
}
|
||||||
|
if disableGlobalPrompts, ok := m["disable_global_prompts"].(bool); ok {
|
||||||
|
opts.DisableGlobalPrompts = disableGlobalPrompts
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Context and Writer are runtime fields, not restored from map
|
||||||
|
// They should be set by the caller if needed
|
||||||
|
|
||||||
|
return opts
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewStack creates a new root stack with the given trace ID and assistant ID
|
// NewStack creates a new root stack with the given trace ID and assistant ID
|
||||||
func NewStack(traceID, assistantID, referer string) *Stack {
|
func NewStack(traceID, assistantID, referer string, opts *Options) *Stack {
|
||||||
if traceID == "" {
|
if traceID == "" {
|
||||||
traceID = uuid.New().String()
|
traceID = uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
@ -25,13 +25,14 @@ func NewStack(traceID, assistantID, referer string) *Stack {
|
||||||
Depth: 0,
|
Depth: 0,
|
||||||
ParentID: "",
|
ParentID: "",
|
||||||
Path: []string{stackID},
|
Path: []string{stackID},
|
||||||
|
Options: opts,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
Status: StackStatusRunning,
|
Status: StackStatusRunning,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChildStack creates a child stack from the current stack
|
// NewChildStack creates a child stack from the current stack
|
||||||
func (s *Stack) NewChildStack(assistantID, referer string) *Stack {
|
func (s *Stack) NewChildStack(assistantID, referer string, opts *Options) *Stack {
|
||||||
stackID := uuid.New().String()
|
stackID := uuid.New().String()
|
||||||
now := time.Now().UnixMilli()
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
|
|
@ -48,6 +49,7 @@ func (s *Stack) NewChildStack(assistantID, referer string) *Stack {
|
||||||
Depth: s.Depth + 1,
|
Depth: s.Depth + 1,
|
||||||
ParentID: s.ID,
|
ParentID: s.ID,
|
||||||
Path: path,
|
Path: path,
|
||||||
|
Options: opts,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
Status: StackStatusRunning,
|
Status: StackStatusRunning,
|
||||||
}
|
}
|
||||||
|
|
@ -134,6 +136,7 @@ func (s *Stack) Clone() *Stack {
|
||||||
Depth: s.Depth,
|
Depth: s.Depth,
|
||||||
ParentID: s.ParentID,
|
ParentID: s.ParentID,
|
||||||
Path: make([]string, len(s.Path)),
|
Path: make([]string, len(s.Path)),
|
||||||
|
Options: s.Options, // Shallow copy of Options pointer
|
||||||
CreatedAt: s.CreatedAt,
|
CreatedAt: s.CreatedAt,
|
||||||
Status: s.Status,
|
Status: s.Status,
|
||||||
Error: s.Error,
|
Error: s.Error,
|
||||||
|
|
@ -165,14 +168,17 @@ func (s *Stack) Clone() *Stack {
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
//
|
//
|
||||||
// stack, traceID, done := context.EnterStack(ctx, assistantID, referer)
|
// stack, traceID, done := context.EnterStack(ctx, assistantID, opts)
|
||||||
// defer done()
|
// defer done()
|
||||||
// // ... your code here ...
|
// // ... your code here ...
|
||||||
func EnterStack(ctx *Context, assistantID, referer string) (*Stack, string, func()) {
|
func EnterStack(ctx *Context, assistantID string, opts *Options) (*Stack, string, func()) {
|
||||||
var stack *Stack
|
var stack *Stack
|
||||||
var parentStack *Stack
|
var parentStack *Stack
|
||||||
var traceID string
|
var traceID string
|
||||||
|
|
||||||
|
// Get referer from ctx (request source)
|
||||||
|
referer := ctx.Referer
|
||||||
|
|
||||||
// Initialize Stacks map if not exists
|
// Initialize Stacks map if not exists
|
||||||
if ctx.Stacks == nil {
|
if ctx.Stacks == nil {
|
||||||
ctx.Stacks = make(map[string]*Stack)
|
ctx.Stacks = make(map[string]*Stack)
|
||||||
|
|
@ -182,14 +188,14 @@ func EnterStack(ctx *Context, assistantID, referer string) (*Stack, string, func
|
||||||
// Create root stack for this assistant call (entry point)
|
// Create root stack for this assistant call (entry point)
|
||||||
// Generate a new trace ID for root
|
// Generate a new trace ID for root
|
||||||
traceID = trace.GenTraceID()
|
traceID = trace.GenTraceID()
|
||||||
stack = NewStack(traceID, assistantID, referer)
|
stack = NewStack(traceID, assistantID, referer, opts)
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
} else {
|
} else {
|
||||||
// Create child stack for nested agent call
|
// Create child stack for nested agent call
|
||||||
// Inherit trace ID from parent
|
// Inherit trace ID from parent
|
||||||
parentStack = ctx.Stack
|
parentStack = ctx.Stack
|
||||||
traceID = parentStack.TraceID
|
traceID = parentStack.TraceID
|
||||||
stack = ctx.Stack.NewChildStack(assistantID, referer)
|
stack = ctx.Stack.NewChildStack(assistantID, referer, opts)
|
||||||
ctx.Stack = stack
|
ctx.Stack = stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,9 @@ func TestNewStack(t *testing.T) {
|
||||||
traceID := "12345678"
|
traceID := "12345678"
|
||||||
assistantID := "test-assistant"
|
assistantID := "test-assistant"
|
||||||
referer := RefererAPI
|
referer := RefererAPI
|
||||||
|
opts := &Options{}
|
||||||
|
|
||||||
stack := NewStack(traceID, assistantID, referer)
|
stack := NewStack(traceID, assistantID, referer, opts)
|
||||||
|
|
||||||
if stack == nil {
|
if stack == nil {
|
||||||
t.Fatal("Expected stack to be created, got nil")
|
t.Fatal("Expected stack to be created, got nil")
|
||||||
|
|
@ -57,7 +58,7 @@ func TestNewStack_GenerateTraceID(t *testing.T) {
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
// Empty traceID should generate a UUID
|
// Empty traceID should generate a UUID
|
||||||
stack := NewStack("", "test-assistant", RefererAPI)
|
stack := NewStack("", "test-assistant", RefererAPI, &Options{})
|
||||||
|
|
||||||
if stack.TraceID == "" {
|
if stack.TraceID == "" {
|
||||||
t.Error("Expected TraceID to be generated, got empty string")
|
t.Error("Expected TraceID to be generated, got empty string")
|
||||||
|
|
@ -74,10 +75,10 @@ func TestNewChildStack(t *testing.T) {
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
// Create parent stack
|
// Create parent stack
|
||||||
parentStack := NewStack("12345678", "parent-assistant", RefererAPI)
|
parentStack := NewStack("12345678", "parent-assistant", RefererAPI, &Options{})
|
||||||
|
|
||||||
// Create child stack
|
// Create child stack
|
||||||
childStack := parentStack.NewChildStack("child-assistant", RefererAgent)
|
childStack := parentStack.NewChildStack("child-assistant", RefererAgent, &Options{})
|
||||||
|
|
||||||
if childStack == nil {
|
if childStack == nil {
|
||||||
t.Fatal("Expected child stack to be created, got nil")
|
t.Fatal("Expected child stack to be created, got nil")
|
||||||
|
|
@ -121,7 +122,7 @@ func TestStackComplete(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
stack := NewStack("12345678", "test-assistant", RefererAPI)
|
stack := NewStack("12345678", "test-assistant", RefererAPI, &Options{})
|
||||||
|
|
||||||
// Wait a bit to have measurable duration
|
// Wait a bit to have measurable duration
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
|
@ -157,7 +158,7 @@ func TestStackFail(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
stack := NewStack("12345678", "test-assistant", RefererAPI)
|
stack := NewStack("12345678", "test-assistant", RefererAPI, &Options{})
|
||||||
|
|
||||||
testError := "test error message"
|
testError := "test error message"
|
||||||
stack.Fail(nil)
|
stack.Fail(nil)
|
||||||
|
|
@ -180,7 +181,7 @@ func TestStackTimeout(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
stack := NewStack("12345678", "test-assistant", RefererAPI)
|
stack := NewStack("12345678", "test-assistant", RefererAPI, &Options{})
|
||||||
|
|
||||||
stack.Timeout()
|
stack.Timeout()
|
||||||
|
|
||||||
|
|
@ -199,9 +200,10 @@ func TestEnterStack_RootCreation(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, traceID, done := EnterStack(ctx, "test-assistant", RefererAPI)
|
stack, traceID, done := EnterStack(ctx, "test-assistant", &Options{})
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
if stack == nil {
|
if stack == nil {
|
||||||
|
|
@ -244,10 +246,11 @@ func TestEnterStack_ChildCreation(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create parent
|
// Create parent
|
||||||
parentStack, parentTraceID, parentDone := EnterStack(ctx, "parent-assistant", RefererAPI)
|
parentStack, parentTraceID, parentDone := EnterStack(ctx, "parent-assistant", &Options{})
|
||||||
defer parentDone()
|
defer parentDone()
|
||||||
|
|
||||||
if parentStack == nil {
|
if parentStack == nil {
|
||||||
|
|
@ -255,7 +258,7 @@ func TestEnterStack_ChildCreation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create child
|
// Create child
|
||||||
childStack, childTraceID, childDone := EnterStack(ctx, "child-assistant", RefererAgent)
|
childStack, childTraceID, childDone := EnterStack(ctx, "child-assistant", &Options{})
|
||||||
defer childDone()
|
defer childDone()
|
||||||
|
|
||||||
if childStack == nil {
|
if childStack == nil {
|
||||||
|
|
@ -289,13 +292,14 @@ func TestEnterStack_DoneCallback(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create parent
|
// Create parent
|
||||||
parentStack, _, parentDone := EnterStack(ctx, "parent-assistant", RefererAPI)
|
parentStack, _, parentDone := EnterStack(ctx, "parent-assistant", &Options{})
|
||||||
|
|
||||||
// Create child
|
// Create child
|
||||||
childStack, _, childDone := EnterStack(ctx, "child-assistant", RefererAgent)
|
childStack, _, childDone := EnterStack(ctx, "child-assistant", &Options{})
|
||||||
|
|
||||||
// Child should be current
|
// Child should be current
|
||||||
if ctx.Stack != childStack {
|
if ctx.Stack != childStack {
|
||||||
|
|
@ -330,16 +334,17 @@ func TestContextGetAllStacks(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create multiple stacks
|
// Create multiple stacks
|
||||||
_, _, done1 := EnterStack(ctx, "assistant1", RefererAPI)
|
_, _, done1 := EnterStack(ctx, "assistant1", &Options{})
|
||||||
defer done1()
|
defer done1()
|
||||||
|
|
||||||
_, _, done2 := EnterStack(ctx, "assistant2", RefererAgent)
|
_, _, done2 := EnterStack(ctx, "assistant2", &Options{})
|
||||||
defer done2()
|
defer done2()
|
||||||
|
|
||||||
_, _, done3 := EnterStack(ctx, "assistant3", RefererAgent)
|
_, _, done3 := EnterStack(ctx, "assistant3", &Options{})
|
||||||
defer done3()
|
defer done3()
|
||||||
|
|
||||||
// Get all stacks
|
// Get all stacks
|
||||||
|
|
@ -356,9 +361,10 @@ func TestContextGetStackByID(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, _, done := EnterStack(ctx, "test-assistant", RefererAPI)
|
stack, _, done := EnterStack(ctx, "test-assistant", &Options{})
|
||||||
defer done()
|
defer done()
|
||||||
|
|
||||||
// Get stack by ID
|
// Get stack by ID
|
||||||
|
|
@ -385,13 +391,14 @@ func TestContextGetStacksByTraceID(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create parent and child (same trace ID)
|
// Create parent and child (same trace ID)
|
||||||
_, traceID, done1 := EnterStack(ctx, "parent-assistant", RefererAPI)
|
_, traceID, done1 := EnterStack(ctx, "parent-assistant", &Options{})
|
||||||
defer done1()
|
defer done1()
|
||||||
|
|
||||||
_, _, done2 := EnterStack(ctx, "child-assistant", RefererAgent)
|
_, _, done2 := EnterStack(ctx, "child-assistant", &Options{})
|
||||||
defer done2()
|
defer done2()
|
||||||
|
|
||||||
// Get stacks by trace ID
|
// Get stacks by trace ID
|
||||||
|
|
@ -415,14 +422,15 @@ func TestContextGetRootStack(t *testing.T) {
|
||||||
|
|
||||||
ctx := &Context{
|
ctx := &Context{
|
||||||
IDGenerator: message.NewIDGenerator(),
|
IDGenerator: message.NewIDGenerator(),
|
||||||
|
Referer: RefererAPI,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create parent
|
// Create parent
|
||||||
parentStack, _, done1 := EnterStack(ctx, "parent-assistant", RefererAPI)
|
parentStack, _, done1 := EnterStack(ctx, "parent-assistant", &Options{})
|
||||||
defer done1()
|
defer done1()
|
||||||
|
|
||||||
// Create child
|
// Create child
|
||||||
_, _, done2 := EnterStack(ctx, "child-assistant", RefererAgent)
|
_, _, done2 := EnterStack(ctx, "child-assistant", &Options{})
|
||||||
defer done2()
|
defer done2()
|
||||||
|
|
||||||
// Get root stack
|
// Get root stack
|
||||||
|
|
@ -445,7 +453,7 @@ func TestStackClone(t *testing.T) {
|
||||||
test.Prepare(t, config.Conf)
|
test.Prepare(t, config.Conf)
|
||||||
defer test.Clean()
|
defer test.Clean()
|
||||||
|
|
||||||
original := NewStack("12345678", "test-assistant", RefererAPI)
|
original := NewStack("12345678", "test-assistant", RefererAPI, &Options{})
|
||||||
original.Complete()
|
original.Complete()
|
||||||
|
|
||||||
clone := original.Clone()
|
clone := original.Clone()
|
||||||
|
|
|
||||||
|
|
@ -235,9 +235,6 @@ type Context struct {
|
||||||
output *output.Output `json:"-"` // Output, it will be used to write response data to the client
|
output *output.Output `json:"-"` // Output, it will be used to write response data to the client
|
||||||
messageMetadata *messageMetadataStore `json:"-"` // Thread-safe message metadata store for delta operations
|
messageMetadata *messageMetadataStore `json:"-"` // Thread-safe message metadata store for delta operations
|
||||||
|
|
||||||
// Skip configuration (history, trace, etc.), nil means don't skip anything
|
|
||||||
Skip *Skip `json:"skip,omitempty"` // Skip configuration (history, trace, etc.), nil means don't skip anything
|
|
||||||
|
|
||||||
// Model capabilities (set by assistant, used by output adapters)
|
// Model capabilities (set by assistant, used by output adapters)
|
||||||
Capabilities *openai.Capabilities `json:"-"` // Model capabilities for the current connector
|
Capabilities *openai.Capabilities `json:"-"` // Model capabilities for the current connector
|
||||||
|
|
||||||
|
|
@ -248,13 +245,6 @@ type Context struct {
|
||||||
Authorized *types.AuthorizedInfo `json:"authorized,omitempty"` // Authorized information
|
Authorized *types.AuthorizedInfo `json:"authorized,omitempty"` // Authorized information
|
||||||
ChatID string `json:"chat_id,omitempty"` // Chat ID, use to select chat
|
ChatID string `json:"chat_id,omitempty"` // Chat ID, use to select chat
|
||||||
AssistantID string `json:"assistant_id,omitempty"` // Assistant ID, use to select assistant
|
AssistantID string `json:"assistant_id,omitempty"` // Assistant ID, use to select assistant
|
||||||
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
|
|
||||||
Retry bool `json:"retry,omitempty"` // Retry mode
|
|
||||||
RetryTimes uint8 `json:"retry_times,omitempty"` // Retry times
|
|
||||||
|
|
||||||
// Locale information
|
// Locale information
|
||||||
Locale string `json:"locale,omitempty"` // Locale
|
Locale string `json:"locale,omitempty"` // Locale
|
||||||
|
|
@ -270,6 +260,31 @@ type Context struct {
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"` // The metadata of the request, it will be used to pass data to the page
|
Metadata map[string]interface{} `json:"metadata,omitempty"` // The metadata of the request, it will be used to pass data to the page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options represents the options for the context
|
||||||
|
type Options struct {
|
||||||
|
|
||||||
|
// Original context, override the default context
|
||||||
|
Context context.Context `json:"-"` // Context, it will be used to pass the context to the call
|
||||||
|
|
||||||
|
// Writer, use to write response data to the client (override the default writer)
|
||||||
|
Writer Writer `json:"writer,omitempty"` // Writer, use to write response data to the client
|
||||||
|
|
||||||
|
// Skip configuration (history, trace, etc.), nil means don't skip anything
|
||||||
|
Skip *Skip `json:"skip,omitempty"` // Skip configuration (history, trace, etc.), nil means don't skip anything
|
||||||
|
|
||||||
|
// Connector, use to select the connector of the LLM Model, Default is Assistant.Connector
|
||||||
|
Connector string `json:"connector,omitempty"` // Connector, use to select the connector of the LLM Model, Default is Assistant.Connector
|
||||||
|
|
||||||
|
// Disable global prompts, default is false
|
||||||
|
DisableGlobalPrompts bool `json:"disable_global_prompts,omitempty"` // Temporarily disable global prompts for this request
|
||||||
|
|
||||||
|
// Search mode, default is true
|
||||||
|
Search *bool `json:"search,omitempty"` // Search mode, default is true
|
||||||
|
|
||||||
|
// Agent mode, use to select the mode of the request, default is "chat"
|
||||||
|
Mode string `json:"mode,omitempty"` // Agent mode, use to select the mode of the request, default is "chat"
|
||||||
|
}
|
||||||
|
|
||||||
// Stack represents the call stack node for tracing agent-to-agent calls
|
// Stack represents the call stack node for tracing agent-to-agent calls
|
||||||
// Uses a flat structure to avoid circular references and memory overhead
|
// Uses a flat structure to avoid circular references and memory overhead
|
||||||
type Stack struct {
|
type Stack struct {
|
||||||
|
|
@ -277,6 +292,9 @@ type Stack struct {
|
||||||
ID string `json:"id"` // Unique stack node ID, used to identify this specific call
|
ID string `json:"id"` // Unique stack node ID, used to identify this specific call
|
||||||
TraceID string `json:"trace_id"` // Shared trace ID for entire call tree, inherited from root
|
TraceID string `json:"trace_id"` // Shared trace ID for entire call tree, inherited from root
|
||||||
|
|
||||||
|
// Options
|
||||||
|
Options *Options `json:"options,omitempty"` // Options for the call
|
||||||
|
|
||||||
// Call context
|
// Call context
|
||||||
AssistantID string `json:"assistant_id"` // Assistant handling this call
|
AssistantID string `json:"assistant_id"` // Assistant handling this call
|
||||||
Referer string `json:"referer,omitempty"` // Call source: api, agent, tool, process, etc.
|
Referer string `json:"referer,omitempty"` // Call source: api, agent, tool, process, etc.
|
||||||
|
|
@ -331,12 +349,11 @@ type HookCreateResponse struct {
|
||||||
DisableGlobalPrompts *bool `json:"disable_global_prompts,omitempty"` // Temporarily disable global prompts for this request
|
DisableGlobalPrompts *bool `json:"disable_global_prompts,omitempty"` // Temporarily disable global prompts for this request
|
||||||
|
|
||||||
// Context adjustments - allow hook to modify context fields
|
// Context adjustments - allow hook to modify context fields
|
||||||
AssistantID string `json:"assistant_id,omitempty"` // Override assistant ID
|
Connector string `json:"connector,omitempty"` // Override connector (call-level)
|
||||||
Connector string `json:"connector,omitempty"` // Override connector
|
Locale string `json:"locale,omitempty"` // Override locale (session-level)
|
||||||
Locale string `json:"locale,omitempty"` // Override locale
|
Theme string `json:"theme,omitempty"` // Override theme (session-level)
|
||||||
Theme string `json:"theme,omitempty"` // Override theme
|
Route string `json:"route,omitempty"` // Override route (session-level)
|
||||||
Route string `json:"route,omitempty"` // Override route
|
Metadata map[string]interface{} `json:"metadata,omitempty"` // Override or merge metadata (session-level)
|
||||||
Metadata map[string]interface{} `json:"metadata,omitempty"` // Override or merge metadata
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextHookPayload payload for the next hook
|
// NextHookPayload payload for the next hook
|
||||||
|
|
@ -372,9 +389,9 @@ type NextHookResponse struct {
|
||||||
|
|
||||||
// DelegateConfig configuration for delegating to another agent
|
// DelegateConfig configuration for delegating to another agent
|
||||||
type DelegateConfig struct {
|
type DelegateConfig struct {
|
||||||
AgentID string `json:"agent_id"` // Required: target agent ID
|
AgentID string `json:"agent_id"` // Required: target agent ID
|
||||||
Messages []Message `json:"messages"` // Messages to send to target agent
|
Messages []Message `json:"messages"` // Messages to send to target agent
|
||||||
|
Options map[string]interface{} `json:"options,omitempty"` // Optional: call-level options for delegation
|
||||||
}
|
}
|
||||||
|
|
||||||
// NextAction defines the action determined by Next hook response
|
// NextAction defines the action determined by Next hook response
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ func newClaudeTestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -401,7 +401,6 @@ func newDeepSeekTestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,6 @@ func newDeepSeekV3TestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -388,7 +388,6 @@ func newGPT5TestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -1508,7 +1508,6 @@ func newTestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -340,7 +340,6 @@ func newTemperatureTestContext(chatID, connectorID string) *context.Context {
|
||||||
Space: plan.NewMemorySharedSpace(),
|
Space: plan.NewMemorySharedSpace(),
|
||||||
ChatID: chatID,
|
ChatID: chatID,
|
||||||
AssistantID: "test-assistant",
|
AssistantID: "test-assistant",
|
||||||
Connector: connectorID,
|
|
||||||
Locale: "en-us",
|
Locale: "en-us",
|
||||||
Theme: "light",
|
Theme: "light",
|
||||||
Client: context.Client{
|
Client: context.Client{
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ func GinCreateCompletions(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
completionReq, ctx, err := context.GetCompletionRequest(c, cache)
|
completionReq, ctx, opts, err := context.GetCompletionRequest(c, cache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("-----------------------------------------------")
|
fmt.Println("-----------------------------------------------")
|
||||||
fmt.Println("Error: ", err.Error())
|
fmt.Println("Error: ", err.Error())
|
||||||
|
|
@ -61,7 +61,7 @@ func GinCreateCompletions(c *gin.Context) {
|
||||||
// Stream the completion (uses default handler which sends to ctx.Writer)
|
// Stream the completion (uses default handler which sends to ctx.Writer)
|
||||||
// The Stream method will automatically close the writer and send [DONE] marker
|
// The Stream method will automatically close the writer and send [DONE] marker
|
||||||
log.Trace("[HTTP] Calling ast.Stream()")
|
log.Trace("[HTTP] Calling ast.Stream()")
|
||||||
_, err = ast.Stream(ctx, completionReq.Messages)
|
_, err = ast.Stream(ctx, completionReq.Messages, opts)
|
||||||
log.Trace("[HTTP] ast.Stream() returned, err=%v", err)
|
log.Trace("[HTTP] ast.Stream() returned, err=%v", err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
|
response.RespondWithError(c, response.StatusInternalServerError, &response.ErrorResponse{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue