fix: narrow finalize boundary-drop guard (#27711) (thanks @scz2011)
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- TUI/stream assembly: preserve streamed text across real tool-boundary drops without keeping stale streamed text when non-text blocks appear only in the final payload. Landed from contributor PR #27711 by @scz2011. (#27674)
|
||||
- Models/MiniMax auth header defaults: set `authHeader: true` for both onboarding-generated MiniMax API providers and implicit built-in MiniMax (`minimax`, `minimax-portal`) provider templates so first requests no longer fail with MiniMax `401 authentication_error` due to missing `Authorization` header. Landed from contributor PRs #27622 by @riccoyuanft and #27631 by @kevinWangSheng. (#27600, #15303)
|
||||
- Pi image-token usage: stop re-injecting history image blocks each turn, process image references from the current prompt only, and prune already-answered user-image blocks in stored history to prevent runaway token growth. (#27602)
|
||||
- BlueBubbles/SSRF: auto-allowlist the configured `serverUrl` hostname for attachment fetches so localhost/private-IP BlueBubbles setups are no longer false-blocked by default SSRF checks. Landed from contributor PR #27648 by @lailoo. (#27599) Thanks @taylorhou for reporting.
|
||||
|
||||
@@ -152,6 +152,35 @@ describe("TuiStreamAssembler", () => {
|
||||
expect(finalText).toBe("Draft line 1");
|
||||
});
|
||||
|
||||
it("prefers final text when non-text blocks appear only in final payload", () => {
|
||||
const assembler = new TuiStreamAssembler();
|
||||
assembler.ingestDelta(
|
||||
"run-5c",
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "text", text: "Draft line 1" },
|
||||
{ type: "text", text: "Draft line 2" },
|
||||
],
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
const finalText = assembler.finalize(
|
||||
"run-5c",
|
||||
{
|
||||
role: "assistant",
|
||||
content: [
|
||||
{ type: "tool_use", name: "search" },
|
||||
{ type: "text", text: "Draft line 2" },
|
||||
],
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
expect(finalText).toBe("Draft line 2");
|
||||
});
|
||||
|
||||
it("accepts richer final payload when it extends streamed text", () => {
|
||||
const assembler = new TuiStreamAssembler();
|
||||
assembler.ingestDelta(
|
||||
|
||||
@@ -97,7 +97,10 @@ export class TuiStreamAssembler {
|
||||
state: RunStreamState,
|
||||
message: unknown,
|
||||
showThinking: boolean,
|
||||
opts?: { protectBoundaryDrops?: boolean },
|
||||
opts?: {
|
||||
protectBoundaryDrops?: boolean;
|
||||
useIncomingNonTextForBoundaryDrops?: boolean;
|
||||
},
|
||||
) {
|
||||
const thinkingText = extractThinkingFromMessage(message);
|
||||
const contentText = extractContentFromMessage(message);
|
||||
@@ -108,9 +111,11 @@ export class TuiStreamAssembler {
|
||||
}
|
||||
if (contentText) {
|
||||
const nextContentBlocks = textBlocks.length > 0 ? textBlocks : [contentText];
|
||||
const useIncomingNonTextForBoundaryDrops = opts?.useIncomingNonTextForBoundaryDrops !== false;
|
||||
const shouldPreserveBoundaryDroppedText =
|
||||
opts?.protectBoundaryDrops === true &&
|
||||
(state.sawNonTextContentBlocks || sawNonTextContentBlocks) &&
|
||||
(state.sawNonTextContentBlocks ||
|
||||
(useIncomingNonTextForBoundaryDrops && sawNonTextContentBlocks)) &&
|
||||
isDroppedBoundaryTextBlockSubset({
|
||||
streamedTextBlocks: state.contentBlocks,
|
||||
finalTextBlocks: nextContentBlocks,
|
||||
@@ -151,7 +156,10 @@ export class TuiStreamAssembler {
|
||||
const streamedDisplayText = state.displayText;
|
||||
const streamedTextBlocks = [...state.contentBlocks];
|
||||
const streamedSawNonTextContentBlocks = state.sawNonTextContentBlocks;
|
||||
this.updateRunState(state, message, showThinking, { protectBoundaryDrops: true });
|
||||
this.updateRunState(state, message, showThinking, {
|
||||
protectBoundaryDrops: true,
|
||||
useIncomingNonTextForBoundaryDrops: false,
|
||||
});
|
||||
const finalComposed = state.displayText;
|
||||
const shouldKeepStreamedText =
|
||||
streamedSawNonTextContentBlocks &&
|
||||
|
||||
Reference in New Issue
Block a user