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,4 +1,4 @@
|
|||
"use client"
|
||||
"use client";
|
||||
|
||||
import {
|
||||
forwardRef,
|
||||
|
@ -6,48 +6,48 @@ import {
|
|||
useRef,
|
||||
useState,
|
||||
type ReactElement,
|
||||
} from "react"
|
||||
import { ArrowDown, ThumbsDown, ThumbsUp } from "lucide-react"
|
||||
} from "react";
|
||||
import { ArrowDown, ThumbsDown, ThumbsUp } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAutoScroll } from "@/hooks/use-auto-scroll"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { type Message } from "@/components/chat-playground/chat-message"
|
||||
import { CopyButton } from "@/components/ui/copy-button"
|
||||
import { MessageInput } from "@/components/chat-playground/message-input"
|
||||
import { MessageList } from "@/components/chat-playground/message-list"
|
||||
import { PromptSuggestions } from "@/components/chat-playground/prompt-suggestions"
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useAutoScroll } from "@/hooks/use-auto-scroll";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { type Message } from "@/components/chat-playground/chat-message";
|
||||
import { CopyButton } from "@/components/ui/copy-button";
|
||||
import { MessageInput } from "@/components/chat-playground/message-input";
|
||||
import { MessageList } from "@/components/chat-playground/message-list";
|
||||
import { PromptSuggestions } from "@/components/chat-playground/prompt-suggestions";
|
||||
|
||||
interface ChatPropsBase {
|
||||
handleSubmit: (
|
||||
event?: { preventDefault?: () => void },
|
||||
options?: { experimental_attachments?: FileList }
|
||||
) => void
|
||||
messages: Array<Message>
|
||||
input: string
|
||||
className?: string
|
||||
handleInputChange: React.ChangeEventHandler<HTMLTextAreaElement>
|
||||
isGenerating: boolean
|
||||
stop?: () => void
|
||||
) => void;
|
||||
messages: Array<Message>;
|
||||
input: string;
|
||||
className?: string;
|
||||
handleInputChange: React.ChangeEventHandler<HTMLTextAreaElement>;
|
||||
isGenerating: boolean;
|
||||
stop?: () => void;
|
||||
onRateResponse?: (
|
||||
messageId: string,
|
||||
rating: "thumbs-up" | "thumbs-down"
|
||||
) => void
|
||||
setMessages?: (messages: any[]) => void
|
||||
transcribeAudio?: (blob: Blob) => Promise<string>
|
||||
) => void;
|
||||
setMessages?: (messages: Message[]) => void;
|
||||
transcribeAudio?: (blob: Blob) => Promise<string>;
|
||||
}
|
||||
|
||||
interface ChatPropsWithoutSuggestions extends ChatPropsBase {
|
||||
append?: never
|
||||
suggestions?: never
|
||||
append?: never;
|
||||
suggestions?: never;
|
||||
}
|
||||
|
||||
interface ChatPropsWithSuggestions extends ChatPropsBase {
|
||||
append: (message: { role: "user"; content: string }) => void
|
||||
suggestions: string[]
|
||||
append: (message: { role: "user"; content: string }) => void;
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
type ChatProps = ChatPropsWithoutSuggestions | ChatPropsWithSuggestions
|
||||
type ChatProps = ChatPropsWithoutSuggestions | ChatPropsWithSuggestions;
|
||||
|
||||
export function Chat({
|
||||
messages,
|
||||
|
@ -63,34 +63,34 @@ export function Chat({
|
|||
setMessages,
|
||||
transcribeAudio,
|
||||
}: ChatProps) {
|
||||
const lastMessage = messages.at(-1)
|
||||
const isEmpty = messages.length === 0
|
||||
const isTyping = lastMessage?.role === "user"
|
||||
const lastMessage = messages.at(-1);
|
||||
const isEmpty = messages.length === 0;
|
||||
const isTyping = lastMessage?.role === "user";
|
||||
|
||||
const messagesRef = useRef(messages)
|
||||
messagesRef.current = messages
|
||||
const messagesRef = useRef(messages);
|
||||
messagesRef.current = messages;
|
||||
|
||||
// Enhanced stop function that marks pending tool calls as cancelled
|
||||
const handleStop = useCallback(() => {
|
||||
stop?.()
|
||||
stop?.();
|
||||
|
||||
if (!setMessages) return
|
||||
if (!setMessages) return;
|
||||
|
||||
const latestMessages = [...messagesRef.current]
|
||||
const latestMessages = [...messagesRef.current];
|
||||
const lastAssistantMessage = latestMessages.findLast(
|
||||
(m) => m.role === "assistant"
|
||||
)
|
||||
m => m.role === "assistant"
|
||||
);
|
||||
|
||||
if (!lastAssistantMessage) return
|
||||
if (!lastAssistantMessage) return;
|
||||
|
||||
let needsUpdate = false
|
||||
let updatedMessage = { ...lastAssistantMessage }
|
||||
let needsUpdate = false;
|
||||
let updatedMessage = { ...lastAssistantMessage };
|
||||
|
||||
if (lastAssistantMessage.toolInvocations) {
|
||||
const updatedToolInvocations = lastAssistantMessage.toolInvocations.map(
|
||||
(toolInvocation) => {
|
||||
toolInvocation => {
|
||||
if (toolInvocation.state === "call") {
|
||||
needsUpdate = true
|
||||
needsUpdate = true;
|
||||
return {
|
||||
...toolInvocation,
|
||||
state: "result",
|
||||
|
@ -98,61 +98,66 @@ export function Chat({
|
|||
content: "Tool execution was cancelled",
|
||||
__cancelled: true, // Special marker to indicate cancellation
|
||||
},
|
||||
} as const
|
||||
} as const;
|
||||
}
|
||||
return toolInvocation
|
||||
return toolInvocation;
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if (needsUpdate) {
|
||||
updatedMessage = {
|
||||
...updatedMessage,
|
||||
toolInvocations: updatedToolInvocations,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (lastAssistantMessage.parts && lastAssistantMessage.parts.length > 0) {
|
||||
const updatedParts = lastAssistantMessage.parts.map((part: any) => {
|
||||
if (
|
||||
part.type === "tool-invocation" &&
|
||||
part.toolInvocation &&
|
||||
part.toolInvocation.state === "call"
|
||||
) {
|
||||
needsUpdate = true
|
||||
return {
|
||||
...part,
|
||||
toolInvocation: {
|
||||
...part.toolInvocation,
|
||||
state: "result",
|
||||
result: {
|
||||
content: "Tool execution was cancelled",
|
||||
__cancelled: true,
|
||||
const updatedParts = lastAssistantMessage.parts.map(
|
||||
(part: {
|
||||
type: string;
|
||||
toolInvocation?: { state: string; toolName: string };
|
||||
}) => {
|
||||
if (
|
||||
part.type === "tool-invocation" &&
|
||||
part.toolInvocation &&
|
||||
part.toolInvocation.state === "call"
|
||||
) {
|
||||
needsUpdate = true;
|
||||
return {
|
||||
...part,
|
||||
toolInvocation: {
|
||||
...part.toolInvocation,
|
||||
state: "result",
|
||||
result: {
|
||||
content: "Tool execution was cancelled",
|
||||
__cancelled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
return part;
|
||||
}
|
||||
return part
|
||||
})
|
||||
);
|
||||
|
||||
if (needsUpdate) {
|
||||
updatedMessage = {
|
||||
...updatedMessage,
|
||||
parts: updatedParts,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate) {
|
||||
const messageIndex = latestMessages.findIndex(
|
||||
(m) => m.id === lastAssistantMessage.id
|
||||
)
|
||||
m => m.id === lastAssistantMessage.id
|
||||
);
|
||||
if (messageIndex !== -1) {
|
||||
latestMessages[messageIndex] = updatedMessage
|
||||
setMessages(latestMessages)
|
||||
latestMessages[messageIndex] = updatedMessage;
|
||||
setMessages(latestMessages);
|
||||
}
|
||||
}
|
||||
}, [stop, setMessages, messagesRef])
|
||||
}, [stop, setMessages, messagesRef]);
|
||||
|
||||
const messageOptions = useCallback(
|
||||
(message: Message) => ({
|
||||
|
@ -189,7 +194,7 @@ export function Chat({
|
|||
),
|
||||
}),
|
||||
[onRateResponse]
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<ChatContainer className={className}>
|
||||
|
@ -237,15 +242,15 @@ export function Chat({
|
|||
</div>
|
||||
</div>
|
||||
</ChatContainer>
|
||||
)
|
||||
);
|
||||
}
|
||||
Chat.displayName = "Chat"
|
||||
Chat.displayName = "Chat";
|
||||
|
||||
export function ChatMessages({
|
||||
messages,
|
||||
children,
|
||||
}: React.PropsWithChildren<{
|
||||
messages: Message[]
|
||||
messages: Message[];
|
||||
}>) {
|
||||
const {
|
||||
containerRef,
|
||||
|
@ -253,7 +258,7 @@ export function ChatMessages({
|
|||
handleScroll,
|
||||
shouldAutoScroll,
|
||||
handleTouchStart,
|
||||
} = useAutoScroll([messages])
|
||||
} = useAutoScroll([messages]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -281,7 +286,7 @@ export function ChatMessages({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export const ChatContainer = forwardRef<
|
||||
|
@ -294,56 +299,56 @@ export const ChatContainer = forwardRef<
|
|||
className={cn("flex flex-col max-h-full w-full", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
ChatContainer.displayName = "ChatContainer"
|
||||
);
|
||||
});
|
||||
ChatContainer.displayName = "ChatContainer";
|
||||
|
||||
interface ChatFormProps {
|
||||
className?: string
|
||||
isPending: boolean
|
||||
className?: string;
|
||||
isPending: boolean;
|
||||
handleSubmit: (
|
||||
event?: { preventDefault?: () => void },
|
||||
options?: { experimental_attachments?: FileList }
|
||||
) => void
|
||||
) => void;
|
||||
children: (props: {
|
||||
files: File[] | null
|
||||
setFiles: React.Dispatch<React.SetStateAction<File[] | null>>
|
||||
}) => ReactElement
|
||||
files: File[] | null;
|
||||
setFiles: React.Dispatch<React.SetStateAction<File[] | null>>;
|
||||
}) => ReactElement;
|
||||
}
|
||||
|
||||
export const ChatForm = forwardRef<HTMLFormElement, ChatFormProps>(
|
||||
({ children, handleSubmit, isPending, className }, ref) => {
|
||||
const [files, setFiles] = useState<File[] | null>(null)
|
||||
const [files, setFiles] = useState<File[] | null>(null);
|
||||
|
||||
const onSubmit = (event: React.FormEvent) => {
|
||||
// if (isPending) {
|
||||
// event.preventDefault()
|
||||
// return
|
||||
// }
|
||||
|
||||
if (!files) {
|
||||
handleSubmit(event)
|
||||
return
|
||||
if (isPending) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
const fileList = createFileList(files)
|
||||
handleSubmit(event, { experimental_attachments: fileList })
|
||||
setFiles(null)
|
||||
}
|
||||
if (!files) {
|
||||
handleSubmit(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const fileList = createFileList(files);
|
||||
handleSubmit(event, { experimental_attachments: fileList });
|
||||
setFiles(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<form ref={ref} onSubmit={onSubmit} className={className}>
|
||||
{children({ files, setFiles })}
|
||||
</form>
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
ChatForm.displayName = "ChatForm"
|
||||
);
|
||||
ChatForm.displayName = "ChatForm";
|
||||
|
||||
function createFileList(files: File[] | FileList): FileList {
|
||||
const dataTransfer = new DataTransfer()
|
||||
const dataTransfer = new DataTransfer();
|
||||
for (const file of Array.from(files)) {
|
||||
dataTransfer.items.add(file)
|
||||
dataTransfer.items.add(file);
|
||||
}
|
||||
return dataTransfer.files
|
||||
return dataTransfer.files;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue