mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-03-21 22:56:47 +00:00
fix: live flow real-time updates during processing (#861)
* fix: live flow real-time updates during processing Three gaps caused transcript pages to require manual refresh after live recording/processing: 1. UserEventsProvider only invalidated list queries on TRANSCRIPT_STATUS, not individual transcript queries. Now parses data.id from the event and calls invalidateTranscript for the specific transcript. 2. useWebSockets had no reconnection logic — a dropped WS silently killed all real-time updates. Added exponential backoff reconnection (1s-30s, max 10 retries) with intentional close detection. 3. No polling fallback — WS was single point of failure. Added conditional refetchInterval to useTranscriptGet that polls every 5s when transcript status is processing/uploaded/recording. * feat: type-safe WebSocket events via OpenAPI stub Define Pydantic models with Literal discriminators for all WS events (9 transcript-level, 5 user-level). Expose via stub GET endpoints so pnpm openapi generates TS discriminated unions with exhaustive switch narrowing on the frontend. - New server/reflector/ws_events.py with TranscriptWsEvent and UserWsEvent - Tighten backend emit signatures with TranscriptEventName literal - Frontend uses generated types, removes Zod schema and manual casts - Fix pre-existing bugs: waveform mapping, FINAL_LONG_SUMMARY field name - STATUS value now typed as TranscriptStatus literal end-to-end - TOPIC handler simplified to query invalidation only (avoids shape mismatch) * fix: restore TOPIC WS handler with immediate state update The setTopics call provides instant topic rendering during live transcription. Query invalidation still follows for full data sync. * fix: align TOPIC WS event data with GetTranscriptTopic shape Convert TranscriptTopic → GetTranscriptTopic in pipeline before emitting, so WS sends segments instead of words. Removes the `as unknown as Topic` cast on the frontend. * fix: use NonEmptyString and TranscriptStatus in user WS event models --------- Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
This commit is contained in:
@@ -1,18 +1,22 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Topic, FinalSummary, Status } from "./webSocketTypes";
|
||||
import { useError } from "../../(errors)/errorContext";
|
||||
import type { components } from "../../reflector-api";
|
||||
import type { components, operations } from "../../reflector-api";
|
||||
type AudioWaveform = components["schemas"]["AudioWaveform"];
|
||||
type GetTranscriptSegmentTopic =
|
||||
components["schemas"]["GetTranscriptSegmentTopic"];
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { $api, WEBSOCKET_URL } from "../../lib/apiClient";
|
||||
import { WEBSOCKET_URL } from "../../lib/apiClient";
|
||||
import {
|
||||
invalidateTranscript,
|
||||
invalidateTranscriptTopics,
|
||||
invalidateTranscriptWaveform,
|
||||
} from "../../lib/apiHooks";
|
||||
import { NonEmptyString } from "../../lib/utils";
|
||||
import { useAuth } from "../../lib/AuthProvider";
|
||||
import { parseNonEmptyString } from "../../lib/utils";
|
||||
|
||||
type TranscriptWsEvent =
|
||||
operations["v1_transcript_get_websocket_events"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
export type UseWebSockets = {
|
||||
transcriptTextLive: string;
|
||||
@@ -27,6 +31,7 @@ export type UseWebSockets = {
|
||||
};
|
||||
|
||||
export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
|
||||
const auth = useAuth();
|
||||
const [transcriptTextLive, setTranscriptTextLive] = useState<string>("");
|
||||
const [translateText, setTranslateText] = useState<string>("");
|
||||
const [title, setTitle] = useState<string>("");
|
||||
@@ -331,156 +336,168 @@ export const useWebSockets = (transcriptId: string | null): UseWebSockets => {
|
||||
};
|
||||
|
||||
if (!transcriptId) return;
|
||||
const tsId = parseNonEmptyString(transcriptId);
|
||||
|
||||
const MAX_RETRIES = 10;
|
||||
const url = `${WEBSOCKET_URL}/v1/transcripts/${transcriptId}/events`;
|
||||
let ws = new WebSocket(url);
|
||||
let ws: WebSocket | null = null;
|
||||
let retryCount = 0;
|
||||
let retryTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let intentionalClose = false;
|
||||
|
||||
ws.onopen = () => {
|
||||
console.debug("WebSocket connection opened");
|
||||
};
|
||||
const connect = () => {
|
||||
const subprotocols = auth.accessToken
|
||||
? ["bearer", auth.accessToken]
|
||||
: undefined;
|
||||
ws = new WebSocket(url, subprotocols);
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const message = JSON.parse(event.data);
|
||||
ws.onopen = () => {
|
||||
console.debug("WebSocket connection opened");
|
||||
retryCount = 0;
|
||||
};
|
||||
|
||||
try {
|
||||
switch (message.event) {
|
||||
case "TRANSCRIPT":
|
||||
const newText = (message.data.text ?? "").trim();
|
||||
const newTranslation = (message.data.translation ?? "").trim();
|
||||
ws.onmessage = (event) => {
|
||||
const message: TranscriptWsEvent = JSON.parse(event.data);
|
||||
|
||||
if (!newText) break;
|
||||
try {
|
||||
switch (message.event) {
|
||||
case "TRANSCRIPT": {
|
||||
const newText = (message.data.text ?? "").trim();
|
||||
const newTranslation = (message.data.translation ?? "").trim();
|
||||
|
||||
console.debug("TRANSCRIPT event:", newText);
|
||||
setTextQueue((prevQueue) => [...prevQueue, newText]);
|
||||
setTranslationQueue((prevQueue) => [...prevQueue, newTranslation]);
|
||||
if (!newText) break;
|
||||
|
||||
setAccumulatedText((prevText) => prevText + " " + newText);
|
||||
break;
|
||||
console.debug("TRANSCRIPT event:", newText);
|
||||
setTextQueue((prevQueue) => [...prevQueue, newText]);
|
||||
setTranslationQueue((prevQueue) => [
|
||||
...prevQueue,
|
||||
newTranslation,
|
||||
]);
|
||||
|
||||
case "TOPIC":
|
||||
setTopics((prevTopics) => {
|
||||
const topic = message.data as Topic;
|
||||
const index = prevTopics.findIndex(
|
||||
(prevTopic) => prevTopic.id === topic.id,
|
||||
);
|
||||
if (index >= 0) {
|
||||
prevTopics[index] = topic;
|
||||
return prevTopics;
|
||||
}
|
||||
setAccumulatedText((prevText) =>
|
||||
prevText.slice(topic.transcript.length),
|
||||
);
|
||||
|
||||
return [...prevTopics, topic];
|
||||
});
|
||||
console.debug("TOPIC event:", message.data);
|
||||
// Invalidate topics query to sync with WebSocket data
|
||||
invalidateTranscriptTopics(
|
||||
queryClient,
|
||||
transcriptId as NonEmptyString,
|
||||
);
|
||||
break;
|
||||
|
||||
case "FINAL_SHORT_SUMMARY":
|
||||
console.debug("FINAL_SHORT_SUMMARY event:", message.data);
|
||||
break;
|
||||
|
||||
case "FINAL_LONG_SUMMARY":
|
||||
if (message.data) {
|
||||
setFinalSummary(message.data);
|
||||
// Invalidate transcript query to sync summary
|
||||
invalidateTranscript(queryClient, transcriptId as NonEmptyString);
|
||||
setAccumulatedText((prevText) => prevText + " " + newText);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "FINAL_TITLE":
|
||||
console.debug("FINAL_TITLE event:", message.data);
|
||||
if (message.data) {
|
||||
case "TOPIC":
|
||||
setTopics((prevTopics) => {
|
||||
const topic = message.data;
|
||||
const index = prevTopics.findIndex(
|
||||
(prevTopic) => prevTopic.id === topic.id,
|
||||
);
|
||||
if (index >= 0) {
|
||||
prevTopics[index] = topic;
|
||||
return prevTopics;
|
||||
}
|
||||
setAccumulatedText((prevText) =>
|
||||
prevText.slice(topic.transcript?.length ?? 0),
|
||||
);
|
||||
return [...prevTopics, topic];
|
||||
});
|
||||
console.debug("TOPIC event:", message.data);
|
||||
invalidateTranscriptTopics(queryClient, tsId);
|
||||
break;
|
||||
|
||||
case "FINAL_SHORT_SUMMARY":
|
||||
console.debug("FINAL_SHORT_SUMMARY event:", message.data);
|
||||
break;
|
||||
|
||||
case "FINAL_LONG_SUMMARY":
|
||||
setFinalSummary({ summary: message.data.long_summary });
|
||||
invalidateTranscript(queryClient, tsId);
|
||||
break;
|
||||
|
||||
case "FINAL_TITLE":
|
||||
console.debug("FINAL_TITLE event:", message.data);
|
||||
setTitle(message.data.title);
|
||||
// Invalidate transcript query to sync title
|
||||
invalidateTranscript(queryClient, transcriptId as NonEmptyString);
|
||||
}
|
||||
break;
|
||||
invalidateTranscript(queryClient, tsId);
|
||||
break;
|
||||
|
||||
case "WAVEFORM":
|
||||
console.debug(
|
||||
"WAVEFORM event length:",
|
||||
message.data.waveform.length,
|
||||
);
|
||||
if (message.data) {
|
||||
setWaveForm(message.data.waveform);
|
||||
invalidateTranscriptWaveform(
|
||||
queryClient,
|
||||
transcriptId as NonEmptyString,
|
||||
case "WAVEFORM":
|
||||
console.debug(
|
||||
"WAVEFORM event length:",
|
||||
message.data.waveform.length,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "DURATION":
|
||||
console.debug("DURATION event:", message.data);
|
||||
if (message.data) {
|
||||
setWaveForm({ data: message.data.waveform });
|
||||
invalidateTranscriptWaveform(queryClient, tsId);
|
||||
break;
|
||||
|
||||
case "DURATION":
|
||||
console.debug("DURATION event:", message.data);
|
||||
setDuration(message.data.duration);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case "STATUS":
|
||||
console.log("STATUS event:", message.data);
|
||||
if (message.data.value === "error") {
|
||||
setError(
|
||||
Error("Websocket error status"),
|
||||
"There was an error processing this meeting.",
|
||||
case "STATUS":
|
||||
console.log("STATUS event:", message.data);
|
||||
if (message.data.value === "error") {
|
||||
setError(
|
||||
Error("Websocket error status"),
|
||||
"There was an error processing this meeting.",
|
||||
);
|
||||
}
|
||||
setStatus(message.data);
|
||||
invalidateTranscript(queryClient, tsId);
|
||||
if (message.data.value === "ended") {
|
||||
intentionalClose = true;
|
||||
ws?.close();
|
||||
}
|
||||
break;
|
||||
|
||||
case "ACTION_ITEMS":
|
||||
console.debug("ACTION_ITEMS event:", message.data);
|
||||
invalidateTranscript(queryClient, tsId);
|
||||
break;
|
||||
|
||||
default: {
|
||||
const _exhaustive: never = message;
|
||||
console.warn(
|
||||
`Received unknown WebSocket event: ${(_exhaustive as TranscriptWsEvent).event}`,
|
||||
);
|
||||
}
|
||||
setStatus(message.data);
|
||||
invalidateTranscript(queryClient, transcriptId as NonEmptyString);
|
||||
if (message.data.value === "ended") {
|
||||
ws.close();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
setError(
|
||||
new Error(`Received unknown WebSocket event: ${message.event}`),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error("WebSocket error:", error);
|
||||
setError(new Error("A WebSocket error occurred."));
|
||||
};
|
||||
ws.onerror = (error) => {
|
||||
console.error("WebSocket error:", error);
|
||||
};
|
||||
|
||||
ws.onclose = (event) => {
|
||||
console.debug("WebSocket connection closed");
|
||||
switch (event.code) {
|
||||
case 1000: // Normal Closure:
|
||||
break;
|
||||
case 1005: // Closure by client FF
|
||||
break;
|
||||
case 1001: // Navigate away
|
||||
break;
|
||||
case 1006: // Closed by client Chrome
|
||||
console.warn(
|
||||
"WebSocket closed by client, likely duplicated connection in react dev mode",
|
||||
ws.onclose = (event) => {
|
||||
console.debug("WebSocket connection closed, code:", event.code);
|
||||
if (intentionalClose) return;
|
||||
|
||||
const normalCodes = [1000, 1001, 1005];
|
||||
if (normalCodes.includes(event.code)) return;
|
||||
|
||||
if (retryCount < MAX_RETRIES) {
|
||||
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000);
|
||||
console.log(
|
||||
`WebSocket reconnecting in ${delay}ms (attempt ${retryCount + 1}/${MAX_RETRIES})`,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
if (retryCount === 0) {
|
||||
setError(
|
||||
new Error("WebSocket connection lost"),
|
||||
"Connection lost. Reconnecting...",
|
||||
);
|
||||
}
|
||||
retryCount++;
|
||||
retryTimeout = setTimeout(connect, delay);
|
||||
} else {
|
||||
setError(
|
||||
new Error(`WebSocket closed unexpectedly with code: ${event.code}`),
|
||||
"Disconnected from the server. Please refresh the page.",
|
||||
);
|
||||
console.log(
|
||||
"Socket is closed. Reconnect will be attempted in 1 second.",
|
||||
event.reason,
|
||||
);
|
||||
// todo handle reconnect with socket.io
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
connect();
|
||||
|
||||
return () => {
|
||||
ws.close();
|
||||
intentionalClose = true;
|
||||
if (retryTimeout) clearTimeout(retryTimeout);
|
||||
ws?.close();
|
||||
};
|
||||
}, [transcriptId]);
|
||||
|
||||
|
||||
@@ -4,14 +4,12 @@ import React, { useEffect, useRef } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { WEBSOCKET_URL } from "./apiClient";
|
||||
import { useAuth } from "./AuthProvider";
|
||||
import { z } from "zod";
|
||||
import { invalidateTranscriptLists, TRANSCRIPT_SEARCH_URL } from "./apiHooks";
|
||||
import { invalidateTranscript, invalidateTranscriptLists } from "./apiHooks";
|
||||
import { parseNonEmptyString } from "./utils";
|
||||
import type { operations } from "../reflector-api";
|
||||
|
||||
const UserEvent = z.object({
|
||||
event: z.string(),
|
||||
});
|
||||
|
||||
type UserEvent = z.TypeOf<typeof UserEvent>;
|
||||
type UserWsEvent =
|
||||
operations["v1_user_get_websocket_events"]["responses"][200]["content"]["application/json"];
|
||||
|
||||
class UserEventsStore {
|
||||
private socket: WebSocket | null = null;
|
||||
@@ -133,23 +131,26 @@ export function UserEventsProvider({
|
||||
if (!detachRef.current) {
|
||||
const onMessage = (event: MessageEvent) => {
|
||||
try {
|
||||
const msg = UserEvent.parse(JSON.parse(event.data));
|
||||
const eventName = msg.event;
|
||||
const msg: UserWsEvent = JSON.parse(event.data);
|
||||
|
||||
const invalidateList = () => invalidateTranscriptLists(queryClient);
|
||||
|
||||
switch (eventName) {
|
||||
switch (msg.event) {
|
||||
case "TRANSCRIPT_CREATED":
|
||||
case "TRANSCRIPT_DELETED":
|
||||
case "TRANSCRIPT_STATUS":
|
||||
case "TRANSCRIPT_FINAL_TITLE":
|
||||
case "TRANSCRIPT_DURATION":
|
||||
invalidateList().then(() => {});
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore other content events for list updates
|
||||
invalidateTranscriptLists(queryClient).then(() => {});
|
||||
invalidateTranscript(
|
||||
queryClient,
|
||||
parseNonEmptyString(msg.data.id),
|
||||
).then(() => {});
|
||||
break;
|
||||
default: {
|
||||
const _exhaustive: never = msg;
|
||||
console.warn(
|
||||
`Unknown user event: ${(_exhaustive as UserWsEvent).event}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Invalid user event message", event.data);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { components } from "../reflector-api";
|
||||
import { useAuth } from "./AuthProvider";
|
||||
import { MeetingId } from "./types";
|
||||
import { NonEmptyString } from "./utils";
|
||||
import type { TranscriptStatus } from "./transcript";
|
||||
|
||||
/*
|
||||
* XXX error types returned from the hooks are not always correct; declared types are ValidationError but real type could be string or any other
|
||||
@@ -104,6 +105,12 @@ export function useTranscriptProcess() {
|
||||
});
|
||||
}
|
||||
|
||||
const ACTIVE_TRANSCRIPT_STATUSES = new Set<TranscriptStatus>([
|
||||
"processing",
|
||||
"uploaded",
|
||||
"recording",
|
||||
]);
|
||||
|
||||
export function useTranscriptGet(transcriptId: NonEmptyString | null) {
|
||||
return $api.useQuery(
|
||||
"get",
|
||||
@@ -117,6 +124,10 @@ export function useTranscriptGet(transcriptId: NonEmptyString | null) {
|
||||
},
|
||||
{
|
||||
enabled: !!transcriptId,
|
||||
refetchInterval: (query) => {
|
||||
const status = query.state.data?.status;
|
||||
return status && ACTIVE_TRANSCRIPT_STATUSES.has(status) ? 5000 : false;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
271
www/app/reflector-api.d.ts
vendored
271
www/app/reflector-api.d.ts
vendored
@@ -568,7 +568,10 @@ export interface paths {
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/** Transcript Get Websocket Events */
|
||||
/**
|
||||
* Transcript WebSocket event schema
|
||||
* @description Stub exposing the discriminated union of all transcript-level WS events for OpenAPI type generation. Real events are delivered over the WebSocket at the same path.
|
||||
*/
|
||||
get: operations["v1_transcript_get_websocket_events"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
@@ -664,6 +667,26 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/events": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* User WebSocket event schema
|
||||
* @description Stub exposing the discriminated union of all user-level WS events for OpenAPI type generation. Real events are delivered over the WebSocket at the same path.
|
||||
*/
|
||||
get: operations["v1_user_get_websocket_events"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/v1/zulip/streams": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -1877,6 +1900,33 @@ export interface components {
|
||||
/** Name */
|
||||
name: string;
|
||||
};
|
||||
/** TranscriptActionItems */
|
||||
TranscriptActionItems: {
|
||||
/** Action Items */
|
||||
action_items: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
};
|
||||
/** TranscriptDuration */
|
||||
TranscriptDuration: {
|
||||
/** Duration */
|
||||
duration: number;
|
||||
};
|
||||
/** TranscriptFinalLongSummary */
|
||||
TranscriptFinalLongSummary: {
|
||||
/** Long Summary */
|
||||
long_summary: string;
|
||||
};
|
||||
/** TranscriptFinalShortSummary */
|
||||
TranscriptFinalShortSummary: {
|
||||
/** Short Summary */
|
||||
short_summary: string;
|
||||
};
|
||||
/** TranscriptFinalTitle */
|
||||
TranscriptFinalTitle: {
|
||||
/** Title */
|
||||
title: string;
|
||||
};
|
||||
/** TranscriptParticipant */
|
||||
TranscriptParticipant: {
|
||||
/** Id */
|
||||
@@ -1917,6 +1967,113 @@ export interface components {
|
||||
/** End */
|
||||
end: number;
|
||||
};
|
||||
/** TranscriptText */
|
||||
TranscriptText: {
|
||||
/** Text */
|
||||
text: string;
|
||||
/** Translation */
|
||||
translation: string | null;
|
||||
};
|
||||
/** TranscriptWaveform */
|
||||
TranscriptWaveform: {
|
||||
/** Waveform */
|
||||
waveform: number[];
|
||||
};
|
||||
/** TranscriptWsActionItems */
|
||||
TranscriptWsActionItems: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "ACTION_ITEMS";
|
||||
data: components["schemas"]["TranscriptActionItems"];
|
||||
};
|
||||
/** TranscriptWsDuration */
|
||||
TranscriptWsDuration: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "DURATION";
|
||||
data: components["schemas"]["TranscriptDuration"];
|
||||
};
|
||||
/** TranscriptWsFinalLongSummary */
|
||||
TranscriptWsFinalLongSummary: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "FINAL_LONG_SUMMARY";
|
||||
data: components["schemas"]["TranscriptFinalLongSummary"];
|
||||
};
|
||||
/** TranscriptWsFinalShortSummary */
|
||||
TranscriptWsFinalShortSummary: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "FINAL_SHORT_SUMMARY";
|
||||
data: components["schemas"]["TranscriptFinalShortSummary"];
|
||||
};
|
||||
/** TranscriptWsFinalTitle */
|
||||
TranscriptWsFinalTitle: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "FINAL_TITLE";
|
||||
data: components["schemas"]["TranscriptFinalTitle"];
|
||||
};
|
||||
/** TranscriptWsStatus */
|
||||
TranscriptWsStatus: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "STATUS";
|
||||
data: components["schemas"]["TranscriptWsStatusData"];
|
||||
};
|
||||
/** TranscriptWsStatusData */
|
||||
TranscriptWsStatusData: {
|
||||
/**
|
||||
* Value
|
||||
* @enum {string}
|
||||
*/
|
||||
value:
|
||||
| "idle"
|
||||
| "uploaded"
|
||||
| "recording"
|
||||
| "processing"
|
||||
| "error"
|
||||
| "ended";
|
||||
};
|
||||
/** TranscriptWsTopic */
|
||||
TranscriptWsTopic: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TOPIC";
|
||||
data: components["schemas"]["GetTranscriptTopic"];
|
||||
};
|
||||
/** TranscriptWsTranscript */
|
||||
TranscriptWsTranscript: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT";
|
||||
data: components["schemas"]["TranscriptText"];
|
||||
};
|
||||
/** TranscriptWsWaveform */
|
||||
TranscriptWsWaveform: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "WAVEFORM";
|
||||
data: components["schemas"]["TranscriptWaveform"];
|
||||
};
|
||||
/** UpdateParticipant */
|
||||
UpdateParticipant: {
|
||||
/** Speaker */
|
||||
@@ -1987,6 +2144,82 @@ export interface components {
|
||||
/** Email */
|
||||
email: string | null;
|
||||
};
|
||||
/** UserTranscriptCreatedData */
|
||||
UserTranscriptCreatedData: {
|
||||
/** Id */
|
||||
id: string;
|
||||
};
|
||||
/** UserTranscriptDeletedData */
|
||||
UserTranscriptDeletedData: {
|
||||
/** Id */
|
||||
id: string;
|
||||
};
|
||||
/** UserTranscriptDurationData */
|
||||
UserTranscriptDurationData: {
|
||||
/** Id */
|
||||
id: string;
|
||||
/** Duration */
|
||||
duration: number;
|
||||
};
|
||||
/** UserTranscriptFinalTitleData */
|
||||
UserTranscriptFinalTitleData: {
|
||||
/** Id */
|
||||
id: string;
|
||||
/** Title */
|
||||
title: string;
|
||||
};
|
||||
/** UserTranscriptStatusData */
|
||||
UserTranscriptStatusData: {
|
||||
/** Id */
|
||||
id: string;
|
||||
/** Value */
|
||||
value: string;
|
||||
};
|
||||
/** UserWsTranscriptCreated */
|
||||
UserWsTranscriptCreated: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT_CREATED";
|
||||
data: components["schemas"]["UserTranscriptCreatedData"];
|
||||
};
|
||||
/** UserWsTranscriptDeleted */
|
||||
UserWsTranscriptDeleted: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT_DELETED";
|
||||
data: components["schemas"]["UserTranscriptDeletedData"];
|
||||
};
|
||||
/** UserWsTranscriptDuration */
|
||||
UserWsTranscriptDuration: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT_DURATION";
|
||||
data: components["schemas"]["UserTranscriptDurationData"];
|
||||
};
|
||||
/** UserWsTranscriptFinalTitle */
|
||||
UserWsTranscriptFinalTitle: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT_FINAL_TITLE";
|
||||
data: components["schemas"]["UserTranscriptFinalTitleData"];
|
||||
};
|
||||
/** UserWsTranscriptStatus */
|
||||
UserWsTranscriptStatus: {
|
||||
/**
|
||||
* @description discriminator enum property added by openapi-typescript
|
||||
* @enum {string}
|
||||
*/
|
||||
event: "TRANSCRIPT_STATUS";
|
||||
data: components["schemas"]["UserTranscriptStatusData"];
|
||||
};
|
||||
/** ValidationError */
|
||||
ValidationError: {
|
||||
/** Location */
|
||||
@@ -3423,7 +3656,16 @@ export interface operations {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": unknown;
|
||||
"application/json":
|
||||
| components["schemas"]["TranscriptWsTranscript"]
|
||||
| components["schemas"]["TranscriptWsTopic"]
|
||||
| components["schemas"]["TranscriptWsStatus"]
|
||||
| components["schemas"]["TranscriptWsFinalTitle"]
|
||||
| components["schemas"]["TranscriptWsFinalLongSummary"]
|
||||
| components["schemas"]["TranscriptWsFinalShortSummary"]
|
||||
| components["schemas"]["TranscriptWsActionItems"]
|
||||
| components["schemas"]["TranscriptWsDuration"]
|
||||
| components["schemas"]["TranscriptWsWaveform"];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
@@ -3607,6 +3849,31 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_user_get_websocket_events: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json":
|
||||
| components["schemas"]["UserWsTranscriptCreated"]
|
||||
| components["schemas"]["UserWsTranscriptDeleted"]
|
||||
| components["schemas"]["UserWsTranscriptStatus"]
|
||||
| components["schemas"]["UserWsTranscriptFinalTitle"]
|
||||
| components["schemas"]["UserWsTranscriptDuration"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
v1_zulip_get_streams: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
Reference in New Issue
Block a user