fix: for telegram streaming response

This commit is contained in:
Arpit Thukral 2026-03-22 23:20:35 +00:00
parent 4d84bd90cd
commit ae729d5fea

View file

@ -1857,6 +1857,29 @@ turnLoop:
"tools_json": formatToolsForLog(providerToolDefs), "tools_json": formatToolsForLog(providerToolDefs),
}) })
// Acquire a streamer once per turn before entering the retry loop.
// GetStreamer calls BeginStream on the channel (e.g. Telegram's
// sendMessageDraft path). Returns nil when streaming is disabled
// in config or the channel doesn't implement StreamingCapable.
var activeStreamer bus.Streamer
if ts.opts.SendResponse && ts.channel != "" && ts.chatID != "" {
if s, ok := al.bus.GetStreamer(turnCtx, ts.channel, ts.chatID); ok {
activeStreamer = s
}
}
// streamingProvider is the interface subset of ChatStream we need.
type streamingProvider interface {
ChatStream(
ctx context.Context,
messages []providers.Message,
tools []providers.ToolDefinition,
model string,
options map[string]any,
onChunk func(accumulated string),
) (*providers.LLMResponse, error)
}
callLLM := func(messagesForCall []providers.Message, toolDefsForCall []providers.ToolDefinition) (*providers.LLMResponse, error) { callLLM := func(messagesForCall []providers.Message, toolDefsForCall []providers.ToolDefinition) (*providers.LLMResponse, error) {
providerCtx, providerCancel := context.WithCancel(turnCtx) providerCtx, providerCancel := context.WithCancel(turnCtx)
ts.setProviderCancel(providerCancel) ts.setProviderCancel(providerCancel)
@ -1864,10 +1887,42 @@ turnLoop:
providerCancel() providerCancel()
ts.clearProviderCancel(providerCancel) ts.clearProviderCancel(providerCancel)
}() }()
al.activeRequests.Add(1) al.activeRequests.Add(1)
defer al.activeRequests.Done() defer al.activeRequests.Done()
// Streaming path: only when a streamer is available AND there are
// no tool definitions for this call (tool-calling iterations use
// normal Chat() so tool JSON is parsed correctly).
if activeStreamer != nil && len(toolDefsForCall) == 0 {
if sp, ok := ts.agent.Provider.(streamingProvider); ok {
resp, streamErr := sp.ChatStream(
providerCtx,
messagesForCall,
toolDefsForCall,
llmModel,
llmOpts,
func(accumulated string) {
_ = activeStreamer.Update(providerCtx, accumulated)
},
)
if streamErr == nil {
// Deliver the final formatted message and mark streamActive
// so preSend skips the duplicate PublishOutbound send.
if finalizeErr := activeStreamer.Finalize(providerCtx, resp.Content); finalizeErr != nil {
logger.WarnCF("agent", "Streamer finalize failed",
map[string]any{"error": finalizeErr.Error()})
}
activeStreamer = nil // prevent double-finalize on retry
return resp, nil
}
// ChatStream failed — cancel streamer and fall through to Chat()
logger.WarnCF("agent", "ChatStream failed, falling back to Chat()",
map[string]any{"error": streamErr.Error()})
activeStreamer.Cancel(providerCtx)
activeStreamer = nil
}
}
if len(activeCandidates) > 1 && al.fallback != nil { if len(activeCandidates) > 1 && al.fallback != nil {
fbResult, fbErr := al.fallback.Execute( fbResult, fbErr := al.fallback.Execute(
providerCtx, providerCtx,
@ -1891,7 +1946,6 @@ turnLoop:
} }
return ts.agent.Provider.Chat(providerCtx, messagesForCall, toolDefsForCall, llmModel, llmOpts) return ts.agent.Provider.Chat(providerCtx, messagesForCall, toolDefsForCall, llmModel, llmOpts)
} }
var response *providers.LLMResponse var response *providers.LLMResponse
var err error var err error
maxRetries := 2 maxRetries := 2