Skip to content

Commit 1f50488

Browse files
format
1 parent db59553 commit 1f50488

File tree

2 files changed

+57
-72
lines changed

2 files changed

+57
-72
lines changed

ee/ui-component/components/chat/AgentChatMessages.tsx

Lines changed: 42 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ export interface BaseDisplayObject {
3535

3636
// Text display object interface
3737
export interface TextDisplayObject extends BaseDisplayObject {
38-
type: 'text';
38+
type: "text";
3939
content: string; // Markdown content
4040
}
4141

4242
// Image display object interface
4343
export interface ImageDisplayObject extends BaseDisplayObject {
44-
type: 'image';
44+
type: "image";
4545
content: string; // Base64 encoded image
4646
caption: string; // Text describing the image
4747
}
@@ -55,7 +55,7 @@ export interface SourceObject {
5555
documentName: string;
5656
documentId: string;
5757
content?: string; // Content from the source
58-
contentType?: 'text' | 'image'; // Type of content
58+
contentType?: "text" | "image"; // Type of content
5959
}
6060

6161
// Define interface for the Tool Call
@@ -230,7 +230,7 @@ const MarkdownContent: React.FC<{ content: string }> = ({ content }) => {
230230
// Helper function to ensure image content is properly formatted
231231
const formatImageSource = (content: string): string => {
232232
// If already a data URI, use as is
233-
if (content.startsWith('data:image/')) {
233+
if (content.startsWith("data:image/")) {
234234
return content;
235235
}
236236

@@ -246,47 +246,37 @@ const formatImageSource = (content: string): string => {
246246
// Component to render display objects
247247
const DisplayObjectRenderer: React.FC<{ object: DisplayObject; isInSourceView?: boolean }> = ({
248248
object,
249-
isInSourceView = false
249+
isInSourceView = false,
250250
}) => {
251-
if (object.type === 'text') {
251+
if (object.type === "text") {
252252
return (
253253
<div className={isInSourceView ? "my-1" : "my-2"}>
254254
<MarkdownContent content={object.content} />
255-
{!isInSourceView && (
256-
<div className="mt-1 text-xs text-muted-foreground">
257-
Source: {object.source}
258-
</div>
259-
)}
255+
{!isInSourceView && <div className="mt-1 text-xs text-muted-foreground">Source: {object.source}</div>}
260256
</div>
261257
);
262-
} else if (object.type === 'image') {
258+
} else if (object.type === "image") {
263259
// Try to determine if the content has the image format information
264-
const hasImagePrefix = object.content.startsWith('data:image');
260+
const hasImagePrefix = object.content.startsWith("data:image");
265261

266262
return (
267263
<div className={isInSourceView ? "my-1" : "my-2"}>
268264
<div className="overflow-hidden rounded-md">
269265
<img
270266
src={hasImagePrefix ? object.content : `data:image/png;base64,${object.content}`}
271-
alt={object.caption || 'Image'}
272-
className="max-w-full h-auto"
273-
onError={(e) => {
267+
alt={object.caption || "Image"}
268+
className="h-auto max-w-full"
269+
onError={e => {
274270
// Fallback chain: try JPEG if PNG fails
275271
const target = e.target as HTMLImageElement;
276-
if (!hasImagePrefix && target.src.includes('image/png')) {
272+
if (!hasImagePrefix && target.src.includes("image/png")) {
277273
target.src = `data:image/jpeg;base64,${object.content}`;
278274
}
279275
}}
280276
/>
281277
</div>
282-
{object.caption && (
283-
<div className="mt-1 text-sm text-muted-foreground">{object.caption}</div>
284-
)}
285-
{!isInSourceView && (
286-
<div className="mt-1 text-xs text-muted-foreground">
287-
Source: {object.source}
288-
</div>
289-
)}
278+
{object.caption && <div className="mt-1 text-sm text-muted-foreground">{object.caption}</div>}
279+
{!isInSourceView && <div className="mt-1 text-xs text-muted-foreground">Source: {object.source}</div>}
290280
</div>
291281
);
292282
}
@@ -298,38 +288,38 @@ const detectImageFormat = (base64String: string): string => {
298288
// Check the first few bytes of the base64 to determine image format
299289
// See: https://en.wikipedia.org/wiki/List_of_file_signatures
300290
try {
301-
if (!base64String || base64String.length < 10) return 'png';
291+
if (!base64String || base64String.length < 10) return "png";
302292

303293
// Decode the first few bytes of the base64 string
304294
const firstBytes = atob(base64String.substring(0, 20));
305295

306296
// Check for common image signatures
307-
if (firstBytes.startsWith('\xFF\xD8\xFF')) return 'jpeg';
308-
if (firstBytes.startsWith('\x89PNG\r\n\x1A\n')) return 'png';
309-
if (firstBytes.startsWith('GIF87a') || firstBytes.startsWith('GIF89a')) return 'gif';
310-
if (firstBytes.startsWith('RIFF') && firstBytes.substring(8, 12) === 'WEBP') return 'webp';
297+
if (firstBytes.startsWith("\xFF\xD8\xFF")) return "jpeg";
298+
if (firstBytes.startsWith("\x89PNG\r\n\x1A\n")) return "png";
299+
if (firstBytes.startsWith("GIF87a") || firstBytes.startsWith("GIF89a")) return "gif";
300+
if (firstBytes.startsWith("RIFF") && firstBytes.substring(8, 12) === "WEBP") return "webp";
311301

312302
// Default to PNG if signature not recognized
313-
return 'png';
303+
return "png";
314304
} catch (e) {
315305
// If any error in detection, default to PNG
316-
return 'png';
306+
return "png";
317307
}
318308
};
319309

320310
// Component to render sources as tags with dropdown content
321311
const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: DisplayObject[] }> = ({
322312
sources,
323-
displayObjects = []
313+
displayObjects = [],
324314
}) => {
325315
const [selectedSource, setSelectedSource] = useState<string | null>(null);
326-
const [animation, setAnimation] = useState<'open' | 'close' | null>(null);
316+
const [animation, setAnimation] = useState<"open" | "close" | null>(null);
327317
const [visibleContent, setVisibleContent] = useState<string | null>(null);
328318

329319
useEffect(() => {
330320
// Set animation state when source is selected
331321
if (selectedSource) {
332-
setAnimation('open');
322+
setAnimation("open");
333323
setVisibleContent(selectedSource);
334324
}
335325
}, [selectedSource]);
@@ -340,7 +330,7 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
340330
const toggleSource = (sourceId: string) => {
341331
if (selectedSource === sourceId) {
342332
// Close animation
343-
setAnimation('close');
333+
setAnimation("close");
344334
setTimeout(() => {
345335
setSelectedSource(null);
346336
setVisibleContent(null);
@@ -355,26 +345,21 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
355345
// Render source content
356346
const renderSourceContent = (source: SourceObject) => {
357347
if (!source.content) {
358-
return (
359-
<div className="text-xs text-muted-foreground">
360-
No content available for this source.
361-
</div>
362-
);
348+
return <div className="text-xs text-muted-foreground">No content available for this source.</div>;
363349
}
364350

365351
// Render based on contentType
366-
if (source.contentType === 'image') {
352+
if (source.contentType === "image") {
367353
const content = source.content;
368354

369355
// Handle different image formats similar to ChatMessages.tsx
370-
if (content.startsWith('data:image/') ||
371-
// If it looks like base64, we'll try as PNG
372-
/^[A-Za-z0-9+/=]+$/.test(content.substring(0, 20))) {
373-
356+
if (
357+
content.startsWith("data:image/") ||
358+
// If it looks like base64, we'll try as PNG
359+
/^[A-Za-z0-9+/=]+$/.test(content.substring(0, 20))
360+
) {
374361
// Format similar to how ChatMessages.tsx does it
375-
const imageUrl = content.startsWith('data:image/')
376-
? content
377-
: `data:image/png;base64,${content}`;
362+
const imageUrl = content.startsWith("data:image/") ? content : `data:image/png;base64,${content}`;
378363

379364
return (
380365
<div className="flex justify-center rounded-md bg-muted p-4">
@@ -387,16 +372,12 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
387372
);
388373
} else {
389374
// If not recognizable as an image format, show as text
390-
return (
391-
<div className="whitespace-pre-wrap rounded-md bg-muted p-4 font-mono text-sm">
392-
{content}
393-
</div>
394-
);
375+
return <div className="whitespace-pre-wrap rounded-md bg-muted p-4 font-mono text-sm">{content}</div>;
395376
}
396377
} else {
397378
// Default to text/markdown with scrollable area
398379
return (
399-
<div className="max-h-[300px] overflow-y-auto pr-1 custom-scrollbar">
380+
<div className="custom-scrollbar max-h-[300px] overflow-y-auto pr-1">
400381
<MarkdownContent content={source.content} />
401382
</div>
402383
);
@@ -409,7 +390,7 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
409390
<style>{scrollbarStyles}</style>
410391

411392
<div className="mb-2 flex flex-wrap gap-2">
412-
{sources.map((source) => (
393+
{sources.map(source => (
413394
<Badge
414395
key={source.sourceId}
415396
variant={selectedSource === source.sourceId ? "default" : "outline"}
@@ -425,8 +406,11 @@ const SourcesRenderer: React.FC<{ sources: SourceObject[]; displayObjects?: Disp
425406
{visibleContent && (
426407
<div
427408
className={`mt-3 overflow-hidden rounded-md border bg-card shadow-sm transition-all duration-200 ease-in-out ${
428-
animation === 'open' ? 'max-h-[400px] opacity-100 p-3' :
429-
animation === 'close' ? 'max-h-0 opacity-0 p-0' : 'p-3'
409+
animation === "open"
410+
? "max-h-[400px] p-3 opacity-100"
411+
: animation === "close"
412+
? "max-h-0 p-0 opacity-0"
413+
: "p-3"
430414
}`}
431415
>
432416
<div className="mb-2 flex items-center justify-between">

ee/ui-component/components/chat/AgentChatTestView.tsx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,43 @@ import { AgentPreviewMessage, AgentUIMessage, DisplayObject, SourceObject, ToolC
55
import { generateUUID } from "@/lib/utils";
66

77
// Sample base64 image (small red square)
8-
const sampleBase64Image = "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
8+
const sampleBase64Image =
9+
"iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
910

1011
// Sample tool call
1112
const sampleToolCall: ToolCall = {
1213
tool_name: "web_search",
1314
tool_args: { query: "sample search query" },
14-
tool_result: { results: ["Sample result 1", "Sample result 2"] }
15+
tool_result: { results: ["Sample result 1", "Sample result 2"] },
1516
};
1617

1718
// Sample display objects
1819
const sampleDisplayObjects: DisplayObject[] = [
1920
{
20-
type: 'text',
21+
type: "text",
2122
content: "This is a **markdown** text display object with _formatting_.",
22-
source: "source-123"
23+
source: "source-123",
2324
},
2425
{
25-
type: 'image',
26+
type: "image",
2627
content: sampleBase64Image,
2728
caption: "A sample image caption",
28-
source: "source-456"
29-
}
29+
source: "source-456",
30+
},
3031
];
3132

3233
// Sample sources
3334
const sampleSources: SourceObject[] = [
3435
{
3536
sourceId: "source-123",
3637
documentName: "Sample Document 1",
37-
documentId: "doc-abc"
38+
documentId: "doc-abc",
3839
},
3940
{
4041
sourceId: "source-456",
4142
documentName: "Sample Image Document",
42-
documentId: "doc-xyz"
43-
}
43+
documentId: "doc-xyz",
44+
},
4445
];
4546

4647
// Create sample messages
@@ -51,7 +52,7 @@ const createSampleMessages = (): AgentUIMessage[] => {
5152
id: generateUUID(),
5253
role: "user",
5354
content: "Show me information about data structures",
54-
createdAt: new Date(Date.now() - 60000)
55+
createdAt: new Date(Date.now() - 60000),
5556
},
5657
// Assistant message with display objects and sources
5758
{
@@ -62,9 +63,9 @@ const createSampleMessages = (): AgentUIMessage[] => {
6263
experimental_agentData: {
6364
tool_history: [sampleToolCall],
6465
displayObjects: sampleDisplayObjects,
65-
sources: sampleSources
66-
}
67-
}
66+
sources: sampleSources,
67+
},
68+
},
6869
];
6970
};
7071

0 commit comments

Comments
 (0)