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:
Max 2025-12-04 10:43:32 +08:00
parent beb63b77de
commit a96946d8bb
42 changed files with 425 additions and 332 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 != "" {

View file

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

View file

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

View file

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

View file

@ -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{}, `

View file

@ -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{}, `

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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