From 3f87db2eead52dc202cffd1d2ad45e7ca1fdc18c Mon Sep 17 00:00:00 2001
From: Francisco Javier Arceo
Date: Thu, 14 Aug 2025 15:51:11 -0400
Subject: [PATCH] adding linted files and nvmrc -- will trigger precommit
failures
Signed-off-by: Francisco Javier Arceo
---
llama_stack/ui/.nvmrc | 2 +
llama_stack/ui/app/api/v1/[...path]/route.ts | 4 +-
llama_stack/ui/app/auth/signin/page.tsx | 4 +-
llama_stack/ui/app/chat-playground/page.tsx | 192 +++++++-------
.../app/logs/chat-completions/[id]/page.tsx | 4 +-
.../ui/app/logs/responses/[id]/page.tsx | 16 +-
.../[fileId]/contents/[contentId]/page.tsx | 124 ++++++---
.../[id]/files/[fileId]/contents/page.tsx | 84 +++++--
.../[id]/files/[fileId]/page.tsx | 90 +++++--
.../ui/app/logs/vector-stores/[id]/page.tsx | 6 +-
.../ui/app/logs/vector-stores/page.tsx | 132 +++++-----
.../chat-completion-detail.test.tsx | 28 +--
.../chat-completion-detail.tsx | 6 +-
.../chat-completion-table.test.tsx | 34 +--
.../chat-completions-table.tsx | 2 +-
.../chat-playground/chat-message.tsx | 156 ++++++------
.../ui/components/chat-playground/chat.tsx | 166 ++++++-------
.../chat-playground/interrupt-prompt.tsx | 12 +-
.../chat-playground/markdown-renderer.tsx | 104 ++++----
.../chat-playground/message-input.tsx | 235 +++++++++---------
.../chat-playground/message-list.tsx | 20 +-
.../chat-playground/prompt-suggestions.tsx | 10 +-
.../chat-playground/typing-indicator.tsx | 4 +-
.../ui/components/layout/app-sidebar.tsx | 103 ++++----
.../logs/logs-table-scroll.test.tsx | 22 +-
.../ui/components/logs/logs-table.test.tsx | 42 ++--
llama_stack/ui/components/logs/logs-table.tsx | 2 +-
.../grouping/grouped-items-display.tsx | 2 +-
.../responses/hooks/function-call-grouping.ts | 2 +-
.../responses/items/message-item.tsx | 2 +-
.../responses/responses-detail.test.tsx | 54 ++--
.../responses/responses-table.test.tsx | 30 +--
.../components/responses/responses-table.tsx | 18 +-
.../components/responses/utils/item-types.ts | 8 +-
.../ui/components/ui/audio-visualizer.tsx | 146 +++++------
llama_stack/ui/components/ui/breadcrumb.tsx | 2 +-
llama_stack/ui/components/ui/button.tsx | 18 +-
llama_stack/ui/components/ui/card.tsx | 6 +-
llama_stack/ui/components/ui/collapsible.tsx | 12 +-
llama_stack/ui/components/ui/copy-button.tsx | 20 +-
.../ui/components/ui/dropdown-menu.tsx | 16 +-
llama_stack/ui/components/ui/file-preview.tsx | 56 ++---
llama_stack/ui/components/ui/input.tsx | 2 +-
llama_stack/ui/components/ui/select.tsx | 34 +--
llama_stack/ui/components/ui/separator.tsx | 2 +-
llama_stack/ui/components/ui/sheet.tsx | 4 +-
llama_stack/ui/components/ui/sidebar.tsx | 36 +--
llama_stack/ui/components/ui/sonner.tsx | 14 +-
llama_stack/ui/components/ui/table.tsx | 8 +-
llama_stack/ui/components/ui/tooltip.tsx | 2 +-
.../vector-stores/vector-store-detail.tsx | 4 +-
llama_stack/ui/e2e/logs-table-scroll.spec.ts | 2 +-
llama_stack/ui/hooks/use-audio-recording.ts | 82 +++---
llama_stack/ui/hooks/use-auto-scroll.ts | 50 ++--
llama_stack/ui/hooks/use-autosize-textarea.ts | 32 +--
llama_stack/ui/hooks/use-copy-to-clipboard.ts | 34 +--
llama_stack/ui/hooks/use-infinite-scroll.ts | 6 +-
llama_stack/ui/hooks/use-mobile.ts | 2 +-
llama_stack/ui/hooks/use-pagination.ts | 10 +-
llama_stack/ui/lib/audio-utils.ts | 54 ++--
llama_stack/ui/lib/config-validator.ts | 8 +-
llama_stack/ui/lib/contents-api.ts | 38 ++-
.../ui/lib/format-message-content.test.ts | 18 +-
llama_stack/ui/lib/format-message-content.ts | 4 +-
llama_stack/ui/lib/truncate-text.ts | 2 +-
65 files changed, 1307 insertions(+), 1137 deletions(-)
create mode 100644 llama_stack/ui/.nvmrc
diff --git a/llama_stack/ui/.nvmrc b/llama_stack/ui/.nvmrc
new file mode 100644
index 000000000..cf2307ecc
--- /dev/null
+++ b/llama_stack/ui/.nvmrc
@@ -0,0 +1,2 @@
+22.5.1
+
diff --git a/llama_stack/ui/app/api/v1/[...path]/route.ts b/llama_stack/ui/app/api/v1/[...path]/route.ts
index 1959f9099..51c1f8004 100644
--- a/llama_stack/ui/app/api/v1/[...path]/route.ts
+++ b/llama_stack/ui/app/api/v1/[...path]/route.ts
@@ -47,7 +47,7 @@ async function proxyRequest(request: NextRequest, method: string) {
const responseText = await response.text();
console.log(
- `Response from FastAPI: ${response.status} ${response.statusText}`,
+ `Response from FastAPI: ${response.status} ${response.statusText}`
);
// Create response with same status and headers
@@ -74,7 +74,7 @@ async function proxyRequest(request: NextRequest, method: string) {
backend_url: BACKEND_URL,
timestamp: new Date().toISOString(),
},
- { status: 500 },
+ { status: 500 }
);
}
}
diff --git a/llama_stack/ui/app/auth/signin/page.tsx b/llama_stack/ui/app/auth/signin/page.tsx
index c9510fd6b..0ccb4a397 100644
--- a/llama_stack/ui/app/auth/signin/page.tsx
+++ b/llama_stack/ui/app/auth/signin/page.tsx
@@ -51,9 +51,9 @@ export default function SignInPage() {
onClick={() => {
console.log("Signing in with GitHub...");
signIn("github", { callbackUrl: "/auth/signin" }).catch(
- (error) => {
+ error => {
console.error("Sign in error:", error);
- },
+ }
);
}}
className="w-full"
diff --git a/llama_stack/ui/app/chat-playground/page.tsx b/llama_stack/ui/app/chat-playground/page.tsx
index d8094af85..b8651aca0 100644
--- a/llama_stack/ui/app/chat-playground/page.tsx
+++ b/llama_stack/ui/app/chat-playground/page.tsx
@@ -29,14 +29,13 @@ export default function ChatPlaygroundPage() {
const isModelsLoading = modelsLoading ?? true;
-
useEffect(() => {
const fetchModels = async () => {
try {
setModelsLoading(true);
setModelsError(null);
const modelList = await client.models.list();
- const llmModels = modelList.filter(model => model.model_type === 'llm');
+ const llmModels = modelList.filter(model => model.model_type === "llm");
setModels(llmModels);
if (llmModels.length > 0) {
setSelectedModel(llmModels[0].identifier);
@@ -53,103 +52,122 @@ export default function ChatPlaygroundPage() {
}, [client]);
const extractTextContent = (content: unknown): string => {
- if (typeof content === 'string') {
+ if (typeof content === "string") {
return content;
}
if (Array.isArray(content)) {
return content
- .filter(item => item && typeof item === 'object' && 'type' in item && item.type === 'text')
- .map(item => (item && typeof item === 'object' && 'text' in item) ? String(item.text) : '')
- .join('');
+ .filter(
+ item =>
+ item &&
+ typeof item === "object" &&
+ "type" in item &&
+ item.type === "text"
+ )
+ .map(item =>
+ item && typeof item === "object" && "text" in item
+ ? String(item.text)
+ : ""
+ )
+ .join("");
}
- if (content && typeof content === 'object' && 'type' in content && content.type === 'text' && 'text' in content) {
- return String(content.text) || '';
+ if (
+ content &&
+ typeof content === "object" &&
+ "type" in content &&
+ content.type === "text" &&
+ "text" in content
+ ) {
+ return String(content.text) || "";
}
- return '';
+ return "";
};
const handleInputChange = (e: React.ChangeEvent) => {
setInput(e.target.value);
};
-const handleSubmit = async (event?: { preventDefault?: () => void }) => {
- event?.preventDefault?.();
- if (!input.trim()) return;
+ const handleSubmit = async (event?: { preventDefault?: () => void }) => {
+ event?.preventDefault?.();
+ if (!input.trim()) return;
- // Add user message to chat
- const userMessage: Message = {
- id: Date.now().toString(),
- role: "user",
- content: input.trim(),
- createdAt: new Date(),
- };
-
- setMessages(prev => [...prev, userMessage]);
- setInput("");
-
- // Use the helper function with the content
- await handleSubmitWithContent(userMessage.content);
-};
-
-const handleSubmitWithContent = async (content: string) => {
- setIsGenerating(true);
- setError(null);
-
- try {
- const messageParams: CompletionCreateParams["messages"] = [
- ...messages.map(msg => {
- const msgContent = typeof msg.content === 'string' ? msg.content : extractTextContent(msg.content);
- if (msg.role === "user") {
- return { role: "user" as const, content: msgContent };
- } else if (msg.role === "assistant") {
- return { role: "assistant" as const, content: msgContent };
- } else {
- return { role: "system" as const, content: msgContent };
- }
- }),
- { role: "user" as const, content }
- ];
-
- const response = await client.chat.completions.create({
- model: selectedModel,
- messages: messageParams,
- stream: true,
- });
-
- const assistantMessage: Message = {
- id: (Date.now() + 1).toString(),
- role: "assistant",
- content: "",
+ // Add user message to chat
+ const userMessage: Message = {
+ id: Date.now().toString(),
+ role: "user",
+ content: input.trim(),
createdAt: new Date(),
};
- setMessages(prev => [...prev, assistantMessage]);
- let fullContent = "";
- for await (const chunk of response) {
- if (chunk.choices && chunk.choices[0]?.delta?.content) {
- const deltaContent = chunk.choices[0].delta.content;
- fullContent += deltaContent;
+ setMessages(prev => [...prev, userMessage]);
+ setInput("");
- flushSync(() => {
- setMessages(prev => {
- const newMessages = [...prev];
- const lastMessage = newMessages[newMessages.length - 1];
- if (lastMessage.role === "assistant") {
- lastMessage.content = fullContent;
- }
- return newMessages;
+ // Use the helper function with the content
+ await handleSubmitWithContent(userMessage.content);
+ };
+
+ const handleSubmitWithContent = async (content: string) => {
+ setIsGenerating(true);
+ setError(null);
+
+ try {
+ const messageParams: CompletionCreateParams["messages"] = [
+ ...messages.map(msg => {
+ const msgContent =
+ typeof msg.content === "string"
+ ? msg.content
+ : extractTextContent(msg.content);
+ if (msg.role === "user") {
+ return { role: "user" as const, content: msgContent };
+ } else if (msg.role === "assistant") {
+ return { role: "assistant" as const, content: msgContent };
+ } else {
+ return { role: "system" as const, content: msgContent };
+ }
+ }),
+ { role: "user" as const, content },
+ ];
+
+ const response = await client.chat.completions.create({
+ model: selectedModel,
+ messages: messageParams,
+ stream: true,
+ });
+
+ const assistantMessage: Message = {
+ id: (Date.now() + 1).toString(),
+ role: "assistant",
+ content: "",
+ createdAt: new Date(),
+ };
+
+ setMessages(prev => [...prev, assistantMessage]);
+ let fullContent = "";
+ for await (const chunk of response) {
+ if (chunk.choices && chunk.choices[0]?.delta?.content) {
+ const deltaContent = chunk.choices[0].delta.content;
+ fullContent += deltaContent;
+
+ flushSync(() => {
+ setMessages(prev => {
+ const newMessages = [...prev];
+ const lastMessage = newMessages[newMessages.length - 1];
+ if (lastMessage.role === "assistant") {
+ lastMessage.content = fullContent;
+ }
+ return newMessages;
+ });
});
- });
+ }
}
+ } catch (err) {
+ console.error("Error sending message:", err);
+ setError("Failed to send message. Please try again.");
+ setMessages(prev => prev.slice(0, -1));
+ } finally {
+ setIsGenerating(false);
}
- } catch (err) {
- console.error("Error sending message:", err);
- setError("Failed to send message. Please try again.");
- setMessages(prev => prev.slice(0, -1));
- } finally {
- setIsGenerating(false);
- }
-};
+ };
const suggestions = [
"Write a Python function that prints 'Hello, World!'",
"Explain step-by-step how to solve this math problem: If x² + 6x + 9 = 25, what is x?",
@@ -163,7 +181,7 @@ const handleSubmitWithContent = async (content: string) => {
content: message.content,
createdAt: new Date(),
};
- setMessages(prev => [...prev, newMessage])
+ setMessages(prev => [...prev, newMessage]);
handleSubmitWithContent(newMessage.content);
};
@@ -177,12 +195,20 @@ const handleSubmitWithContent = async (content: string) => {
Chat Playground (Completions)
-
@@ -284,7 +326,7 @@ export default function ContentDetailPage() {
{
+ onChange={e => {
const newMetadata = { ...editedMetadata };
delete newMetadata[key];
newMetadata[e.target.value] = value;
@@ -294,11 +336,13 @@ export default function ContentDetailPage() {
className="flex-1"
/>
{
+ value={
+ typeof value === "string" ? value : JSON.stringify(value)
+ }
+ onChange={e => {
setEditedMetadata({
...editedMetadata,
- [key]: e.target.value
+ [key]: e.target.value,
});
}}
placeholder="Value"
@@ -312,7 +356,7 @@ export default function ContentDetailPage() {
onClick={() => {
setEditedMetadata({
...editedMetadata,
- ['']: ''
+ [""]: "",
});
}}
>
@@ -325,7 +369,7 @@ export default function ContentDetailPage() {
{key}:
- {typeof value === 'string' ? value : JSON.stringify(value)}
+ {typeof value === "string" ? value : JSON.stringify(value)}
))}
@@ -351,15 +395,15 @@ export default function ContentDetailPage() {
value={`${getTextFromContent(content.content).length} chars`}
/>
{content.metadata.chunk_window && (
-
+
)}
{file && (
<>
-
+
>
)}
{store && (
diff --git a/llama_stack/ui/app/logs/vector-stores/[id]/files/[fileId]/contents/page.tsx b/llama_stack/ui/app/logs/vector-stores/[id]/files/[fileId]/contents/page.tsx
index d43223c6c..594ef72a6 100644
--- a/llama_stack/ui/app/logs/vector-stores/[id]/files/[fileId]/contents/page.tsx
+++ b/llama_stack/ui/app/logs/vector-stores/[id]/files/[fileId]/contents/page.tsx
@@ -18,7 +18,10 @@ import {
PropertiesCard,
PropertyItem,
} from "@/components/layout/detail-layout";
-import { PageBreadcrumb, BreadcrumbSegment } from "@/components/layout/page-breadcrumb";
+import {
+ PageBreadcrumb,
+ BreadcrumbSegment,
+} from "@/components/layout/page-breadcrumb";
import {
Table,
TableBody,
@@ -37,12 +40,12 @@ export default function ContentsListPage() {
const client = useAuthClient();
const getTextFromContent = (content: any): string => {
- if (typeof content === 'string') {
+ if (typeof content === "string") {
return content;
- } else if (content && content.type === 'text') {
+ } else if (content && content.type === "text") {
return content.text;
}
- return '';
+ return "";
};
const [store, setStore] = useState
(null);
@@ -65,7 +68,9 @@ export default function ContentsListPage() {
const response = await client.vectorStores.retrieve(vectorStoreId);
setStore(response as VectorStore);
} catch (err) {
- setErrorStore(err instanceof Error ? err : new Error("Failed to load vector store."));
+ setErrorStore(
+ err instanceof Error ? err : new Error("Failed to load vector store.")
+ );
} finally {
setIsLoadingStore(false);
}
@@ -80,10 +85,15 @@ export default function ContentsListPage() {
setIsLoadingFile(true);
setErrorFile(null);
try {
- const response = await client.vectorStores.files.retrieve(vectorStoreId, fileId);
+ const response = await client.vectorStores.files.retrieve(
+ vectorStoreId,
+ fileId
+ );
setFile(response as VectorStoreFile);
} catch (err) {
- setErrorFile(err instanceof Error ? err : new Error("Failed to load file."));
+ setErrorFile(
+ err instanceof Error ? err : new Error("Failed to load file.")
+ );
} finally {
setIsLoadingFile(false);
}
@@ -99,10 +109,16 @@ export default function ContentsListPage() {
setErrorContents(null);
try {
const contentsAPI = new ContentsAPI(client);
- const contentsResponse = await contentsAPI.listContents(vectorStoreId, fileId, { limit: 100 });
+ const contentsResponse = await contentsAPI.listContents(
+ vectorStoreId,
+ fileId,
+ { limit: 100 }
+ );
setContents(contentsResponse.data);
} catch (err) {
- setErrorContents(err instanceof Error ? err : new Error("Failed to load contents."));
+ setErrorContents(
+ err instanceof Error ? err : new Error("Failed to load contents.")
+ );
} finally {
setIsLoadingContents(false);
}
@@ -116,26 +132,36 @@ export default function ContentsListPage() {
await contentsAPI.deleteContent(vectorStoreId, fileId, contentId);
setContents(contents.filter(content => content.id !== contentId));
} catch (err) {
- console.error('Failed to delete content:', err);
+ console.error("Failed to delete content:", err);
}
};
const handleViewContent = (contentId: string) => {
- router.push(`/logs/vector-stores/${vectorStoreId}/files/${fileId}/contents/${contentId}`);
+ router.push(
+ `/logs/vector-stores/${vectorStoreId}/files/${fileId}/contents/${contentId}`
+ );
};
const title = `Contents in File: ${fileId}`;
const breadcrumbSegments: BreadcrumbSegment[] = [
{ label: "Vector Stores", href: "/logs/vector-stores" },
- { label: store?.name || vectorStoreId, href: `/logs/vector-stores/${vectorStoreId}` },
+ {
+ label: store?.name || vectorStoreId,
+ href: `/logs/vector-stores/${vectorStoreId}`,
+ },
{ label: "Files", href: `/logs/vector-stores/${vectorStoreId}` },
- { label: fileId, href: `/logs/vector-stores/${vectorStoreId}/files/${fileId}` },
+ {
+ label: fileId,
+ href: `/logs/vector-stores/${vectorStoreId}/files/${fileId}`,
+ },
{ label: "Contents" },
];
if (errorStore) {
- return ;
+ return (
+
+ );
}
if (isLoadingStore) {
return ;
@@ -175,7 +201,7 @@ export default function ContentsListPage() {
- {contents.map((content) => (
+ {contents.map(content => (
) : (
-
- File not found.
-
+
File not found.
)}
@@ -192,16 +225,27 @@ export default function FileDetailPage() {
- Content Items:
+
+ Content Items:
+
{contents.content.length}
- Total Characters:
- {contents.content.reduce((total, item) => total + item.text.length, 0)}
+
+ Total Characters:
+
+
+ {contents.content.reduce(
+ (total, item) => total + item.text.length,
+ 0
+ )}
+
-
Preview:
+
+ Preview:
+
{contents.content[0]?.text.substring(0, 200)}...
diff --git a/llama_stack/ui/app/logs/vector-stores/[id]/page.tsx b/llama_stack/ui/app/logs/vector-stores/[id]/page.tsx
index f27c9d802..d2e974a63 100644
--- a/llama_stack/ui/app/logs/vector-stores/[id]/page.tsx
+++ b/llama_stack/ui/app/logs/vector-stores/[id]/page.tsx
@@ -34,9 +34,7 @@ export default function VectorStoreDetailPage() {
setStore(response as VectorStore);
} catch (err) {
setErrorStore(
- err instanceof Error
- ? err
- : new Error("Failed to load vector store."),
+ err instanceof Error ? err : new Error("Failed to load vector store.")
);
} finally {
setIsLoadingStore(false);
@@ -59,7 +57,7 @@ export default function VectorStoreDetailPage() {
setFiles((result as any).data);
} catch (err) {
setErrorFiles(
- err instanceof Error ? err : new Error("Failed to load files."),
+ err instanceof Error ? err : new Error("Failed to load files.")
);
} finally {
setIsLoadingFiles(false);
diff --git a/llama_stack/ui/app/logs/vector-stores/page.tsx b/llama_stack/ui/app/logs/vector-stores/page.tsx
index 664513caa..e0a5e9f13 100644
--- a/llama_stack/ui/app/logs/vector-stores/page.tsx
+++ b/llama_stack/ui/app/logs/vector-stores/page.tsx
@@ -53,11 +53,11 @@ export default function VectorStoresPage() {
const renderContent = () => {
if (status === "loading") {
return (
-
-
-
-
-
+
+
+
+
+
);
}
@@ -70,72 +70,72 @@ export default function VectorStoresPage() {
}
return (
-
-
-
-
- ID
- Name
- Created
- Completed
- Cancelled
- Failed
- In Progress
- Total
- Usage Bytes
- Provider ID
- Provider Vector DB ID
-
-
-
- {stores.map((store) => {
- const fileCounts = store.file_counts;
- const metadata = store.metadata || {};
- const providerId = metadata.provider_id ?? "";
- const providerDbId = metadata.provider_vector_db_id ?? "";
+
+
+
+
+ ID
+ Name
+ Created
+ Completed
+ Cancelled
+ Failed
+ In Progress
+ Total
+ Usage Bytes
+ Provider ID
+ Provider Vector DB ID
+
+
+
+ {stores.map(store => {
+ const fileCounts = store.file_counts;
+ const metadata = store.metadata || {};
+ const providerId = metadata.provider_id ?? "";
+ const providerDbId = metadata.provider_vector_db_id ?? "";
- return (
- router.push(`/logs/vector-stores/${store.id}`)}
- className="cursor-pointer hover:bg-muted/50"
+ return (
+ router.push(`/logs/vector-stores/${store.id}`)}
+ className="cursor-pointer hover:bg-muted/50"
+ >
+
+
- );
- })}
-
-
-
+ {store.id}
+
+
+ {store.name}
+
+ {new Date(store.created_at * 1000).toLocaleString()}
+
+ {fileCounts.completed}
+ {fileCounts.cancelled}
+ {fileCounts.failed}
+ {fileCounts.in_progress}
+ {fileCounts.total}
+ {store.usage_bytes}
+ {providerId}
+ {providerDbId}
+
+ );
+ })}
+
+
+
);
};
return (
-
-
Vector Stores
- {renderContent()}
-
+
+
Vector Stores
+ {renderContent()}
+
);
}
diff --git a/llama_stack/ui/components/chat-completions/chat-completion-detail.test.tsx b/llama_stack/ui/components/chat-completions/chat-completion-detail.test.tsx
index 5348dbc3a..52258eda9 100644
--- a/llama_stack/ui/components/chat-completions/chat-completion-detail.test.tsx
+++ b/llama_stack/ui/components/chat-completions/chat-completion-detail.test.tsx
@@ -14,7 +14,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={true}
error={null}
id="test-id"
- />,
+ />
);
// Use the data-slot attribute for Skeletons
const skeletons = container.querySelectorAll('[data-slot="skeleton"]');
@@ -28,10 +28,10 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{ name: "Error", message: "Network Error" }}
id="err-id"
- />,
+ />
);
expect(
- screen.getByText(/Error loading details for ID err-id: Network Error/),
+ screen.getByText(/Error loading details for ID err-id: Network Error/)
).toBeInTheDocument();
});
@@ -42,11 +42,11 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{ name: "Error", message: "" }}
id="err-id"
- />,
+ />
);
// Use regex to match the error message regardless of whitespace
expect(
- screen.getByText(/Error loading details for ID\s*err-id\s*:/),
+ screen.getByText(/Error loading details for ID\s*err-id\s*:/)
).toBeInTheDocument();
});
@@ -57,11 +57,11 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={{} as Error}
id="err-id"
- />,
+ />
);
// Use regex to match the error message regardless of whitespace
expect(
- screen.getByText(/Error loading details for ID\s*err-id\s*:/),
+ screen.getByText(/Error loading details for ID\s*err-id\s*:/)
).toBeInTheDocument();
});
@@ -72,10 +72,10 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id="notfound-id"
- />,
+ />
);
expect(
- screen.getByText("No details found for ID: notfound-id."),
+ screen.getByText("No details found for ID: notfound-id.")
).toBeInTheDocument();
});
@@ -100,7 +100,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
- />,
+ />
);
// Input
expect(screen.getByText("Input")).toBeInTheDocument();
@@ -112,7 +112,7 @@ describe("ChatCompletionDetailView", () => {
expect(screen.getByText("Properties")).toBeInTheDocument();
expect(screen.getByText("Created:")).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("ID:")).toBeInTheDocument();
expect(screen.getByText("comp_123")).toBeInTheDocument();
@@ -150,7 +150,7 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
- />,
+ />
);
// Output should include the tool call block (should be present twice: input and output)
const toolCallLabels = screen.getAllByText("Tool Call");
@@ -178,13 +178,13 @@ describe("ChatCompletionDetailView", () => {
isLoading={false}
error={null}
id={mockCompletion.id}
- />,
+ />
);
// Input section should be present but empty
expect(screen.getByText("Input")).toBeInTheDocument();
// Output section should show fallback message
expect(
- screen.getByText("No message found in assistant's choice."),
+ screen.getByText("No message found in assistant's choice.")
).toBeInTheDocument();
// Properties should show N/A for finish reason
expect(screen.getByText("Finish Reason:")).toBeInTheDocument();
diff --git a/llama_stack/ui/components/chat-completions/chat-completion-detail.tsx b/llama_stack/ui/components/chat-completions/chat-completion-detail.tsx
index 200807864..583a09981 100644
--- a/llama_stack/ui/components/chat-completions/chat-completion-detail.tsx
+++ b/llama_stack/ui/components/chat-completions/chat-completion-detail.tsx
@@ -53,11 +53,11 @@ export function ChatCompletionDetailView({
{completion.choices?.[0]?.message?.tool_calls &&
Array.isArray(completion.choices[0].message.tool_calls) &&
!completion.input_messages?.some(
- (im) =>
+ im =>
im.role === "assistant" &&
im.tool_calls &&
Array.isArray(im.tool_calls) &&
- im.tool_calls.length > 0,
+ im.tool_calls.length > 0
)
? completion.choices[0].message.tool_calls.map(
(toolCall: any, index: number) => {
@@ -72,7 +72,7 @@ export function ChatCompletionDetailView({
message={assistantToolCallMessage}
/>
);
- },
+ }
)
: null}
diff --git a/llama_stack/ui/components/chat-completions/chat-completion-table.test.tsx b/llama_stack/ui/components/chat-completions/chat-completion-table.test.tsx
index 9171e0106..1cae95ddf 100644
--- a/llama_stack/ui/components/chat-completions/chat-completion-table.test.tsx
+++ b/llama_stack/ui/components/chat-completions/chat-completion-table.test.tsx
@@ -83,7 +83,7 @@ describe("ChatCompletionsTable", () => {
// Default pass-through implementations
truncateText.mockImplementation((text: string | undefined) => text);
extractTextFromContentPart.mockImplementation((content: unknown) =>
- typeof content === "string" ? content : "extracted text",
+ typeof content === "string" ? content : "extracted text"
);
extractDisplayableText.mockImplementation((message: unknown) => {
const msg = message as { content?: string };
@@ -138,7 +138,7 @@ describe("ChatCompletionsTable", () => {
if (row) {
fireEvent.click(row);
expect(mockPush).toHaveBeenCalledWith(
- "/logs/chat-completions/completion_123",
+ "/logs/chat-completions/completion_123"
);
} else {
throw new Error('Row with "Test prompt" not found for router mock test.');
@@ -162,7 +162,7 @@ describe("ChatCompletionsTable", () => {
expect(tableCaption).toBeInTheDocument();
if (tableCaption) {
const captionSkeleton = tableCaption.querySelector(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(captionSkeleton).toBeInTheDocument();
}
@@ -172,7 +172,7 @@ describe("ChatCompletionsTable", () => {
expect(tableBody).toBeInTheDocument();
if (tableBody) {
const bodySkeletons = tableBody.querySelectorAll(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(bodySkeletons.length).toBeGreaterThan(0);
}
@@ -192,14 +192,14 @@ describe("ChatCompletionsTable", () => {
render(
);
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
test.each([{ name: "Error", message: "" }, {}])(
"renders default error message when error has no message",
- (errorObject) => {
+ errorObject => {
mockedUsePagination.mockReturnValue({
data: [],
status: "error",
@@ -210,14 +210,14 @@ describe("ChatCompletionsTable", () => {
render(
);
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
screen.getByText(
- "An unexpected error occurred while loading the data.",
- ),
+ "An unexpected error occurred while loading the data."
+ )
).toBeInTheDocument();
- },
+ }
);
});
@@ -225,7 +225,7 @@ describe("ChatCompletionsTable", () => {
test('renders "No chat completions found." and no table when data array is empty', () => {
render(
);
expect(
- screen.getByText("No chat completions found."),
+ screen.getByText("No chat completions found.")
).toBeInTheDocument();
// Ensure that the table structure is NOT rendered in the empty state
@@ -292,7 +292,7 @@ describe("ChatCompletionsTable", () => {
// Table caption
expect(
- screen.getByText("A list of your recent chat completions."),
+ screen.getByText("A list of your recent chat completions.")
).toBeInTheDocument();
// Table headers
@@ -306,14 +306,14 @@ describe("ChatCompletionsTable", () => {
expect(screen.getByText("Test output")).toBeInTheDocument();
expect(screen.getByText("llama-test-model")).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("Another input")).toBeInTheDocument();
expect(screen.getByText("Another output")).toBeInTheDocument();
expect(screen.getByText("llama-another-model")).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710001000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710001000 * 1000).toLocaleString())
).toBeInTheDocument();
});
});
@@ -328,7 +328,7 @@ describe("ChatCompletionsTable", () => {
return typeof text === "string" && text.length > effectiveMaxLength
? text.slice(0, effectiveMaxLength) + "..."
: text;
- },
+ }
);
const longInput =
@@ -368,7 +368,7 @@ describe("ChatCompletionsTable", () => {
// The truncated text should be present for both input and output
const truncatedTexts = screen.getAllByText(
- longInput.slice(0, 10) + "...",
+ longInput.slice(0, 10) + "..."
);
expect(truncatedTexts.length).toBe(2); // one for input, one for output
});
@@ -420,7 +420,7 @@ describe("ChatCompletionsTable", () => {
// Verify the extracted text appears in the table
expect(screen.getByText("Extracted input")).toBeInTheDocument();
expect(
- screen.getByText("Extracted output from assistant"),
+ screen.getByText("Extracted output from assistant")
).toBeInTheDocument();
});
});
diff --git a/llama_stack/ui/components/chat-completions/chat-completions-table.tsx b/llama_stack/ui/components/chat-completions/chat-completions-table.tsx
index 65f6c71af..d30ec1538 100644
--- a/llama_stack/ui/components/chat-completions/chat-completions-table.tsx
+++ b/llama_stack/ui/components/chat-completions/chat-completions-table.tsx
@@ -38,7 +38,7 @@ export function ChatCompletionsTable({
limit: number;
model?: string;
order?: string;
- },
+ }
) => {
const response = await client.chat.completions.list({
after: params.after,
diff --git a/llama_stack/ui/components/chat-playground/chat-message.tsx b/llama_stack/ui/components/chat-playground/chat-message.tsx
index e5d621c81..02e6933cc 100644
--- a/llama_stack/ui/components/chat-playground/chat-message.tsx
+++ b/llama_stack/ui/components/chat-playground/chat-message.tsx
@@ -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
["animation"]
+type Animation = VariantProps["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]: any;
+ };
}
-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?: any;
}
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 = ({
@@ -150,21 +150,21 @@ export const ChatMessage: React.FC = ({
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 = ({
{files ? (
{files.map((file, index) => {
- return
+ return ;
})}
) : null}
@@ -195,7 +195,7 @@ export const ChatMessage: React.FC = ({
) : null}
- )
+ );
}
if (parts && parts.length > 0) {
@@ -230,23 +230,23 @@ export const ChatMessage: React.FC
= ({
) : null}
- )
+ );
} else if (part.type === "reasoning") {
- return
+ return
;
} else if (part.type === "tool-invocation") {
return (
- )
+ );
}
- return null
- })
+ return null;
+ });
}
if (toolInvocations && toolInvocations.length > 0) {
- return
+ return
;
}
return (
@@ -272,17 +272,17 @@ export const ChatMessage: React.FC
= ({
) : null}
- )
-}
+ );
+};
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 (
@@ -319,20 +319,20 @@ const ReasoningBlock = ({ part }: { part: ReasoningPart }) => {
- )
-}
+ );
+};
function ToolCall({
toolInvocations,
}: Pick
) {
- if (!toolInvocations?.length) return null
+ if (!toolInvocations?.length) return null;
return (
{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({
- )
+ );
}
switch (invocation.state) {
@@ -373,7 +373,7 @@ function ToolCall({
- )
+ );
case "result":
return (
- )
+ );
default:
- return null
+ return null;
}
})}
- )
+ );
}
diff --git a/llama_stack/ui/components/chat-playground/chat.tsx b/llama_stack/ui/components/chat-playground/chat.tsx
index ee83fd9bb..de4860f22 100644
--- a/llama_stack/ui/components/chat-playground/chat.tsx
+++ b/llama_stack/ui/components/chat-playground/chat.tsx
@@ -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
- input: string
- className?: string
- handleInputChange: React.ChangeEventHandler
- isGenerating: boolean
- stop?: () => void
+ ) => void;
+ messages: Array;
+ input: string;
+ className?: string;
+ handleInputChange: React.ChangeEventHandler;
+ isGenerating: boolean;
+ stop?: () => void;
onRateResponse?: (
messageId: string,
rating: "thumbs-up" | "thumbs-down"
- ) => void
- setMessages?: (messages: any[]) => void
- transcribeAudio?: (blob: Blob) => Promise
+ ) => void;
+ setMessages?: (messages: any[]) => void;
+ transcribeAudio?: (blob: Blob) => Promise;
}
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,17 +98,17 @@ 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,
- }
+ };
}
}
@@ -119,7 +119,7 @@ export function Chat({
part.toolInvocation &&
part.toolInvocation.state === "call"
) {
- needsUpdate = true
+ needsUpdate = true;
return {
...part,
toolInvocation: {
@@ -130,29 +130,29 @@ export function Chat({
__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 +189,7 @@ export function Chat({
),
}),
[onRateResponse]
- )
+ );
return (
@@ -237,15 +237,15 @@ export function Chat({
- )
+ );
}
-Chat.displayName = "Chat"
+Chat.displayName = "Chat";
export function ChatMessages({
messages,
children,
}: React.PropsWithChildren<{
- messages: Message[]
+ messages: Message[];
}>) {
const {
containerRef,
@@ -253,7 +253,7 @@ export function ChatMessages({
handleScroll,
shouldAutoScroll,
handleTouchStart,
- } = useAutoScroll([messages])
+ } = useAutoScroll([messages]);
return (
)}
- )
+ );
}
export const ChatContainer = forwardRef<
@@ -294,26 +294,26 @@ 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>
- }) => ReactElement
+ files: File[] | null;
+ setFiles: React.Dispatch>;
+ }) => ReactElement;
}
export const ChatForm = forwardRef(
({ children, handleSubmit, isPending, className }, ref) => {
- const [files, setFiles] = useState(null)
+ const [files, setFiles] = useState(null);
const onSubmit = (event: React.FormEvent) => {
// if (isPending) {
@@ -322,28 +322,28 @@ export const ChatForm = forwardRef(
// }
if (!files) {
- handleSubmit(event)
- return
+ handleSubmit(event);
+ return;
}
- const fileList = createFileList(files)
- handleSubmit(event, { experimental_attachments: fileList })
- setFiles(null)
- }
+ const fileList = createFileList(files);
+ handleSubmit(event, { experimental_attachments: fileList });
+ setFiles(null);
+ };
return (
- )
+ );
}
-)
-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;
}
diff --git a/llama_stack/ui/components/chat-playground/interrupt-prompt.tsx b/llama_stack/ui/components/chat-playground/interrupt-prompt.tsx
index 757863c62..157de7da1 100644
--- a/llama_stack/ui/components/chat-playground/interrupt-prompt.tsx
+++ b/llama_stack/ui/components/chat-playground/interrupt-prompt.tsx
@@ -1,11 +1,11 @@
-"use client"
+"use client";
-import { AnimatePresence, motion } from "framer-motion"
-import { X } from "lucide-react"
+import { AnimatePresence, motion } from "framer-motion";
+import { X } from "lucide-react";
interface InterruptPromptProps {
- isOpen: boolean
- close: () => void
+ isOpen: boolean;
+ close: () => void;
}
export function InterruptPrompt({ isOpen, close }: InterruptPromptProps) {
@@ -37,5 +37,5 @@ export function InterruptPrompt({ isOpen, close }: InterruptPromptProps) {
)}
- )
+ );
}
diff --git a/llama_stack/ui/components/chat-playground/markdown-renderer.tsx b/llama_stack/ui/components/chat-playground/markdown-renderer.tsx
index 1c2781eaf..a0e4227a6 100644
--- a/llama_stack/ui/components/chat-playground/markdown-renderer.tsx
+++ b/llama_stack/ui/components/chat-playground/markdown-renderer.tsx
@@ -1,12 +1,12 @@
-import React, { Suspense, useEffect, useState } from "react"
-import Markdown from "react-markdown"
-import remarkGfm from "remark-gfm"
+import React, { Suspense, useEffect, useState } from "react";
+import Markdown from "react-markdown";
+import remarkGfm from "remark-gfm";
-import { cn } from "@/lib/utils"
-import { CopyButton } from "@/components/ui/copy-button"
+import { cn } from "@/lib/utils";
+import { CopyButton } from "@/components/ui/copy-button";
interface MarkdownRendererProps {
- children: string
+ children: string;
}
export function MarkdownRenderer({ children }: MarkdownRendererProps) {
@@ -16,34 +16,34 @@ export function MarkdownRenderer({ children }: MarkdownRendererProps) {
{children}
- )
+ );
}
interface HighlightedPre extends React.HTMLAttributes {
- children: string
- language: string
+ children: string;
+ language: string;
}
const HighlightedPre = React.memo(
({ children, language, ...props }: HighlightedPre) => {
- const [tokens, setTokens] = useState(null)
- const [isSupported, setIsSupported] = useState(false)
+ const [tokens, setTokens] = useState(null);
+ const [isSupported, setIsSupported] = useState(false);
useEffect(() => {
- let mounted = true
+ let mounted = true;
const loadAndHighlight = async () => {
try {
- const { codeToTokens, bundledLanguages } = await import("shiki")
+ const { codeToTokens, bundledLanguages } = await import("shiki");
- if (!mounted) return
+ if (!mounted) return;
if (!(language in bundledLanguages)) {
- setIsSupported(false)
- return
+ setIsSupported(false);
+ return;
}
- setIsSupported(true)
+ setIsSupported(true);
const { tokens: highlightedTokens } = await codeToTokens(children, {
lang: language as keyof typeof bundledLanguages,
@@ -52,31 +52,31 @@ const HighlightedPre = React.memo(
light: "github-light",
dark: "github-dark",
},
- })
+ });
if (mounted) {
- setTokens(highlightedTokens)
+ setTokens(highlightedTokens);
}
} catch (error) {
if (mounted) {
- setIsSupported(false)
+ setIsSupported(false);
}
}
- }
+ };
- loadAndHighlight()
+ loadAndHighlight();
return () => {
- mounted = false
- }
- }, [children, language])
+ mounted = false;
+ };
+ }, [children, language]);
if (!isSupported) {
- return {children}
+ return {children}
;
}
if (!tokens) {
- return {children}
+ return {children}
;
}
return (
@@ -89,7 +89,7 @@ const HighlightedPre = React.memo(
const style =
typeof token.htmlStyle === "string"
? undefined
- : token.htmlStyle
+ : token.htmlStyle;
return (
{token.content}
- )
+ );
})}
{lineIndex !== tokens.length - 1 && "\n"}
@@ -107,15 +107,15 @@ const HighlightedPre = React.memo(
))}
- )
+ );
}
-)
-HighlightedPre.displayName = "HighlightedCode"
+);
+HighlightedPre.displayName = "HighlightedCode";
interface CodeBlockProps extends React.HTMLAttributes {
- children: React.ReactNode
- className?: string
- language: string
+ children: React.ReactNode;
+ className?: string;
+ language: string;
}
const CodeBlock = ({
@@ -127,12 +127,12 @@ const CodeBlock = ({
const code =
typeof children === "string"
? children
- : childrenTakeAllStringContents(children)
+ : childrenTakeAllStringContents(children);
const preClass = cn(
"overflow-x-scroll rounded-md border bg-background/50 p-4 font-mono text-sm [scrollbar-width:none]",
className
- )
+ );
return (
@@ -152,27 +152,27 @@ const CodeBlock = ({
- )
-}
+ );
+};
function childrenTakeAllStringContents(element: any): string {
if (typeof element === "string") {
- return element
+ return element;
}
if (element?.props?.children) {
- let children = element.props.children
+ const children = element.props.children;
if (Array.isArray(children)) {
return children
- .map((child) => childrenTakeAllStringContents(child))
- .join("")
+ .map(child => childrenTakeAllStringContents(child))
+ .join("");
} else {
- return childrenTakeAllStringContents(children)
+ return childrenTakeAllStringContents(children);
}
}
- return ""
+ return "";
}
const COMPONENTS = {
@@ -185,7 +185,7 @@ const COMPONENTS = {
a: withClass("a", "text-primary underline underline-offset-2"),
blockquote: withClass("blockquote", "border-l-2 border-primary pl-4"),
code: ({ children, className, node, ...rest }: any) => {
- const match = /language-(\w+)/.exec(className || "")
+ const match = /language-(\w+)/.exec(className || "");
return match ? (
{children}
@@ -199,7 +199,7 @@ const COMPONENTS = {
>
{children}
- )
+ );
},
pre: ({ children }: any) => children,
ol: withClass("ol", "list-decimal space-y-2 pl-6"),
@@ -220,14 +220,14 @@ const COMPONENTS = {
tr: withClass("tr", "m-0 border-t p-0 even:bg-muted"),
p: withClass("p", "whitespace-pre-wrap"),
hr: withClass("hr", "border-foreground/20"),
-}
+};
function withClass(Tag: keyof JSX.IntrinsicElements, classes: string) {
const Component = ({ node, ...props }: any) => (
- )
- Component.displayName = Tag
- return Component
+ );
+ Component.displayName = Tag;
+ return Component;
}
-export default MarkdownRenderer
+export default MarkdownRenderer;
diff --git a/llama_stack/ui/components/chat-playground/message-input.tsx b/llama_stack/ui/components/chat-playground/message-input.tsx
index 4a29386d9..57fac0ab4 100644
--- a/llama_stack/ui/components/chat-playground/message-input.tsx
+++ b/llama_stack/ui/components/chat-playground/message-input.tsx
@@ -1,41 +1,41 @@
-"use client"
+"use client";
-import React, { useEffect, useRef, useState } from "react"
-import { AnimatePresence, motion } from "framer-motion"
-import { ArrowUp, Info, Loader2, Mic, Paperclip, Square } from "lucide-react"
-import { omit } from "remeda"
+import React, { useEffect, useRef, useState } from "react";
+import { AnimatePresence, motion } from "framer-motion";
+import { ArrowUp, Info, Loader2, Mic, Paperclip, Square } from "lucide-react";
+import { omit } from "remeda";
-import { cn } from "@/lib/utils"
-import { useAudioRecording } from "@/hooks/use-audio-recording"
-import { useAutosizeTextArea } from "@/hooks/use-autosize-textarea"
-import { AudioVisualizer } from "@/components/ui/audio-visualizer"
-import { Button } from "@/components/ui/button"
-import { FilePreview } from "@/components/ui/file-preview"
-import { InterruptPrompt } from "@/components/chat-playground/interrupt-prompt"
+import { cn } from "@/lib/utils";
+import { useAudioRecording } from "@/hooks/use-audio-recording";
+import { useAutosizeTextArea } from "@/hooks/use-autosize-textarea";
+import { AudioVisualizer } from "@/components/ui/audio-visualizer";
+import { Button } from "@/components/ui/button";
+import { FilePreview } from "@/components/ui/file-preview";
+import { InterruptPrompt } from "@/components/chat-playground/interrupt-prompt";
interface MessageInputBaseProps
extends React.TextareaHTMLAttributes {
- value: string
- submitOnEnter?: boolean
- stop?: () => void
- isGenerating: boolean
- enableInterrupt?: boolean
- transcribeAudio?: (blob: Blob) => Promise
+ value: string;
+ submitOnEnter?: boolean;
+ stop?: () => void;
+ isGenerating: boolean;
+ enableInterrupt?: boolean;
+ transcribeAudio?: (blob: Blob) => Promise;
}
interface MessageInputWithoutAttachmentProps extends MessageInputBaseProps {
- allowAttachments?: false
+ allowAttachments?: false;
}
interface MessageInputWithAttachmentsProps extends MessageInputBaseProps {
- allowAttachments: true
- files: File[] | null
- setFiles: React.Dispatch>
+ allowAttachments: true;
+ files: File[] | null;
+ setFiles: React.Dispatch>;
}
type MessageInputProps =
| MessageInputWithoutAttachmentProps
- | MessageInputWithAttachmentsProps
+ | MessageInputWithAttachmentsProps;
export function MessageInput({
placeholder = "Ask AI...",
@@ -48,8 +48,8 @@ export function MessageInput({
transcribeAudio,
...props
}: MessageInputProps) {
- const [isDragging, setIsDragging] = useState(false)
- const [showInterruptPrompt, setShowInterruptPrompt] = useState(false)
+ const [isDragging, setIsDragging] = useState(false);
+ const [showInterruptPrompt, setShowInterruptPrompt] = useState(false);
const {
isListening,
@@ -61,123 +61,122 @@ export function MessageInput({
stopRecording,
} = useAudioRecording({
transcribeAudio,
- onTranscriptionComplete: (text) => {
- props.onChange?.({ target: { value: text } } as any)
+ onTranscriptionComplete: text => {
+ props.onChange?.({ target: { value: text } } as any);
},
- })
+ });
useEffect(() => {
if (!isGenerating) {
- setShowInterruptPrompt(false)
+ setShowInterruptPrompt(false);
}
- }, [isGenerating])
+ }, [isGenerating]);
const addFiles = (files: File[] | null) => {
if (props.allowAttachments) {
- props.setFiles((currentFiles) => {
+ props.setFiles(currentFiles => {
if (currentFiles === null) {
- return files
+ return files;
}
if (files === null) {
- return currentFiles
+ return currentFiles;
}
- return [...currentFiles, ...files]
- })
+ return [...currentFiles, ...files];
+ });
}
- }
+ };
const onDragOver = (event: React.DragEvent) => {
- if (props.allowAttachments !== true) return
- event.preventDefault()
- setIsDragging(true)
- }
+ if (props.allowAttachments !== true) return;
+ event.preventDefault();
+ setIsDragging(true);
+ };
const onDragLeave = (event: React.DragEvent) => {
- if (props.allowAttachments !== true) return
- event.preventDefault()
- setIsDragging(false)
- }
+ if (props.allowAttachments !== true) return;
+ event.preventDefault();
+ setIsDragging(false);
+ };
const onDrop = (event: React.DragEvent) => {
- setIsDragging(false)
- if (props.allowAttachments !== true) return
- event.preventDefault()
- const dataTransfer = event.dataTransfer
+ setIsDragging(false);
+ if (props.allowAttachments !== true) return;
+ event.preventDefault();
+ const dataTransfer = event.dataTransfer;
if (dataTransfer.files.length) {
- addFiles(Array.from(dataTransfer.files))
+ addFiles(Array.from(dataTransfer.files));
}
- }
+ };
const onPaste = (event: React.ClipboardEvent) => {
- const items = event.clipboardData?.items
- if (!items) return
+ const items = event.clipboardData?.items;
+ if (!items) return;
- const text = event.clipboardData.getData("text")
+ const text = event.clipboardData.getData("text");
if (text && text.length > 500 && props.allowAttachments) {
- event.preventDefault()
- const blob = new Blob([text], { type: "text/plain" })
+ event.preventDefault();
+ const blob = new Blob([text], { type: "text/plain" });
const file = new File([blob], "Pasted text", {
type: "text/plain",
lastModified: Date.now(),
- })
- addFiles([file])
- return
+ });
+ addFiles([file]);
+ return;
}
const files = Array.from(items)
- .map((item) => item.getAsFile())
- .filter((file) => file !== null)
+ .map(item => item.getAsFile())
+ .filter(file => file !== null);
if (props.allowAttachments && files.length > 0) {
- addFiles(files)
+ addFiles(files);
}
- }
+ };
const onKeyDown = (event: React.KeyboardEvent) => {
if (submitOnEnter && event.key === "Enter" && !event.shiftKey) {
- event.preventDefault()
+ event.preventDefault();
if (isGenerating && stop && enableInterrupt) {
if (showInterruptPrompt) {
- stop()
- setShowInterruptPrompt(false)
- event.currentTarget.form?.requestSubmit()
+ stop();
+ setShowInterruptPrompt(false);
+ event.currentTarget.form?.requestSubmit();
} else if (
props.value ||
(props.allowAttachments && props.files?.length)
) {
- setShowInterruptPrompt(true)
- return
+ setShowInterruptPrompt(true);
+ return;
}
}
- event.currentTarget.form?.requestSubmit()
+ event.currentTarget.form?.requestSubmit();
}
- onKeyDownProp?.(event)
- }
+ onKeyDownProp?.(event);
+ };
- const textAreaRef = useRef(null)
- const [textAreaHeight, setTextAreaHeight] = useState(0)
+ const textAreaRef = useRef(null);
+ const [textAreaHeight, setTextAreaHeight] = useState(0);
useEffect(() => {
if (textAreaRef.current) {
- setTextAreaHeight(textAreaRef.current.offsetHeight)
+ setTextAreaHeight(textAreaRef.current.offsetHeight);
}
- }, [props.value])
+ }, [props.value]);
const showFileList =
- props.allowAttachments && props.files && props.files.length > 0
-
+ props.allowAttachments && props.files && props.files.length > 0;
useAutosizeTextArea({
ref: textAreaRef,
maxHeight: 240,
borderWidth: 1,
dependencies: [props.value, showFileList],
- })
+ });
return (
- {props.files?.map((file) => {
+ {props.files?.map(file => {
return (
{
- props.setFiles((files) => {
- if (!files) return null
+ props.setFiles(files => {
+ if (!files) return null;
const filtered = Array.from(files).filter(
- (f) => f !== file
- )
- if (filtered.length === 0) return null
- return filtered
- })
+ f => f !== file
+ );
+ if (filtered.length === 0) return null;
+ return filtered;
+ });
}}
/>
- )
+ );
})}
@@ -256,8 +255,8 @@ export function MessageInput({
aria-label="Attach a file"
disabled={true}
onClick={async () => {
- const files = await showFileUploadDialog()
- addFiles(files)
+ const files = await showFileUploadDialog();
+ addFiles(files);
}}
>
@@ -308,12 +307,12 @@ export function MessageInput({
onStopRecording={stopRecording}
/>
- )
+ );
}
-MessageInput.displayName = "MessageInput"
+MessageInput.displayName = "MessageInput";
interface FileUploadOverlayProps {
- isDragging: boolean
+ isDragging: boolean;
}
function FileUploadOverlay({ isDragging }: FileUploadOverlayProps) {
@@ -333,29 +332,29 @@ function FileUploadOverlay({ isDragging }: FileUploadOverlayProps) {
)}
- )
+ );
}
function showFileUploadDialog() {
- const input = document.createElement("input")
+ const input = document.createElement("input");
- input.type = "file"
- input.multiple = true
- input.accept = "*/*"
- input.click()
+ input.type = "file";
+ input.multiple = true;
+ input.accept = "*/*";
+ input.click();
- return new Promise((resolve) => {
- input.onchange = (e) => {
- const files = (e.currentTarget as HTMLInputElement).files
+ return new Promise(resolve => {
+ input.onchange = e => {
+ const files = (e.currentTarget as HTMLInputElement).files;
if (files) {
- resolve(Array.from(files))
- return
+ resolve(Array.from(files));
+ return;
}
- resolve(null)
- }
- })
+ resolve(null);
+ };
+ });
}
function TranscribingOverlay() {
@@ -385,12 +384,12 @@ function TranscribingOverlay() {
Transcribing audio...
- )
+ );
}
interface RecordingPromptProps {
- isVisible: boolean
- onStopRecording: () => void
+ isVisible: boolean;
+ onStopRecording: () => void;
}
function RecordingPrompt({ isVisible, onStopRecording }: RecordingPromptProps) {
@@ -418,15 +417,15 @@ function RecordingPrompt({ isVisible, onStopRecording }: RecordingPromptProps) {
)}
- )
+ );
}
interface RecordingControlsProps {
- isRecording: boolean
- isTranscribing: boolean
- audioStream: MediaStream | null
- textAreaHeight: number
- onStopRecording: () => void
+ isRecording: boolean;
+ isTranscribing: boolean;
+ audioStream: MediaStream | null;
+ textAreaHeight: number;
+ onStopRecording: () => void;
}
function RecordingControls({
@@ -448,7 +447,7 @@ function RecordingControls({
onClick={onStopRecording}
/>
- )
+ );
}
if (isTranscribing) {
@@ -459,8 +458,8 @@ function RecordingControls({
>
- )
+ );
}
- return null
+ return null;
}
diff --git a/llama_stack/ui/components/chat-playground/message-list.tsx b/llama_stack/ui/components/chat-playground/message-list.tsx
index 5fe8409f4..5e8647748 100644
--- a/llama_stack/ui/components/chat-playground/message-list.tsx
+++ b/llama_stack/ui/components/chat-playground/message-list.tsx
@@ -2,18 +2,18 @@ import {
ChatMessage,
type ChatMessageProps,
type Message,
-} from "@/components/chat-playground/chat-message"
-import { TypingIndicator } from "@/components/chat-playground/typing-indicator"
+} from "@/components/chat-playground/chat-message";
+import { TypingIndicator } from "@/components/chat-playground/typing-indicator";
-type AdditionalMessageOptions = Omit
+type AdditionalMessageOptions = Omit;
interface MessageListProps {
- messages: Message[]
- showTimeStamps?: boolean
- isTyping?: boolean
+ messages: Message[];
+ showTimeStamps?: boolean;
+ isTyping?: boolean;
messageOptions?:
| AdditionalMessageOptions
- | ((message: Message) => AdditionalMessageOptions)
+ | ((message: Message) => AdditionalMessageOptions);
}
export function MessageList({
@@ -28,7 +28,7 @@ export function MessageList({
const additionalOptions =
typeof messageOptions === "function"
? messageOptions(message)
- : messageOptions
+ : messageOptions;
return (
- )
+ );
})}
{isTyping && }
- )
+ );
}
diff --git a/llama_stack/ui/components/chat-playground/prompt-suggestions.tsx b/llama_stack/ui/components/chat-playground/prompt-suggestions.tsx
index 9afaa4e66..075cce406 100644
--- a/llama_stack/ui/components/chat-playground/prompt-suggestions.tsx
+++ b/llama_stack/ui/components/chat-playground/prompt-suggestions.tsx
@@ -1,7 +1,7 @@
interface PromptSuggestionsProps {
- label: string
- append: (message: { role: "user"; content: string }) => void
- suggestions: string[]
+ label: string;
+ append: (message: { role: "user"; content: string }) => void;
+ suggestions: string[];
}
export function PromptSuggestions({
@@ -13,7 +13,7 @@ export function PromptSuggestions({
{label}
- {suggestions.map((suggestion) => (
+ {suggestions.map(suggestion => (
append({ role: "user", content: suggestion })}
@@ -24,5 +24,5 @@ export function PromptSuggestions({
))}
- )
+ );
}
diff --git a/llama_stack/ui/components/chat-playground/typing-indicator.tsx b/llama_stack/ui/components/chat-playground/typing-indicator.tsx
index 07055d428..8950c066b 100644
--- a/llama_stack/ui/components/chat-playground/typing-indicator.tsx
+++ b/llama_stack/ui/components/chat-playground/typing-indicator.tsx
@@ -1,4 +1,4 @@
-import { Dot } from "lucide-react"
+import { Dot } from "lucide-react";
export function TypingIndicator() {
return (
@@ -11,5 +11,5 @@ export function TypingIndicator() {
- )
+ );
}
diff --git a/llama_stack/ui/components/layout/app-sidebar.tsx b/llama_stack/ui/components/layout/app-sidebar.tsx
index 2ff106e01..bee3d6a70 100644
--- a/llama_stack/ui/components/layout/app-sidebar.tsx
+++ b/llama_stack/ui/components/layout/app-sidebar.tsx
@@ -56,18 +56,19 @@ const manageItems = [
},
];
-const optimizeItems: { title: string; url: string; icon: React.ElementType }[] = [
+const optimizeItems: { title: string; url: string; icon: React.ElementType }[] =
+ [
{
- title: "Evaluations",
- url: "",
- icon: Compass,
+ title: "Evaluations",
+ url: "",
+ icon: Compass,
},
{
- title: "Fine-tuning",
- url: "",
- icon: Settings2,
+ title: "Fine-tuning",
+ url: "",
+ icon: Settings2,
},
-];
+ ];
interface SidebarItem {
title: string;
@@ -79,7 +80,7 @@ export function AppSidebar() {
const pathname = usePathname();
const renderSidebarItems = (items: SidebarItem[]) => {
- return items.map((item) => {
+ return items.map(item => {
const isActive = pathname.startsWith(item.url);
return (
@@ -88,14 +89,14 @@ export function AppSidebar() {
className={cn(
"justify-start",
isActive &&
- "bg-gray-200 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-900 dark:text-gray-100",
+ "bg-gray-200 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700 text-gray-900 dark:text-gray-100"
)}
>
{item.title}
@@ -106,46 +107,48 @@ export function AppSidebar() {
});
};
-return (
-
-
- Llama Stack
-
-
-
- Create
-
- {renderSidebarItems(createItems)}
-
-
+ return (
+
+
+ Llama Stack
+
+
+
+ Create
+
+ {renderSidebarItems(createItems)}
+
+
-
- Manage
-
- {renderSidebarItems(manageItems)}
-
-
+
+ Manage
+
+ {renderSidebarItems(manageItems)}
+
+
-
- Optimize
-
-
- {optimizeItems.map((item) => (
-
-
-
- {item.title}
- (Coming Soon)
-
-
- ))}
-
-
-
-
-
+
+ Optimize
+
+
+ {optimizeItems.map(item => (
+
+
+
+ {item.title}
+
+ (Coming Soon)
+
+
+
+ ))}
+
+
+
+
+
);
}
diff --git a/llama_stack/ui/components/logs/logs-table-scroll.test.tsx b/llama_stack/ui/components/logs/logs-table-scroll.test.tsx
index a5c3fde46..9952f750b 100644
--- a/llama_stack/ui/components/logs/logs-table-scroll.test.tsx
+++ b/llama_stack/ui/components/logs/logs-table-scroll.test.tsx
@@ -67,7 +67,7 @@ describe("LogsTable Viewport Loading", () => {
() => {
expect(mockLoadMore).toHaveBeenCalled();
},
- { timeout: 300 },
+ { timeout: 300 }
);
expect(mockLoadMore).toHaveBeenCalledTimes(1);
@@ -81,11 +81,11 @@ describe("LogsTable Viewport Loading", () => {
{...defaultProps}
status="loading-more"
onLoadMore={mockLoadMore}
- />,
+ />
);
// Wait for possible triggers
- await new Promise((resolve) => setTimeout(resolve, 300));
+ await new Promise(resolve => setTimeout(resolve, 300));
expect(mockLoadMore).not.toHaveBeenCalled();
});
@@ -94,15 +94,11 @@ describe("LogsTable Viewport Loading", () => {
const mockLoadMore = jest.fn();
render(
- ,
+
);
// Wait for possible triggers
- await new Promise((resolve) => setTimeout(resolve, 300));
+ await new Promise(resolve => setTimeout(resolve, 300));
expect(mockLoadMore).not.toHaveBeenCalled();
});
@@ -111,18 +107,18 @@ describe("LogsTable Viewport Loading", () => {
const mockLoadMore = jest.fn();
render(
- ,
+
);
// Wait for possible triggers
- await new Promise((resolve) => setTimeout(resolve, 300));
+ await new Promise(resolve => setTimeout(resolve, 300));
expect(mockLoadMore).not.toHaveBeenCalled();
});
test("sentinel element should not be rendered when loading", () => {
const { container } = render(
- ,
+
);
// Check that no sentinel row with height: 1 exists
@@ -132,7 +128,7 @@ describe("LogsTable Viewport Loading", () => {
test("sentinel element should be rendered when not loading and hasMore", () => {
const { container } = render(
- ,
+
);
// Check that sentinel row exists
diff --git a/llama_stack/ui/components/logs/logs-table.test.tsx b/llama_stack/ui/components/logs/logs-table.test.tsx
index 9d129879b..b86cf1c12 100644
--- a/llama_stack/ui/components/logs/logs-table.test.tsx
+++ b/llama_stack/ui/components/logs/logs-table.test.tsx
@@ -70,7 +70,7 @@ describe("LogsTable", () => {
describe("Loading State", () => {
test("renders skeleton UI when isLoading is true", () => {
const { container } = render(
- ,
+
);
// Check for skeleton in the table caption
@@ -78,7 +78,7 @@ describe("LogsTable", () => {
expect(tableCaption).toBeInTheDocument();
if (tableCaption) {
const captionSkeleton = tableCaption.querySelector(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(captionSkeleton).toBeInTheDocument();
}
@@ -88,7 +88,7 @@ describe("LogsTable", () => {
expect(tableBody).toBeInTheDocument();
if (tableBody) {
const bodySkeletons = tableBody.querySelectorAll(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(bodySkeletons.length).toBeGreaterThan(0);
}
@@ -102,7 +102,7 @@ describe("LogsTable", () => {
test("renders correct number of skeleton rows", () => {
const { container } = render(
- ,
+
);
const skeletonRows = container.querySelectorAll("tbody tr");
@@ -118,10 +118,10 @@ describe("LogsTable", () => {
{...defaultProps}
status="error"
error={{ name: "Error", message: errorMessage } as Error}
- />,
+ />
);
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
@@ -132,29 +132,25 @@ describe("LogsTable", () => {
{...defaultProps}
status="error"
error={{ name: "Error", message: "" } as Error}
- />,
+ />
);
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
- screen.getByText(
- "An unexpected error occurred while loading the data.",
- ),
+ screen.getByText("An unexpected error occurred while loading the data.")
).toBeInTheDocument();
});
test("renders default error message when error prop is an object without message", () => {
render(
- ,
+
);
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
- screen.getByText(
- "An unexpected error occurred while loading the data.",
- ),
+ screen.getByText("An unexpected error occurred while loading the data.")
).toBeInTheDocument();
});
@@ -164,7 +160,7 @@ describe("LogsTable", () => {
{...defaultProps}
status="error"
error={{ name: "Error", message: "Test error" } as Error}
- />,
+ />
);
const table = screen.queryByRole("table");
expect(table).not.toBeInTheDocument();
@@ -178,7 +174,7 @@ describe("LogsTable", () => {
{...defaultProps}
data={[]}
emptyMessage="Custom empty message"
- />,
+ />
);
expect(screen.getByText("Custom empty message")).toBeInTheDocument();
@@ -214,7 +210,7 @@ describe("LogsTable", () => {
{...defaultProps}
data={mockData}
caption="Custom table caption"
- />,
+ />
);
// Table caption
@@ -311,8 +307,8 @@ describe("LogsTable", () => {
// Verify truncated text is displayed
const truncatedTexts = screen.getAllByText("This is a ...");
expect(truncatedTexts).toHaveLength(2); // one for input, one for output
- truncatedTexts.forEach((textElement) =>
- expect(textElement).toBeInTheDocument(),
+ truncatedTexts.forEach(textElement =>
+ expect(textElement).toBeInTheDocument()
);
});
@@ -332,12 +328,12 @@ describe("LogsTable", () => {
// Model name should not be passed to truncateText
expect(truncateText).not.toHaveBeenCalledWith(
- "very-long-model-name-that-should-not-be-truncated",
+ "very-long-model-name-that-should-not-be-truncated"
);
// Full model name should be displayed
expect(
- screen.getByText("very-long-model-name-that-should-not-be-truncated"),
+ screen.getByText("very-long-model-name-that-should-not-be-truncated")
).toBeInTheDocument();
});
});
diff --git a/llama_stack/ui/components/logs/logs-table.tsx b/llama_stack/ui/components/logs/logs-table.tsx
index 3d4e609c7..717b122ca 100644
--- a/llama_stack/ui/components/logs/logs-table.tsx
+++ b/llama_stack/ui/components/logs/logs-table.tsx
@@ -142,7 +142,7 @@ export function LogsTable({
{caption}
- {data.map((row) => (
+ {data.map(row => (
router.push(row.detailPath)}
diff --git a/llama_stack/ui/components/responses/grouping/grouped-items-display.tsx b/llama_stack/ui/components/responses/grouping/grouped-items-display.tsx
index 6ddc0eacc..5eaa93fac 100644
--- a/llama_stack/ui/components/responses/grouping/grouped-items-display.tsx
+++ b/llama_stack/ui/components/responses/grouping/grouped-items-display.tsx
@@ -22,7 +22,7 @@ export function GroupedItemsDisplay({
return (
<>
- {groupedItems.map((groupedItem) => {
+ {groupedItems.map(groupedItem => {
// If this is a function call with an output, render the grouped component
if (
groupedItem.outputItem &&
diff --git a/llama_stack/ui/components/responses/hooks/function-call-grouping.ts b/llama_stack/ui/components/responses/hooks/function-call-grouping.ts
index 2994354d5..203cd688f 100644
--- a/llama_stack/ui/components/responses/hooks/function-call-grouping.ts
+++ b/llama_stack/ui/components/responses/hooks/function-call-grouping.ts
@@ -18,7 +18,7 @@ export interface GroupedItem {
* @returns Array of grouped items with their outputs
*/
export function useFunctionCallGrouping(
- items: AnyResponseItem[],
+ items: AnyResponseItem[]
): GroupedItem[] {
return useMemo(() => {
const groupedItems: GroupedItem[] = [];
diff --git a/llama_stack/ui/components/responses/items/message-item.tsx b/llama_stack/ui/components/responses/items/message-item.tsx
index 5590e4460..68054c48f 100644
--- a/llama_stack/ui/components/responses/items/message-item.tsx
+++ b/llama_stack/ui/components/responses/items/message-item.tsx
@@ -20,7 +20,7 @@ export function MessageItemComponent({
content = item.content;
} else if (Array.isArray(item.content)) {
content = item.content
- .map((c) => {
+ .map(c => {
return c.type === "input_text" || c.type === "output_text"
? c.text
: JSON.stringify(c);
diff --git a/llama_stack/ui/components/responses/responses-detail.test.tsx b/llama_stack/ui/components/responses/responses-detail.test.tsx
index f426dc059..4c14db340 100644
--- a/llama_stack/ui/components/responses/responses-detail.test.tsx
+++ b/llama_stack/ui/components/responses/responses-detail.test.tsx
@@ -18,7 +18,7 @@ describe("ResponseDetailView", () => {
describe("Loading State", () => {
test("renders loading skeleton when isLoading is true", () => {
const { container } = render(
- ,
+
);
// Check for skeleton elements
@@ -36,13 +36,13 @@ describe("ResponseDetailView", () => {
,
+ />
);
expect(screen.getByText("Responses Details")).toBeInTheDocument();
// The error message is split across elements, so we check for parts
expect(
- screen.getByText(/Error loading details for ID/),
+ screen.getByText(/Error loading details for ID/)
).toBeInTheDocument();
expect(screen.getByText(/test_id/)).toBeInTheDocument();
expect(screen.getByText(/Network Error/)).toBeInTheDocument();
@@ -53,11 +53,11 @@ describe("ResponseDetailView", () => {
,
+ />
);
expect(
- screen.getByText(/Error loading details for ID/),
+ screen.getByText(/Error loading details for ID/)
).toBeInTheDocument();
expect(screen.getByText(/test_id/)).toBeInTheDocument();
});
@@ -124,14 +124,14 @@ describe("ResponseDetailView", () => {
// Check properties - use regex to handle text split across elements
expect(screen.getByText(/Created/)).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
// Check for the specific ID label (not Previous Response ID)
expect(
screen.getByText((content, element) => {
return element?.tagName === "STRONG" && content === "ID:";
- }),
+ })
).toBeInTheDocument();
expect(screen.getByText("resp_123")).toBeInTheDocument();
@@ -166,7 +166,7 @@ describe("ResponseDetailView", () => {
};
render(
- ,
+
);
// Should show required properties
@@ -179,7 +179,7 @@ describe("ResponseDetailView", () => {
expect(screen.queryByText("Top P")).not.toBeInTheDocument();
expect(screen.queryByText("Parallel Tool Calls")).not.toBeInTheDocument();
expect(
- screen.queryByText("Previous Response ID"),
+ screen.queryByText("Previous Response ID")
).not.toBeInTheDocument();
});
@@ -196,7 +196,7 @@ describe("ResponseDetailView", () => {
// The error is shown in the properties sidebar, not as a separate "Error" label
expect(
- screen.getByText("invalid_request: The request was invalid"),
+ screen.getByText("invalid_request: The request was invalid")
).toBeInTheDocument();
});
});
@@ -218,7 +218,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
isLoadingInputItems={true}
- />,
+ />
);
// Check for skeleton loading in input items section
@@ -227,7 +227,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
isLoadingInputItems={true}
- />,
+ />
);
const skeletons = container.querySelectorAll('[data-slot="skeleton"]');
@@ -243,16 +243,16 @@ describe("ResponseDetailView", () => {
name: "Error",
message: "Failed to load input items",
}}
- />,
+ />
);
expect(
screen.getByText(
- "Error loading input items: Failed to load input items",
- ),
+ "Error loading input items: Failed to load input items"
+ )
).toBeInTheDocument();
expect(
- screen.getByText("Falling back to response input data."),
+ screen.getByText("Falling back to response input data.")
).toBeInTheDocument();
// Should still show fallback input data
@@ -276,7 +276,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
inputItems={mockInputItems}
- />,
+ />
);
// Should show input items data, not response.input
@@ -295,7 +295,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={mockResponse}
inputItems={emptyInputItems}
- />,
+ />
);
// Should show fallback input data
@@ -313,7 +313,7 @@ describe("ResponseDetailView", () => {
{...defaultProps}
response={responseWithoutInput}
inputItems={null}
- />,
+ />
);
expect(screen.getByText("No input data available.")).toBeInTheDocument();
@@ -443,7 +443,7 @@ describe("ResponseDetailView", () => {
render();
expect(
- screen.getByText('input_function({"param": "value"})'),
+ screen.getByText('input_function({"param": "value"})')
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
});
@@ -468,7 +468,7 @@ describe("ResponseDetailView", () => {
render();
expect(
- screen.getByText("web_search_call(status: completed)"),
+ screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(screen.getByText("(Web Search)")).toBeInTheDocument();
@@ -522,7 +522,7 @@ describe("ResponseDetailView", () => {
render();
expect(
- screen.getByText("First output Second output"),
+ screen.getByText("First output Second output")
).toBeInTheDocument();
expect(screen.getByText("Assistant")).toBeInTheDocument();
});
@@ -549,7 +549,7 @@ describe("ResponseDetailView", () => {
render();
expect(
- screen.getByText('search_function({"query": "test"})'),
+ screen.getByText('search_function({"query": "test"})')
).toBeInTheDocument();
expect(screen.getByText("Function Call")).toBeInTheDocument();
});
@@ -598,7 +598,7 @@ describe("ResponseDetailView", () => {
render();
expect(
- screen.getByText("web_search_call(status: completed)"),
+ screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
expect(screen.getByText(/Function Call/)).toBeInTheDocument();
expect(screen.getByText("(Web Search)")).toBeInTheDocument();
@@ -625,7 +625,7 @@ describe("ResponseDetailView", () => {
// Should show JSON stringified content
expect(
- screen.getByText(/custom_field.*custom_value/),
+ screen.getByText(/custom_field.*custom_value/)
).toBeInTheDocument();
expect(screen.getByText("(unknown_type)")).toBeInTheDocument();
});
@@ -676,7 +676,7 @@ describe("ResponseDetailView", () => {
// Should show the function call and message as separate items (not grouped)
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(
- screen.getByText('get_weather({"city": "Tokyo"})'),
+ screen.getByText('get_weather({"city": "Tokyo"})')
).toBeInTheDocument();
expect(screen.getByText("Assistant")).toBeInTheDocument();
expect(screen.getByText("sunny and warm")).toBeInTheDocument();
@@ -717,7 +717,7 @@ describe("ResponseDetailView", () => {
expect(screen.getByText("Function Call")).toBeInTheDocument();
expect(screen.getByText("Arguments")).toBeInTheDocument();
expect(
- screen.getByText('get_weather({"city": "Tokyo"})'),
+ screen.getByText('get_weather({"city": "Tokyo"})')
).toBeInTheDocument();
// Use getAllByText since there are multiple "Output" elements (card title and output label)
const outputElements = screen.getAllByText("Output");
diff --git a/llama_stack/ui/components/responses/responses-table.test.tsx b/llama_stack/ui/components/responses/responses-table.test.tsx
index 0338b9151..4936a7be1 100644
--- a/llama_stack/ui/components/responses/responses-table.test.tsx
+++ b/llama_stack/ui/components/responses/responses-table.test.tsx
@@ -146,7 +146,7 @@ describe("ResponsesTable", () => {
expect(tableCaption).toBeInTheDocument();
if (tableCaption) {
const captionSkeleton = tableCaption.querySelector(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(captionSkeleton).toBeInTheDocument();
}
@@ -156,7 +156,7 @@ describe("ResponsesTable", () => {
expect(tableBody).toBeInTheDocument();
if (tableBody) {
const bodySkeletons = tableBody.querySelectorAll(
- '[data-slot="skeleton"]',
+ '[data-slot="skeleton"]'
);
expect(bodySkeletons.length).toBeGreaterThan(0);
}
@@ -176,14 +176,14 @@ describe("ResponsesTable", () => {
render();
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(screen.getByText(errorMessage)).toBeInTheDocument();
});
test.each([{ name: "Error", message: "" }, {}])(
"renders default error message when error has no message",
- (errorObject) => {
+ errorObject => {
mockedUsePagination.mockReturnValue({
data: [],
status: "error",
@@ -194,14 +194,14 @@ describe("ResponsesTable", () => {
render();
expect(
- screen.getByText("Unable to load chat completions"),
+ screen.getByText("Unable to load chat completions")
).toBeInTheDocument();
expect(
screen.getByText(
- "An unexpected error occurred while loading the data.",
- ),
+ "An unexpected error occurred while loading the data."
+ )
).toBeInTheDocument();
- },
+ }
);
});
@@ -275,7 +275,7 @@ describe("ResponsesTable", () => {
// Table caption
expect(
- screen.getByText("A list of your recent responses."),
+ screen.getByText("A list of your recent responses.")
).toBeInTheDocument();
// Table headers
@@ -289,14 +289,14 @@ describe("ResponsesTable", () => {
expect(screen.getByText("Test output")).toBeInTheDocument();
expect(screen.getByText("llama-test-model")).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710000000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710000000 * 1000).toLocaleString())
).toBeInTheDocument();
expect(screen.getByText("Another input")).toBeInTheDocument();
expect(screen.getByText("Another output")).toBeInTheDocument();
expect(screen.getByText("llama-another-model")).toBeInTheDocument();
expect(
- screen.getByText(new Date(1710001000 * 1000).toLocaleString()),
+ screen.getByText(new Date(1710001000 * 1000).toLocaleString())
).toBeInTheDocument();
});
});
@@ -487,7 +487,7 @@ describe("ResponsesTable", () => {
render();
expect(
- screen.getByText('search_function({"query": "test"})'),
+ screen.getByText('search_function({"query": "test"})')
).toBeInTheDocument();
});
@@ -548,7 +548,7 @@ describe("ResponsesTable", () => {
render();
expect(
- screen.getByText("web_search_call(status: completed)"),
+ screen.getByText("web_search_call(status: completed)")
).toBeInTheDocument();
});
@@ -623,7 +623,7 @@ describe("ResponsesTable", () => {
return typeof text === "string" && text.length > effectiveMaxLength
? text.slice(0, effectiveMaxLength) + "..."
: text;
- },
+ }
);
const longInput =
@@ -665,7 +665,7 @@ describe("ResponsesTable", () => {
// The truncated text should be present for both input and output
const truncatedTexts = screen.getAllByText(
- longInput.slice(0, 10) + "...",
+ longInput.slice(0, 10) + "..."
);
expect(truncatedTexts.length).toBe(2); // one for input, one for output
});
diff --git a/llama_stack/ui/components/responses/responses-table.tsx b/llama_stack/ui/components/responses/responses-table.tsx
index a3e8c0c15..dc8edd246 100644
--- a/llama_stack/ui/components/responses/responses-table.tsx
+++ b/llama_stack/ui/components/responses/responses-table.tsx
@@ -27,7 +27,7 @@ interface ResponsesTableProps {
* Helper function to convert ResponseListResponse.Data to OpenAIResponse
*/
const convertResponseListData = (
- responseData: ResponseListResponse.Data,
+ responseData: ResponseListResponse.Data
): OpenAIResponse => {
return {
id: responseData.id,
@@ -56,9 +56,7 @@ function getInputText(response: OpenAIResponse): string {
}
function getOutputText(response: OpenAIResponse): string {
- const firstMessage = response.output.find((item) =>
- isMessageItem(item as any),
- );
+ const firstMessage = response.output.find(item => isMessageItem(item as any));
if (firstMessage) {
const content = extractContentFromItem(firstMessage as MessageItem);
if (content) {
@@ -66,15 +64,15 @@ function getOutputText(response: OpenAIResponse): string {
}
}
- const functionCall = response.output.find((item) =>
- isFunctionCallItem(item as any),
+ const functionCall = response.output.find(item =>
+ isFunctionCallItem(item as any)
);
if (functionCall) {
return formatFunctionCall(functionCall as FunctionCallItem);
}
- const webSearchCall = response.output.find((item) =>
- isWebSearchCallItem(item as any),
+ const webSearchCall = response.output.find(item =>
+ isWebSearchCallItem(item as any)
);
if (webSearchCall) {
return formatWebSearchCall(webSearchCall as WebSearchCallItem);
@@ -95,7 +93,7 @@ function extractContentFromItem(item: {
} else if (Array.isArray(item.content)) {
const textContent = item.content.find(
(c: ResponseInputMessageContent) =>
- c.type === "input_text" || c.type === "output_text",
+ c.type === "input_text" || c.type === "output_text"
);
return textContent?.text || "";
}
@@ -131,7 +129,7 @@ export function ResponsesTable({ paginationOptions }: ResponsesTableProps) {
limit: number;
model?: string;
order?: string;
- },
+ }
) => {
const response = await client.responses.list({
after: params.after,
diff --git a/llama_stack/ui/components/responses/utils/item-types.ts b/llama_stack/ui/components/responses/utils/item-types.ts
index 2bde49119..72f31cf38 100644
--- a/llama_stack/ui/components/responses/utils/item-types.ts
+++ b/llama_stack/ui/components/responses/utils/item-types.ts
@@ -29,7 +29,7 @@ export type AnyResponseItem =
| FunctionCallOutputItem;
export function isMessageInput(
- item: ResponseInput,
+ item: ResponseInput
): item is ResponseInput & { type: "message" } {
return item.type === "message";
}
@@ -39,19 +39,19 @@ export function isMessageItem(item: AnyResponseItem): item is MessageItem {
}
export function isFunctionCallItem(
- item: AnyResponseItem,
+ item: AnyResponseItem
): item is FunctionCallItem {
return item.type === "function_call" && "name" in item;
}
export function isWebSearchCallItem(
- item: AnyResponseItem,
+ item: AnyResponseItem
): item is WebSearchCallItem {
return item.type === "web_search_call";
}
export function isFunctionCallOutputItem(
- item: AnyResponseItem,
+ item: AnyResponseItem
): item is FunctionCallOutputItem {
return (
item.type === "function_call_output" &&
diff --git a/llama_stack/ui/components/ui/audio-visualizer.tsx b/llama_stack/ui/components/ui/audio-visualizer.tsx
index e1c23c57b..772ed5eef 100644
--- a/llama_stack/ui/components/ui/audio-visualizer.tsx
+++ b/llama_stack/ui/components/ui/audio-visualizer.tsx
@@ -1,6 +1,6 @@
-"use client"
+"use client";
-import { useEffect, useRef } from "react"
+import { useEffect, useRef } from "react";
// Configuration constants for the audio analyzer
const AUDIO_CONFIG = {
@@ -14,12 +14,12 @@ const AUDIO_CONFIG = {
MAX_INTENSITY: 255, // Maximum gray value (brighter)
INTENSITY_RANGE: 155, // MAX_INTENSITY - MIN_INTENSITY
},
-} as const
+} as const;
interface AudioVisualizerProps {
- stream: MediaStream | null
- isRecording: boolean
- onClick: () => void
+ stream: MediaStream | null;
+ isRecording: boolean;
+ onClick: () => void;
}
export function AudioVisualizer({
@@ -28,91 +28,91 @@ export function AudioVisualizer({
onClick,
}: AudioVisualizerProps) {
// Refs for managing audio context and animation
- const canvasRef = useRef(null)
- const audioContextRef = useRef(null)
- const analyserRef = useRef(null)
- const animationFrameRef = useRef()
- const containerRef = useRef(null)
+ const canvasRef = useRef(null);
+ const audioContextRef = useRef(null);
+ const analyserRef = useRef(null);
+ const animationFrameRef = useRef();
+ const containerRef = useRef(null);
// Cleanup function to stop visualization and close audio context
const cleanup = () => {
if (animationFrameRef.current) {
- cancelAnimationFrame(animationFrameRef.current)
+ cancelAnimationFrame(animationFrameRef.current);
}
if (audioContextRef.current) {
- audioContextRef.current.close()
+ audioContextRef.current.close();
}
- }
+ };
// Cleanup on unmount
useEffect(() => {
- return cleanup
- }, [])
+ return cleanup;
+ }, []);
// Start or stop visualization based on recording state
useEffect(() => {
if (stream && isRecording) {
- startVisualization()
+ startVisualization();
} else {
- cleanup()
+ cleanup();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [stream, isRecording])
+ }, [stream, isRecording]);
// Handle window resize
useEffect(() => {
const handleResize = () => {
if (canvasRef.current && containerRef.current) {
- const container = containerRef.current
- const canvas = canvasRef.current
- const dpr = window.devicePixelRatio || 1
+ const container = containerRef.current;
+ const canvas = canvasRef.current;
+ const dpr = window.devicePixelRatio || 1;
// Set canvas size based on container and device pixel ratio
- const rect = container.getBoundingClientRect()
+ const rect = container.getBoundingClientRect();
// Account for the 2px total margin (1px on each side)
- canvas.width = (rect.width - 2) * dpr
- canvas.height = (rect.height - 2) * dpr
+ canvas.width = (rect.width - 2) * dpr;
+ canvas.height = (rect.height - 2) * dpr;
// Scale canvas CSS size to match container minus margins
- canvas.style.width = `${rect.width - 2}px`
- canvas.style.height = `${rect.height - 2}px`
+ canvas.style.width = `${rect.width - 2}px`;
+ canvas.style.height = `${rect.height - 2}px`;
}
- }
+ };
- window.addEventListener("resize", handleResize)
+ window.addEventListener("resize", handleResize);
// Initial setup
- handleResize()
+ handleResize();
- return () => window.removeEventListener("resize", handleResize)
- }, [])
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
// Initialize audio context and start visualization
const startVisualization = async () => {
try {
- const audioContext = new AudioContext()
- audioContextRef.current = audioContext
+ const audioContext = new AudioContext();
+ audioContextRef.current = audioContext;
- const analyser = audioContext.createAnalyser()
- analyser.fftSize = AUDIO_CONFIG.FFT_SIZE
- analyser.smoothingTimeConstant = AUDIO_CONFIG.SMOOTHING
- analyserRef.current = analyser
+ const analyser = audioContext.createAnalyser();
+ analyser.fftSize = AUDIO_CONFIG.FFT_SIZE;
+ analyser.smoothingTimeConstant = AUDIO_CONFIG.SMOOTHING;
+ analyserRef.current = analyser;
- const source = audioContext.createMediaStreamSource(stream!)
- source.connect(analyser)
+ const source = audioContext.createMediaStreamSource(stream!);
+ source.connect(analyser);
- draw()
+ draw();
} catch (error) {
- console.error("Error starting visualization:", error)
+ console.error("Error starting visualization:", error);
}
- }
+ };
// Calculate the color intensity based on bar height
const getBarColor = (normalizedHeight: number) => {
const intensity =
Math.floor(normalizedHeight * AUDIO_CONFIG.COLOR.INTENSITY_RANGE) +
- AUDIO_CONFIG.COLOR.MIN_INTENSITY
- return `rgb(${intensity}, ${intensity}, ${intensity})`
- }
+ AUDIO_CONFIG.COLOR.MIN_INTENSITY;
+ return `rgb(${intensity}, ${intensity}, ${intensity})`;
+ };
// Draw a single bar of the visualizer
const drawBar = (
@@ -123,52 +123,52 @@ export function AudioVisualizer({
height: number,
color: string
) => {
- ctx.fillStyle = color
+ ctx.fillStyle = color;
// Draw upper bar (above center)
- ctx.fillRect(x, centerY - height, width, height)
+ ctx.fillRect(x, centerY - height, width, height);
// Draw lower bar (below center)
- ctx.fillRect(x, centerY, width, height)
- }
+ ctx.fillRect(x, centerY, width, height);
+ };
// Main drawing function
const draw = () => {
- if (!isRecording) return
+ if (!isRecording) return;
- const canvas = canvasRef.current
- const ctx = canvas?.getContext("2d")
- if (!canvas || !ctx || !analyserRef.current) return
+ const canvas = canvasRef.current;
+ const ctx = canvas?.getContext("2d");
+ if (!canvas || !ctx || !analyserRef.current) return;
- const dpr = window.devicePixelRatio || 1
- ctx.scale(dpr, dpr)
+ const dpr = window.devicePixelRatio || 1;
+ ctx.scale(dpr, dpr);
- const analyser = analyserRef.current
- const bufferLength = analyser.frequencyBinCount
- const frequencyData = new Uint8Array(bufferLength)
+ const analyser = analyserRef.current;
+ const bufferLength = analyser.frequencyBinCount;
+ const frequencyData = new Uint8Array(bufferLength);
const drawFrame = () => {
- animationFrameRef.current = requestAnimationFrame(drawFrame)
+ animationFrameRef.current = requestAnimationFrame(drawFrame);
// Get current frequency data
- analyser.getByteFrequencyData(frequencyData)
+ analyser.getByteFrequencyData(frequencyData);
// Clear canvas - use CSS pixels for clearing
- ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr)
+ ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);
// Calculate dimensions in CSS pixels
const barWidth = Math.max(
AUDIO_CONFIG.MIN_BAR_WIDTH,
canvas.width / dpr / bufferLength - AUDIO_CONFIG.BAR_SPACING
- )
- const centerY = canvas.height / dpr / 2
- let x = 0
+ );
+ const centerY = canvas.height / dpr / 2;
+ let x = 0;
// Draw each frequency bar
for (let i = 0; i < bufferLength; i++) {
- const normalizedHeight = frequencyData[i] / 255 // Convert to 0-1 range
+ const normalizedHeight = frequencyData[i] / 255; // Convert to 0-1 range
const barHeight = Math.max(
AUDIO_CONFIG.MIN_BAR_HEIGHT,
normalizedHeight * centerY
- )
+ );
drawBar(
ctx,
@@ -177,14 +177,14 @@ export function AudioVisualizer({
barWidth,
barHeight,
getBarColor(normalizedHeight)
- )
+ );
- x += barWidth + AUDIO_CONFIG.BAR_SPACING
+ x += barWidth + AUDIO_CONFIG.BAR_SPACING;
}
- }
+ };
- drawFrame()
- }
+ drawFrame();
+ };
return (
- )
+ );
}
diff --git a/llama_stack/ui/components/ui/breadcrumb.tsx b/llama_stack/ui/components/ui/breadcrumb.tsx
index f63ae19af..9d88a372a 100644
--- a/llama_stack/ui/components/ui/breadcrumb.tsx
+++ b/llama_stack/ui/components/ui/breadcrumb.tsx
@@ -14,7 +14,7 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
data-slot="breadcrumb-list"
className={cn(
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/button.tsx b/llama_stack/ui/components/ui/button.tsx
index a2df8dce6..66ab90e53 100644
--- a/llama_stack/ui/components/ui/button.tsx
+++ b/llama_stack/ui/components/ui/button.tsx
@@ -1,8 +1,8 @@
-import * as React from "react"
-import { Slot } from "@radix-ui/react-slot"
-import { cva, type VariantProps } from "class-variance-authority"
+import * as React from "react";
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
@@ -33,7 +33,7 @@ const buttonVariants = cva(
size: "default",
},
}
-)
+);
function Button({
className,
@@ -43,9 +43,9 @@ function Button({
...props
}: React.ComponentProps<"button"> &
VariantProps & {
- asChild?: boolean
+ asChild?: boolean;
}) {
- const Comp = asChild ? Slot : "button"
+ const Comp = asChild ? Slot : "button";
return (
- )
+ );
}
-export { Button, buttonVariants }
+export { Button, buttonVariants };
diff --git a/llama_stack/ui/components/ui/card.tsx b/llama_stack/ui/components/ui/card.tsx
index 113d66c74..93a82d9c1 100644
--- a/llama_stack/ui/components/ui/card.tsx
+++ b/llama_stack/ui/components/ui/card.tsx
@@ -8,7 +8,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
- className,
+ className
)}
{...props}
/>
@@ -21,7 +21,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
- className,
+ className
)}
{...props}
/>
@@ -54,7 +54,7 @@ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/collapsible.tsx b/llama_stack/ui/components/ui/collapsible.tsx
index ae9fad04a..90935c6b2 100644
--- a/llama_stack/ui/components/ui/collapsible.tsx
+++ b/llama_stack/ui/components/ui/collapsible.tsx
@@ -1,11 +1,11 @@
-"use client"
+"use client";
-import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
+import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
function Collapsible({
...props
}: React.ComponentProps) {
- return
+ return ;
}
function CollapsibleTrigger({
@@ -16,7 +16,7 @@ function CollapsibleTrigger({
data-slot="collapsible-trigger"
{...props}
/>
- )
+ );
}
function CollapsibleContent({
@@ -27,7 +27,7 @@ function CollapsibleContent({
data-slot="collapsible-content"
{...props}
/>
- )
+ );
}
-export { Collapsible, CollapsibleTrigger, CollapsibleContent }
+export { Collapsible, CollapsibleTrigger, CollapsibleContent };
diff --git a/llama_stack/ui/components/ui/copy-button.tsx b/llama_stack/ui/components/ui/copy-button.tsx
index 51d2ca2d4..433e2474c 100644
--- a/llama_stack/ui/components/ui/copy-button.tsx
+++ b/llama_stack/ui/components/ui/copy-button.tsx
@@ -1,21 +1,21 @@
-"use client"
+"use client";
-import { Check, Copy } from "lucide-react"
+import { Check, Copy } from "lucide-react";
-import { cn } from "@/lib/utils"
-import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
-import { Button } from "@/components/ui/button"
+import { cn } from "@/lib/utils";
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard";
+import { Button } from "@/components/ui/button";
type CopyButtonProps = {
- content: string
- copyMessage?: string
-}
+ content: string;
+ copyMessage?: string;
+};
export function CopyButton({ content, copyMessage }: CopyButtonProps) {
const { isCopied, handleCopy } = useCopyToClipboard({
text: content,
copyMessage,
- })
+ });
return (
- )
+ );
}
diff --git a/llama_stack/ui/components/ui/dropdown-menu.tsx b/llama_stack/ui/components/ui/dropdown-menu.tsx
index 1fc1f4ee3..9cde4a3ca 100644
--- a/llama_stack/ui/components/ui/dropdown-menu.tsx
+++ b/llama_stack/ui/components/ui/dropdown-menu.tsx
@@ -43,7 +43,7 @@ function DropdownMenuContent({
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
- className,
+ className
)}
{...props}
/>
@@ -75,7 +75,7 @@ function DropdownMenuItem({
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
- className,
+ className
)}
{...props}
/>
@@ -93,7 +93,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
- className,
+ className
)}
checked={checked}
{...props}
@@ -129,7 +129,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
- className,
+ className
)}
{...props}
>
@@ -156,7 +156,7 @@ function DropdownMenuLabel({
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
- className,
+ className
)}
{...props}
/>
@@ -185,7 +185,7 @@ function DropdownMenuShortcut({
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
- className,
+ className
)}
{...props}
/>
@@ -212,7 +212,7 @@ function DropdownMenuSubTrigger({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
- className,
+ className
)}
{...props}
>
@@ -231,7 +231,7 @@ function DropdownMenuSubContent({
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/file-preview.tsx b/llama_stack/ui/components/ui/file-preview.tsx
index 8f0ed7da2..5d7dfda7e 100644
--- a/llama_stack/ui/components/ui/file-preview.tsx
+++ b/llama_stack/ui/components/ui/file-preview.tsx
@@ -1,18 +1,18 @@
-"use client"
+"use client";
-import React, { useEffect } from "react"
-import { motion } from "framer-motion"
-import { FileIcon, X } from "lucide-react"
+import React, { useEffect } from "react";
+import { motion } from "framer-motion";
+import { FileIcon, X } from "lucide-react";
interface FilePreviewProps {
- file: File
- onRemove?: () => void
+ file: File;
+ onRemove?: () => void;
}
export const FilePreview = React.forwardRef(
(props, ref) => {
if (props.file.type.startsWith("image/")) {
- return
+ return ;
}
if (
@@ -20,13 +20,13 @@ export const FilePreview = React.forwardRef(
props.file.name.endsWith(".txt") ||
props.file.name.endsWith(".md")
) {
- return
+ return ;
}
- return
+ return ;
}
-)
-FilePreview.displayName = "FilePreview"
+);
+FilePreview.displayName = "FilePreview";
const ImageFilePreview = React.forwardRef(
({ file, onRemove }, ref) => {
@@ -62,23 +62,23 @@ const ImageFilePreview = React.forwardRef(
) : null}
- )
+ );
}
-)
-ImageFilePreview.displayName = "ImageFilePreview"
+);
+ImageFilePreview.displayName = "ImageFilePreview";
const TextFilePreview = React.forwardRef(
({ file, onRemove }, ref) => {
- const [preview, setPreview] = React.useState("")
+ const [preview, setPreview] = React.useState("");
useEffect(() => {
- const reader = new FileReader()
- reader.onload = (e) => {
- const text = e.target?.result as string
- setPreview(text.slice(0, 50) + (text.length > 50 ? "..." : ""))
- }
- reader.readAsText(file)
- }, [file])
+ const reader = new FileReader();
+ reader.onload = e => {
+ const text = e.target?.result as string;
+ setPreview(text.slice(0, 50) + (text.length > 50 ? "..." : ""));
+ };
+ reader.readAsText(file);
+ }, [file]);
return (
(
) : null}
- )
+ );
}
-)
-TextFilePreview.displayName = "TextFilePreview"
+);
+TextFilePreview.displayName = "TextFilePreview";
const GenericFilePreview = React.forwardRef(
({ file, onRemove }, ref) => {
@@ -147,7 +147,7 @@ const GenericFilePreview = React.forwardRef(
) : null}
- )
+ );
}
-)
-GenericFilePreview.displayName = "GenericFilePreview"
+);
+GenericFilePreview.displayName = "GenericFilePreview";
diff --git a/llama_stack/ui/components/ui/input.tsx b/llama_stack/ui/components/ui/input.tsx
index b1a060f50..0316cc455 100644
--- a/llama_stack/ui/components/ui/input.tsx
+++ b/llama_stack/ui/components/ui/input.tsx
@@ -11,7 +11,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/select.tsx b/llama_stack/ui/components/ui/select.tsx
index dcbbc0ca0..c10e42aa5 100644
--- a/llama_stack/ui/components/ui/select.tsx
+++ b/llama_stack/ui/components/ui/select.tsx
@@ -1,27 +1,27 @@
-"use client"
+"use client";
-import * as React from "react"
-import * as SelectPrimitive from "@radix-ui/react-select"
-import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
-import { cn } from "@/lib/utils"
+import { cn } from "@/lib/utils";
function Select({
...props
}: React.ComponentProps) {
- return
+ return ;
}
function SelectGroup({
...props
}: React.ComponentProps) {
- return
+ return ;
}
function SelectValue({
...props
}: React.ComponentProps) {
- return
+ return ;
}
function SelectTrigger({
@@ -30,7 +30,7 @@ function SelectTrigger({
children,
...props
}: React.ComponentProps & {
- size?: "sm" | "default"
+ size?: "sm" | "default";
}) {
return (
- )
+ );
}
function SelectContent({
@@ -82,7 +82,7 @@ function SelectContent({
- )
+ );
}
function SelectLabel({
@@ -95,7 +95,7 @@ function SelectLabel({
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...props}
/>
- )
+ );
}
function SelectItem({
@@ -119,7 +119,7 @@ function SelectItem({
{children}
- )
+ );
}
function SelectSeparator({
@@ -132,7 +132,7 @@ function SelectSeparator({
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...props}
/>
- )
+ );
}
function SelectScrollUpButton({
@@ -150,7 +150,7 @@ function SelectScrollUpButton({
>
- )
+ );
}
function SelectScrollDownButton({
@@ -168,7 +168,7 @@ function SelectScrollDownButton({
>
- )
+ );
}
export {
@@ -182,4 +182,4 @@ export {
SelectSeparator,
SelectTrigger,
SelectValue,
-}
+};
diff --git a/llama_stack/ui/components/ui/separator.tsx b/llama_stack/ui/components/ui/separator.tsx
index 06d1380a9..7f8187751 100644
--- a/llama_stack/ui/components/ui/separator.tsx
+++ b/llama_stack/ui/components/ui/separator.tsx
@@ -18,7 +18,7 @@ function Separator({
orientation={orientation}
className={cn(
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/sheet.tsx b/llama_stack/ui/components/ui/sheet.tsx
index d30779f4f..6d6efec6a 100644
--- a/llama_stack/ui/components/ui/sheet.tsx
+++ b/llama_stack/ui/components/ui/sheet.tsx
@@ -37,7 +37,7 @@ function SheetOverlay({
data-slot="sheet-overlay"
className={cn(
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
- className,
+ className
)}
{...props}
/>
@@ -67,7 +67,7 @@ function SheetContent({
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
side === "bottom" &&
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
- className,
+ className
)}
{...props}
>
diff --git a/llama_stack/ui/components/ui/sidebar.tsx b/llama_stack/ui/components/ui/sidebar.tsx
index f8a0a3ed5..58228e56e 100644
--- a/llama_stack/ui/components/ui/sidebar.tsx
+++ b/llama_stack/ui/components/ui/sidebar.tsx
@@ -85,12 +85,12 @@ function SidebarProvider({
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
},
- [setOpenProp, open],
+ [setOpenProp, open]
);
// Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => {
- return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open);
+ return isMobile ? setOpenMobile(open => !open) : setOpen(open => !open);
}, [isMobile, setOpen, setOpenMobile]);
// Adds a keyboard shortcut to toggle the sidebar.
@@ -123,7 +123,7 @@ function SidebarProvider({
setOpenMobile,
toggleSidebar,
}),
- [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar],
+ [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]
);
return (
@@ -140,7 +140,7 @@ function SidebarProvider({
}
className={cn(
"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full",
- className,
+ className
)}
{...props}
>
@@ -171,7 +171,7 @@ function Sidebar({
data-slot="sidebar"
className={cn(
"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col",
- className,
+ className
)}
{...props}
>
@@ -223,7 +223,7 @@ function Sidebar({
"group-data-[side=right]:rotate-180",
variant === "floating" || variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
- : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"
)}
/>
@@ -267,7 +267,7 @@ function SidebarTrigger({
variant="ghost"
size="icon"
className={cn("size-7", className)}
- onClick={(event) => {
+ onClick={event => {
onClick?.(event);
toggleSidebar();
}}
@@ -297,7 +297,7 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
"hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
- className,
+ className
)}
{...props}
/>
@@ -311,7 +311,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
className={cn(
"bg-background relative flex w-full flex-1 flex-col",
"md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
- className,
+ className
)}
{...props}
/>
@@ -375,7 +375,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
data-sidebar="content"
className={cn(
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
- className,
+ className
)}
{...props}
/>
@@ -407,7 +407,7 @@ function SidebarGroupLabel({
className={cn(
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
- className,
+ className
)}
{...props}
/>
@@ -430,7 +430,7 @@ function SidebarGroupAction({
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 md:after:hidden",
"group-data-[collapsible=icon]:hidden",
- className,
+ className
)}
{...props}
/>
@@ -492,7 +492,7 @@ const sidebarMenuButtonVariants = cva(
variant: "default",
size: "default",
},
- },
+ }
);
function SidebarMenuButton({
@@ -570,7 +570,7 @@ function SidebarMenuAction({
"group-data-[collapsible=icon]:hidden",
showOnHover &&
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
- className,
+ className
)}
{...props}
/>
@@ -592,7 +592,7 @@ function SidebarMenuBadge({
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
- className,
+ className
)}
{...props}
/>
@@ -645,7 +645,7 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<"ul">) {
className={cn(
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
"group-data-[collapsible=icon]:hidden",
- className,
+ className
)}
{...props}
/>
@@ -691,7 +691,7 @@ function SidebarMenuSubButton({
size === "sm" && "text-xs",
size === "md" && "text-sm",
"group-data-[collapsible=icon]:hidden",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/sonner.tsx b/llama_stack/ui/components/ui/sonner.tsx
index 957524edb..f1259836a 100644
--- a/llama_stack/ui/components/ui/sonner.tsx
+++ b/llama_stack/ui/components/ui/sonner.tsx
@@ -1,10 +1,10 @@
-"use client"
+"use client";
-import { useTheme } from "next-themes"
-import { Toaster as Sonner, ToasterProps } from "sonner"
+import { useTheme } from "next-themes";
+import { Toaster as Sonner, ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => {
- const { theme = "system" } = useTheme()
+ const { theme = "system" } = useTheme();
return (
{
}
{...props}
/>
- )
-}
+ );
+};
-export { Toaster }
+export { Toaster };
diff --git a/llama_stack/ui/components/ui/table.tsx b/llama_stack/ui/components/ui/table.tsx
index 4b3c98ea4..1980f3ad3 100644
--- a/llama_stack/ui/components/ui/table.tsx
+++ b/llama_stack/ui/components/ui/table.tsx
@@ -45,7 +45,7 @@ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
data-slot="table-footer"
className={cn(
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
- className,
+ className
)}
{...props}
/>
@@ -58,7 +58,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
data-slot="table-row"
className={cn(
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
- className,
+ className
)}
{...props}
/>
@@ -71,7 +71,7 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
data-slot="table-head"
className={cn(
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
- className,
+ className
)}
{...props}
/>
@@ -84,7 +84,7 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
data-slot="table-cell"
className={cn(
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
- className,
+ className
)}
{...props}
/>
diff --git a/llama_stack/ui/components/ui/tooltip.tsx b/llama_stack/ui/components/ui/tooltip.tsx
index bf4a342a9..95e0faaf3 100644
--- a/llama_stack/ui/components/ui/tooltip.tsx
+++ b/llama_stack/ui/components/ui/tooltip.tsx
@@ -47,7 +47,7 @@ function TooltipContent({
sideOffset={sideOffset}
className={cn(
"bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
- className,
+ className
)}
{...props}
>
diff --git a/llama_stack/ui/components/vector-stores/vector-store-detail.tsx b/llama_stack/ui/components/vector-stores/vector-store-detail.tsx
index 6e26d2e3d..d3d0fa249 100644
--- a/llama_stack/ui/components/vector-stores/vector-store-detail.tsx
+++ b/llama_stack/ui/components/vector-stores/vector-store-detail.tsx
@@ -85,9 +85,9 @@ export function VectorStoreDetailView({
- {files.map((file) => (
+ {files.map(file => (
-
+
{
const scrollContainer = page.locator("div.overflow-auto").first();
// Scroll to near the bottom
- await scrollContainer.evaluate((element) => {
+ await scrollContainer.evaluate(element => {
element.scrollTop = element.scrollHeight - element.clientHeight - 100;
});
diff --git a/llama_stack/ui/hooks/use-audio-recording.ts b/llama_stack/ui/hooks/use-audio-recording.ts
index dd58ce6e7..4d08837e9 100644
--- a/llama_stack/ui/hooks/use-audio-recording.ts
+++ b/llama_stack/ui/hooks/use-audio-recording.ts
@@ -1,85 +1,85 @@
-import { useEffect, useRef, useState } from "react"
+import { useEffect, useRef, useState } from "react";
-import { recordAudio } from "@/lib/audio-utils"
+import { recordAudio } from "@/lib/audio-utils";
interface UseAudioRecordingOptions {
- transcribeAudio?: (blob: Blob) => Promise
- onTranscriptionComplete?: (text: string) => void
+ transcribeAudio?: (blob: Blob) => Promise;
+ onTranscriptionComplete?: (text: string) => void;
}
export function useAudioRecording({
transcribeAudio,
onTranscriptionComplete,
}: UseAudioRecordingOptions) {
- const [isListening, setIsListening] = useState(false)
- const [isSpeechSupported, setIsSpeechSupported] = useState(!!transcribeAudio)
- const [isRecording, setIsRecording] = useState(false)
- const [isTranscribing, setIsTranscribing] = useState(false)
- const [audioStream, setAudioStream] = useState(null)
- const activeRecordingRef = useRef(null)
+ const [isListening, setIsListening] = useState(false);
+ const [isSpeechSupported, setIsSpeechSupported] = useState(!!transcribeAudio);
+ const [isRecording, setIsRecording] = useState(false);
+ const [isTranscribing, setIsTranscribing] = useState(false);
+ const [audioStream, setAudioStream] = useState(null);
+ const activeRecordingRef = useRef(null);
useEffect(() => {
const checkSpeechSupport = async () => {
const hasMediaDevices = !!(
navigator.mediaDevices && navigator.mediaDevices.getUserMedia
- )
- setIsSpeechSupported(hasMediaDevices && !!transcribeAudio)
- }
+ );
+ setIsSpeechSupported(hasMediaDevices && !!transcribeAudio);
+ };
- checkSpeechSupport()
- }, [transcribeAudio])
+ checkSpeechSupport();
+ }, [transcribeAudio]);
const stopRecording = async () => {
- setIsRecording(false)
- setIsTranscribing(true)
+ setIsRecording(false);
+ setIsTranscribing(true);
try {
// First stop the recording to get the final blob
- recordAudio.stop()
+ recordAudio.stop();
// Wait for the recording promise to resolve with the final blob
- const recording = await activeRecordingRef.current
+ const recording = await activeRecordingRef.current;
if (transcribeAudio) {
- const text = await transcribeAudio(recording)
- onTranscriptionComplete?.(text)
+ const text = await transcribeAudio(recording);
+ onTranscriptionComplete?.(text);
}
} catch (error) {
- console.error("Error transcribing audio:", error)
+ console.error("Error transcribing audio:", error);
} finally {
- setIsTranscribing(false)
- setIsListening(false)
+ setIsTranscribing(false);
+ setIsListening(false);
if (audioStream) {
- audioStream.getTracks().forEach((track) => track.stop())
- setAudioStream(null)
+ audioStream.getTracks().forEach(track => track.stop());
+ setAudioStream(null);
}
- activeRecordingRef.current = null
+ activeRecordingRef.current = null;
}
- }
+ };
const toggleListening = async () => {
if (!isListening) {
try {
- setIsListening(true)
- setIsRecording(true)
+ setIsListening(true);
+ setIsRecording(true);
// Get audio stream first
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
- })
- setAudioStream(stream)
+ });
+ setAudioStream(stream);
// Start recording with the stream
- activeRecordingRef.current = recordAudio(stream)
+ activeRecordingRef.current = recordAudio(stream);
} catch (error) {
- console.error("Error recording audio:", error)
- setIsListening(false)
- setIsRecording(false)
+ console.error("Error recording audio:", error);
+ setIsListening(false);
+ setIsRecording(false);
if (audioStream) {
- audioStream.getTracks().forEach((track) => track.stop())
- setAudioStream(null)
+ audioStream.getTracks().forEach(track => track.stop());
+ setAudioStream(null);
}
}
} else {
- await stopRecording()
+ await stopRecording();
}
- }
+ };
return {
isListening,
@@ -89,5 +89,5 @@ export function useAudioRecording({
audioStream,
toggleListening,
stopRecording,
- }
+ };
}
diff --git a/llama_stack/ui/hooks/use-auto-scroll.ts b/llama_stack/ui/hooks/use-auto-scroll.ts
index 4d22c2cef..170aca688 100644
--- a/llama_stack/ui/hooks/use-auto-scroll.ts
+++ b/llama_stack/ui/hooks/use-auto-scroll.ts
@@ -1,67 +1,67 @@
-import { useEffect, useRef, useState } from "react"
+import { useEffect, useRef, useState } from "react";
// How many pixels from the bottom of the container to enable auto-scroll
-const ACTIVATION_THRESHOLD = 50
+const ACTIVATION_THRESHOLD = 50;
// Minimum pixels of scroll-up movement required to disable auto-scroll
-const MIN_SCROLL_UP_THRESHOLD = 10
+const MIN_SCROLL_UP_THRESHOLD = 10;
export function useAutoScroll(dependencies: React.DependencyList) {
- const containerRef = useRef(null)
- const previousScrollTop = useRef(null)
- const [shouldAutoScroll, setShouldAutoScroll] = useState(true)
+ const containerRef = useRef(null);
+ const previousScrollTop = useRef(null);
+ const [shouldAutoScroll, setShouldAutoScroll] = useState(true);
const scrollToBottom = () => {
if (containerRef.current) {
- containerRef.current.scrollTop = containerRef.current.scrollHeight
+ containerRef.current.scrollTop = containerRef.current.scrollHeight;
}
- }
+ };
const handleScroll = () => {
if (containerRef.current) {
- const { scrollTop, scrollHeight, clientHeight } = containerRef.current
+ const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
const distanceFromBottom = Math.abs(
scrollHeight - scrollTop - clientHeight
- )
+ );
const isScrollingUp = previousScrollTop.current
? scrollTop < previousScrollTop.current
- : false
+ : false;
const scrollUpDistance = previousScrollTop.current
? previousScrollTop.current - scrollTop
- : 0
+ : 0;
const isDeliberateScrollUp =
- isScrollingUp && scrollUpDistance > MIN_SCROLL_UP_THRESHOLD
+ isScrollingUp && scrollUpDistance > MIN_SCROLL_UP_THRESHOLD;
if (isDeliberateScrollUp) {
- setShouldAutoScroll(false)
+ setShouldAutoScroll(false);
} else {
- const isScrolledToBottom = distanceFromBottom < ACTIVATION_THRESHOLD
- setShouldAutoScroll(isScrolledToBottom)
+ const isScrolledToBottom = distanceFromBottom < ACTIVATION_THRESHOLD;
+ setShouldAutoScroll(isScrolledToBottom);
}
- previousScrollTop.current = scrollTop
+ previousScrollTop.current = scrollTop;
}
- }
+ };
const handleTouchStart = () => {
- setShouldAutoScroll(false)
- }
+ setShouldAutoScroll(false);
+ };
useEffect(() => {
if (containerRef.current) {
- previousScrollTop.current = containerRef.current.scrollTop
+ previousScrollTop.current = containerRef.current.scrollTop;
}
- }, [])
+ }, []);
useEffect(() => {
if (shouldAutoScroll) {
- scrollToBottom()
+ scrollToBottom();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, dependencies)
+ }, dependencies);
return {
containerRef,
@@ -69,5 +69,5 @@ export function useAutoScroll(dependencies: React.DependencyList) {
handleScroll,
shouldAutoScroll,
handleTouchStart,
- }
+ };
}
diff --git a/llama_stack/ui/hooks/use-autosize-textarea.ts b/llama_stack/ui/hooks/use-autosize-textarea.ts
index a0a36bb02..a38359033 100644
--- a/llama_stack/ui/hooks/use-autosize-textarea.ts
+++ b/llama_stack/ui/hooks/use-autosize-textarea.ts
@@ -1,10 +1,10 @@
-import { useLayoutEffect, useRef } from "react"
+import { useLayoutEffect, useRef } from "react";
interface UseAutosizeTextAreaProps {
- ref: React.RefObject
- maxHeight?: number
- borderWidth?: number
- dependencies: React.DependencyList
+ ref: React.RefObject;
+ maxHeight?: number;
+ borderWidth?: number;
+ dependencies: React.DependencyList;
}
export function useAutosizeTextArea({
@@ -13,27 +13,27 @@ export function useAutosizeTextArea({
borderWidth = 0,
dependencies,
}: UseAutosizeTextAreaProps) {
- const originalHeight = useRef(null)
+ const originalHeight = useRef(null);
useLayoutEffect(() => {
- if (!ref.current) return
+ if (!ref.current) return;
- const currentRef = ref.current
- const borderAdjustment = borderWidth * 2
+ const currentRef = ref.current;
+ const borderAdjustment = borderWidth * 2;
if (originalHeight.current === null) {
- originalHeight.current = currentRef.scrollHeight - borderAdjustment
+ originalHeight.current = currentRef.scrollHeight - borderAdjustment;
}
- currentRef.style.removeProperty("height")
- const scrollHeight = currentRef.scrollHeight
+ currentRef.style.removeProperty("height");
+ const scrollHeight = currentRef.scrollHeight;
// Make sure we don't go over maxHeight
- const clampedToMax = Math.min(scrollHeight, maxHeight)
+ const clampedToMax = Math.min(scrollHeight, maxHeight);
// Make sure we don't go less than the original height
- const clampedToMin = Math.max(clampedToMax, originalHeight.current)
+ const clampedToMin = Math.max(clampedToMax, originalHeight.current);
- currentRef.style.height = `${clampedToMin + borderAdjustment}px`
+ currentRef.style.height = `${clampedToMin + borderAdjustment}px`;
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [maxHeight, ref, ...dependencies])
+ }, [maxHeight, ref, ...dependencies]);
}
diff --git a/llama_stack/ui/hooks/use-copy-to-clipboard.ts b/llama_stack/ui/hooks/use-copy-to-clipboard.ts
index e2468d811..90043c4a0 100644
--- a/llama_stack/ui/hooks/use-copy-to-clipboard.ts
+++ b/llama_stack/ui/hooks/use-copy-to-clipboard.ts
@@ -1,36 +1,36 @@
-import { useCallback, useRef, useState } from "react"
-import { toast } from "sonner"
+import { useCallback, useRef, useState } from "react";
+import { toast } from "sonner";
type UseCopyToClipboardProps = {
- text: string
- copyMessage?: string
-}
+ text: string;
+ copyMessage?: string;
+};
export function useCopyToClipboard({
text,
copyMessage = "Copied to clipboard!",
}: UseCopyToClipboardProps) {
- const [isCopied, setIsCopied] = useState(false)
- const timeoutRef = useRef(null)
+ const [isCopied, setIsCopied] = useState(false);
+ const timeoutRef = useRef(null);
const handleCopy = useCallback(() => {
navigator.clipboard
.writeText(text)
.then(() => {
- toast.success(copyMessage)
- setIsCopied(true)
+ toast.success(copyMessage);
+ setIsCopied(true);
if (timeoutRef.current) {
- clearTimeout(timeoutRef.current)
- timeoutRef.current = null
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
}
timeoutRef.current = setTimeout(() => {
- setIsCopied(false)
- }, 2000)
+ setIsCopied(false);
+ }, 2000);
})
.catch(() => {
- toast.error("Failed to copy to clipboard.")
- })
- }, [text, copyMessage])
+ toast.error("Failed to copy to clipboard.");
+ });
+ }, [text, copyMessage]);
- return { isCopied, handleCopy }
+ return { isCopied, handleCopy };
}
diff --git a/llama_stack/ui/hooks/use-infinite-scroll.ts b/llama_stack/ui/hooks/use-infinite-scroll.ts
index 08a64a899..889c3f9fb 100644
--- a/llama_stack/ui/hooks/use-infinite-scroll.ts
+++ b/llama_stack/ui/hooks/use-infinite-scroll.ts
@@ -20,7 +20,7 @@ interface UseInfiniteScrollOptions {
*/
export function useInfiniteScroll(
onLoadMore: (() => void) | undefined,
- options: UseInfiniteScrollOptions = {},
+ options: UseInfiniteScrollOptions = {}
) {
const { enabled = true, threshold = 0.1, rootMargin = "100px" } = options;
const sentinelRef = useRef(null);
@@ -29,7 +29,7 @@ export function useInfiniteScroll(
if (!onLoadMore || !enabled) return;
const observer = new IntersectionObserver(
- (entries) => {
+ entries => {
const [entry] = entries;
if (entry.isIntersecting) {
onLoadMore();
@@ -38,7 +38,7 @@ export function useInfiniteScroll(
{
threshold,
rootMargin,
- },
+ }
);
const sentinel = sentinelRef.current;
diff --git a/llama_stack/ui/hooks/use-mobile.ts b/llama_stack/ui/hooks/use-mobile.ts
index a93d58393..48fab93c0 100644
--- a/llama_stack/ui/hooks/use-mobile.ts
+++ b/llama_stack/ui/hooks/use-mobile.ts
@@ -4,7 +4,7 @@ const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState(
- undefined,
+ undefined
);
React.useEffect(() => {
diff --git a/llama_stack/ui/hooks/use-pagination.ts b/llama_stack/ui/hooks/use-pagination.ts
index 58847ece5..9fa4fa338 100644
--- a/llama_stack/ui/hooks/use-pagination.ts
+++ b/llama_stack/ui/hooks/use-pagination.ts
@@ -38,7 +38,7 @@ interface UsePaginationParams extends UsePaginationOptions {
limit: number;
model?: string;
order?: string;
- },
+ }
) => Promise>;
errorMessagePrefix: string;
enabled?: boolean;
@@ -81,7 +81,7 @@ export function usePagination({
const fetchLimit = targetRows || limit;
try {
- setState((prev) => ({
+ setState(prev => ({
...prev,
status: isInitialLoad ? "loading" : "loading-more",
error: null,
@@ -94,7 +94,7 @@ export function usePagination({
...(order && { order }),
});
- setState((prev) => ({
+ setState(prev => ({
...prev,
data: isInitialLoad
? response.data
@@ -124,14 +124,14 @@ export function usePagination({
? new Error(`${errorMessage} ${err.message}`)
: new Error(errorMessage);
- setState((prev) => ({
+ setState(prev => ({
...prev,
error,
status: "error",
}));
}
},
- [limit, model, order, fetchFunction, errorMessagePrefix, client, router],
+ [limit, model, order, fetchFunction, errorMessagePrefix, client, router]
);
/**
diff --git a/llama_stack/ui/lib/audio-utils.ts b/llama_stack/ui/lib/audio-utils.ts
index b9ad9a3ef..24c4becfd 100644
--- a/llama_stack/ui/lib/audio-utils.ts
+++ b/llama_stack/ui/lib/audio-utils.ts
@@ -1,50 +1,50 @@
type RecordAudioType = {
- (stream: MediaStream): Promise
- stop: () => void
- currentRecorder?: MediaRecorder
-}
+ (stream: MediaStream): Promise;
+ stop: () => void;
+ currentRecorder?: MediaRecorder;
+};
export const recordAudio = (function (): RecordAudioType {
const func = async function recordAudio(stream: MediaStream): Promise {
try {
const mediaRecorder = new MediaRecorder(stream, {
mimeType: "audio/webm;codecs=opus",
- })
- const audioChunks: Blob[] = []
+ });
+ const audioChunks: Blob[] = [];
return new Promise((resolve, reject) => {
- mediaRecorder.ondataavailable = (event) => {
+ mediaRecorder.ondataavailable = event => {
if (event.data.size > 0) {
- audioChunks.push(event.data)
+ audioChunks.push(event.data);
}
- }
+ };
mediaRecorder.onstop = () => {
- const audioBlob = new Blob(audioChunks, { type: "audio/webm" })
- resolve(audioBlob)
- }
+ const audioBlob = new Blob(audioChunks, { type: "audio/webm" });
+ resolve(audioBlob);
+ };
mediaRecorder.onerror = () => {
- reject(new Error("MediaRecorder error occurred"))
- }
+ reject(new Error("MediaRecorder error occurred"));
+ };
- mediaRecorder.start(1000)
- ;(func as RecordAudioType).currentRecorder = mediaRecorder
- })
+ mediaRecorder.start(1000);
+ (func as RecordAudioType).currentRecorder = mediaRecorder;
+ });
} catch (error) {
const errorMessage =
- error instanceof Error ? error.message : "Unknown error occurred"
- throw new Error("Failed to start recording: " + errorMessage)
+ error instanceof Error ? error.message : "Unknown error occurred";
+ throw new Error("Failed to start recording: " + errorMessage);
}
- }
+ };
- ;(func as RecordAudioType).stop = () => {
- const recorder = (func as RecordAudioType).currentRecorder
+ (func as RecordAudioType).stop = () => {
+ const recorder = (func as RecordAudioType).currentRecorder;
if (recorder && recorder.state !== "inactive") {
- recorder.stop()
+ recorder.stop();
}
- delete (func as RecordAudioType).currentRecorder
- }
+ delete (func as RecordAudioType).currentRecorder;
+ };
- return func as RecordAudioType
-})()
+ return func as RecordAudioType;
+})();
diff --git a/llama_stack/ui/lib/config-validator.ts b/llama_stack/ui/lib/config-validator.ts
index 19f4397b8..0020942f9 100644
--- a/llama_stack/ui/lib/config-validator.ts
+++ b/llama_stack/ui/lib/config-validator.ts
@@ -27,19 +27,19 @@ export function validateServerConfig() {
!optionalConfigs.GITHUB_CLIENT_SECRET
) {
console.log(
- "\n📝 GitHub OAuth not configured (authentication features disabled)",
+ "\n📝 GitHub OAuth not configured (authentication features disabled)"
);
console.log(" To enable GitHub OAuth:");
console.log(" 1. Go to https://github.com/settings/applications/new");
console.log(
- " 2. Set Application name: Llama Stack UI (or your preferred name)",
+ " 2. Set Application name: Llama Stack UI (or your preferred name)"
);
console.log(" 3. Set Homepage URL: http://localhost:8322");
console.log(
- " 4. Set Authorization callback URL: http://localhost:8322/api/auth/callback/github",
+ " 4. Set Authorization callback URL: http://localhost:8322/api/auth/callback/github"
);
console.log(
- " 5. Create the app and copy the Client ID and Client Secret",
+ " 5. Create the app and copy the Client ID and Client Secret"
);
console.log(" 6. Add them to your .env.local file:");
console.log(" GITHUB_CLIENT_ID=your_client_id");
diff --git a/llama_stack/ui/lib/contents-api.ts b/llama_stack/ui/lib/contents-api.ts
index b8fcdb1a2..da6d0d324 100644
--- a/llama_stack/ui/lib/contents-api.ts
+++ b/llama_stack/ui/lib/contents-api.ts
@@ -32,11 +32,18 @@ export interface VectorStoreListContentsResponse {
export class ContentsAPI {
constructor(private client: LlamaStackClient) {}
- async getFileContents(vectorStoreId: string, fileId: string): Promise {
+ async getFileContents(
+ vectorStoreId: string,
+ fileId: string
+ ): Promise {
return this.client.vectorStores.files.content(vectorStoreId, fileId);
}
- async getContent(vectorStoreId: string, fileId: string, contentId: string): Promise {
+ async getContent(
+ vectorStoreId: string,
+ fileId: string,
+ contentId: string
+ ): Promise {
const contentsResponse = await this.listContents(vectorStoreId, fileId);
const targetContent = contentsResponse.data.find(c => c.id === contentId);
@@ -56,7 +63,11 @@ export class ContentsAPI {
throw new Error("Individual content updates not yet implemented in API");
}
- async deleteContent(vectorStoreId: string, fileId: string, contentId: string): Promise {
+ async deleteContent(
+ vectorStoreId: string,
+ fileId: string,
+ contentId: string
+ ): Promise {
throw new Error("Individual content deletion not yet implemented in API");
}
@@ -70,7 +81,10 @@ export class ContentsAPI {
before?: string;
}
): Promise {
- const fileContents = await this.client.vectorStores.files.content(vectorStoreId, fileId);
+ const fileContents = await this.client.vectorStores.files.content(
+ vectorStoreId,
+ fileId
+ );
const contentItems: VectorStoreContentItem[] = [];
fileContents.content.forEach((content, contentIndex) => {
@@ -78,10 +92,16 @@ export class ContentsAPI {
// Extract actual fields from the API response
const embedding = rawContent.embedding || undefined;
- const created_timestamp = rawContent.created_timestamp || rawContent.created_at || Date.now() / 1000;
+ const created_timestamp =
+ rawContent.created_timestamp ||
+ rawContent.created_at ||
+ Date.now() / 1000;
const chunkMetadata = rawContent.chunk_metadata || {};
- const contentId = rawContent.chunk_metadata?.chunk_id || rawContent.id || `content_${fileId}_${contentIndex}`;
- const objectType = rawContent.object || 'vector_store.file.content';
+ const contentId =
+ rawContent.chunk_metadata?.chunk_id ||
+ rawContent.id ||
+ `content_${fileId}_${contentIndex}`;
+ const objectType = rawContent.object || "vector_store.file.content";
contentItems.push({
id: contentId,
object: objectType,
@@ -92,7 +112,7 @@ export class ContentsAPI {
embedding: embedding,
metadata: {
...chunkMetadata, // chunk_metadata fields from API
- content_length: content.type === 'text' ? content.text.length : 0,
+ content_length: content.type === "text" ? content.text.length : 0,
},
});
});
@@ -104,7 +124,7 @@ export class ContentsAPI {
}
return {
- object: 'list',
+ object: "list",
data: filteredItems,
has_more: contentItems.length > (options?.limit || contentItems.length),
};
diff --git a/llama_stack/ui/lib/format-message-content.test.ts b/llama_stack/ui/lib/format-message-content.test.ts
index cf4055b51..e082eec26 100644
--- a/llama_stack/ui/lib/format-message-content.test.ts
+++ b/llama_stack/ui/lib/format-message-content.test.ts
@@ -18,7 +18,7 @@ describe("extractTextFromContentPart", () => {
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?",
+ "Which planet do humans live on?"
);
});
@@ -37,7 +37,7 @@ describe("extractTextFromContentPart", () => {
{ type: "text", text: "It's an image." },
];
expect(extractTextFromContentPart(content)).toBe(
- "Look at this: [Image] It's an image.",
+ "Look at this: [Image] It's an image."
);
});
@@ -77,7 +77,7 @@ describe("extractTextFromContentPart", () => {
{ type: "text", text: "Last part." },
] as any;
expect(extractTextFromContentPart(content)).toBe(
- "First part. Just a string. [Image] Last part.",
+ "First part. Just a string. [Image] Last part."
);
});
});
@@ -125,7 +125,7 @@ describe("extractDisplayableText (composite function)", () => {
tool_calls: [toolCall],
};
expect(extractDisplayableText(messageWithEffectivelyEmptyContent)).toBe(
- mockFormatToolCallToString(toolCall),
+ mockFormatToolCallToString(toolCall)
);
const messageWithEmptyContent: ChatMessage = {
@@ -134,7 +134,7 @@ describe("extractDisplayableText (composite function)", () => {
tool_calls: [toolCall],
};
expect(extractDisplayableText(messageWithEmptyContent)).toBe(
- mockFormatToolCallToString(toolCall),
+ mockFormatToolCallToString(toolCall)
);
});
@@ -149,7 +149,7 @@ describe("extractDisplayableText (composite function)", () => {
};
const expectedToolCallStr = mockFormatToolCallToString(toolCall);
expect(extractDisplayableText(message)).toBe(
- `The result is: ${expectedToolCallStr}`,
+ `The result is: ${expectedToolCallStr}`
);
});
@@ -167,7 +167,7 @@ describe("extractDisplayableText (composite function)", () => {
};
const expectedToolCallStr = mockFormatToolCallToString(toolCall);
expect(extractDisplayableText(message)).toBe(
- `Okay, checking weather for London. ${expectedToolCallStr}`,
+ `Okay, checking weather for London. ${expectedToolCallStr}`
);
});
@@ -178,7 +178,7 @@ describe("extractDisplayableText (composite function)", () => {
tool_calls: [],
};
expect(extractDisplayableText(messageEmptyToolCalls)).toBe(
- "No tools here.",
+ "No tools here."
);
const messageUndefinedToolCalls: ChatMessage = {
@@ -187,7 +187,7 @@ describe("extractDisplayableText (composite function)", () => {
tool_calls: undefined,
};
expect(extractDisplayableText(messageUndefinedToolCalls)).toBe(
- "Still no tools.",
+ "Still no tools."
);
});
});
diff --git a/llama_stack/ui/lib/format-message-content.ts b/llama_stack/ui/lib/format-message-content.ts
index 3e7e03a12..ab79775c6 100644
--- a/llama_stack/ui/lib/format-message-content.ts
+++ b/llama_stack/ui/lib/format-message-content.ts
@@ -2,7 +2,7 @@ import { ChatMessage, ChatMessageContentPart } from "@/lib/types";
import { formatToolCallToString } from "@/lib/format-tool-call";
export function extractTextFromContentPart(
- content: string | ChatMessageContentPart[] | null | undefined,
+ content: string | ChatMessageContentPart[] | null | undefined
): string {
if (content === null || content === undefined) {
return "";
@@ -37,7 +37,7 @@ export function extractTextFromContentPart(
}
export function extractDisplayableText(
- message: ChatMessage | undefined | null,
+ message: ChatMessage | undefined | null
): string {
if (!message) {
return "";
diff --git a/llama_stack/ui/lib/truncate-text.ts b/llama_stack/ui/lib/truncate-text.ts
index 63e2194f5..59fc1f5ff 100644
--- a/llama_stack/ui/lib/truncate-text.ts
+++ b/llama_stack/ui/lib/truncate-text.ts
@@ -1,6 +1,6 @@
export function truncateText(
text: string | null | undefined,
- maxLength: number = 50,
+ maxLength: number = 50
): string {
if (!text) return "N/A";
if (text.length <= maxLength) return text;