chore: remove typing interval

This commit is contained in:
rdmclin2
2026-03-25 20:36:29 +08:00
parent a1e91ab30d
commit 44f0c0ed0c
4 changed files with 3 additions and 72 deletions

View File

@@ -26,7 +26,6 @@ import {
renderStopped,
splitMessage,
} from './replyTemplate';
import { startTypingKeepAlive, stopTypingKeepAlive } from './typingKeepAlive';
const log = debug('lobe-server:bot:agent-bridge');
@@ -391,9 +390,7 @@ export class AgentBridgeService {
const queueMode = isQueueAgentRuntimeEnabled();
let queueHandoffSucceeded = false;
// Keep typing indicator alive (e.g. Telegram expires after ~5s, Discord after ~10s)
const platformThreadId = botContext?.platformThreadId ?? thread.id;
startTypingKeepAlive(platformThreadId, () => thread.startTyping());
try {
// executeWithCallback handles progress message (post + edit at each step)
@@ -421,9 +418,8 @@ export class AgentBridgeService {
} finally {
AgentBridgeService.activeThreads.delete(thread.id);
// In queue mode, the callback owns cleanup only after webhook handoff succeeds.
// If setup fails before that point, clean up locally to avoid leaked keepalive/reactions.
// If setup fails before that point, clean up locally to avoid leaked reactions.
if (!queueMode || !queueHandoffSucceeded) {
stopTypingKeepAlive(platformThreadId);
await this.removeReceivedReaction(thread, message, client);
}
}
@@ -492,9 +488,7 @@ export class AgentBridgeService {
);
await thread.startTyping();
// Keep typing indicator alive
const platformThreadId = botContext?.platformThreadId ?? thread.id;
startTypingKeepAlive(platformThreadId, () => thread.startTyping());
try {
// executeWithCallback handles progress message (post + edit at each step)
@@ -527,7 +521,6 @@ export class AgentBridgeService {
AgentBridgeService.activeThreads.delete(thread.id);
// In queue mode, the callback owns cleanup only after webhook handoff succeeds.
if (!queueMode || !queueHandoffSucceeded) {
stopTypingKeepAlive(platformThreadId);
await this.removeReceivedReaction(thread, message, opts.client);
}
}

View File

@@ -17,7 +17,6 @@ import {
renderStopped,
splitMessage,
} from './replyTemplate';
import { stopTypingKeepAlive } from './typingKeepAlive';
const log = debug('lobe-server:bot:callback');
@@ -85,9 +84,6 @@ export class BotCallbackService {
await this.handleStep(body, messenger, progressMessageId, client);
}
} else if (type === 'completion') {
// Stop typing keepalive before sending the final message
stopTypingKeepAlive(platformThreadId);
await this.handleCompletion(
body,
messenger,

View File

@@ -5,8 +5,6 @@ const mockExecAgent = vi.hoisted(() => vi.fn());
const mockFormatPrompt = vi.hoisted(() => vi.fn());
const mockGetPlatform = vi.hoisted(() => vi.fn());
const mockIsQueueAgentRuntimeEnabled = vi.hoisted(() => vi.fn());
const mockStartTypingKeepAlive = vi.hoisted(() => vi.fn());
const mockStopTypingKeepAlive = vi.hoisted(() => vi.fn());
vi.mock('@/database/models/topic', () => ({
TopicModel: vi.fn(),
@@ -49,11 +47,6 @@ vi.mock('@/server/services/bot/platforms', () => ({
},
}));
vi.mock('@/server/services/bot/typingKeepAlive', () => ({
startTypingKeepAlive: mockStartTypingKeepAlive,
stopTypingKeepAlive: mockStopTypingKeepAlive,
}));
const { AgentBridgeService } = await import('../AgentBridgeService');
const FAKE_DB = {} as any;
@@ -119,7 +112,7 @@ describe('AgentBridgeService', () => {
mockIsQueueAgentRuntimeEnabled.mockReturnValue(true);
});
it('cleans up keepalive and received reaction when queue-mode mention setup fails before callback handoff', async () => {
it('cleans up received reaction when queue-mode mention setup fails before callback handoff', async () => {
const service = new AgentBridgeService(FAKE_DB, USER_ID);
const thread = createThread();
const message = createMessage();
@@ -132,8 +125,6 @@ describe('AgentBridgeService', () => {
debounceMs: 0,
});
expect(mockStartTypingKeepAlive).toHaveBeenCalledWith(THREAD_ID, expect.any(Function));
expect(mockStopTypingKeepAlive).toHaveBeenCalledWith(THREAD_ID);
const [mentionReactionThreadId, mentionReactionMessageId, mentionReactionEmoji] =
thread.adapter.removeReaction.mock.calls[0];
expect(mentionReactionThreadId).toBe(THREAD_ID);
@@ -142,7 +133,7 @@ describe('AgentBridgeService', () => {
expect(mockExecAgent).not.toHaveBeenCalled();
});
it('cleans up keepalive and received reaction when queue-mode subscribed-message setup fails before callback handoff', async () => {
it('cleans up received reaction when queue-mode subscribed-message setup fails before callback handoff', async () => {
const service = new AgentBridgeService(FAKE_DB, USER_ID);
const thread = createThread({ topicId: 'topic-1' });
const message = createMessage();
@@ -155,8 +146,6 @@ describe('AgentBridgeService', () => {
debounceMs: 0,
});
expect(mockStartTypingKeepAlive).toHaveBeenCalledWith(THREAD_ID, expect.any(Function));
expect(mockStopTypingKeepAlive).toHaveBeenCalledWith(THREAD_ID);
const [replyReactionThreadId, replyReactionMessageId, replyReactionEmoji] =
thread.adapter.removeReaction.mock.calls[0];
expect(replyReactionThreadId).toBe(THREAD_ID);

View File

@@ -1,47 +0,0 @@
import debug from 'debug';
const log = debug('lobe-server:bot:typing-keepalive');
const TYPING_INTERVAL_MS = 4000;
/**
* In-memory registry of active typing intervals.
* Keyed by platformThreadId so both AgentBridgeService (start)
* and BotCallbackService (stop) can reference the same entry.
*/
const activeIntervals = new Map<string, NodeJS.Timeout>();
/**
* Start a repeating typing indicator for a thread.
* Calls `typingFn` immediately, then every TYPING_INTERVAL_MS.
* Returns a cleanup function (also accessible via `stopTypingKeepAlive`).
*/
export function startTypingKeepAlive(threadId: string, typingFn: () => Promise<void>): () => void {
// Clear any existing interval for this thread (safety)
stopTypingKeepAlive(threadId);
log('start: threadId=%s, interval=%dms', threadId, TYPING_INTERVAL_MS);
const interval = setInterval(() => {
typingFn().catch(() => {
// Typing failures are non-critical — ignore silently
});
}, TYPING_INTERVAL_MS);
activeIntervals.set(threadId, interval);
return () => stopTypingKeepAlive(threadId);
}
/**
* Stop the typing keepalive for a thread.
* Safe to call even if no interval is active.
*/
export function stopTypingKeepAlive(threadId: string): void {
const interval = activeIntervals.get(threadId);
if (interval) {
clearInterval(interval);
activeIntervals.delete(threadId);
log('stop: threadId=%s', threadId);
}
}