diff --git a/pkg/channels/telegram/telegram.go b/pkg/channels/telegram/telegram.go index a2facc505..9d59dec08 100644 --- a/pkg/channels/telegram/telegram.go +++ b/pkg/channels/telegram/telegram.go @@ -2,9 +2,9 @@ package telegram import ( "context" - "fmt" "crypto/rand" "encoding/binary" + "fmt" "net/http" "net/url" "os" @@ -798,10 +798,13 @@ func (c *TelegramChannel) BeginStream(ctx context.Context, chatID string) (chann return nil, err } + streamCfg := c.config.Channels.Telegram.Streaming return &telegramStreamer{ - bot: c.bot, - chatID: cid, - draftID: cryptoRandInt(), // non-zero random draft ID + bot: c.bot, + chatID: cid, + draftID: cryptoRandInt(), + throttleInterval: time.Duration(streamCfg.ThrottleSeconds) * time.Second, + minGrowth: streamCfg.MinGrowthChars, }, nil } @@ -809,20 +812,17 @@ func (c *TelegramChannel) BeginStream(ctx context.Context, chatID string) (chann // On first API error (e.g. bot lacks forum mode), it silently degrades: Update // becomes a no-op, while Finalize still delivers the final message. type telegramStreamer struct { - bot *telego.Bot - chatID int64 - draftID int - lastLen int - lastAt time.Time - failed bool - mu sync.Mutex + bot *telego.Bot + chatID int64 + draftID int + throttleInterval time.Duration + minGrowth int + lastLen int + lastAt time.Time + failed bool + mu sync.Mutex } -const ( - streamThrottleInterval = 3 * time.Second - streamMinGrowth = 200 // minimum character growth to send an update -) - func (s *telegramStreamer) Update(ctx context.Context, content string) error { s.mu.Lock() defer s.mu.Unlock() @@ -834,7 +834,7 @@ func (s *telegramStreamer) Update(ctx context.Context, content string) error { // Throttle: skip if not enough time or content has passed now := time.Now() growth := len(content) - s.lastLen - if s.lastLen > 0 && now.Sub(s.lastAt) < streamThrottleInterval && growth < streamMinGrowth { + if s.lastLen > 0 && now.Sub(s.lastAt) < s.throttleInterval && growth < s.minGrowth { return nil } diff --git a/pkg/config/config.go b/pkg/config/config.go index adea040ba..bea77a392 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -238,7 +238,9 @@ type PlaceholderConfig struct { } type StreamingConfig struct { - Enabled bool `json:"enabled,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_STREAMING_ENABLED"` + Enabled bool `json:"enabled,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_STREAMING_ENABLED"` + ThrottleSeconds int `json:"throttle_seconds,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_STREAMING_THROTTLE_SECONDS"` + MinGrowthChars int `json:"min_growth_chars,omitempty" env:"PICOCLAW_CHANNELS_TELEGRAM_STREAMING_MIN_GROWTH_CHARS"` } type WhatsAppConfig struct { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index fcfda6c72..6b9e3a3a0 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -58,7 +58,7 @@ func DefaultConfig() *Config { Enabled: true, Text: "Thinking... 💭", }, - Streaming: StreamingConfig{Enabled: true}, + Streaming: StreamingConfig{Enabled: true, ThrottleSeconds: 3, MinGrowthChars: 200}, }, Feishu: FeishuConfig{ Enabled: false, diff --git a/pkg/providers/openai_compat/provider.go b/pkg/providers/openai_compat/provider.go index 3049320a5..802aebe6c 100644 --- a/pkg/providers/openai_compat/provider.go +++ b/pkg/providers/openai_compat/provider.go @@ -248,7 +248,11 @@ func (p *Provider) ChatStream( } // parseStreamResponse parses an OpenAI-compatible SSE stream. -func parseStreamResponse(ctx context.Context, reader io.Reader, onChunk func(accumulated string)) (*LLMResponse, error) { +func parseStreamResponse( + ctx context.Context, + reader io.Reader, + onChunk func(accumulated string), +) (*LLMResponse, error) { var textContent strings.Builder var finishReason string var usage *UsageInfo