Skip to content

Commit f22f63e

Browse files
committed
refactor examples & examples validation
1 parent edb463b commit f22f63e

File tree

4 files changed

+61
-93
lines changed

4 files changed

+61
-93
lines changed

packages/widgets/src/lib/components/InferenceWidget/shared/WidgetExamples/WidgetExamples.svelte

Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts">
2+
import { randomItem } from "$lib/utils/ViewUtils.js";
3+
24
import type { ExampleRunOpts, WidgetProps } from "../types.js";
35
import type { WidgetExample, WidgetExampleAttribute } from "@huggingface/tasks";
46
@@ -9,38 +11,36 @@
911
1012
import IconCaretDownV2 from "../../..//Icons/IconCaretDownV2.svelte";
1113
import WidgetExamplesGroup from "./WidgetExamplesGroup.svelte";
12-
import { getQueryParamVal, getWidgetExample } from "../../..//InferenceWidget/shared/helpers.js";
14+
import { getQueryParamVal } from "../../..//InferenceWidget/shared/helpers.js";
1315
1416
export let isLoading = false;
15-
export let model: WidgetProps["model"];
1617
export let callApiOnMount: WidgetProps["callApiOnMount"];
1718
export let exampleQueryParams: WidgetExampleAttribute[] = [];
1819
export let applyWidgetExample: (sample: TWidgetExample, opts?: ExampleRunOpts) => void;
19-
export let validateExample: (sample: WidgetExample) => sample is TWidgetExample;
2020
21-
export let examplesAll: TWidgetExample[];
21+
export let validExamples: TWidgetExample[];
2222
2323
interface ExamplesGroup {
2424
group: string;
2525
examples: TWidgetExample[];
2626
}
2727
28-
$: exampleGroups = getExamplesGroups(examplesAll);
28+
$: exampleGroups = getExamplesGroups(validExamples);
2929
$: examples = exampleGroups?.[0]?.examples ?? [];
3030
// for examples with multiple groups, a group needs to be selected first, before an example can be clicked
3131
$: clickable = exampleGroups?.length === 1;
3232
let containerEl: HTMLElement;
3333
let isOptionsVisible = false;
3434
let title = "Examples";
3535
36-
function getExamplesGroups(_examplesAll: TWidgetExample[]): ExamplesGroup[] {
37-
const examplesAll = _examplesAll.map((sample, idx) => ({
36+
function getExamplesGroups(_examples: TWidgetExample[]): ExamplesGroup[] {
37+
const examples = _examples.map((sample, idx) => ({
3838
example_title: `Example ${++idx}`,
3939
group: "Group 1",
4040
...sample,
4141
}));
4242
const examplesGroups: ExamplesGroup[] = [];
43-
for (const example of examplesAll) {
43+
for (const example of examples) {
4444
const groupExists = examplesGroups.find(({ group }) => group === example.group);
4545
if (!groupExists) {
4646
examplesGroups.push({ group: example.group as string, examples: [] });
@@ -109,7 +109,7 @@
109109
applyWidgetExample(exampleFromQueryParams);
110110
} else {
111111
// run random widget example
112-
const example = getWidgetExample<TWidgetExample>(model, validateExample);
112+
const example = randomItem(validExamples);
113113
if (callApiOnMount && example) {
114114
applyWidgetExample(example, { inferenceOpts: { isOnLoadCall: true } });
115115
}
@@ -120,54 +120,52 @@
120120

121121
<svelte:window on:click={onClick} />
122122

123-
{#if examplesAll.length}
124-
<div class="ml-auto flex gap-x-1">
125-
<!-- Example Groups -->
126-
{#if exampleGroups.length > 1}
127-
<WidgetExamplesGroup
128-
on:groupSelected={changeGroup}
129-
{isLoading}
130-
groupNames={exampleGroups.map(({ group }) => group)}
131-
/>
132-
{/if}
133-
134-
<!-- Example picker -->
123+
<div class="ml-auto flex gap-x-1">
124+
<!-- Example Groups -->
125+
{#if exampleGroups.length > 1}
126+
<WidgetExamplesGroup
127+
on:groupSelected={changeGroup}
128+
{isLoading}
129+
groupNames={exampleGroups.map(({ group }) => group)}
130+
/>
131+
{/if}
132+
133+
<!-- Example picker -->
134+
<div
135+
class="relative mb-1.5
136+
{isLoading || !clickable ? 'pointer-events-none opacity-50' : ''}
137+
{isOptionsVisible ? 'z-10' : ''}"
138+
bind:this={containerEl}
139+
>
140+
<!-- svelte-ignore a11y-click-events-have-key-events -->
135141
<div
136-
class="relative mb-1.5
137-
{isLoading || !clickable ? 'pointer-events-none opacity-50' : ''}
138-
{isOptionsVisible ? 'z-10' : ''}"
139-
bind:this={containerEl}
142+
class="inline-flex w-32 justify-between rounded-md border border-gray-100 px-4 py-1"
143+
on:click={toggleOptionsVisibility}
140144
>
141-
<!-- svelte-ignore a11y-click-events-have-key-events -->
145+
<div class="truncate text-sm">{title}</div>
146+
<IconCaretDownV2
147+
classNames="-mr-1 ml-2 h-5 w-5 transition ease-in-out transform {isOptionsVisible && '-rotate-180'}"
148+
/>
149+
</div>
150+
151+
{#if isOptionsVisible}
142152
<div
143-
class="inline-flex w-32 justify-between rounded-md border border-gray-100 px-4 py-1"
144-
on:click={toggleOptionsVisibility}
153+
class="absolute right-0 mt-1 w-full origin-top-right rounded-md ring-1 ring-black ring-opacity-10"
154+
transition:slide
145155
>
146-
<div class="truncate text-sm">{title}</div>
147-
<IconCaretDownV2
148-
classNames="-mr-1 ml-2 h-5 w-5 transition ease-in-out transform {isOptionsVisible && '-rotate-180'}"
149-
/>
150-
</div>
151-
152-
{#if isOptionsVisible}
153-
<div
154-
class="absolute right-0 mt-1 w-full origin-top-right rounded-md ring-1 ring-black ring-opacity-10"
155-
transition:slide
156-
>
157-
<div class="rounded-md bg-white py-1" role="none">
158-
{#each examples as { example_title }, i}
159-
<!-- svelte-ignore a11y-click-events-have-key-events a11y-mouse-events-have-key-events -->
160-
<div
161-
class="cursor-pointer truncate px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-200"
162-
on:mouseover={() => _previewInputSample(i)}
163-
on:click={() => _applyWidgetExample(i)}
164-
>
165-
{example_title}
166-
</div>
167-
{/each}
168-
</div>
156+
<div class="rounded-md bg-white py-1" role="none">
157+
{#each examples as { example_title }, i}
158+
<!-- svelte-ignore a11y-click-events-have-key-events a11y-mouse-events-have-key-events -->
159+
<div
160+
class="cursor-pointer truncate px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-200"
161+
on:mouseover={() => _previewInputSample(i)}
162+
on:click={() => _applyWidgetExample(i)}
163+
>
164+
{example_title}
165+
</div>
166+
{/each}
169167
</div>
170-
{/if}
171-
</div>
168+
</div>
169+
{/if}
172170
</div>
173-
{/if}
171+
</div>

packages/widgets/src/lib/components/InferenceWidget/shared/WidgetHeader/WidgetHeader.svelte

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,21 @@
1616
export let title: string | null = null;
1717
export let isLoading = false;
1818
export let isDisabled = false;
19-
export let applyWidgetExample: (sample: TWidgetExample, opts?: ExampleRunOpts) => void = () => {};
20-
export let validateExample: (sample: WidgetExample) => sample is TWidgetExample = (
21-
sample
22-
): sample is TWidgetExample => true;
19+
export let applyWidgetExample: ((sample: TWidgetExample, opts?: ExampleRunOpts) => void) | undefined = undefined;
20+
export let validateExample: ((sample: WidgetExample) => sample is TWidgetExample) | undefined = undefined;
2321
export let callApiOnMount: WidgetProps["callApiOnMount"] = false;
2422
export let exampleQueryParams: WidgetExampleAttribute[] = [];
2523
2624
const pipeline = model?.pipeline_tag;
2725
2826
$: task = pipeline ? getPipelineTask(pipeline) : undefined;
2927
30-
$: examplesAll = getExamples(isDisabled);
28+
$: validExamples = getValidExamples(isDisabled);
3129
32-
function getExamples(isDisabled: boolean): TWidgetExample[] {
30+
function getValidExamples(isDisabled: boolean): TWidgetExample[] {
3331
const examples = (model?.widgetData ?? []).filter(
34-
(sample): sample is TWidgetExample => validateExample(sample) && (!isDisabled || sample.output !== undefined)
32+
(sample): sample is TWidgetExample =>
33+
(validateExample?.(sample) ?? false) && (!isDisabled || sample.output !== undefined)
3534
);
3635
3736
// if there are no examples with outputs AND model.inference !== InferenceDisplayability.Yes
@@ -76,15 +75,7 @@
7675
<PipelineTag classNames="mr-2 mb-1.5" {pipeline} />
7776
</a>
7877
{/if}
79-
{#if examplesAll.length}
80-
<WidgetExamples
81-
{examplesAll}
82-
{isLoading}
83-
{applyWidgetExample}
84-
{validateExample}
85-
{model}
86-
{callApiOnMount}
87-
{exampleQueryParams}
88-
/>
78+
{#if validExamples.length && applyWidgetExample}
79+
<WidgetExamples {validExamples} {isLoading} {applyWidgetExample} {callApiOnMount} {exampleQueryParams} />
8980
{/if}
9081
</div>

packages/widgets/src/lib/components/InferenceWidget/shared/helpers.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,6 @@ export function getQueryParamVal(key: WidgetExampleAttribute): QueryParamVal {
2121
return value;
2222
}
2323

24-
export function getWidgetExample<TWidgetExample extends WidgetExample>(
25-
model: ModelData,
26-
validateExample: (sample: WidgetExample) => sample is TWidgetExample
27-
): TWidgetExample | undefined {
28-
const validExamples = model?.widgetData?.filter(
29-
(sample): sample is TWidgetExample => sample && validateExample(sample)
30-
);
31-
return validExamples?.length ? randomItem(validExamples) : undefined;
32-
}
33-
3424
// Update current url search params, keeping existing keys intact.
3525
export function updateUrl(obj: Partial<Record<WidgetExampleAttribute, string | undefined>>): void {
3626
if (!window) {

packages/widgets/src/lib/components/InferenceWidget/widgets/ZeroShotImageClassificationWidget/ZeroShotImageClassificationWidget.svelte

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
import type { WidgetProps, ExampleRunOpts, InferenceRunOpts } from "../../shared/types.js";
33
import type { WidgetExampleAssetAndZeroShotInput } from "@huggingface/tasks";
44
5-
import { onMount } from "svelte";
6-
75
import WidgetFileInput from "../../shared/WidgetFileInput/WidgetFileInput.svelte";
86
import WidgetDropzone from "../../shared/WidgetDropzone/WidgetDropzone.svelte";
97
import WidgetTextInput from "../../shared/WidgetTextInput/WidgetTextInput.svelte";
108
import WidgetSubmitBtn from "../../shared/WidgetSubmitBtn/WidgetSubmitBtn.svelte";
119
import WidgetWrapper from "../../shared/WidgetWrapper/WidgetWrapper.svelte";
1210
import WidgetOutputChart from "../../shared/WidgetOutputChart/WidgetOutputChart.svelte";
13-
import { addInferenceParameters, callInferenceApi, getWidgetExample } from "../../shared/helpers.js";
11+
import { addInferenceParameters, callInferenceApi } from "../../shared/helpers.js";
1412
import { isAssetAndZeroShotInput } from "../../shared/inputValidation.js";
1513
import { widgetStates } from "../../stores.js";
1614
@@ -148,15 +146,6 @@
148146
error = res.error;
149147
}
150148
}
151-
152-
onMount(() => {
153-
(async () => {
154-
const example = getWidgetExample<WidgetExampleAssetAndZeroShotInput>(model, isAssetAndZeroShotInput);
155-
if (callApiOnMount && example) {
156-
await applyWidgetExample(example, { inferenceOpts: { isOnLoadCall: true } });
157-
}
158-
})();
159-
});
160149
</script>
161150

162151
<WidgetWrapper {apiUrl} {includeCredentials} {model} let:WidgetInfo let:WidgetHeader let:WidgetFooter>

0 commit comments

Comments
 (0)