Merge branch 'feat-api-speaker-reassignment' of github.com:Monadical-SAS/reflector into sara/feat-speaker-reassign

This commit is contained in:
Sara
2023-12-12 11:48:20 +01:00
16 changed files with 992 additions and 7 deletions

View File

@@ -251,6 +251,23 @@ class Transcript(BaseModel):
url += f"?token={token}"
return url
def find_empty_speaker(self) -> int:
"""
Find an empty speaker seat
"""
speakers = set(
word.speaker
for topic in self.topics
for word in topic.words
if word.speaker is not None
)
i = 0
while True:
if i not in speakers:
return i
i += 1
raise Exception("No empty speaker found")
class TranscriptController:
async def get_all(

View File

@@ -57,6 +57,7 @@ class Word(BaseModel):
class TranscriptSegment(BaseModel):
text: str
start: float
end: float
speaker: int = 0
@@ -127,6 +128,7 @@ class Transcript(BaseModel):
current_segment = TranscriptSegment(
text=word.text,
start=word.start,
end=word.end,
speaker=word.speaker,
)
continue
@@ -138,6 +140,7 @@ class Transcript(BaseModel):
current_segment = TranscriptSegment(
text=word.text,
start=word.start,
end=word.end,
speaker=word.speaker,
)
continue
@@ -145,6 +148,7 @@ class Transcript(BaseModel):
# if the word is the end of a sentence, and we have enough content,
# add the word to the current segment and push it
current_segment.text += word.text
current_segment.end = word.end
have_punc = PUNC_RE.search(word.text)
if have_punc and (len(current_segment.text) > MAX_SEGMENT_LENGTH):

View File

@@ -122,6 +122,7 @@ class GetTranscriptTopic(BaseModel):
title: str
summary: str
timestamp: float
duration: float | None
transcript: str
segments: list[GetTranscriptSegmentTopic] = []
@@ -131,6 +132,7 @@ class GetTranscriptTopic(BaseModel):
# In previous version, words were missing
# Just output a segment with speaker 0
text = topic.transcript
duration = None
segments = [
GetTranscriptSegmentTopic(
text=topic.transcript,
@@ -142,6 +144,7 @@ class GetTranscriptTopic(BaseModel):
# New versions include words
transcript = ProcessorTranscript(words=topic.words)
text = transcript.text
duration = transcript.duration
segments = [
GetTranscriptSegmentTopic(
text=segment.text,
@@ -157,6 +160,7 @@ class GetTranscriptTopic(BaseModel):
timestamp=topic.timestamp,
transcript=text,
segments=segments,
duration=duration,
)
@@ -171,6 +175,44 @@ class GetTranscriptTopicWithWords(GetTranscriptTopic):
return instance
class SpeakerWords(BaseModel):
speaker: int
words: list[Word]
class GetTranscriptTopicWithWordsPerSpeaker(GetTranscriptTopic):
words_per_speaker: list[SpeakerWords] = []
@classmethod
def from_transcript_topic(cls, topic: TranscriptTopic):
instance = super().from_transcript_topic(topic)
if topic.words:
words_per_speakers = []
# group words by speaker
words = []
for word in topic.words:
if words and words[-1].speaker != word.speaker:
words_per_speakers.append(
SpeakerWords(
speaker=words[-1].speaker,
words=words,
)
)
words = []
words.append(word)
if words:
words_per_speakers.append(
SpeakerWords(
speaker=words[-1].speaker,
words=words,
)
)
instance.words_per_speaker = words_per_speakers
return instance
@router.get("/transcripts/{transcript_id}", response_model=GetTranscript)
async def transcript_get(
transcript_id: str,
@@ -247,3 +289,26 @@ async def transcript_get_topics_with_words(
GetTranscriptTopicWithWords.from_transcript_topic(topic)
for topic in transcript.topics
]
@router.get(
"/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker",
response_model=GetTranscriptTopicWithWordsPerSpeaker,
)
async def transcript_get_topics_with_words_per_speaker(
transcript_id: str,
topic_id: str,
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
):
user_id = user["sub"] if user else None
transcript = await transcripts_controller.get_by_id_for_http(
transcript_id, user_id=user_id
)
# get the topic from the transcript
topic = next((t for t in transcript.topics if t.id == topic_id), None)
if not topic:
raise HTTPException(status_code=404, detail="Topic not found")
# convert to GetTranscriptTopicWithWordsPerSpeaker
return GetTranscriptTopicWithWordsPerSpeaker.from_transcript_topic(topic)

View File

@@ -59,7 +59,7 @@ async def transcript_add_participant(
)
# ensure the speaker is unique
if transcript.participants:
if participant.speaker is not None:
for p in transcript.participants:
if p.speaker == participant.speaker:
raise HTTPException(

View File

@@ -7,14 +7,15 @@ from typing import Annotated, Optional
import reflector.auth as auth
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from pydantic import BaseModel, Field
from reflector.db.transcripts import transcripts_controller
router = APIRouter()
class SpeakerAssignment(BaseModel):
speaker: int
speaker: Optional[int] = Field(None, ge=0)
participant: Optional[str] = Field(None)
timestamp_from: float
timestamp_to: float
@@ -23,6 +24,11 @@ class SpeakerAssignmentStatus(BaseModel):
status: str
class SpeakerMerge(BaseModel):
speaker_from: int
speaker_to: int
@router.patch("/transcripts/{transcript_id}/speaker/assign")
async def transcript_assign_speaker(
transcript_id: str,
@@ -37,6 +43,44 @@ async def transcript_assign_speaker(
if not transcript:
raise HTTPException(status_code=404, detail="Transcript not found")
if assignment.speaker is None and assignment.participant is None:
raise HTTPException(
status_code=400,
detail="Either speaker or participant must be provided",
)
if assignment.speaker is not None and assignment.participant is not None:
raise HTTPException(
status_code=400,
detail="Only one of speaker or participant must be provided",
)
# if it's a participant, search for it
if assignment.speaker is not None:
speaker = assignment.speaker
elif assignment.participant is not None:
participant = next(
(
participant
for participant in transcript.participants
if participant.id == assignment.participant
),
None,
)
if not participant:
raise HTTPException(
status_code=404,
detail="Participant not found",
)
# if the participant does not have a speaker, create one
if participant.speaker is None:
participant.speaker = transcript.find_empty_speaker()
await transcripts_controller.upsert_participant(transcript, participant)
speaker = participant.speaker
# reassign speakers from words in the transcript
ts_from = assignment.timestamp_from
ts_to = assignment.timestamp_to
@@ -45,7 +89,70 @@ async def transcript_assign_speaker(
changed = False
for word in topic.words:
if ts_from <= word.start <= ts_to:
word.speaker = assignment.speaker
word.speaker = speaker
changed = True
if changed:
changed_topics.append(topic)
# batch changes
for topic in changed_topics:
transcript.upsert_topic(topic)
await transcripts_controller.update(
transcript,
{
"topics": transcript.topics_dump(),
},
)
return SpeakerAssignmentStatus(status="ok")
@router.patch("/transcripts/{transcript_id}/speaker/merge")
async def transcript_merge_speaker(
transcript_id: str,
merge: SpeakerMerge,
user: Annotated[Optional[auth.UserInfo], Depends(auth.current_user_optional)],
) -> SpeakerAssignmentStatus:
user_id = user["sub"] if user else None
transcript = await transcripts_controller.get_by_id_for_http(
transcript_id, user_id=user_id
)
if not transcript:
raise HTTPException(status_code=404, detail="Transcript not found")
# ensure both speaker are not assigned to the 2 differents participants
participant_from = next(
(
participant
for participant in transcript.participants
if participant.speaker == merge.speaker_from
),
None,
)
participant_to = next(
(
participant
for participant in transcript.participants
if participant.speaker == merge.speaker_to
),
None,
)
if participant_from and participant_to:
raise HTTPException(
status_code=400,
detail="Both speakers are assigned to participants",
)
# reassign speakers from words in the transcript
speaker_from = merge.speaker_from
speaker_to = merge.speaker_to
changed_topics = []
for topic in transcript.topics:
changed = False
for word in topic.words:
if word.speaker == speaker_from:
word.speaker = speaker_to
changed = True
if changed:
changed_topics.append(topic)

View File

@@ -115,3 +115,287 @@ async def test_transcript_reassign_speaker(fake_transcript_with_topics):
assert topics[0]["segments"][0]["speaker"] == 4
assert len(topics[1]["segments"]) == 1
assert topics[1]["segments"][0]["speaker"] == 4
@pytest.mark.asyncio
async def test_transcript_merge_speaker(fake_transcript_with_topics):
from reflector.app import app
transcript_id = fake_transcript_with_topics.id
async with AsyncClient(app=app, base_url="http://test/v1") as ac:
# check the transcript exists
response = await ac.get(f"/transcripts/{transcript_id}")
assert response.status_code == 200
# check initial topics of the transcript
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 0
assert topics[0]["words"][1]["speaker"] == 0
assert topics[1]["words"][0]["speaker"] == 0
assert topics[1]["words"][1]["speaker"] == 0
# reassign speaker
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"speaker": 1,
"timestamp_from": 0,
"timestamp_to": 1,
},
)
assert response.status_code == 200
# check topics again
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 1
assert topics[0]["words"][1]["speaker"] == 1
assert topics[1]["words"][0]["speaker"] == 0
assert topics[1]["words"][1]["speaker"] == 0
# merge speakers
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/merge",
json={
"speaker_from": 1,
"speaker_to": 0,
},
)
assert response.status_code == 200
# check topics again
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 0
assert topics[0]["words"][1]["speaker"] == 0
assert topics[1]["words"][0]["speaker"] == 0
assert topics[1]["words"][1]["speaker"] == 0
@pytest.mark.asyncio
async def test_transcript_reassign_with_participant(fake_transcript_with_topics):
from reflector.app import app
transcript_id = fake_transcript_with_topics.id
async with AsyncClient(app=app, base_url="http://test/v1") as ac:
# check the transcript exists
response = await ac.get(f"/transcripts/{transcript_id}")
assert response.status_code == 200
transcript = response.json()
assert len(transcript["participants"]) == 0
# create 2 participants
response = await ac.post(
f"/transcripts/{transcript_id}/participants",
json={
"name": "Participant 1",
},
)
assert response.status_code == 200
participant1_id = response.json()["id"]
response = await ac.post(
f"/transcripts/{transcript_id}/participants",
json={
"name": "Participant 2",
},
)
assert response.status_code == 200
participant2_id = response.json()["id"]
# check participants speakers
response = await ac.get(f"/transcripts/{transcript_id}/participants")
assert response.status_code == 200
participants = response.json()
assert len(participants) == 2
assert participants[0]["name"] == "Participant 1"
assert participants[0]["speaker"] is None
assert participants[1]["name"] == "Participant 2"
assert participants[1]["speaker"] is None
# check initial topics of the transcript
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 0
assert topics[0]["words"][1]["speaker"] == 0
assert topics[1]["words"][0]["speaker"] == 0
assert topics[1]["words"][1]["speaker"] == 0
# check through segments
assert len(topics[0]["segments"]) == 1
assert topics[0]["segments"][0]["speaker"] == 0
assert len(topics[1]["segments"]) == 1
assert topics[1]["segments"][0]["speaker"] == 0
# reassign speaker from a participant
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"participant": participant1_id,
"timestamp_from": 0,
"timestamp_to": 1,
},
)
assert response.status_code == 200
# check participants if speaker has been assigned
# first participant should have 1, because it's not used yet.
response = await ac.get(f"/transcripts/{transcript_id}/participants")
assert response.status_code == 200
participants = response.json()
assert len(participants) == 2
assert participants[0]["name"] == "Participant 1"
assert participants[0]["speaker"] == 1
assert participants[1]["name"] == "Participant 2"
assert participants[1]["speaker"] is None
# check topics again
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 1
assert topics[0]["words"][1]["speaker"] == 1
assert topics[1]["words"][0]["speaker"] == 0
assert topics[1]["words"][1]["speaker"] == 0
# check segments
assert len(topics[0]["segments"]) == 1
assert topics[0]["segments"][0]["speaker"] == 1
assert len(topics[1]["segments"]) == 1
assert topics[1]["segments"][0]["speaker"] == 0
# reassign participant, middle of 2 topics
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"participant": participant2_id,
"timestamp_from": 1,
"timestamp_to": 2.5,
},
)
assert response.status_code == 200
# check participants if speaker has been assigned
# first participant should have 1, because it's not used yet.
response = await ac.get(f"/transcripts/{transcript_id}/participants")
assert response.status_code == 200
participants = response.json()
assert len(participants) == 2
assert participants[0]["name"] == "Participant 1"
assert participants[0]["speaker"] == 1
assert participants[1]["name"] == "Participant 2"
assert participants[1]["speaker"] == 2
# check topics again
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 1
assert topics[0]["words"][1]["speaker"] == 2
assert topics[1]["words"][0]["speaker"] == 2
assert topics[1]["words"][1]["speaker"] == 0
# check segments
assert len(topics[0]["segments"]) == 2
assert topics[0]["segments"][0]["speaker"] == 1
assert topics[0]["segments"][1]["speaker"] == 2
assert len(topics[1]["segments"]) == 2
assert topics[1]["segments"][0]["speaker"] == 2
assert topics[1]["segments"][1]["speaker"] == 0
# reassign speaker, everything
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"participant": participant1_id,
"timestamp_from": 0,
"timestamp_to": 100,
},
)
assert response.status_code == 200
# check topics again
response = await ac.get(f"/transcripts/{transcript_id}/topics/with-words")
assert response.status_code == 200
topics = response.json()
assert len(topics) == 2
# check through words
assert topics[0]["words"][0]["speaker"] == 1
assert topics[0]["words"][1]["speaker"] == 1
assert topics[1]["words"][0]["speaker"] == 1
assert topics[1]["words"][1]["speaker"] == 1
# check segments
assert len(topics[0]["segments"]) == 1
assert topics[0]["segments"][0]["speaker"] == 1
assert len(topics[1]["segments"]) == 1
assert topics[1]["segments"][0]["speaker"] == 1
@pytest.mark.asyncio
async def test_transcript_reassign_edge_cases(fake_transcript_with_topics):
from reflector.app import app
transcript_id = fake_transcript_with_topics.id
async with AsyncClient(app=app, base_url="http://test/v1") as ac:
# check the transcript exists
response = await ac.get(f"/transcripts/{transcript_id}")
assert response.status_code == 200
transcript = response.json()
assert len(transcript["participants"]) == 0
# try reassign without any participant_id or speaker
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"timestamp_from": 0,
"timestamp_to": 1,
},
)
assert response.status_code == 400
# try reassing with both participant_id and speaker
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"participant": "123",
"speaker": 1,
"timestamp_from": 0,
"timestamp_to": 1,
},
)
assert response.status_code == 400
# try reassing with non-existing participant_id
response = await ac.patch(
f"/transcripts/{transcript_id}/speaker/assign",
json={
"participant": "123",
"timestamp_from": 0,
"timestamp_to": 1,
},
)
assert response.status_code == 404

