mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-08-16 14:38:00 +00:00
feat(UI): Adding linter and prettier for UI (#3156)
This commit is contained in:
parent
61582f327c
commit
e69acbafbf
73 changed files with 1452 additions and 1226 deletions
|
@ -1,18 +1,18 @@
|
|||
"use client"
|
||||
"use client";
|
||||
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { motion } from "framer-motion"
|
||||
import { Ban, ChevronRight, Code2, Loader2, Terminal } from "lucide-react"
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { motion } from "framer-motion";
|
||||
import { Ban, ChevronRight, Code2, Loader2, Terminal } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from "@/components/ui/collapsible"
|
||||
import { FilePreview } from "@/components/ui/file-preview"
|
||||
import { MarkdownRenderer } from "@/components/chat-playground/markdown-renderer"
|
||||
} from "@/components/ui/collapsible";
|
||||
import { FilePreview } from "@/components/ui/file-preview";
|
||||
import { MarkdownRenderer } from "@/components/chat-playground/markdown-renderer";
|
||||
|
||||
const chatBubbleVariants = cva(
|
||||
"group/message relative break-words rounded-lg p-3 text-sm sm:max-w-[70%]",
|
||||
|
@ -52,66 +52,66 @@ const chatBubbleVariants = cva(
|
|||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
type Animation = VariantProps<typeof chatBubbleVariants>["animation"]
|
||||
type Animation = VariantProps<typeof chatBubbleVariants>["animation"];
|
||||
|
||||
interface Attachment {
|
||||
name?: string
|
||||
contentType?: string
|
||||
url: string
|
||||
name?: string;
|
||||
contentType?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface PartialToolCall {
|
||||
state: "partial-call"
|
||||
toolName: string
|
||||
state: "partial-call";
|
||||
toolName: string;
|
||||
}
|
||||
|
||||
interface ToolCall {
|
||||
state: "call"
|
||||
toolName: string
|
||||
state: "call";
|
||||
toolName: string;
|
||||
}
|
||||
|
||||
interface ToolResult {
|
||||
state: "result"
|
||||
toolName: string
|
||||
state: "result";
|
||||
toolName: string;
|
||||
result: {
|
||||
__cancelled?: boolean
|
||||
[key: string]: any
|
||||
}
|
||||
__cancelled?: boolean;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
type ToolInvocation = PartialToolCall | ToolCall | ToolResult
|
||||
type ToolInvocation = PartialToolCall | ToolCall | ToolResult;
|
||||
|
||||
interface ReasoningPart {
|
||||
type: "reasoning"
|
||||
reasoning: string
|
||||
type: "reasoning";
|
||||
reasoning: string;
|
||||
}
|
||||
|
||||
interface ToolInvocationPart {
|
||||
type: "tool-invocation"
|
||||
toolInvocation: ToolInvocation
|
||||
type: "tool-invocation";
|
||||
toolInvocation: ToolInvocation;
|
||||
}
|
||||
|
||||
interface TextPart {
|
||||
type: "text"
|
||||
text: string
|
||||
type: "text";
|
||||
text: string;
|
||||
}
|
||||
|
||||
// For compatibility with AI SDK types, not used
|
||||
interface SourcePart {
|
||||
type: "source"
|
||||
source?: any
|
||||
type: "source";
|
||||
source?: unknown;
|
||||
}
|
||||
|
||||
interface FilePart {
|
||||
type: "file"
|
||||
mimeType: string
|
||||
data: string
|
||||
type: "file";
|
||||
mimeType: string;
|
||||
data: string;
|
||||
}
|
||||
|
||||
interface StepStartPart {
|
||||
type: "step-start"
|
||||
type: "step-start";
|
||||
}
|
||||
|
||||
type MessagePart =
|
||||
|
@ -120,22 +120,22 @@ type MessagePart =
|
|||
| ToolInvocationPart
|
||||
| SourcePart
|
||||
| FilePart
|
||||
| StepStartPart
|
||||
| StepStartPart;
|
||||
|
||||
export interface Message {
|
||||
id: string
|
||||
role: "user" | "assistant" | (string & {})
|
||||
content: string
|
||||
createdAt?: Date
|
||||
experimental_attachments?: Attachment[]
|
||||
toolInvocations?: ToolInvocation[]
|
||||
parts?: MessagePart[]
|
||||
id: string;
|
||||
role: "user" | "assistant" | (string & {});
|
||||
content: string;
|
||||
createdAt?: Date;
|
||||
experimental_attachments?: Attachment[];
|
||||
toolInvocations?: ToolInvocation[];
|
||||
parts?: MessagePart[];
|
||||
}
|
||||
|
||||
export interface ChatMessageProps extends Message {
|
||||
showTimeStamp?: boolean
|
||||
animation?: Animation
|
||||
actions?: React.ReactNode
|
||||
showTimeStamp?: boolean;
|
||||
animation?: Animation;
|
||||
actions?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const ChatMessage: React.FC<ChatMessageProps> = ({
|
||||
|
@ -150,21 +150,21 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||
parts,
|
||||
}) => {
|
||||
const files = useMemo(() => {
|
||||
return experimental_attachments?.map((attachment) => {
|
||||
const dataArray = dataUrlToUint8Array(attachment.url)
|
||||
return experimental_attachments?.map(attachment => {
|
||||
const dataArray = dataUrlToUint8Array(attachment.url);
|
||||
const file = new File([dataArray], attachment.name ?? "Unknown", {
|
||||
type: attachment.contentType,
|
||||
})
|
||||
return file
|
||||
})
|
||||
}, [experimental_attachments])
|
||||
});
|
||||
return file;
|
||||
});
|
||||
}, [experimental_attachments]);
|
||||
|
||||
const isUser = role === "user"
|
||||
const isUser = role === "user";
|
||||
|
||||
const formattedTime = createdAt?.toLocaleTimeString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
})
|
||||
});
|
||||
|
||||
if (isUser) {
|
||||
return (
|
||||
|
@ -174,7 +174,7 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||
{files ? (
|
||||
<div className="mb-1 flex flex-wrap gap-2">
|
||||
{files.map((file, index) => {
|
||||
return <FilePreview file={file} key={index} />
|
||||
return <FilePreview file={file} key={index} />;
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -195,7 +195,7 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||
</time>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (parts && parts.length > 0) {
|
||||
|
@ -230,23 +230,23 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||
</time>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
} else if (part.type === "reasoning") {
|
||||
return <ReasoningBlock key={`reasoning-${index}`} part={part} />
|
||||
return <ReasoningBlock key={`reasoning-${index}`} part={part} />;
|
||||
} else if (part.type === "tool-invocation") {
|
||||
return (
|
||||
<ToolCall
|
||||
key={`tool-${index}`}
|
||||
toolInvocations={[part.toolInvocation]}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
return null
|
||||
})
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
if (toolInvocations && toolInvocations.length > 0) {
|
||||
return <ToolCall toolInvocations={toolInvocations} />
|
||||
return <ToolCall toolInvocations={toolInvocations} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -272,17 +272,17 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
|
|||
</time>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function dataUrlToUint8Array(data: string) {
|
||||
const base64 = data.split(",")[1]
|
||||
const buf = Buffer.from(base64, "base64")
|
||||
return new Uint8Array(buf)
|
||||
const base64 = data.split(",")[1];
|
||||
const buf = Buffer.from(base64, "base64");
|
||||
return new Uint8Array(buf);
|
||||
}
|
||||
|
||||
const ReasoningBlock = ({ part }: { part: ReasoningPart }) => {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="mb-2 flex flex-col items-start sm:max-w-[70%]">
|
||||
|
@ -319,20 +319,20 @@ const ReasoningBlock = ({ part }: { part: ReasoningPart }) => {
|
|||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
function ToolCall({
|
||||
toolInvocations,
|
||||
}: Pick<ChatMessageProps, "toolInvocations">) {
|
||||
if (!toolInvocations?.length) return null
|
||||
if (!toolInvocations?.length) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{toolInvocations.map((invocation, index) => {
|
||||
const isCancelled =
|
||||
invocation.state === "result" &&
|
||||
invocation.result.__cancelled === true
|
||||
invocation.result.__cancelled === true;
|
||||
|
||||
if (isCancelled) {
|
||||
return (
|
||||
|
@ -350,7 +350,7 @@ function ToolCall({
|
|||
</span>
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
switch (invocation.state) {
|
||||
|
@ -373,7 +373,7 @@ function ToolCall({
|
|||
</span>
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
case "result":
|
||||
return (
|
||||
<div
|
||||
|
@ -395,11 +395,11 @@ function ToolCall({
|
|||
{JSON.stringify(invocation.result, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
default:
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue