Skip to content

Commit 14fa5a4

Browse files
committed
fix(channels): safely parse non-string user message content
Guard user message content parsing with Array.isArray to prevent runtime TypeError exceptions when message content is undefined, null, or non-array objects.
1 parent 06a1a84 commit 14fa5a4

2 files changed

Lines changed: 121 additions & 4 deletions

File tree

app/src/components/channels/chat-messages.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@ export function toVisibleChatItems(
6262
const text =
6363
typeof message.content === "string"
6464
? message.content
65-
: message.content
66-
.filter((part) => part.type === "text")
67-
.map((part) => part.text)
68-
.join("\n");
65+
: Array.isArray(message.content)
66+
? message.content
67+
.filter((part) => part.type === "text")
68+
.map((part) => part.text)
69+
.join("\n")
70+
: "";
6971

7072
return text ? [{ kind: "text", id: message.id, role: "user", text }] : [];
7173
});

app/tests/chat-messages.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import { describe, expect, test } from "bun:test";
2+
import type { Message } from "@ag-ui/core";
3+
import { toVisibleChatItems } from "../src/components/channels/chat-messages";
4+
5+
describe("toVisibleChatItems", () => {
6+
test("projects a user message with plain string content", () => {
7+
const messages: Message[] = [
8+
{ id: "msg-1", role: "user", content: "Hello there" },
9+
];
10+
11+
expect(toVisibleChatItems(messages)).toEqual([
12+
{ kind: "text", id: "msg-1", role: "user", text: "Hello there" },
13+
]);
14+
});
15+
16+
test("projects a user message with array content parts", () => {
17+
const messages: Message[] = [
18+
{
19+
id: "msg-2",
20+
role: "user",
21+
content: [
22+
{ type: "text", text: "First line" },
23+
{ type: "text", text: "Second line" },
24+
],
25+
},
26+
];
27+
28+
expect(toVisibleChatItems(messages)).toEqual([
29+
{
30+
kind: "text",
31+
id: "msg-2",
32+
role: "user",
33+
text: "First line\nSecond line",
34+
},
35+
]);
36+
});
37+
38+
test("filters out non-text parts from user message array content", () => {
39+
const messages: Message[] = [
40+
{
41+
id: "msg-3",
42+
role: "user",
43+
content: [
44+
{ type: "text", text: "Visible text" },
45+
{ type: "binary", mimeType: "image/png", data: "..." } as never,
46+
],
47+
},
48+
];
49+
50+
expect(toVisibleChatItems(messages)).toEqual([
51+
{ kind: "text", id: "msg-3", role: "user", text: "Visible text" },
52+
]);
53+
});
54+
55+
test("handles undefined, null, or empty content gracefully without throwing", () => {
56+
const messages: Message[] = [
57+
{ id: "msg-4", role: "user", content: undefined as never },
58+
{ id: "msg-5", role: "user", content: null as never },
59+
{ id: "msg-6", role: "user", content: "" },
60+
{ id: "msg-7", role: "user", content: [] },
61+
];
62+
63+
expect(toVisibleChatItems(messages)).toEqual([]);
64+
});
65+
66+
test("projects assistant messages and pairs tool calls with results", () => {
67+
const messages: Message[] = [
68+
{
69+
id: "msg-8",
70+
role: "assistant",
71+
content: "Let me check that for you.",
72+
toolCalls: [
73+
{
74+
id: "call-1",
75+
type: "function",
76+
function: { name: "computer_read", arguments: "{}" },
77+
},
78+
],
79+
},
80+
{
81+
id: "msg-9",
82+
role: "tool",
83+
toolCallId: "call-1",
84+
content: "Page title: Example",
85+
} as Message,
86+
];
87+
88+
expect(toVisibleChatItems(messages)).toEqual([
89+
{
90+
kind: "text",
91+
id: "msg-8",
92+
role: "assistant",
93+
text: "Let me check that for you.",
94+
},
95+
{
96+
kind: "tool",
97+
id: "call-1",
98+
toolCall: {
99+
id: "call-1",
100+
type: "function",
101+
function: { name: "computer_read", arguments: "{}" },
102+
},
103+
result: "Page title: Example",
104+
},
105+
]);
106+
});
107+
108+
test("ignores non-user and non-assistant messages", () => {
109+
const messages: Message[] = [
110+
{ id: "msg-10", role: "system", content: "System prompt" } as Message,
111+
];
112+
113+
expect(toVisibleChatItems(messages)).toEqual([]);
114+
});
115+
});

0 commit comments

Comments
 (0)