View File

@@ -0,0 +1,26 @@
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_transcript_topics(fake_transcript_with_topics):
from reflector.app import app
transcript_id = fake_transcript_with_topics.id
async with AsyncClient(app=app, base_url="http://test/v1") as ac:
# check the transcript exists
response = await ac.get(f"/transcripts/{transcript_id}/topics")
assert response.status_code == 200
assert len(response.json()) == 2
topic_id = response.json()[0]["id"]
# get words per speakers
response = await ac.get(
f"/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker"
)
assert response.status_code == 200
data = response.json()
assert len(data["words_per_speaker"]) == 1
assert data["words_per_speaker"][0]["speaker"] == 0
assert len(data["words_per_speaker"][0]["words"]) == 2

View File

@@ -9,12 +9,15 @@ models/GetTranscript.ts
models/GetTranscriptSegmentTopic.ts
models/GetTranscriptTopic.ts
models/GetTranscriptTopicWithWords.ts
models/GetTranscriptTopicWithWordsPerSpeaker.ts
models/HTTPValidationError.ts
models/PageGetTranscript.ts
models/Participant.ts
models/RtcOffer.ts
models/SpeakerAssignment.ts
models/SpeakerAssignmentStatus.ts
models/SpeakerMerge.ts
models/SpeakerWords.ts
models/TranscriptParticipant.ts
models/UpdateParticipant.ts
models/UpdateTranscript.ts

