llama-stack-mirror/llama_stack/ui/lib/format-message-content.test.ts
ehhuang 2708312168
feat(ui): implement chat completion views (#2201)
# What does this PR do?
 Implements table and detail views for chat completions

<img width="1548" alt="image"
src="https://github.com/user-attachments/assets/01061b7f-0d47-4b3b-b5ac-2df8f9035ef6"
/>
<img width="1549" alt="image"
src="https://github.com/user-attachments/assets/738d8612-8258-4c2c-858b-bee39030649f"
/>


## Test Plan
npm run test
2025-05-22 22:05:54 -07:00

193 lines
6.2 KiB
TypeScript

import {
extractTextFromContentPart,
extractDisplayableText,
} from "./format-message-content";
import { ChatMessage } from "@/lib/types";
describe("extractTextFromContentPart", () => {
it("should return an empty string for null or undefined input", () => {
expect(extractTextFromContentPart(null)).toBe("");
expect(extractTextFromContentPart(undefined)).toBe("");
});
it("should return the string itself if input is a string", () => {
expect(extractTextFromContentPart("Hello, world!")).toBe("Hello, world!");
expect(extractTextFromContentPart("")).toBe("");
});
it("should extract text from an array of text content objects", () => {
const content = [{ type: "text", text: "Which planet do humans live on?" }];
expect(extractTextFromContentPart(content)).toBe(
"Which planet do humans live on?",
);
});
it("should join text from multiple text content objects in an array", () => {
const content = [
{ type: "text", text: "Hello," },
{ type: "text", text: "world!" },
];
expect(extractTextFromContentPart(content)).toBe("Hello, world!");
});
it("should handle mixed text and image_url types in an array", () => {
const content = [
{ type: "text", text: "Look at this:" },
{ type: "image_url", image_url: { url: "http://example.com/image.png" } },
{ type: "text", text: "It's an image." },
];
expect(extractTextFromContentPart(content)).toBe(
"Look at this: [Image] It's an image.",
);
});
it("should return '[Image]' for an array with only an image_url object", () => {
const content = [
{ type: "image_url", image_url: { url: "http://example.com/image.png" } },
];
expect(extractTextFromContentPart(content)).toBe("[Image]");
});
it("should return an empty string for an empty array", () => {
expect(extractTextFromContentPart([])).toBe("");
});
it("should handle arrays with plain strings", () => {
const content = ["This is", " a test."] as any;
expect(extractTextFromContentPart(content)).toBe("This is a test.");
});
it("should filter out malformed or unrecognized objects in an array", () => {
const content = [
{ type: "text", text: "Valid" },
{ type: "unknown" },
{ text: "Missing type" },
null,
undefined,
{ type: "text", noTextProperty: true },
] as any;
expect(extractTextFromContentPart(content)).toBe("Valid");
});
it("should handle an array of mixed valid items and plain strings", () => {
const content = [
{ type: "text", text: "First part." },
"Just a string.",
{ type: "image_url", image_url: { url: "http://example.com/image.png" } },
{ type: "text", text: "Last part." },
] as any;
expect(extractTextFromContentPart(content)).toBe(
"First part. Just a string. [Image] Last part.",
);
});
});
describe("extractDisplayableText (composite function)", () => {
const mockFormatToolCallToString = (toolCall: any) => {
if (!toolCall || !toolCall.function || !toolCall.function.name) return "";
const args = toolCall.function.arguments
? JSON.stringify(toolCall.function.arguments)
: "";
return `${toolCall.function.name}(${args})`;
};
it("should return empty string for null or undefined message", () => {
expect(extractDisplayableText(null)).toBe("");
expect(extractDisplayableText(undefined)).toBe("");
});
it("should return only content part if no tool calls", () => {
const message: ChatMessage = {
role: "assistant",
content: "Hello there!",
};
expect(extractDisplayableText(message)).toBe("Hello there!");
});
it("should return only content part for complex content if no tool calls", () => {
const message: ChatMessage = {
role: "user",
content: [
{ type: "text", text: "Part 1" },
{ type: "text", text: "Part 2" },
],
};
expect(extractDisplayableText(message)).toBe("Part 1 Part 2");
});
it("should return only formatted tool call if content is empty or null", () => {
const toolCall = {
function: { name: "search", arguments: { query: "cats" } },
};
const messageWithEffectivelyEmptyContent: ChatMessage = {
role: "assistant",
content: "",
tool_calls: [toolCall],
};
expect(extractDisplayableText(messageWithEffectivelyEmptyContent)).toBe(
mockFormatToolCallToString(toolCall),
);
const messageWithEmptyContent: ChatMessage = {
role: "assistant",
content: "",
tool_calls: [toolCall],
};
expect(extractDisplayableText(messageWithEmptyContent)).toBe(
mockFormatToolCallToString(toolCall),
);
});
it("should combine content and formatted tool call", () => {
const toolCall = {
function: { name: "calculator", arguments: { expr: "2+2" } },
};
const message: ChatMessage = {
role: "assistant",
content: "The result is:",
tool_calls: [toolCall],
};
const expectedToolCallStr = mockFormatToolCallToString(toolCall);
expect(extractDisplayableText(message)).toBe(
`The result is: ${expectedToolCallStr}`,
);
});
it("should handle message with content an array and a tool call", () => {
const toolCall = {
function: { name: "get_weather", arguments: { city: "London" } },
};
const message: ChatMessage = {
role: "assistant",
content: [
{ type: "text", text: "Okay, checking weather for" },
{ type: "text", text: "London." },
],
tool_calls: [toolCall],
};
const expectedToolCallStr = mockFormatToolCallToString(toolCall);
expect(extractDisplayableText(message)).toBe(
`Okay, checking weather for London. ${expectedToolCallStr}`,
);
});
it("should return only content if tool_calls array is empty or undefined", () => {
const messageEmptyToolCalls: ChatMessage = {
role: "assistant",
content: "No tools here.",
tool_calls: [],
};
expect(extractDisplayableText(messageEmptyToolCalls)).toBe(
"No tools here.",
);
const messageUndefinedToolCalls: ChatMessage = {
role: "assistant",
content: "Still no tools.",
tool_calls: undefined,
};
expect(extractDisplayableText(messageUndefinedToolCalls)).toBe(
"Still no tools.",
);
});
});