@gradio/client 2.4.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,589 @@
1
+ import type { GradioEvent, StatusMessage } from "../types";
2
+
3
+ // Keyed on the app id, so two different apps served on the same port never
4
+ // share a history. The id is regenerated whenever an app starts, so a restart
5
+ // begins a fresh history and leaves the previous one orphaned; `prune_apps`
6
+ // keeps those from accumulating.
7
+ //
8
+ // The key also carries the logged-in user, because local storage outlives a
9
+ // session: without it, whoever logs into an authenticated app next on this
10
+ // browser would open the previous user's runs.
11
+ const KEY_ROOT = "gradio:run-history:";
12
+ const STORAGE_PREFIX = `${KEY_ROOT}v2:`;
13
+ const REPLAY_PREFIX = `${KEY_ROOT}replay:v2:`;
14
+ const MAX_RUNS = 100;
15
+ const MAX_APPS = 8;
16
+
17
+ export type AppId = string | number | null | undefined;
18
+
19
+ /**
20
+ * Which history to read or write. `Config` satisfies this shape, so callers
21
+ * that hold one can pass it straight through.
22
+ */
23
+ export interface RunHistoryScope {
24
+ app_id?: AppId;
25
+ /** The authenticated user, when the app uses `auth`. */
26
+ username?: string | null;
27
+ }
28
+
29
+ /**
30
+ * Run history is a side effect of submitting, never the point of it, so no
31
+ * failure in here may propagate into the caller and break the app. Every
32
+ * exported function routes through this.
33
+ */
34
+ function safely<T>(operation: () => T, fallback: T): T {
35
+ try {
36
+ return operation();
37
+ } catch (error) {
38
+ console.warn("Could not update the run history.", error);
39
+ return fallback;
40
+ }
41
+ }
42
+
43
+ export type RunStatus = "running" | "completed" | "failed";
44
+
45
+ export interface StoredRunComponent {
46
+ type: string;
47
+ component_class_id: string;
48
+ props: Record<string, unknown>;
49
+ }
50
+
51
+ export interface StoredRun {
52
+ id: string;
53
+ endpoint: string | number;
54
+ api_name: string;
55
+ fn_index: number;
56
+ page: string;
57
+ inputs: unknown;
58
+ outputs: unknown | null;
59
+ input_components?: (StoredRunComponent | null)[];
60
+ output_components?: (StoredRunComponent | null)[];
61
+ status: RunStatus;
62
+ error?: string;
63
+ started_at: string;
64
+ /** When the server started running the function, if it reported queueing. */
65
+ process_started_at?: string;
66
+ completed_at?: string;
67
+ /** How long the function itself took, server-reported where available. */
68
+ duration_ms?: number;
69
+ /** How long the run waited in the queue before the function started. */
70
+ queued_ms?: number;
71
+ /** Whether the run produced its output in chunks, i.e. came from a generator. */
72
+ streamed?: boolean;
73
+ }
74
+
75
+ interface StartRunOptions extends RunHistoryScope {
76
+ endpoint: string | number;
77
+ api_name: string;
78
+ fn_index: number;
79
+ inputs: unknown;
80
+ input_components?: (StoredRunComponent | null)[];
81
+ output_components?: (StoredRunComponent | null)[];
82
+ }
83
+
84
+ /**
85
+ * The URL of the run history page for an app. `root` never carries a trailing
86
+ * slash, so it cannot simply be concatenated with the path.
87
+ */
88
+ export function run_history_url(
89
+ root: string,
90
+ api_prefix = "/gradio_api"
91
+ ): string {
92
+ return `${root.replace(/\/+$/, "")}${api_prefix}/runs`;
93
+ }
94
+
95
+ function storage_key(scope: RunHistoryScope | null | undefined): string | null {
96
+ if (typeof window === "undefined") return null;
97
+ const app_id = scope?.app_id;
98
+ if (app_id === null || app_id === undefined || app_id === "") return null;
99
+
100
+ try {
101
+ if (!window.localStorage) return null;
102
+ // Encoded so that a username containing the separator cannot be made to
103
+ // collide with another user's key.
104
+ const user = scope?.username
105
+ ? `:user:${encodeURIComponent(scope.username)}`
106
+ : "";
107
+ return `${STORAGE_PREFIX}${app_id}${user}`;
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+
113
+ function replay_key(scope: RunHistoryScope | null | undefined): string | null {
114
+ const key = storage_key(scope);
115
+ return key ? key.replace(STORAGE_PREFIX, REPLAY_PREFIX) : null;
116
+ }
117
+
118
+ /** When a run was most recently saved under a key, for deciding what to drop. */
119
+ function last_saved_at(key: string): number {
120
+ try {
121
+ const runs = JSON.parse(window.localStorage.getItem(key) || "[]");
122
+ // Runs are stored newest first.
123
+ return Array.isArray(runs) && runs.length
124
+ ? Date.parse(runs[0]?.started_at) || 0
125
+ : 0;
126
+ } catch {
127
+ return 0;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Drops the histories of long-gone app instances. Every restart mints a new app
133
+ * id, so without this the browser would keep every history an app ever had and
134
+ * eventually run out of room for the current one.
135
+ */
136
+ function prune_apps(current_key: string): void {
137
+ const keys = Object.keys(window.localStorage).filter((key) =>
138
+ key.startsWith(KEY_ROOT)
139
+ );
140
+ const stale = [
141
+ // Keys written by an older layout can never be read again.
142
+ ...keys.filter(
143
+ (key) => !key.startsWith(STORAGE_PREFIX) && !key.startsWith(REPLAY_PREFIX)
144
+ ),
145
+ ...keys
146
+ .filter((key) => key.startsWith(STORAGE_PREFIX) && key !== current_key)
147
+ .sort((a, b) => last_saved_at(b) - last_saved_at(a))
148
+ .slice(MAX_APPS - 1)
149
+ ];
150
+ for (const key of stale) {
151
+ try {
152
+ window.localStorage.removeItem(key);
153
+ window.sessionStorage.removeItem(
154
+ key.replace(STORAGE_PREFIX, REPLAY_PREFIX)
155
+ );
156
+ } catch {
157
+ // Nothing to do if the browser will not let us clean up.
158
+ }
159
+ }
160
+ }
161
+
162
+ function make_id(): string {
163
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
164
+ return crypto.randomUUID();
165
+ }
166
+ return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
167
+ }
168
+
169
+ function clone_for_storage(value: unknown): unknown {
170
+ const seen = new WeakSet<object>();
171
+ try {
172
+ return JSON.parse(
173
+ JSON.stringify(value, (_key, item) => {
174
+ if (typeof item === "bigint") return item.toString();
175
+ if (typeof item === "object" && item !== null) {
176
+ if (seen.has(item)) return "[Circular]";
177
+ seen.add(item);
178
+ }
179
+ return item;
180
+ })
181
+ );
182
+ } catch {
183
+ return "[Unserializable value]";
184
+ }
185
+ }
186
+
187
+ function read_run_history_impl(
188
+ scope: RunHistoryScope | null | undefined
189
+ ): StoredRun[] {
190
+ const key = storage_key(scope);
191
+ if (!key) return [];
192
+
193
+ try {
194
+ const value = JSON.parse(window.localStorage.getItem(key) || "[]");
195
+ return Array.isArray(value) ? value : [];
196
+ } catch {
197
+ return [];
198
+ }
199
+ }
200
+
201
+ function write_run_history(
202
+ scope: RunHistoryScope | null | undefined,
203
+ runs: StoredRun[]
204
+ ): void {
205
+ const key = storage_key(scope);
206
+ if (!key) return;
207
+
208
+ let next = runs.slice(0, MAX_RUNS);
209
+ let pruned = false;
210
+ while (next.length > 0) {
211
+ try {
212
+ window.localStorage.setItem(key, JSON.stringify(next));
213
+ return;
214
+ } catch {
215
+ // Reclaim the space held by app instances that are long gone before
216
+ // giving up on the oldest runs of the current one.
217
+ if (!pruned) {
218
+ pruned = true;
219
+ prune_apps(key);
220
+ continue;
221
+ }
222
+ next = next.slice(0, -1);
223
+ }
224
+ }
225
+ try {
226
+ window.localStorage.removeItem(key);
227
+ } catch {
228
+ // Run history must never interfere with an app submission.
229
+ }
230
+ }
231
+
232
+ function clear_run_history_impl(
233
+ scope: RunHistoryScope | null | undefined
234
+ ): void {
235
+ const key = storage_key(scope);
236
+ if (!key) return;
237
+ try {
238
+ window.localStorage.removeItem(key);
239
+ } catch {
240
+ // Storage may be disabled by the browser.
241
+ }
242
+ notify_run_history_change();
243
+ }
244
+
245
+ function delete_run_history_impl(
246
+ scope: RunHistoryScope | null | undefined,
247
+ id: string
248
+ ): void {
249
+ write_run_history(
250
+ scope,
251
+ read_run_history_impl(scope).filter((run) => run.id !== id)
252
+ );
253
+ notify_run_history_change();
254
+ }
255
+
256
+ const CHANGE_EVENT = "gradio:run-history-change";
257
+
258
+ function notify_run_history_change(): void {
259
+ if (typeof window === "undefined") return;
260
+ try {
261
+ window.dispatchEvent(new Event(CHANGE_EVENT));
262
+ } catch {
263
+ // Nothing depends on the notification arriving.
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Subscribes to runs being added, deleted or cleared. `storage` covers other
269
+ * tabs; the custom event covers this one, which `storage` never fires for.
270
+ * Only counts change, not per-run progress, so listeners stay cheap.
271
+ *
272
+ * @returns a function that unsubscribes.
273
+ */
274
+ function on_run_history_change_impl(listener: () => void): () => void {
275
+ if (typeof window === "undefined") return () => {};
276
+ window.addEventListener(CHANGE_EVENT, listener);
277
+ window.addEventListener("storage", listener);
278
+ return () => {
279
+ window.removeEventListener(CHANGE_EVENT, listener);
280
+ window.removeEventListener("storage", listener);
281
+ };
282
+ }
283
+
284
+ function stage_run_history_replay_impl(
285
+ scope: RunHistoryScope | null | undefined,
286
+ run: StoredRun
287
+ ): void {
288
+ const key = replay_key(scope);
289
+ if (!key) return;
290
+ try {
291
+ window.sessionStorage.setItem(key, JSON.stringify(run));
292
+ } catch {
293
+ // Session storage may be disabled by the browser.
294
+ }
295
+ }
296
+
297
+ function consume_run_history_replay_impl(
298
+ scope: RunHistoryScope | null | undefined
299
+ ): StoredRun | null {
300
+ const key = replay_key(scope);
301
+ if (!key) return null;
302
+ try {
303
+ const value = window.sessionStorage.getItem(key);
304
+ window.sessionStorage.removeItem(key);
305
+ return value ? (JSON.parse(value) as StoredRun) : null;
306
+ } catch {
307
+ return null;
308
+ }
309
+ }
310
+
311
+ /**
312
+ * The parts of an app config a replayed run writes back into. Kept structural
313
+ * so every entry point — the SPA and the SSR app each carry their own `Config`
314
+ * declaration — can hand its config straight over.
315
+ */
316
+ export interface ReplayTarget {
317
+ components: { id: number; type: string; props: Record<string, any> }[];
318
+ dependencies: {
319
+ id: number;
320
+ api_name?: string | null;
321
+ inputs: number[];
322
+ outputs: number[];
323
+ }[];
324
+ }
325
+
326
+ function restore_run_impl(config: ReplayTarget, run: StoredRun): boolean {
327
+ const dependency = config.dependencies.find(
328
+ (item) =>
329
+ item.id === run.fn_index ||
330
+ (typeof item.api_name === "string" &&
331
+ `/${item.api_name.replace(/^\//, "")}` === run.api_name)
332
+ );
333
+ if (!dependency) return false;
334
+
335
+ const inputs = Array.isArray(run.inputs)
336
+ ? run.inputs
337
+ : Object.values((run.inputs ?? {}) as Record<string, unknown>);
338
+ const outputs = Array.isArray(run.outputs)
339
+ ? run.outputs
340
+ : run.outputs === null || run.outputs === undefined
341
+ ? []
342
+ : [run.outputs];
343
+
344
+ const restore = (ids: number[], saved: unknown[]): void => {
345
+ for (const [index, id] of ids.entries()) {
346
+ const component = config.components.find((item) => item.id === id);
347
+ if (!component || index >= saved.length) continue;
348
+ // `gr.State` is held on the server and always saved as null, so
349
+ // writing it back would wipe out the component's real default.
350
+ if (component.type === "state") continue;
351
+ component.props.value = saved[index];
352
+ }
353
+ };
354
+
355
+ restore(dependency.inputs, inputs);
356
+ restore(dependency.outputs, outputs);
357
+ return true;
358
+ }
359
+
360
+ function apply_run_history_replay_impl(
361
+ config: ReplayTarget & RunHistoryScope
362
+ ): boolean {
363
+ const run = consume_run_history_replay_impl(config);
364
+ return run ? restore_run_impl(config, run) : false;
365
+ }
366
+
367
+ function start_run_history_impl(options: StartRunOptions): string | null {
368
+ const key = storage_key(options);
369
+ if (!key) return null;
370
+
371
+ // A new app id means a new key, so clear out the ones left behind before
372
+ // adding to this one.
373
+ prune_apps(key);
374
+
375
+ const run: StoredRun = {
376
+ id: make_id(),
377
+ endpoint: options.endpoint,
378
+ api_name: options.api_name,
379
+ fn_index: options.fn_index,
380
+ page: `${window.location.pathname}${window.location.search}`,
381
+ inputs: clone_for_storage(options.inputs),
382
+ outputs: null,
383
+ ...(options.input_components
384
+ ? {
385
+ input_components: clone_for_storage(
386
+ options.input_components
387
+ ) as (StoredRunComponent | null)[]
388
+ }
389
+ : {}),
390
+ ...(options.output_components
391
+ ? {
392
+ output_components: clone_for_storage(
393
+ options.output_components
394
+ ) as (StoredRunComponent | null)[]
395
+ }
396
+ : {}),
397
+ status: "running",
398
+ started_at: new Date().toISOString()
399
+ };
400
+ write_run_history(options, [run, ...read_run_history_impl(options)]);
401
+ notify_run_history_change();
402
+ return run.id;
403
+ }
404
+
405
+ function update_run_inputs_impl(
406
+ scope: RunHistoryScope | null | undefined,
407
+ id: string | null,
408
+ inputs: unknown
409
+ ): void {
410
+ if (!id) return;
411
+
412
+ const runs = read_run_history_impl(scope);
413
+ const run = runs.find((item) => item.id === id);
414
+ if (!run) return;
415
+ run.inputs = clone_for_storage(inputs);
416
+ write_run_history(scope, runs);
417
+ }
418
+
419
+ function update_run_history_impl(
420
+ scope: RunHistoryScope | null | undefined,
421
+ id: string | null,
422
+ event: GradioEvent
423
+ ): void {
424
+ if (!id) return;
425
+
426
+ const runs = read_run_history_impl(scope);
427
+ const run = runs.find((item) => item.id === id);
428
+ if (!run) return;
429
+
430
+ let finished = false;
431
+ if (event.type === "data") {
432
+ run.outputs = clone_for_storage(event.data);
433
+ deferred_outputs.set(id, run.outputs);
434
+ } else if (
435
+ event.type === "status" &&
436
+ event.original_msg === "process_starts"
437
+ ) {
438
+ mark_process_start(run, event.time);
439
+ } else if (
440
+ event.type === "status" &&
441
+ (event.stage === "generating" || event.stage === "streaming")
442
+ ) {
443
+ run.streamed = true;
444
+ } else if (event.type === "status" && event.stage === "complete") {
445
+ run.status = "completed";
446
+ mark_complete(run, event);
447
+ finished = true;
448
+ } else if (event.type === "status" && event.stage === "error") {
449
+ run.status = "failed";
450
+ run.error =
451
+ typeof event.message === "string"
452
+ ? event.message
453
+ : JSON.stringify(event.message || "Unknown error");
454
+ mark_complete(run, event);
455
+ finished = true;
456
+ } else {
457
+ // Logs, renders and progress updates change nothing worth saving, and
458
+ // writing serialises every run we hold.
459
+ return;
460
+ }
461
+
462
+ if (finished) {
463
+ // Every call re-reads from storage, so a chunk that was held back above is
464
+ // missing from `run`. Put the last one back before this final write.
465
+ if (deferred_outputs.has(id)) run.outputs = deferred_outputs.get(id);
466
+ deferred_outputs.delete(id);
467
+ last_write.delete(id);
468
+ } else if (run.streamed) {
469
+ // A generator emits an event per chunk. Saving each one would stringify
470
+ // the whole history on the main thread over and over, so write at most
471
+ // once an interval and let the run's completion flush the rest.
472
+ const since = Date.now() - (last_write.get(id) ?? 0);
473
+ if (since < STREAM_WRITE_INTERVAL_MS) return;
474
+ last_write.set(id, Date.now());
475
+ deferred_outputs.delete(id);
476
+ }
477
+
478
+ write_run_history(scope, runs);
479
+ }
480
+
481
+ const STREAM_WRITE_INTERVAL_MS = 500;
482
+ /** When each in-flight streamed run was last written. */
483
+ const last_write = new Map<string, number>();
484
+ /** The newest output of a streamed run that has not been written yet. */
485
+ const deferred_outputs = new Map<string, unknown>();
486
+
487
+ function mark_process_start(run: StoredRun, time: Date | undefined): void {
488
+ // The queue tells us when the function actually started, which lets us
489
+ // report a runtime that excludes however long the run sat in the queue.
490
+ if (run.process_started_at) return;
491
+ const started = time || new Date();
492
+ run.process_started_at = started.toISOString();
493
+ run.queued_ms = Math.max(0, started.getTime() - Date.parse(run.started_at));
494
+ }
495
+
496
+ function mark_complete(run: StoredRun, event: StatusMessage): void {
497
+ const completed = event.time || new Date();
498
+ run.completed_at = completed.toISOString();
499
+ // `cache_duration` is how long the function took on the server, which the
500
+ // queue reports on every completed run (not just cached ones). A generator
501
+ // reports it per chunk though, so the final value covers only the last one
502
+ // and the elapsed time is the honest number for a streamed run.
503
+ const server_duration =
504
+ !run.streamed &&
505
+ typeof event.cache_duration === "number" &&
506
+ event.cache_duration >= 0
507
+ ? event.cache_duration * 1000
508
+ : null;
509
+ run.duration_ms =
510
+ server_duration ??
511
+ Math.max(
512
+ 0,
513
+ completed.getTime() - Date.parse(run.process_started_at || run.started_at)
514
+ );
515
+ }
516
+
517
+ // Public API. Each of these is a no-op if anything goes wrong.
518
+
519
+ export function read_run_history(
520
+ scope: RunHistoryScope | null | undefined
521
+ ): StoredRun[] {
522
+ return safely(() => read_run_history_impl(scope), []);
523
+ }
524
+
525
+ export function clear_run_history(
526
+ scope: RunHistoryScope | null | undefined
527
+ ): void {
528
+ safely(() => clear_run_history_impl(scope), undefined);
529
+ }
530
+
531
+ export function delete_run_history(
532
+ scope: RunHistoryScope | null | undefined,
533
+ id: string
534
+ ): void {
535
+ safely(() => delete_run_history_impl(scope, id), undefined);
536
+ }
537
+
538
+ export function stage_run_history_replay(
539
+ scope: RunHistoryScope | null | undefined,
540
+ run: StoredRun
541
+ ): void {
542
+ safely(() => stage_run_history_replay_impl(scope, run), undefined);
543
+ }
544
+
545
+ export function consume_run_history_replay(
546
+ scope: RunHistoryScope | null | undefined
547
+ ): StoredRun | null {
548
+ return safely(() => consume_run_history_replay_impl(scope), null);
549
+ }
550
+
551
+ /**
552
+ * Applies the run staged by the history page, if this page load is the one it
553
+ * was staged for. Every entry point that renders an app has to call this, or
554
+ * "Load run" silently does nothing on that entry point.
555
+ *
556
+ * @returns whether a staged run was found and applied.
557
+ */
558
+ export function apply_run_history_replay(
559
+ config: ReplayTarget & RunHistoryScope
560
+ ): boolean {
561
+ return safely(() => apply_run_history_replay_impl(config), false);
562
+ }
563
+
564
+ export function start_run_history(options: StartRunOptions): string | null {
565
+ return safely(() => start_run_history_impl(options), null);
566
+ }
567
+
568
+ export function update_run_inputs(
569
+ scope: RunHistoryScope | null | undefined,
570
+ id: string | null,
571
+ inputs: unknown
572
+ ): void {
573
+ safely(() => update_run_inputs_impl(scope, id, inputs), undefined);
574
+ }
575
+
576
+ export function update_run_history(
577
+ scope: RunHistoryScope | null | undefined,
578
+ id: string | null,
579
+ event: GradioEvent
580
+ ): void {
581
+ safely(() => update_run_history_impl(scope, id, event), undefined);
582
+ }
583
+
584
+ export function on_run_history_change(listener: () => void): () => void {
585
+ return safely(
586
+ () => on_run_history_change_impl(() => safely(listener, undefined)),
587
+ () => {}
588
+ );
589
+ }
@@ -29,6 +29,11 @@ import {
29
29
  } from "../constants";
30
30
  import { apply_diff_stream, close_stream } from "./stream";
31
31
  import { Client } from "../client";
32
+ import {
33
+ start_run_history,
34
+ update_run_history,
35
+ update_run_inputs
36
+ } from "./run_history";
32
37
 
33
38
  export function submit(
34
39
  this: Client,
@@ -73,12 +78,65 @@ export function submit(
73
78
  );
74
79
 
75
80
  let resolved_data = map_data_to_params(data, endpoint_info);
76
-
77
- let stream: EventSource | null;
78
81
  let protocol = config.protocol ?? "ws";
79
82
  if (protocol === "ws") {
80
83
  throw new Error(WS_PROTOCOL_MSG);
81
84
  }
85
+ const history_endpoint =
86
+ typeof dependency.api_name === "string"
87
+ ? `/${dependency.api_name}`
88
+ : endpoint;
89
+ const history_api_name =
90
+ typeof dependency.api_name === "string"
91
+ ? `/${dependency.api_name}`
92
+ : `Function ${fn_index}`;
93
+ // Kept index-aligned with the stored payloads (null for components we
94
+ // cannot resolve) so every saved value stays matched to its component.
95
+ const component_metadata = (id: number) => {
96
+ const component = config.components.find((item) => item.id === id);
97
+ if (!component) return null;
98
+ return {
99
+ type: component.type,
100
+ component_class_id: component.component_class_id,
101
+ props: component.props
102
+ };
103
+ };
104
+ // The run history covers the same endpoints the API page documents, which
105
+ // it selects with exactly this predicate (see `ApiDocs.svelte`). That also
106
+ // keeps out the dependencies Gradio wires up for itself, since example
107
+ // loading, flagging and clear buttons are all "undocumented" or "private"
108
+ // and some of them fire on page load. The trade is that a component whose
109
+ // UI submits through an undocumented dependency — `gr.ChatInterface` does
110
+ // — records nothing for its in-app use.
111
+ const is_documented_endpoint = dependency.api_visibility === "public";
112
+ // Either side may opt out: the app for everyone who uses it, and this
113
+ // caller for itself.
114
+ const history_enabled =
115
+ config.run_history !== false && this.options.record_history !== false;
116
+ const history_scope = { app_id: config.app_id, username: config.username };
117
+ const history_run_id =
118
+ !history_enabled || !is_documented_endpoint
119
+ ? null
120
+ : start_run_history({
121
+ ...history_scope,
122
+ endpoint: history_endpoint,
123
+ api_name: history_api_name,
124
+ fn_index,
125
+ // Aligned to `dependency.inputs` the same way the submitted payload
126
+ // is, so this placeholder matches the components until the uploaded
127
+ // files are swapped in by `update_run_inputs` below.
128
+ inputs: handle_payload(
129
+ resolved_data,
130
+ dependency,
131
+ config.components,
132
+ "input",
133
+ true
134
+ ),
135
+ input_components: dependency.inputs.map(component_metadata),
136
+ output_components: dependency.outputs.map(component_metadata)
137
+ });
138
+
139
+ let stream: EventSource | null;
82
140
  let event_id_final = "";
83
141
  let event_id_cb: () => string = () => event_id_final;
84
142
 
@@ -103,6 +161,7 @@ export function submit(
103
161
 
104
162
  // event subscription methods
105
163
  function fire_event(event: GradioEvent): void {
164
+ update_run_history(history_scope, history_run_id, event);
106
165
  if (all_events || events_to_publish[event.type]) {
107
166
  push_event(event);
108
167
  }
@@ -180,6 +239,7 @@ export function submit(
180
239
  "input",
181
240
  true
182
241
  );
242
+ update_run_inputs(history_scope, history_run_id, input_data || []);
183
243
  payload = {
184
244
  data: input_data || [],
185
245
  event_data,