Example casted after prodigy-update fires

Hello prodi.gy community :waving_hand:
I am experiencing a weird error I’m not sure about its source.

Since I updated from 1.16 to 1.18 (same error with 1.17) I have the following issue:
Often no task details are available in the UI for the current example. This also happens when I rerun with the same dataset, so that sometimes an example has task details and sometimes it doesn’t.
The missing details result in my custom UI not rendering correctly.

When I start the Application with my custom recipe no prodigymount event is triggered (or not visible in the console) . When I answer the first question the console shows the following:

image

Note here the detail: null
Sometimes there are examples with the necessary detail (so basically the example itself) to render everything. In those cases prodigyupdate and prodigyanswer are triggered multiple times (up to 13 times per example).

What works is showing the text of the current example in the html I have to the left side of the UI.

With some debugging I came to the Idea, that prodigy might trigger the “prodigyupdate” event, before the stream is fully casted. Is that possible? Were there any changes in that regard since 1.16?
Thats the JS I use to listen to the event:

        document.addEventListener("prodigyupdate", event => {
            console.log("prodigyupdate", event);
            this.setProdigyTaskConfig(event.detail.task);
        });

after which I render my Interface:

    setProdigyTaskConfig(taskConfig) {
        this.#taskConfig = taskConfig;
        this.#render();
    }

I hope I gave enough detail to the problem. If there Is any other thing I could provide please let me know.

Thanks in advance!

Hi and welcome – and very cool to see a project making use of custom JavaScript UIs! We did extend the JS events in Prodigy v1.18 (see changelog) so this was my first thought – although, that wouldn't explain why you're seeing the same behaviour in v1.17 :thinking:

One change in v1.18 is that there's now also a prodigyload event that fires after the app has mounted and when custom JavaScript is loaded. So you could try using that intead of prodigymount and see if it resolves the problem?

