mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-06 23:55:12 +00:00
perf(ui): split chat role normalization imports
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { ChatItem, MessageGroup, ToolCard } from "../types/chat-types.ts";
|
||||
import { extractTextCached } from "./message-extract.ts";
|
||||
import { normalizeMessage, normalizeRoleForGrouping } from "./message-normalizer.ts";
|
||||
import { normalizeMessage } from "./message-normalizer.ts";
|
||||
import { normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
import { messageMatchesSearchQuery } from "./search-match.ts";
|
||||
import { extractToolCards, extractToolPreview } from "./tool-cards.ts";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
isRenderableControlUiAvatarUrl,
|
||||
resolveAssistantTextAvatar,
|
||||
} from "../views/agents-utils.ts";
|
||||
import { normalizeRoleForGrouping } from "./message-normalizer.ts";
|
||||
import { normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
|
||||
export function renderChatAvatar(
|
||||
role: string,
|
||||
|
||||
@@ -24,11 +24,8 @@ import {
|
||||
extractThinkingCached,
|
||||
formatReasoningMarkdown,
|
||||
} from "./message-extract.ts";
|
||||
import {
|
||||
isToolResultMessage,
|
||||
normalizeMessage,
|
||||
normalizeRoleForGrouping,
|
||||
} from "./message-normalizer.ts";
|
||||
import { isToolResultMessage, normalizeMessage } from "./message-normalizer.ts";
|
||||
import { normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
import { isTtsSupported, speakText, stopTts, isTtsSpeaking } from "./speech.ts";
|
||||
import {
|
||||
extractToolCards,
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import {
|
||||
normalizeMessage,
|
||||
normalizeRoleForGrouping,
|
||||
isToolResultMessage,
|
||||
} from "./message-normalizer.ts";
|
||||
import { normalizeMessage } from "./message-normalizer.ts";
|
||||
|
||||
describe("message-normalizer", () => {
|
||||
describe("normalizeMessage", () => {
|
||||
@@ -390,69 +386,4 @@ describe("message-normalizer", () => {
|
||||
expect(result.senderLabel).toBe("Iris");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeRoleForGrouping", () => {
|
||||
it("returns tool for toolresult", () => {
|
||||
expect(normalizeRoleForGrouping("toolresult")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("toolResult")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("TOOLRESULT")).toBe("tool");
|
||||
});
|
||||
|
||||
it("returns tool for tool_result", () => {
|
||||
expect(normalizeRoleForGrouping("tool_result")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("TOOL_RESULT")).toBe("tool");
|
||||
});
|
||||
|
||||
it("returns tool for tool", () => {
|
||||
expect(normalizeRoleForGrouping("tool")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("Tool")).toBe("tool");
|
||||
});
|
||||
|
||||
it("returns tool for function", () => {
|
||||
expect(normalizeRoleForGrouping("function")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("Function")).toBe("tool");
|
||||
});
|
||||
|
||||
it("preserves user role", () => {
|
||||
expect(normalizeRoleForGrouping("user")).toBe("user");
|
||||
expect(normalizeRoleForGrouping("User")).toBe("User");
|
||||
});
|
||||
|
||||
it("preserves assistant role", () => {
|
||||
expect(normalizeRoleForGrouping("assistant")).toBe("assistant");
|
||||
});
|
||||
|
||||
it("preserves system role", () => {
|
||||
expect(normalizeRoleForGrouping("system")).toBe("system");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isToolResultMessage", () => {
|
||||
it("returns true for toolresult role", () => {
|
||||
expect(isToolResultMessage({ role: "toolresult" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "toolResult" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "TOOLRESULT" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for tool_result role", () => {
|
||||
expect(isToolResultMessage({ role: "tool_result" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "TOOL_RESULT" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for other roles", () => {
|
||||
expect(isToolResultMessage({ role: "user" })).toBe(false);
|
||||
expect(isToolResultMessage({ role: "assistant" })).toBe(false);
|
||||
expect(isToolResultMessage({ role: "tool" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for missing role", () => {
|
||||
expect(isToolResultMessage({})).toBe(false);
|
||||
expect(isToolResultMessage({ content: "test" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for non-string role", () => {
|
||||
expect(isToolResultMessage({ role: 123 })).toBe(false);
|
||||
expect(isToolResultMessage({ role: null })).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,7 @@ import { mediaKindFromMime } from "../../../../src/media/constants.js";
|
||||
import { splitMediaFromOutput } from "../../../../src/media/parse.js";
|
||||
import { parseInlineDirectives } from "../../../../src/utils/directive-tags.js";
|
||||
import type { NormalizedMessage, MessageContentItem } from "../types/chat-types.ts";
|
||||
export { isToolResultMessage, normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
|
||||
function coerceCanvasPreview(
|
||||
value: unknown,
|
||||
@@ -389,39 +390,3 @@ export function normalizeMessage(message: unknown): NormalizedMessage {
|
||||
...(replyTarget ? { replyTarget } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize role for grouping purposes.
|
||||
*/
|
||||
export function normalizeRoleForGrouping(role: string): string {
|
||||
const lower = role.toLowerCase();
|
||||
// Preserve original casing when it's already a core role.
|
||||
if (role === "user" || role === "User") {
|
||||
return role;
|
||||
}
|
||||
if (role === "assistant") {
|
||||
return "assistant";
|
||||
}
|
||||
if (role === "system") {
|
||||
return "system";
|
||||
}
|
||||
// Keep tool-related roles distinct so the UI can style/toggle them.
|
||||
if (
|
||||
lower === "toolresult" ||
|
||||
lower === "tool_result" ||
|
||||
lower === "tool" ||
|
||||
lower === "function"
|
||||
) {
|
||||
return "tool";
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a message is a tool result message based on its role.
|
||||
*/
|
||||
export function isToolResultMessage(message: unknown): boolean {
|
||||
const m = message as Record<string, unknown>;
|
||||
const role = typeof m.role === "string" ? m.role.toLowerCase() : "";
|
||||
return role === "toolresult" || role === "tool_result";
|
||||
}
|
||||
|
||||
41
ui/src/ui/chat/role-normalizer.test.ts
Normal file
41
ui/src/ui/chat/role-normalizer.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isToolResultMessage, normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
|
||||
describe("normalizeRoleForGrouping", () => {
|
||||
it("returns tool for tool result role variants", () => {
|
||||
expect(normalizeRoleForGrouping("toolresult")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("toolResult")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("TOOLRESULT")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("tool_result")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("TOOL_RESULT")).toBe("tool");
|
||||
});
|
||||
|
||||
it("returns tool for tool and function roles", () => {
|
||||
expect(normalizeRoleForGrouping("tool")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("Tool")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("function")).toBe("tool");
|
||||
expect(normalizeRoleForGrouping("Function")).toBe("tool");
|
||||
});
|
||||
|
||||
it("preserves core roles", () => {
|
||||
expect(normalizeRoleForGrouping("user")).toBe("user");
|
||||
expect(normalizeRoleForGrouping("User")).toBe("User");
|
||||
expect(normalizeRoleForGrouping("assistant")).toBe("assistant");
|
||||
expect(normalizeRoleForGrouping("system")).toBe("system");
|
||||
});
|
||||
|
||||
it("detects only tool result role variants", () => {
|
||||
expect(isToolResultMessage({ role: "toolresult" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "toolResult" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "TOOLRESULT" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "tool_result" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "TOOL_RESULT" })).toBe(true);
|
||||
expect(isToolResultMessage({ role: "user" })).toBe(false);
|
||||
expect(isToolResultMessage({ role: "assistant" })).toBe(false);
|
||||
expect(isToolResultMessage({ role: "tool" })).toBe(false);
|
||||
expect(isToolResultMessage({})).toBe(false);
|
||||
expect(isToolResultMessage({ content: "test" })).toBe(false);
|
||||
expect(isToolResultMessage({ role: 123 })).toBe(false);
|
||||
expect(isToolResultMessage({ role: null })).toBe(false);
|
||||
});
|
||||
});
|
||||
35
ui/src/ui/chat/role-normalizer.ts
Normal file
35
ui/src/ui/chat/role-normalizer.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Normalize role for grouping purposes.
|
||||
*/
|
||||
export function normalizeRoleForGrouping(role: string): string {
|
||||
const lower = role.toLowerCase();
|
||||
// Preserve original casing when it's already a core role.
|
||||
if (role === "user" || role === "User") {
|
||||
return role;
|
||||
}
|
||||
if (role === "assistant") {
|
||||
return "assistant";
|
||||
}
|
||||
if (role === "system") {
|
||||
return "system";
|
||||
}
|
||||
// Keep tool-related roles distinct so the UI can style/toggle them.
|
||||
if (
|
||||
lower === "toolresult" ||
|
||||
lower === "tool_result" ||
|
||||
lower === "tool" ||
|
||||
lower === "function"
|
||||
) {
|
||||
return "tool";
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a message is a tool result message based on its role.
|
||||
*/
|
||||
export function isToolResultMessage(message: unknown): boolean {
|
||||
const m = message as Record<string, unknown>;
|
||||
const role = typeof m.role === "string" ? m.role.toLowerCase() : "";
|
||||
return role === "toolresult" || role === "tool_result";
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import type { SidebarContent } from "../sidebar-content.ts";
|
||||
import { formatToolDetail, resolveToolDisplay } from "../tool-display.ts";
|
||||
import type { ToolCard } from "../types/chat-types.ts";
|
||||
import { extractTextCached } from "./message-extract.ts";
|
||||
import { isToolResultMessage } from "./message-normalizer.ts";
|
||||
import { isToolResultMessage } from "./role-normalizer.ts";
|
||||
import { formatToolOutputForSidebar, getTruncatedPreview } from "./tool-helpers.ts";
|
||||
|
||||
export type ToolPreview = NonNullable<ToolCard["preview"]>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ChatItem, MessageGroup } from "../types/chat-types.ts";
|
||||
import { isToolResultMessage, normalizeRoleForGrouping } from "./message-normalizer.ts";
|
||||
import { isToolResultMessage, normalizeRoleForGrouping } from "./role-normalizer.ts";
|
||||
import { getOrCreateSessionCacheValue } from "./session-cache.ts";
|
||||
import { extractToolCards } from "./tool-cards.ts";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user