Skip to content

Conversation

@mishig25
Copy link
Collaborator

@mishig25 mishig25 commented Nov 29, 2023

Refactor WidgetWrapper

(continuing the widget rewrite)

  1. We discussed about removing WidgetWrapper. Instead, WidgetWrapper is still kept but simplified. Specificlly, WidgetWrapper does 2 tasks: 1. common styling for all widgets (just like App inside moon); 2. gets a model status (is it loaded, loadable, or too big)
  2. With the simplified WidgetWrapper, there is no longer WidgetHeader or WidgetFooter inside WidgetWrapper. Therefore, every widget has to explicitly call WidgetHeader or WidgetFooter.
  3. Refactored WidgetExamples component: 1. to use less reactive statements $:; 2. have better variable names (favouring exampleXYZ over inputSampleXYZ); 3. WidgetExamples has the logic to run a random widget example onMount (previously, this logic was inside WidgetWrapper)
  4. Removed widget maximize feature (cc: @gary149) from WidgetFooter. I don't think anyone was using. But happy to put it back.

@mishig25 mishig25 marked this pull request as draft November 29, 2023 20:32
@mishig25 mishig25 changed the title Refactor WidgetWrapper [Widget] Refactor WidgetWrapper Nov 29, 2023
@mishig25 mishig25 marked this pull request as ready for review November 29, 2023 21:01
Copy link
Member

@julien-c julien-c left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not related to this PR, but is there any way to generate the validators from some sort of task-specific JSONschema (one for each task)?

i.e. we'd have a single source of truth to define the shape of inputs/outputs

@mishig25
Copy link
Collaborator Author

not related to this PR, but is there any way to generate the validators from some sort of task-specific JSONschema (one for each task)?

Yes. There is a lib called ajv https://github.com/ajv-validator/ajv. But I think I/we can implement something simple/small in our use case.

i.e. we'd have a single source of truth to define the shape of inputs/outputs

indeed, that would be great. Can also be used to auto generate the docs page https://huggingface.co/docs/hub/models-widgets-examples#widget-examples

@coyotte508
Copy link
Member

maybe after we switch to using @huggingface/inference? Since the vlaidation is also done there

@julien-c
Copy link
Member

There is a lib called ajv

lol, going full circle (we used to use ajv before switching to Joi)

maybe after we switch to using @huggingface/inference? Since the vlaidation is also done there

Yes why not, and anyways the single source of truth's idea is that it would also be used in our Python libraries (cc @Wauplin)

switch to using @huggingface/inference

how complex/disruptive would this be? (and what the other advantages?)

@mishig25
Copy link
Collaborator Author

maybe after we switch to using @huggingface/inference? Since the vlaidation is also done there

@huggingface/inference validates api-output. Does it also validate input to the api? Also, we need to validate widget inputs and outputs (which can differ from inference input/output). For instance, on Text2Img widget, inference output is fileBlob while example output is url to file

@coyotte508
Copy link
Member

how complex/disruptive would this be? (and what the other advantages?)

The main advantage is putting in common the options (eg regarding cache, model loading) + making sure @huggingface/inference is up-to-date / everything's compatible

@coyotte508
Copy link
Member

@huggingface/inference validates api-output. Does it also validate input to the api?

No (and no plan to), we rely on the type system for that.

Copy link
Member

@coyotte508 coyotte508 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I love that we pass a lot less props to WidgetWrapper each time.

Two main comments:

  • about calling getContext at top-level
  • about form

The rest you are free to ignore if you want ^^

PS: we should probably fix the Icon links (lots of //) but that comes from an earlier PR

@mishig25 mishig25 requested a review from coyotte508 December 5, 2023 13:20
Copy link
Member

@coyotte508 coyotte508 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@SBrandeis
Copy link
Contributor

Reviewing this now

Copy link
Contributor

@SBrandeis SBrandeis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactor.
I haven't tested all the widgets.
I was a bit confused at first by the examples logic living in WidgetHeader, but that's probably just a wording issue.

{:else}
Minimize
{/if}
</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale for removing the maximize feature?

Copy link
Collaborator Author

@mishig25 mishig25 Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can put it back. I thought that this feature was not being used anywhere (point 4)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I would have kept it as it was 👀

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in edb463b

applyWidgetExample(exampleFromQueryParams);
} else {
// run random widget example
const example = getWidgetExample<TWidgetExample>(model, validateExample);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use allExamples here no?
It's already extracted from the widget and validated
Will allow us to remove the validateExample & model props from this component too

Suggested change
const example = getWidgetExample<TWidgetExample>(model, validateExample);
const example = randomItem(allExamples);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled as part of f22f63e

Comment on lines 19 to 22
export let applyWidgetExample: (sample: TWidgetExample, opts?: ExampleRunOpts) => void = () => {};
export let validateExample: (sample: WidgetExample) => sample is TWidgetExample = (
sample
): sample is TWidgetExample => true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit confused by those defaults, maybe safer to have undefined as a default?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled as part of f22f63e

// if there are no examples with outputs AND model.inference !== InferenceDisplayability.Yes
// then widget will show InferenceDisplayability error to the user without showing anything else
if (isDisabled && !examples.length) {
$widgetNoInference[model.id] = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to the current PR: do stores get persisted when refreshing the page?

What happens when the store value changes? (Are the consuming components getting updated?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do stores get persisted when refreshing the page?

Svelte stores don't persist.

What happens when the store value changes? (Are the consuming components getting updated?)

if a consuming component had UI or reactive statement dependent on a store, that UI or reactive statement gets updated when store gets a update

Comment on lines 162 to 166
let:isDisabled
let:modelLoadInfo
let:WidgetInfo
let:WidgetHeader
let:WidgetFooter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be simpler / cleaner if isDisabled / modelLoadInfo were in a store?
From what I understand they are only exposed to be "re-injected" in WidgetHeader / WidgetInfo, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handled in 9861a29

Mishig and others added 2 commits December 5, 2023 08:10
…pers.ts

Co-authored-by: Simon Brandeis <33657802+SBrandeis@users.noreply.github.com>
…getHeader/WidgetHeader.svelte

Co-authored-by: Simon Brandeis <33657802+SBrandeis@users.noreply.github.com>
@mishig25
Copy link
Collaborator Author

mishig25 commented Dec 5, 2023

I was a bit confused at first by the examples logic living in WidgetHeader, but that's probably just a wording issue.

update: commit f22f63e

Worked bit more on wording and var names

WidgetExamples component is inside WidgetHeader. And there is a widget examples logic inside WidgetHeader that does:

<script>
...
const validExamples = areThereValidWidgetExamples()
...
</script>

<div>
{if validExamples}
<WidgetExamples>
{/if}
</div>

Copy link
Contributor

@SBrandeis SBrandeis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok LGTM
Sorry for the delay in reviewing
Note that different widgets for the same model on the same page will share some state due to stores

@mishig25 mishig25 merged commit 3782a46 into main Dec 18, 2023
@mishig25 mishig25 deleted the widgets_refactor_5 branch December 18, 2023 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants