mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 05:20:22 +00:00
fix(msteams): prevent duplicate text when stream exceeds 4000 char limit (#59297)
When a streamed response exceeds TEAMS_MAX_CHARS, the stream sets streamFailed=true and finalizes. Previously, hasContent returned false when streamFailed was true, causing preparePayload to pass through the full payload for block delivery, duplicating already-streamed text. Now tracks streamed length and strips the already-delivered prefix from fallback payloads. Fixes #58601 thanks @bradgroux
This commit is contained in:
@@ -55,17 +55,44 @@ export function createTeamsReplyStreamController(params: {
|
||||
},
|
||||
|
||||
preparePayload(payload: ReplyPayload): ReplyPayload | undefined {
|
||||
if (!stream || !streamReceivedTokens || !stream.hasContent || stream.isFinalized) {
|
||||
if (!stream || !streamReceivedTokens) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
// Stream handled this text segment — finalize it and reset so any
|
||||
const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
|
||||
|
||||
// Stream failed after partial delivery (e.g. > 4000 chars). Send only
|
||||
// the unstreamed suffix via block delivery to avoid duplicate text.
|
||||
if (stream.isFailed) {
|
||||
streamReceivedTokens = false;
|
||||
|
||||
if (!payload.text) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
const streamedLength = stream.streamedLength;
|
||||
if (streamedLength <= 0) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
const remainingText = payload.text.slice(streamedLength);
|
||||
if (!remainingText) {
|
||||
return hasMedia ? { ...payload, text: undefined } : undefined;
|
||||
}
|
||||
|
||||
return { ...payload, text: remainingText };
|
||||
}
|
||||
|
||||
if (!stream.hasContent || stream.isFinalized) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
// Stream handled this text segment. Finalize it and reset so any
|
||||
// subsequent text segments (after tool calls) use fallback delivery.
|
||||
// finalize() is idempotent; the later call in markDispatchIdle is a no-op.
|
||||
streamReceivedTokens = false;
|
||||
pendingFinalize = stream.finalize();
|
||||
|
||||
const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
|
||||
if (!hasMedia) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user