This commit is contained in:
HugeFrog24
2026-05-25 23:39:40 +02:00
parent 7c74d91bbb
commit 4fc9d8a5c5
6 changed files with 144 additions and 101 deletions
+26 -18
View File
@@ -55,7 +55,10 @@ func (b *Bot) handleVoiceMessage(ctx context.Context, message *models.Message, u
chatMemory := b.getOrCreateChatMemory(chatID)
contextMessages := b.prepareContextMessages(chatMemory)
segments, err := b.getAnthropicResponse(ctx, contextMessages, isNewChat, isOwner, false, username, firstName, lastName, isPremium, languageCode, messageTime)
// Voice path passes nil for onSegment: tool-call narration across multiple
// TTS clips would be jarring, so we accumulate everything and synthesize one
// audio clip from the joined text.
response, err := b.getAnthropicResponse(ctx, contextMessages, isNewChat, isOwner, false, username, firstName, lastName, isPremium, languageCode, messageTime, nil)
if err != nil {
ErrorLogger.Printf("Error getting Anthropic response for voice: %v", err)
if err := b.sendResponse(ctx, chatID, b.anthropicErrorResponse(err, userID), businessConnectionID); err != nil {
@@ -64,10 +67,6 @@ func (b *Bot) handleVoiceMessage(ctx context.Context, message *models.Message, u
return
}
// Voice replies always synthesize as one audio clip — tool-call narration
// across multiple TTS clips would be jarring, so we join here.
response := strings.Join(segments, "\n\n")
audioReader, err := b.generateSpeech(ctx, response)
if err != nil {
// TTS failed — fall back to text so the user still gets a reply.
@@ -365,22 +364,31 @@ func (b *Bot) handleUpdate(ctx context.Context, tgBot *bot.Bot, update *models.U
// Determine if the text contains only emojis
isEmojiOnly := isOnlyEmojis(text)
// Get response from Anthropic
segments, err := b.getAnthropicResponse(ctx, contextMessages, isNewChatFlag, isOwner, isEmojiOnly, username, firstName, lastName, isPremium, languageCode, messageTime)
// Stream Anthropic's reply, sending each completed text block to Telegram
// as it arrives — gives the conversational rhythm Claude uses around tool
// calls (text → pause for tool → text → pause → text), rather than a long
// upfront wait followed by all bubbles at once.
joined, err := b.getAnthropicResponse(
ctx, contextMessages, isNewChatFlag, isOwner, isEmojiOnly,
username, firstName, lastName, isPremium, languageCode, messageTime,
func(seg string) error {
return b.sendOneSegment(ctx, chatID, seg, businessConnectionID)
},
)
if err != nil {
ErrorLogger.Printf("Error getting Anthropic response: %v", err)
// Errors go out as a single message — no need to fan out a one-line error.
if err := b.sendResponse(ctx, chatID, b.anthropicErrorResponse(err, userID), businessConnectionID); err != nil {
ErrorLogger.Printf("Error sending response: %v", err)
if sendErr := b.sendResponse(ctx, chatID, b.anthropicErrorResponse(err, userID), businessConnectionID); sendErr != nil {
ErrorLogger.Printf("Error sending response: %v", sendErr)
}
return
}
// Successful LLM reply: deliver each text block as its own Telegram bubble,
// matching the conversational rhythm Claude uses around tool calls.
if err := b.sendMultiResponse(ctx, chatID, segments, businessConnectionID); err != nil {
ErrorLogger.Printf("Error sending response: %v", err)
return
// Record the full turn once, at end-of-stream. Same 1-reply-per-prompt
// invariant as the non-streaming path: one DB row, one answered_on stamp,
// one chat-memory entry containing the joined segments.
if _, storeErr := b.screenOutgoingMessage(chatID, joined); storeErr != nil {
ErrorLogger.Printf("Error recording assistant turn: %v", storeErr)
}
}
@@ -419,13 +427,13 @@ func (b *Bot) generateStickerResponse(ctx context.Context, message Message, cont
// "Sent a sticker: <emoji>"), so the full conversation history is preserved.
if message.StickerFileID != "" {
messageTime := int(message.Timestamp.Unix())
segments, err := b.getAnthropicResponse(ctx, contextMessages, false, false, true, message.Username, "", "", false, "", messageTime)
// Sticker reactions are casual chit-chat; tool use is unusual here, so
// pass nil for onSegment and return the joined text for a single bubble.
response, err := b.getAnthropicResponse(ctx, contextMessages, false, false, true, message.Username, "", "", false, "", messageTime, nil)
if err != nil {
return "", err
}
// Sticker reactions are casual chit-chat; tool use is unusual here, so
// join into one message rather than fanning out as multiple bubbles.
return strings.Join(segments, "\n\n"), nil
return response, nil
}
return "Hmm, that's interesting!", nil