From a7c8170c4ad1ce5d148885a45e6bb8f7e0475c0f Mon Sep 17 00:00:00 2001 From: viettranx Date: Tue, 14 Apr 2026 23:35:40 +0700 Subject: [PATCH] feat(agent): audio config context propagation in tool callbacks Add audio config context helpers in pipeline callbacks. Propagate voice/model selections through agent loop resolver for tool invocation. --- internal/agent/loop_pipeline_tool_callbacks.go | 17 +++++++++++++++++ internal/agent/loop_types.go | 16 +++++++++++----- internal/agent/resolver.go | 1 + 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/agent/loop_pipeline_tool_callbacks.go b/internal/agent/loop_pipeline_tool_callbacks.go index b7743f3e..f0f85668 100644 --- a/internal/agent/loop_pipeline_tool_callbacks.go +++ b/internal/agent/loop_pipeline_tool_callbacks.go @@ -35,6 +35,15 @@ func (l *Loop) makeExecuteToolCall(req *RunRequest, bridgeRS *runState) func(ctx toolStart := time.Now().UTC() toolSpanID := l.emitToolSpanStart(ctx, toolStart, tc.Name, tc.ID, string(argsJSON)) + // Inject agent audio snapshot so TTS tool (and any future audio consumers) + // can read agent-level voice/model config without an extra DB lookup. + if l.agentUUID != uuid.Nil { + ctx = store.WithAgentAudio(ctx, store.AgentAudioSnapshot{ + AgentID: l.agentUUID, + OtherConfig: append([]byte(nil), l.agentOtherConfig...), // defensive copy at dispatch + }) + } + result := l.tools.ExecuteWithContext(ctx, registryName, tc.Arguments, req.Channel, req.ChatID, req.PeerKind, req.SessionKey, nil) toolDuration := time.Since(toolStart) @@ -84,6 +93,14 @@ func (l *Loop) makeExecuteToolRaw(req *RunRequest) func(ctx context.Context, tc start := time.Now().UTC() spanID := l.emitToolSpanStart(ctx, start, tc.Name, tc.ID, string(argsJSON)) + // Inject agent audio snapshot (parallel path — same as sequential makeExecuteToolCall). + if l.agentUUID != uuid.Nil { + ctx = store.WithAgentAudio(ctx, store.AgentAudioSnapshot{ + AgentID: l.agentUUID, + OtherConfig: append([]byte(nil), l.agentOtherConfig...), // defensive copy at dispatch + }) + } + result := l.tools.ExecuteWithContext(ctx, registryName, tc.Arguments, req.Channel, req.ChatID, req.PeerKind, req.SessionKey, nil) dur := time.Since(start) diff --git a/internal/agent/loop_types.go b/internal/agent/loop_types.go index e0140bef..68dc32e2 100644 --- a/internal/agent/loop_types.go +++ b/internal/agent/loop_types.go @@ -2,6 +2,7 @@ package agent import ( "context" + "encoding/json" "sync" "sync/atomic" @@ -78,8 +79,11 @@ type Loop struct { // agentUUID is the canonical DB primary key. Use for SQL WHERE/JOIN, // DomainEvent.AgentID, OTel span attributes, and context propagation via // store.WithAgentID. See docs/agent-identity-conventions.md. - agentUUID uuid.UUID - tenantID uuid.UUID // agent's owning tenant + agentUUID uuid.UUID + tenantID uuid.UUID // agent's owning tenant + // agentOtherConfig is a defensive byte copy of agents.other_config JSONB. + // Copied once at Loop construction; used to build AgentAudioSnapshot at tool dispatch. + agentOtherConfig json.RawMessage agentType string // "open" or "predefined" defaultTimezone string // system default timezone for bootstrap pre-fill provider providers.Provider @@ -319,9 +323,10 @@ type LoopConfig struct { ShellDenyGroups map[string]bool // Agent UUID + tenant for context propagation to tools - AgentUUID uuid.UUID - TenantID uuid.UUID // agent's owning tenant — injected into execution context - AgentType string // "open" or "predefined" + AgentUUID uuid.UUID + TenantID uuid.UUID // agent's owning tenant — injected into execution context + AgentOtherConfig json.RawMessage // raw other_config JSONB — copied defensively in NewLoop + AgentType string // "open" or "predefined" DisplayName string // human-readable agent display name (for runtime section) IsTeamLead bool // agent leads a team (from resolver detection) @@ -449,6 +454,7 @@ func NewLoop(cfg LoopConfig) *Loop { displayName: cfg.DisplayName, agentUUID: cfg.AgentUUID, tenantID: cfg.TenantID, + agentOtherConfig: append([]byte(nil), cfg.AgentOtherConfig...), // defensive copy agentType: cfg.AgentType, provider: cfg.Provider, model: cfg.Model, diff --git a/internal/agent/resolver.go b/internal/agent/resolver.go index 7fe15b07..3bf864d0 100644 --- a/internal/agent/resolver.go +++ b/internal/agent/resolver.go @@ -451,6 +451,7 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc { DisplayName: ag.DisplayName, AgentUUID: ag.ID, TenantID: ag.TenantID, + AgentOtherConfig: ag.OtherConfig, AgentType: ag.AgentType, IsTeamLead: isTeamLead, AutoInjector: deps.AutoInjector,