Skip to content

Commit 239e512

Browse files
committed
feat(bot): remove non-stream assistant response mode
1 parent 84c300d commit 239e512

13 files changed

Lines changed: 114 additions & 623 deletions

.env.example

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,12 @@ OPENCODE_MODEL_ID=big-pickle
5050
# Supported locales: en, de, es, fr, ru, zh
5151
# BOT_LOCALE=en
5252

53-
# Service message batching interval in seconds (thinking + tool calls, default: 5)
54-
# Recommended: keep >=2 to reduce risk of hitting Telegram rate limits (about 1 message/sec)
55-
# 0 = send immediately (can hit rate limits if there are many tool calls)
56-
# SERVICE_MESSAGES_INTERVAL_SEC=5
57-
5853
# Hide thinking indicator messages (default: false)
5954
# HIDE_THINKING_MESSAGES=false
6055

6156
# Hide tool call service messages (default: false)
6257
# HIDE_TOOL_CALL_MESSAGES=false
6358

64-
# Stream assistant responses while they are being generated (default: true)
65-
# RESPONSE_STREAMING=true
66-
6759
# Assistant message formatting mode (default: markdown)
6860
# markdown = convert assistant replies to Telegram MarkdownV2
6961
# raw = show assistant replies as plain text

README.md

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -147,32 +147,30 @@ When installed via npm, the configuration wizard handles the initial setup. The
147147
- **Windows:** `%APPDATA%\opencode-telegram-bot\.env`
148148
- **Linux:** `~/.config/opencode-telegram-bot/.env`
149149

150-
| Variable | Description | Required | Default |
151-
| ------------------------------- | ------------------------------------------------------------------------------------------------------------ | :------: | ------------------------ |
152-
| `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes ||
153-
| `TELEGRAM_ALLOWED_USER_ID` | Your numeric Telegram user ID | Yes ||
154-
| `TELEGRAM_PROXY_URL` | Proxy URL for Telegram API (SOCKS5/HTTP) | No ||
155-
| `OPENCODE_API_URL` | OpenCode server URL | No | `http://localhost:4096` |
156-
| `OPENCODE_SERVER_USERNAME` | Server auth username | No | `opencode` |
157-
| `OPENCODE_SERVER_PASSWORD` | Server auth password | No ||
158-
| `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
159-
| `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
160-
| `BOT_LOCALE` | Bot UI language (supported locale code, e.g. `en`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
161-
| `SESSIONS_LIST_LIMIT` | Sessions per page in `/sessions` | No | `10` |
162-
| `PROJECTS_LIST_LIMIT` | Projects per page in `/projects` | No | `10` |
163-
| `COMMANDS_LIST_LIMIT` | Commands per page in `/commands` | No | `10` |
164-
| `TASK_LIMIT` | Maximum number of scheduled tasks that can exist at once | No | `10` |
165-
| `SERVICE_MESSAGES_INTERVAL_SEC` | Service messages interval (thinking + tool calls); keep `>=2` to avoid Telegram rate limits, `0` = immediate | No | `5` |
166-
| `HIDE_THINKING_MESSAGES` | Hide `💭 Thinking...` service messages | No | `false` |
167-
| `HIDE_TOOL_CALL_MESSAGES` | Hide tool-call service messages (`💻 bash ...`, `📖 read ...`, etc.) | No | `false` |
168-
| `RESPONSE_STREAMING` | Stream assistant replies while they are generated across one or more Telegram messages | No | `true` |
169-
| `MESSAGE_FORMAT_MODE` | Assistant reply formatting mode: `markdown` (Telegram MarkdownV2) or `raw` | No | `markdown` |
170-
| `CODE_FILE_MAX_SIZE_KB` | Max file size (KB) to send as document | No | `100` |
171-
| `STT_API_URL` | Whisper-compatible API base URL (enables voice/audio transcription) | No ||
172-
| `STT_API_KEY` | API key for your STT provider | No ||
173-
| `STT_MODEL` | STT model name passed to `/audio/transcriptions` | No | `whisper-large-v3-turbo` |
174-
| `STT_LANGUAGE` | Optional language hint (empty = provider auto-detect) | No ||
175-
| `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
150+
| Variable | Description | Required | Default |
151+
| -------------------------- | -------------------------------------------------------------------------------- | :------: | ------------------------ |
152+
| `TELEGRAM_BOT_TOKEN` | Bot token from @BotFather | Yes ||
153+
| `TELEGRAM_ALLOWED_USER_ID` | Your numeric Telegram user ID | Yes ||
154+
| `TELEGRAM_PROXY_URL` | Proxy URL for Telegram API (SOCKS5/HTTP) | No ||
155+
| `OPENCODE_API_URL` | OpenCode server URL | No | `http://localhost:4096` |
156+
| `OPENCODE_SERVER_USERNAME` | Server auth username | No | `opencode` |
157+
| `OPENCODE_SERVER_PASSWORD` | Server auth password | No ||
158+
| `OPENCODE_MODEL_PROVIDER` | Default model provider | Yes | `opencode` |
159+
| `OPENCODE_MODEL_ID` | Default model ID | Yes | `big-pickle` |
160+
| `BOT_LOCALE` | Bot UI language (supported locale code, e.g. `en`, `de`, `es`, `fr`, `ru`, `zh`) | No | `en` |
161+
| `SESSIONS_LIST_LIMIT` | Sessions per page in `/sessions` | No | `10` |
162+
| `PROJECTS_LIST_LIMIT` | Projects per page in `/projects` | No | `10` |
163+
| `COMMANDS_LIST_LIMIT` | Commands per page in `/commands` | No | `10` |
164+
| `TASK_LIMIT` | Maximum number of scheduled tasks that can exist at once | No | `10` |
165+
| `HIDE_THINKING_MESSAGES` | Hide `💭 Thinking...` service messages | No | `false` |
166+
| `HIDE_TOOL_CALL_MESSAGES` | Hide tool-call service messages (`💻 bash ...`, `📖 read ...`, etc.) | No | `false` |
167+
| `MESSAGE_FORMAT_MODE` | Assistant reply formatting mode: `markdown` (Telegram MarkdownV2) or `raw` | No | `markdown` |
168+
| `CODE_FILE_MAX_SIZE_KB` | Max file size (KB) to send as document | No | `100` |
169+
| `STT_API_URL` | Whisper-compatible API base URL (enables voice/audio transcription) | No ||
170+
| `STT_API_KEY` | API key for your STT provider | No ||
171+
| `STT_MODEL` | STT model name passed to `/audio/transcriptions` | No | `whisper-large-v3-turbo` |
172+
| `STT_LANGUAGE` | Optional language hint (empty = provider auto-detect) | No ||
173+
| `LOG_LEVEL` | Log level (`debug`, `info`, `warn`, `error`) | No | `info` |
176174

177175
> **Keep your `.env` file private.** It contains your bot token. Never commit it to version control.
178176

src/bot/index.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ function prepareStreamingPayload(messageText: string): StreamingMessagePayload |
133133
}
134134

