mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-25 16:12:13 +00:00
fix: stabilize flaky tests and sanitize directive-only chat tags
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { stripInlineDirectiveTagsForDisplay } from "./directive-tags.js";
|
||||
import {
|
||||
stripInlineDirectiveTagsForDisplay,
|
||||
stripInlineDirectiveTagsFromMessageForDisplay,
|
||||
} from "./directive-tags.js";
|
||||
|
||||
describe("stripInlineDirectiveTagsForDisplay", () => {
|
||||
test("removes reply and audio directives", () => {
|
||||
@@ -23,3 +26,34 @@ describe("stripInlineDirectiveTagsForDisplay", () => {
|
||||
expect(result.text).toBe(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe("stripInlineDirectiveTagsFromMessageForDisplay", () => {
|
||||
test("strips inline directives from text content blocks", () => {
|
||||
const input = {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "hello [[reply_to_current]] world [[audio_as_voice]]" }],
|
||||
};
|
||||
const result = stripInlineDirectiveTagsFromMessageForDisplay(input);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.content).toEqual([{ type: "text", text: "hello world " }]);
|
||||
});
|
||||
|
||||
test("preserves empty-string text when directives are entire content", () => {
|
||||
const input = {
|
||||
role: "assistant",
|
||||
content: [{ type: "text", text: "[[reply_to_current]]" }],
|
||||
};
|
||||
const result = stripInlineDirectiveTagsFromMessageForDisplay(input);
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.content).toEqual([{ type: "text", text: "" }]);
|
||||
});
|
||||
|
||||
test("returns original message when content is not an array", () => {
|
||||
const input = {
|
||||
role: "assistant",
|
||||
content: "plain text",
|
||||
};
|
||||
const result = stripInlineDirectiveTagsFromMessageForDisplay(input);
|
||||
expect(result).toEqual(input);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,6 +29,17 @@ type StripInlineDirectiveTagsResult = {
|
||||
changed: boolean;
|
||||
};
|
||||
|
||||
type MessageTextPart = {
|
||||
type: "text";
|
||||
text: string;
|
||||
} & Record<string, unknown>;
|
||||
|
||||
type MessagePart = Record<string, unknown> | null | undefined;
|
||||
|
||||
export type DisplayMessageWithContent = {
|
||||
content?: unknown;
|
||||
} & Record<string, unknown>;
|
||||
|
||||
export function stripInlineDirectiveTagsForDisplay(text: string): StripInlineDirectiveTagsResult {
|
||||
if (!text) {
|
||||
return { text, changed: false };
|
||||
@@ -41,6 +52,36 @@ export function stripInlineDirectiveTagsForDisplay(text: string): StripInlineDir
|
||||
};
|
||||
}
|
||||
|
||||
function isMessageTextPart(part: MessagePart): part is MessageTextPart {
|
||||
return Boolean(part) && part?.type === "text" && typeof part.text === "string";
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips inline directive tags from message text blocks while preserving message shape.
|
||||
* Empty post-strip text stays empty-string to preserve caller semantics.
|
||||
*/
|
||||
export function stripInlineDirectiveTagsFromMessageForDisplay(
|
||||
message: DisplayMessageWithContent | undefined,
|
||||
): DisplayMessageWithContent | undefined {
|
||||
if (!message) {
|
||||
return message;
|
||||
}
|
||||
if (!Array.isArray(message.content)) {
|
||||
return message;
|
||||
}
|
||||
const cleaned = message.content.map((part) => {
|
||||
if (!part || typeof part !== "object") {
|
||||
return part;
|
||||
}
|
||||
const record = part as MessagePart;
|
||||
if (!isMessageTextPart(record)) {
|
||||
return part;
|
||||
}
|
||||
return { ...record, text: stripInlineDirectiveTagsForDisplay(record.text).text };
|
||||
});
|
||||
return { ...message, content: cleaned };
|
||||
}
|
||||
|
||||
export function parseInlineDirectives(
|
||||
text?: string,
|
||||
options: InlineDirectiveParseOptions = {},
|
||||
|
||||
Reference in New Issue
Block a user