(In general, it's definitely possible for the prodigyupdate event to fire multiple times until the example is ready, depending on whether new examples are fetched and what's needed to render the example in place. If you want to check whether it's a new example vs. an existing updated one, you can store the _task_hash property in a variable and check if it changed. But not 100% sure if this is relevant in your example from how I understand your use case.)

Hi Ines!
Similar error occurs unfortunately with prodigyload. What I can report so far:

With prodigyload instead of prodigymount I exeperience the same error as mentioned initially. But when I set a timeout like:

constructor(CustomSelector) {
        this.#CustomSelector = CustomSelector;
        document.addEventListener("prodigyload", event => {
            console.log("prodigyload", event, window.prodigy.content);
            this.#rootContainerNode = document.queryCustomSelector(CustomSelector);
            if (!this.#rootContainerNode) {

                const checkInterval = setInterval(() => {
                    this.#rootContainerNode = document.queryCustomSelector(CustomSelector);
                    if (this.#rootContainerNode) {
                        clearInterval(checkInterval);
                        this.#rootContainerNode.classList.add("custom-container");
                        this.#setup();
                    }
                }, 100);
                return;
            }
            this.#rootContainerNode.classList.add("custom-container");
            this.#setup();
        });
    }

I’m able to render the first example correctly and every following will be rendered broken initially, but when I refresh the page everything works fine.

Same wait function with "prodigymount” results in the first example not rendering correctly but like 80% of the following are rendered corretly instantly.

The wait function is actually (shame on me) vibe-coded, since I’m not really proficient in JS & web-developement. I will loop in someone who actually worked on the UI and wrote the code (with their bare hands), maybe that will give a bit more insights :slight_smile:
Thanks and best

I will loop in someone who actually worked on the UI and wrote the code (with their bare hands)

Hey that’s me!

I did some testing by logging all events Prodigy emits to the console and compared the behavior between Prodigy 1.16.0 (which I last tested my custom UI on) and 1.18.2, which we are seeing this issue with after upgrading. For reference, this is the code producing the following outputs:

["prodigymount", "prodigyload", "prodigyscriptload", "prodigyupdate", "prodigyanswer", "prodigyundo", "prodigysave", "prodigyspanselected", "prodigyend"].forEach(name => {
    document.addEventListener(name, e => {
        console.log(new Date().toISOString(), name, {
            "event.detail": e.detail,
            "window.prodigy.content": window.prodigy.content,
        });
    });
});

On 1.16.0, the output when stepping through an example dataset is shown below. prodigymountis emitted on initial load as well as every time the preloaded examples are exhausted and new examples are loaded from the server (we use a batch_size of 3 here as well as instant_submit:true). When the UI changes in any way, prodigyupdateis emitted (presumably as part of a React re-render?), and prodigyanswerwhen an answer is submitted - as configured with the batch_size, 3 answers are submitted until new examples are loaded and a new mount event occurs.
Importantly, during all of those events, the current task config is available - always in the global prodigy state, and apart from the mounts also in the emitted event’s details. This makes sense, as the prodigy task UI can only mount as soon as a task is actually there to display. So far so good.


Now, the output of the same script, but on 1.18.2. It’s immediately noticeable that the first time the page loads, all 4 events emitted at that time do not have any task config available. For prodigyload and prodigyendthat would make sense, as at that time no task exists (yet), but even during the initial prodigymountno task config exists, which doesn’t make sense - if the UI is being mounted, there should be something to render already. Presumably, Prodigy only populates the global state after this event has been emitted in 1.18, whereas in 1.16 that happened before this event. As such, there is no way to get the current task config on page load without manually polling until window.prodigy.contentis populated, or the ugly setTimeout hack Haris mentioned above.
The same issue exists with the subsequent prodigymountevents after a new batch has been loaded from the server.


Overall, from my understanding it seems that window.prodigy.contentis mistakenly populated after the mount event now, so there is no way to cleanly get the task config on initial (and subsequent) loads. The best fix IMHO would be to again have window.prodigy.content populated at the time prodigymountis emitted. An alternative (or combined) fix could be to populate event.detailduring mount events to be consistent with the other events, which do all have the task as their detail.

P.S. I also checked with 1.17.5. There, no initial prodigymountevent is emitted in the first place - in fact, no events at all are emitted during initial load. Submitting and re-fetching task batches does work correctly though, and window.prodigy.contentis populated during subsequent mount events as with 1.16.0.

Hi @Haris223 & @_adrian!

Thank you very much for a very helpful debugging info! I've traced the refactor we did in 1.18 and, indeed, there's a change with respect to the timing of the window.prodigy object update.
In 1.16 window.prodigy object was being updated earlier in the cycle so it was available by the time prodigymount event was triggered
Currently, window.prodigy object is being updated in componentDidUpdate method that emits prodigyupdate event.
This new lifecycle prioritizes loading the app even though the first batch of the tasks may not be available yet which might often be the case if the tasks require some complex processing.
While it makes a lot of sense in most scenarios, it is true that this change has compromised the timing guarantees of custom js scripts listening to events and we should have been more upfront about it - sorry!

Could you try listening to prodigyupdate event in your script? This is the only event that guarantees the task is available in the content.

Hi @magdaaniol, thanks a lot for taking a look! prodigyupdate does indeed always seem to have the task config available, but there’s one small issue: It doesn’t seem to be emitted during initial page load. In my screenshot above, it’s a bit hard to tell due to unfortunate timing, but the first time prodigyupdate is emitted is when I press the accept/reject button, which is 10 seconds after the initial page load (26 seconds vs 36 seconds). As such, when using prodigyupdate, we still don’t get any information for the initial task and can’t show our custom UI when loading the page for the first time. I think the fix here would be to also emit prodigyupdatewhen the very first task is loaded, and not just during subsequent tasks / user interaction (or perhaps that’s the intention anyways and doesn’t happen for some reason/bug?).

Hi @_adrian,

Just to let you know that we are revising how the events are emitted - you're right that the emission of prodigyupdate is inconsistent for the first task. We'll update as soon as possible. Thank you!

2 Likes

Hi @_adrian,

It tool a bit longer than we'd hoped for but we have just released Prodigy 1.18.4 that fixes the issues with prodigyupdate. It should now fire consistently on the first task.

1 Like