View File

@@ -19,12 +19,14 @@ import type {
CreateTranscript,
DeletionStatus,
GetTranscript,
GetTranscriptTopicWithWordsPerSpeaker,
HTTPValidationError,
PageGetTranscript,
Participant,
RtcOffer,
SpeakerAssignment,
SpeakerAssignmentStatus,
SpeakerMerge,
UpdateParticipant,
UpdateTranscript,
} from "../models";
@@ -39,6 +41,8 @@ import {
DeletionStatusToJSON,
GetTranscriptFromJSON,
GetTranscriptToJSON,
GetTranscriptTopicWithWordsPerSpeakerFromJSON,
GetTranscriptTopicWithWordsPerSpeakerToJSON,
HTTPValidationErrorFromJSON,
HTTPValidationErrorToJSON,
PageGetTranscriptFromJSON,
@@ -51,6 +55,8 @@ import {
SpeakerAssignmentToJSON,
SpeakerAssignmentStatusFromJSON,
SpeakerAssignmentStatusToJSON,
SpeakerMergeFromJSON,
SpeakerMergeToJSON,
UpdateParticipantFromJSON,
UpdateParticipantToJSON,
UpdateTranscriptFromJSON,
@@ -106,10 +112,20 @@ export interface V1TranscriptGetTopicsWithWordsRequest {
transcriptId: any;
}
export interface V1TranscriptGetTopicsWithWordsPerSpeakerRequest {
transcriptId: any;
topicId: any;
}
export interface V1TranscriptGetWebsocketEventsRequest {
transcriptId: any;
}
export interface V1TranscriptMergeSpeakerRequest {
transcriptId: any;
speakerMerge: SpeakerMerge;
}
export interface V1TranscriptRecordWebrtcRequest {
transcriptId: any;
rtcOffer: RtcOffer;
@@ -917,6 +933,82 @@ export class DefaultApi extends runtime.BaseAPI {
return await response.value();
}
/**
* Transcript Get Topics With Words Per Speaker
*/
async v1TranscriptGetTopicsWithWordsPerSpeakerRaw(
requestParameters: V1TranscriptGetTopicsWithWordsPerSpeakerRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<GetTranscriptTopicWithWordsPerSpeaker>> {
if (
requestParameters.transcriptId === null ||
requestParameters.transcriptId === undefined
) {
throw new runtime.RequiredError(
"transcriptId",
"Required parameter requestParameters.transcriptId was null or undefined when calling v1TranscriptGetTopicsWithWordsPerSpeaker.",
);
}
if (
requestParameters.topicId === null ||
requestParameters.topicId === undefined
) {
throw new runtime.RequiredError(
"topicId",
"Required parameter requestParameters.topicId was null or undefined when calling v1TranscriptGetTopicsWithWordsPerSpeaker.",
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
if (this.configuration && this.configuration.accessToken) {
// oauth required
headerParameters["Authorization"] = await this.configuration.accessToken(
"OAuth2AuthorizationCodeBearer",
[],
);
}
const response = await this.request(
{
path: `/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker`
.replace(
`{${"transcript_id"}}`,
encodeURIComponent(String(requestParameters.transcriptId)),
)
.replace(
`{${"topic_id"}}`,
encodeURIComponent(String(requestParameters.topicId)),
),
method: "GET",
headers: headerParameters,
query: queryParameters,
},
initOverrides,
);
return new runtime.JSONApiResponse(response, (jsonValue) =>
GetTranscriptTopicWithWordsPerSpeakerFromJSON(jsonValue),
);
}
/**
* Transcript Get Topics With Words Per Speaker
*/
async v1TranscriptGetTopicsWithWordsPerSpeaker(
requestParameters: V1TranscriptGetTopicsWithWordsPerSpeakerRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<GetTranscriptTopicWithWordsPerSpeaker> {
const response = await this.v1TranscriptGetTopicsWithWordsPerSpeakerRaw(
requestParameters,
initOverrides,
);
return await response.value();
}
/**
* Transcript Get Websocket Events
*/
@@ -972,6 +1064,80 @@ export class DefaultApi extends runtime.BaseAPI {
return await response.value();
}
/**
* Transcript Merge Speaker
*/
async v1TranscriptMergeSpeakerRaw(
requestParameters: V1TranscriptMergeSpeakerRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<SpeakerAssignmentStatus>> {
if (
requestParameters.transcriptId === null ||
requestParameters.transcriptId === undefined
) {
throw new runtime.RequiredError(
"transcriptId",
"Required parameter requestParameters.transcriptId was null or undefined when calling v1TranscriptMergeSpeaker.",
);
}
if (
requestParameters.speakerMerge === null ||
requestParameters.speakerMerge === undefined
) {
throw new runtime.RequiredError(
"speakerMerge",
"Required parameter requestParameters.speakerMerge was null or undefined when calling v1TranscriptMergeSpeaker.",
);
}
const queryParameters: any = {};
const headerParameters: runtime.HTTPHeaders = {};
headerParameters["Content-Type"] = "application/json";
if (this.configuration && this.configuration.accessToken) {
// oauth required
headerParameters["Authorization"] = await this.configuration.accessToken(
"OAuth2AuthorizationCodeBearer",
[],
);
}
const response = await this.request(
{
path: `/v1/transcripts/{transcript_id}/speaker/merge`.replace(
`{${"transcript_id"}}`,
encodeURIComponent(String(requestParameters.transcriptId)),
),
method: "PATCH",
headers: headerParameters,
query: queryParameters,
body: SpeakerMergeToJSON(requestParameters.speakerMerge),
},
initOverrides,
);
return new runtime.JSONApiResponse(response, (jsonValue) =>
SpeakerAssignmentStatusFromJSON(jsonValue),
);
}
/**
* Transcript Merge Speaker
*/
async v1TranscriptMergeSpeaker(
requestParameters: V1TranscriptMergeSpeakerRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<SpeakerAssignmentStatus> {
const response = await this.v1TranscriptMergeSpeakerRaw(
requestParameters,
initOverrides,
);
return await response.value();
}
/**
* Transcript Record Webrtc
*/

View File

@@ -43,6 +43,12 @@ export interface GetTranscriptTopic {
* @memberof GetTranscriptTopic
*/
timestamp: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopic
*/
duration: any | null;
/**
*
* @type {any}
@@ -66,6 +72,7 @@ export function instanceOfGetTranscriptTopic(value: object): boolean {
isInstance = isInstance && "title" in value;
isInstance = isInstance && "summary" in value;
isInstance = isInstance && "timestamp" in value;
isInstance = isInstance && "duration" in value;
isInstance = isInstance && "transcript" in value;
return isInstance;
@@ -87,6 +94,7 @@ export function GetTranscriptTopicFromJSONTyped(
title: json["title"],
summary: json["summary"],
timestamp: json["timestamp"],
duration: json["duration"],
transcript: json["transcript"],
segments: !exists(json, "segments") ? undefined : json["segments"],
};
@@ -106,6 +114,7 @@ export function GetTranscriptTopicToJSON(
title: value.title,
summary: value.summary,
timestamp: value.timestamp,
duration: value.duration,
transcript: value.transcript,
segments: value.segments,
};

View File

@@ -43,6 +43,12 @@ export interface GetTranscriptTopicWithWords {
* @memberof GetTranscriptTopicWithWords
*/
timestamp: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWords
*/
duration: any | null;
/**
*
* @type {any}
@@ -72,6 +78,7 @@ export function instanceOfGetTranscriptTopicWithWords(value: object): boolean {
isInstance = isInstance && "title" in value;
isInstance = isInstance && "summary" in value;
isInstance = isInstance && "timestamp" in value;
isInstance = isInstance && "duration" in value;
isInstance = isInstance && "transcript" in value;
return isInstance;
@@ -95,6 +102,7 @@ export function GetTranscriptTopicWithWordsFromJSONTyped(
title: json["title"],
summary: json["summary"],
timestamp: json["timestamp"],
duration: json["duration"],
transcript: json["transcript"],
segments: !exists(json, "segments") ? undefined : json["segments"],
words: !exists(json, "words") ? undefined : json["words"],
@@ -115,6 +123,7 @@ export function GetTranscriptTopicWithWordsToJSON(
title: value.title,
summary: value.summary,
timestamp: value.timestamp,
duration: value.duration,
transcript: value.transcript,
segments: value.segments,
words: value.words,

View File

@@ -0,0 +1,135 @@
/* tslint:disable */
/* eslint-disable */
/**
* FastAPI
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 0.1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { exists, mapValues } from "../runtime";
/**
*
* @export
* @interface GetTranscriptTopicWithWordsPerSpeaker
*/
export interface GetTranscriptTopicWithWordsPerSpeaker {
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
id: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
title: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
summary: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
timestamp: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
duration: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
transcript: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
segments?: any | null;
/**
*
* @type {any}
* @memberof GetTranscriptTopicWithWordsPerSpeaker
*/
wordsPerSpeaker?: any | null;
}
/**
* Check if a given object implements the GetTranscriptTopicWithWordsPerSpeaker interface.
*/
export function instanceOfGetTranscriptTopicWithWordsPerSpeaker(
value: object,
): boolean {
let isInstance = true;
isInstance = isInstance && "id" in value;
isInstance = isInstance && "title" in value;
isInstance = isInstance && "summary" in value;
isInstance = isInstance && "timestamp" in value;
isInstance = isInstance && "duration" in value;
isInstance = isInstance && "transcript" in value;
return isInstance;
}
export function GetTranscriptTopicWithWordsPerSpeakerFromJSON(
json: any,
): GetTranscriptTopicWithWordsPerSpeaker {
return GetTranscriptTopicWithWordsPerSpeakerFromJSONTyped(json, false);
}
export function GetTranscriptTopicWithWordsPerSpeakerFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): GetTranscriptTopicWithWordsPerSpeaker {
if (json === undefined || json === null) {
return json;
}
return {
id: json["id"],
title: json["title"],
summary: json["summary"],
timestamp: json["timestamp"],
duration: json["duration"],
transcript: json["transcript"],
segments: !exists(json, "segments") ? undefined : json["segments"],
wordsPerSpeaker: !exists(json, "words_per_speaker")
? undefined
: json["words_per_speaker"],
};
}
export function GetTranscriptTopicWithWordsPerSpeakerToJSON(
value?: GetTranscriptTopicWithWordsPerSpeaker | null,
): any {
if (value === undefined) {
return undefined;
}
if (value === null) {
return null;
}
return {
id: value.id,
title: value.title,
summary: value.summary,
timestamp: value.timestamp,
duration: value.duration,
transcript: value.transcript,
segments: value.segments,
words_per_speaker: value.wordsPerSpeaker,
};
}

View File

@@ -24,7 +24,13 @@ export interface SpeakerAssignment {
* @type {any}
* @memberof SpeakerAssignment
*/
speaker: any | null;
speaker?: any | null;
/**
*
* @type {any}
* @memberof SpeakerAssignment
*/
participant?: any | null;
/**
*
* @type {any}
@@ -44,7 +50,6 @@ export interface SpeakerAssignment {
*/
export function instanceOfSpeakerAssignment(value: object): boolean {
let isInstance = true;
isInstance = isInstance && "speaker" in value;
isInstance = isInstance && "timestampFrom" in value;
isInstance = isInstance && "timestampTo" in value;
@@ -63,7 +68,8 @@ export function SpeakerAssignmentFromJSONTyped(
return json;
}
return {
speaker: json["speaker"],
speaker: !exists(json, "speaker") ? undefined : json["speaker"],
participant: !exists(json, "participant") ? undefined : json["participant"],
timestampFrom: json["timestamp_from"],
timestampTo: json["timestamp_to"],
};
@@ -78,6 +84,7 @@ export function SpeakerAssignmentToJSON(value?: SpeakerAssignment | null): any {
}
return {
speaker: value.speaker,
participant: value.participant,
timestamp_from: value.timestampFrom,
timestamp_to: value.timestampTo,
};

View File

@@ -0,0 +1,75 @@
/* tslint:disable */
/* eslint-disable */
/**
* FastAPI
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 0.1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { exists, mapValues } from "../runtime";
/**
*
* @export
* @interface SpeakerMerge
*/
export interface SpeakerMerge {
/**
*
* @type {any}
* @memberof SpeakerMerge
*/
speakerFrom: any | null;
/**
*
* @type {any}
* @memberof SpeakerMerge
*/
speakerTo: any | null;
}
/**
* Check if a given object implements the SpeakerMerge interface.
*/
export function instanceOfSpeakerMerge(value: object): boolean {
let isInstance = true;
isInstance = isInstance && "speakerFrom" in value;
isInstance = isInstance && "speakerTo" in value;
return isInstance;
}
export function SpeakerMergeFromJSON(json: any): SpeakerMerge {
return SpeakerMergeFromJSONTyped(json, false);
}
export function SpeakerMergeFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): SpeakerMerge {
if (json === undefined || json === null) {
return json;
}
return {
speakerFrom: json["speaker_from"],
speakerTo: json["speaker_to"],
};
}
export function SpeakerMergeToJSON(value?: SpeakerMerge | null): any {
if (value === undefined) {
return undefined;
}
if (value === null) {
return null;
}
return {
speaker_from: value.speakerFrom,
speaker_to: value.speakerTo,
};
}

View File

@@ -0,0 +1,75 @@
/* tslint:disable */
/* eslint-disable */
/**
* FastAPI
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 0.1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
import { exists, mapValues } from "../runtime";
/**
*
* @export
* @interface SpeakerWords
*/
export interface SpeakerWords {
/**
*
* @type {any}
* @memberof SpeakerWords
*/
speaker: any | null;
/**
*
* @type {any}
* @memberof SpeakerWords
*/
words: any | null;
}
/**
* Check if a given object implements the SpeakerWords interface.
*/
export function instanceOfSpeakerWords(value: object): boolean {
let isInstance = true;
isInstance = isInstance && "speaker" in value;
isInstance = isInstance && "words" in value;
return isInstance;
}
export function SpeakerWordsFromJSON(json: any): SpeakerWords {
return SpeakerWordsFromJSONTyped(json, false);
}
export function SpeakerWordsFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): SpeakerWords {
if (json === undefined || json === null) {
return json;
}
return {
speaker: json["speaker"],
words: json["words"],
};
}
export function SpeakerWordsToJSON(value?: SpeakerWords | null): any {
if (value === undefined) {
return undefined;
}
if (value === null) {
return null;
}
return {
speaker: value.speaker,
words: value.words,
};
}

View File

@@ -8,12 +8,15 @@ export * from "./GetTranscript";
export * from "./GetTranscriptSegmentTopic";
export * from "./GetTranscriptTopic";
export * from "./GetTranscriptTopicWithWords";
export * from "./GetTranscriptTopicWithWordsPerSpeaker";
export * from "./HTTPValidationError";
export * from "./PageGetTranscript";
export * from "./Participant";
export * from "./RtcOffer";
export * from "./SpeakerAssignment";
export * from "./SpeakerAssignmentStatus";
export * from "./SpeakerMerge";
export * from "./SpeakerWords";
export * from "./TranscriptParticipant";
export * from "./UpdateParticipant";
export * from "./UpdateTranscript";