135135
const toolMessageBatcher = new ToolMessageBatcher({
136-
intervalSeconds: 5,
137136
sendText: async (sessionId, text) => {
138137
if (!botInstance || !chatIdInstance) {
139138
return;
@@ -348,7 +347,6 @@ async function ensureEventSubscription(directory: string): Promise<void> {
348347
return;
349348
}
350349

351-
toolMessageBatcher.setIntervalSeconds(config.bot.serviceMessagesIntervalSec);
352350
summaryAggregator.setTypingIndicatorEnabled(true);
353351
summaryAggregator.setOnCleared(() => {
354352
toolMessageBatcher.clearAll("summary_aggregator_clear");
@@ -357,10 +355,6 @@ async function ensureEventSubscription(directory: string): Promise<void> {
357355
});
358356

359357
summaryAggregator.setOnPartial((sessionId, messageId, messageText) => {
360-
if (!config.bot.responseStreaming) {
361-
return;
362-
}
363-
364358
if (!botInstance || !chatIdInstance) {
365359
return;
366360
}
@@ -404,7 +398,6 @@ async function ensureEventSubscription(directory: string): Promise<void> {
404398

405399
try {
406400
await finalizeAssistantResponse({
407-
responseStreaming: config.bot.responseStreaming,
408401
sessionId,
409402
messageId,
410403
messageText,
@@ -611,7 +604,6 @@ async function ensureEventSubscription(directory: string): Promise<void> {
611604
await toolCallStreamer.breakSession(sessionId, "thinking_started");
612605

613606
deliverThinkingMessage(sessionId, toolMessageBatcher, {
614-
responseStreaming: config.bot.responseStreaming,
615607
hideThinkingMessages: config.bot.hideThinkingMessages,
616608
});
617609

@@ -793,10 +785,6 @@ async function ensureEventSubscription(directory: string): Promise<void> {
793785

794786
export function createBot(): Bot<Context> {
795787
clearAllInteractionState("bot_startup");
796-
toolMessageBatcher.setIntervalSeconds(config.bot.serviceMessagesIntervalSec);
797-
logger.debug(
798-
`[ToolBatcher] Service messages interval: ${config.bot.serviceMessagesIntervalSec}s`,
799-
);
800788

801789
const botOptions: ConstructorParameters<typeof Bot<Context>>[1] = {};
802790

src/bot/utils/finalize-assistant-response.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { TelegramTextFormat } from "./telegram-text.js";
33
import { logger } from "../../utils/logger.js";
44

55
interface FinalizeAssistantResponseOptions {
6-
responseStreaming: boolean;
76
sessionId: string;
87
messageId: string;
98
messageText: string;
@@ -22,7 +21,6 @@ interface FinalizeAssistantResponseOptions {
2221
}
2322

2423
export async function finalizeAssistantResponse({
25-
responseStreaming,
2624
sessionId,
2725
messageId,
2826
messageText,
@@ -37,22 +35,20 @@ export async function finalizeAssistantResponse({
3735
}: FinalizeAssistantResponseOptions): Promise<boolean> {
3836
let streamedMessageIds: number[] = [];
3937

40-
if (responseStreaming) {
41-
const preparedStreamPayload = prepareStreamingPayload(messageText);
42-
if (preparedStreamPayload) {
43-
preparedStreamPayload.sendOptions = undefined;
44-
preparedStreamPayload.editOptions = undefined;
45-
}
38+
const preparedStreamPayload = prepareStreamingPayload(messageText);
39+
if (preparedStreamPayload) {
40+
preparedStreamPayload.sendOptions = undefined;
41+
preparedStreamPayload.editOptions = undefined;
42+
}
4643

47-
const result = await responseStreamer.complete(
48-
sessionId,
49-
messageId,
50-
preparedStreamPayload ?? undefined,
51-
);
44+
const result = await responseStreamer.complete(
45+
sessionId,
46+
messageId,
47+
preparedStreamPayload ?? undefined,
48+
);
5249

53-
if (result.streamed) {
54-
streamedMessageIds = result.telegramMessageIds;
55-
}
50+
if (result.streamed) {
51+
streamedMessageIds = result.telegramMessageIds;
5652
}
5753

5854
await flushPendingServiceMessages();
@@ -63,7 +59,10 @@ export async function finalizeAssistantResponse({
6359
try {
6460
await deleteMessages(streamedMessageIds);
6561
} catch (err) {
66-
logger.warn("[FinalizeResponse] Failed to delete streamed messages, sending with keyboard anyway:", err);
62+
logger.warn(
63+
"[FinalizeResponse] Failed to delete streamed messages, sending with keyboard anyway:",
64+
err,
65+
);
6766
}
6867
}
6968

src/bot/utils/thinking-message.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { ToolMessageBatcher } from "../../summary/tool-message-batcher.js";
22
import { t } from "../../i18n/index.js";
33

44
interface ThinkingMessageOptions {
5-
responseStreaming: boolean;
65
hideThinkingMessages: boolean;
76
}
87

@@ -18,6 +17,5 @@ export function deliverThinkingMessage(
1817
}
1918

2019
const message = t("bot.thinking");
21-
void options.responseStreaming;
2220
batcher.sendTextNow(sessionId, message, "thinking_started");
2321
}

src/config.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,6 @@ function getOptionalPositiveIntEnvVar(key: string, defaultValue: number): number
3232
return parsedValue;
3333
}
3434

35-
function getOptionalNonNegativeIntEnvVarFromKeys(keys: string[], defaultValue: number): number {
36-
for (const key of keys) {
37-
const value = getEnvVar(key, false);
38-
if (!value) {
39-
continue;
40-
}
41-
42-
const parsedValue = Number.parseInt(value, 10);
43-
if (Number.isNaN(parsedValue) || parsedValue < 0) {
44-
return defaultValue;
45-
}
46-
47-
return parsedValue;
48-
}
49-
50-
return defaultValue;
51-
}
52-
5335
function getOptionalLocaleEnvVar(key: string, defaultValue: Locale): Locale {
5436
const value = getEnvVar(key, false);
5537
return normalizeLocale(value, defaultValue);
@@ -117,13 +99,8 @@ export const config = {
11799
commandsListLimit: getOptionalPositiveIntEnvVar("COMMANDS_LIST_LIMIT", 10),
118100
taskLimit: getOptionalPositiveIntEnvVar("TASK_LIMIT", 10),
119101
locale: getOptionalLocaleEnvVar("BOT_LOCALE", "en"),
120-
serviceMessagesIntervalSec: getOptionalNonNegativeIntEnvVarFromKeys(
121-
["SERVICE_MESSAGES_INTERVAL_SEC", "TOOL_MESSAGES_INTERVAL_SEC"],
122-
5,
123-
),
124102
hideThinkingMessages: getOptionalBooleanEnvVar("HIDE_THINKING_MESSAGES", false),
125103
hideToolCallMessages: getOptionalBooleanEnvVar("HIDE_TOOL_CALL_MESSAGES", false),
126-
responseStreaming: getOptionalBooleanEnvVar("RESPONSE_STREAMING", true),
127104
messageFormatMode: getOptionalMessageFormatModeEnvVar("MESSAGE_FORMAT_MODE", "markdown"),
128105
},
129106
files: {

0 commit comments

Comments
 (0)