mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-08-16 06:27:58 +00:00
feat(UI): Adding linter and prettier for UI (#3156)
This commit is contained in:
parent
61582f327c
commit
e69acbafbf
73 changed files with 1452 additions and 1226 deletions
|
@ -1,50 +1,50 @@
|
|||
type RecordAudioType = {
|
||||
(stream: MediaStream): Promise<Blob>
|
||||
stop: () => void
|
||||
currentRecorder?: MediaRecorder
|
||||
}
|
||||
(stream: MediaStream): Promise<Blob>;
|
||||
stop: () => void;
|
||||
currentRecorder?: MediaRecorder;
|
||||
};
|
||||
|
||||
export const recordAudio = (function (): RecordAudioType {
|
||||
const func = async function recordAudio(stream: MediaStream): Promise<Blob> {
|
||||
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;
|
||||
})();
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -11,7 +11,7 @@ export interface VectorStoreContentItem {
|
|||
vector_store_id: string;
|
||||
file_id: string;
|
||||
content: VectorStoreContent;
|
||||
metadata: Record<string, any>;
|
||||
metadata: Record<string, unknown>;
|
||||
embedding?: number[];
|
||||
}
|
||||
|
||||
|
@ -32,11 +32,18 @@ export interface VectorStoreListContentsResponse {
|
|||
export class ContentsAPI {
|
||||
constructor(private client: LlamaStackClient) {}
|
||||
|
||||
async getFileContents(vectorStoreId: string, fileId: string): Promise<VectorStoreContentsResponse> {
|
||||
async getFileContents(
|
||||
vectorStoreId: string,
|
||||
fileId: string
|
||||
): Promise<VectorStoreContentsResponse> {
|
||||
return this.client.vectorStores.files.content(vectorStoreId, fileId);
|
||||
}
|
||||
|
||||
async getContent(vectorStoreId: string, fileId: string, contentId: string): Promise<VectorStoreContentItem> {
|
||||
async getContent(
|
||||
vectorStoreId: string,
|
||||
fileId: string,
|
||||
contentId: string
|
||||
): Promise<VectorStoreContentItem> {
|
||||
const contentsResponse = await this.listContents(vectorStoreId, fileId);
|
||||
const targetContent = contentsResponse.data.find(c => c.id === contentId);
|
||||
|
||||
|
@ -47,16 +54,11 @@ export class ContentsAPI {
|
|||
return targetContent;
|
||||
}
|
||||
|
||||
async updateContent(
|
||||
vectorStoreId: string,
|
||||
fileId: string,
|
||||
contentId: string,
|
||||
updates: { content?: string; metadata?: Record<string, any> }
|
||||
): Promise<VectorStoreContentItem> {
|
||||
async updateContent(): Promise<VectorStoreContentItem> {
|
||||
throw new Error("Individual content updates not yet implemented in API");
|
||||
}
|
||||
|
||||
async deleteContent(vectorStoreId: string, fileId: string, contentId: string): Promise<VectorStoreContentDeleteResponse> {
|
||||
async deleteContent(): Promise<VectorStoreContentDeleteResponse> {
|
||||
throw new Error("Individual content deletion not yet implemented in API");
|
||||
}
|
||||
|
||||
|
@ -70,18 +72,27 @@ export class ContentsAPI {
|
|||
before?: string;
|
||||
}
|
||||
): Promise<VectorStoreListContentsResponse> {
|
||||
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) => {
|
||||
const rawContent = content as any;
|
||||
const rawContent = content as Record<string, unknown>;
|
||||
|
||||
// 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 +103,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 +115,7 @@ export class ContentsAPI {
|
|||
}
|
||||
|
||||
return {
|
||||
object: 'list',
|
||||
object: "list",
|
||||
data: filteredItems,
|
||||
has_more: contentItems.length > (options?.limit || contentItems.length),
|
||||
};
|
||||
|
|
|
@ -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."
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -53,7 +53,7 @@ describe("extractTextFromContentPart", () => {
|
|||
});
|
||||
|
||||
it("should handle arrays with plain strings", () => {
|
||||
const content = ["This is", " a test."] as any;
|
||||
const content = ["This is", " a test."] as unknown;
|
||||
expect(extractTextFromContentPart(content)).toBe("This is a test.");
|
||||
});
|
||||
|
||||
|
@ -65,7 +65,7 @@ describe("extractTextFromContentPart", () => {
|
|||
null,
|
||||
undefined,
|
||||
{ type: "text", noTextProperty: true },
|
||||
] as any;
|
||||
] as unknown;
|
||||
expect(extractTextFromContentPart(content)).toBe("Valid");
|
||||
});
|
||||
|
||||
|
@ -75,15 +75,17 @@ describe("extractTextFromContentPart", () => {
|
|||
"Just a string.",
|
||||
{ type: "image_url", image_url: { url: "http://example.com/image.png" } },
|
||||
{ type: "text", text: "Last part." },
|
||||
] as any;
|
||||
] as unknown;
|
||||
expect(extractTextFromContentPart(content)).toBe(
|
||||
"First part. Just a string. [Image] Last part.",
|
||||
"First part. Just a string. [Image] Last part."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractDisplayableText (composite function)", () => {
|
||||
const mockFormatToolCallToString = (toolCall: any) => {
|
||||
const mockFormatToolCallToString = (toolCall: {
|
||||
function?: { name?: string; arguments?: unknown };
|
||||
}) => {
|
||||
if (!toolCall || !toolCall.function || !toolCall.function.name) return "";
|
||||
const args = toolCall.function.arguments
|
||||
? JSON.stringify(toolCall.function.arguments)
|
||||
|
@ -125,7 +127,7 @@ describe("extractDisplayableText (composite function)", () => {
|
|||
tool_calls: [toolCall],
|
||||
};
|
||||
expect(extractDisplayableText(messageWithEffectivelyEmptyContent)).toBe(
|
||||
mockFormatToolCallToString(toolCall),
|
||||
mockFormatToolCallToString(toolCall)
|
||||
);
|
||||
|
||||
const messageWithEmptyContent: ChatMessage = {
|
||||
|
@ -134,7 +136,7 @@ describe("extractDisplayableText (composite function)", () => {
|
|||
tool_calls: [toolCall],
|
||||
};
|
||||
expect(extractDisplayableText(messageWithEmptyContent)).toBe(
|
||||
mockFormatToolCallToString(toolCall),
|
||||
mockFormatToolCallToString(toolCall)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -149,7 +151,7 @@ describe("extractDisplayableText (composite function)", () => {
|
|||
};
|
||||
const expectedToolCallStr = mockFormatToolCallToString(toolCall);
|
||||
expect(extractDisplayableText(message)).toBe(
|
||||
`The result is: ${expectedToolCallStr}`,
|
||||
`The result is: ${expectedToolCallStr}`
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -167,7 +169,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 +180,7 @@ describe("extractDisplayableText (composite function)", () => {
|
|||
tool_calls: [],
|
||||
};
|
||||
expect(extractDisplayableText(messageEmptyToolCalls)).toBe(
|
||||
"No tools here.",
|
||||
"No tools here."
|
||||
);
|
||||
|
||||
const messageUndefinedToolCalls: ChatMessage = {
|
||||
|
@ -187,7 +189,7 @@ describe("extractDisplayableText (composite function)", () => {
|
|||
tool_calls: undefined,
|
||||
};
|
||||
expect(extractDisplayableText(messageUndefinedToolCalls)).toBe(
|
||||
"Still no tools.",
|
||||
"Still no tools."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 "";
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* with `name` and `arguments`.
|
||||
* @returns A formatted string or an empty string if data is malformed.
|
||||
*/
|
||||
export function formatToolCallToString(toolCall: any): string {
|
||||
export function formatToolCallToString(toolCall: {
|
||||
function?: { name?: string; arguments?: unknown };
|
||||
}): string {
|
||||
if (
|
||||
!toolCall ||
|
||||
!toolCall.function ||
|
||||
|
@ -24,7 +26,7 @@ export function formatToolCallToString(toolCall: any): string {
|
|||
} else {
|
||||
try {
|
||||
argsString = JSON.stringify(args);
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue