feat: add image previews in dataset dialog, enable popularity sorting in model search, refine training config serialization
This commit is contained in:
parent
b0cb7a7305
commit
b0062535a7
3 changed files with 103 additions and 1 deletions
|
|
@ -27,6 +27,14 @@ type CheckFormatResponse = {
|
|||
total_rows?: number | null;
|
||||
};
|
||||
|
||||
type PreviewImagePayload = {
|
||||
type: "image";
|
||||
mime?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
data?: string;
|
||||
};
|
||||
|
||||
type DatasetPreviewDialogProps = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
|
|
@ -123,6 +131,36 @@ export function DatasetPreviewDialog({
|
|||
),
|
||||
cell: ({ getValue }: { getValue: () => unknown }) => {
|
||||
const value = getValue();
|
||||
const images = collectPreviewImages(value);
|
||||
if (images.length > 0) {
|
||||
return (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{images.slice(0, 4).map((image, index) => {
|
||||
const mime = image.mime || "image/jpeg";
|
||||
const src = image.data ? `data:${mime};base64,${image.data}` : "";
|
||||
const width = image.width ?? 128;
|
||||
const height = image.height ?? 128;
|
||||
return (
|
||||
<img
|
||||
key={`${colName}-img-${index}`}
|
||||
src={src}
|
||||
alt={`preview-${index}`}
|
||||
className="h-16 w-auto max-w-40 rounded-md border object-contain bg-muted"
|
||||
width={width}
|
||||
height={height}
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{images.length > 4 && (
|
||||
<span className="text-xs text-muted-foreground self-end">
|
||||
+{images.length - 4} more
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const text = formatCell(value);
|
||||
if (!text) {
|
||||
return (
|
||||
|
|
@ -285,3 +323,41 @@ function formatCell(value: unknown): string {
|
|||
return JSON.stringify(value).slice(0, 500);
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function isPreviewImagePayload(value: unknown): value is PreviewImagePayload {
|
||||
if (!value || typeof value !== "object") return false;
|
||||
const record = value as Record<string, unknown>;
|
||||
return (
|
||||
record.type === "image" &&
|
||||
typeof record.data === "string" &&
|
||||
record.data.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
function collectPreviewImages(value: unknown): PreviewImagePayload[] {
|
||||
const images: PreviewImagePayload[] = [];
|
||||
const stack: unknown[] = [value];
|
||||
let steps = 0;
|
||||
|
||||
while (stack.length > 0 && steps < 200) {
|
||||
steps += 1;
|
||||
const current = stack.pop();
|
||||
if (isPreviewImagePayload(current)) {
|
||||
images.push(current);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Array.isArray(current)) {
|
||||
for (const item of current) stack.push(item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current && typeof current === "object") {
|
||||
for (const nested of Object.values(current as Record<string, unknown>)) {
|
||||
stack.push(nested);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,10 @@ export const useTrainingConfigStore = create<TrainingConfigStore>()(
|
|||
}),
|
||||
{
|
||||
name: "unsloth_training_config_v1",
|
||||
partialize: (state) => {
|
||||
const { modelType, ...rest } = state;
|
||||
return rest;
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,28 @@ const EXCLUDED_TAGS = new Set([
|
|||
"ctranslate2",
|
||||
]);
|
||||
|
||||
function withPopularitySort(
|
||||
input: Parameters<typeof fetch>[0],
|
||||
init?: Parameters<typeof fetch>[1],
|
||||
): ReturnType<typeof fetch> {
|
||||
const rawUrl =
|
||||
typeof input === "string"
|
||||
? input
|
||||
: input instanceof URL
|
||||
? input.toString()
|
||||
: input.url;
|
||||
const url = new URL(rawUrl);
|
||||
|
||||
if (!url.searchParams.has("sort")) {
|
||||
url.searchParams.set("sort", "downloads");
|
||||
}
|
||||
if (!url.searchParams.has("direction")) {
|
||||
url.searchParams.set("direction", "-1");
|
||||
}
|
||||
|
||||
return fetch(url, init);
|
||||
}
|
||||
|
||||
function mapModel(raw: unknown): HfModelResult | null {
|
||||
const m = raw as {
|
||||
name: string;
|
||||
|
|
@ -53,10 +75,10 @@ export function useHfModelSearch(
|
|||
listModels({
|
||||
search: {
|
||||
...(query.trim() ? { query } : { owner: "unsloth" }),
|
||||
tags: ["transformers"],
|
||||
...(task ? { task } : {}),
|
||||
},
|
||||
additionalFields: ["safetensors", "tags"],
|
||||
fetch: withPopularitySort,
|
||||
...(accessToken ? { credentials: { accessToken } } : {}),
|
||||
}) as AsyncGenerator<unknown>,
|
||||
[query, task, accessToken],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue