Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""add_room_background_information

Revision ID: 082fa608201c
Revises: b7df9609542c
Create Date: 2025-07-29 01:41:37.912195

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '082fa608201c'
down_revision: Union[str, None] = 'b7df9609542c'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
op.add_column('room', sa.Column('background_information', sa.Text(), nullable=True))


def downgrade() -> None:
op.drop_column('room', 'background_information')
4 changes: 4 additions & 0 deletions server/reflector/db/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
sqlalchemy.Column(
"is_shared", sqlalchemy.Boolean, nullable=False, server_default=false()
),
sqlalchemy.Column("background_information", sqlalchemy.Text),
sqlalchemy.Index("idx_room_is_shared", "is_shared"),
)

Expand All @@ -58,6 +59,7 @@ class Room(BaseModel):
"none", "prompt", "automatic", "automatic-2nd-participant"
] = "automatic-2nd-participant"
is_shared: bool = False
background_information: str = ""


class RoomController:
Expand Down Expand Up @@ -106,6 +108,7 @@ async def add(
recording_type: str,
recording_trigger: str,
is_shared: bool,
background_information: str = "",
):
"""
Add a new room
Expand All @@ -121,6 +124,7 @@ async def add(
recording_type=recording_type,
recording_trigger=recording_trigger,
is_shared=is_shared,
background_information=background_information,
)
query = rooms.insert().values(**room.model_dump())
try:
Expand Down
32 changes: 32 additions & 0 deletions server/reflector/pipelines/main_live_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,47 @@ class PipelineMainFinalSummaries(PipelineMainFromTopics):
Generate summaries from the topics
"""

async def get_room(self):
"""Get room information for the transcript"""
if not self._transcript.room_id:
return None
return await rooms_controller.get_by_id(self._transcript.room_id)

def get_processors(self) -> list:
return [
TranscriptFinalSummaryProcessor.as_threaded(
transcript=self._transcript,
room=getattr(self, '_room', None),
callback=self.on_long_summary,
on_short_summary=self.on_short_summary,
),
]

async def create(self) -> Pipeline:
self.prepare()

# get transcript
self._transcript = transcript = await self.get_transcript()

# get room information
self._room = await self.get_room()

# create pipeline
processors = self.get_processors()
pipeline = Pipeline(*processors)
pipeline.options = self
pipeline.logger.bind(transcript_id=transcript.id)
pipeline.logger.info(f"{self.__class__.__name__} pipeline created")

# push topics
topics = self.get_transcript_topics(transcript)
for topic in topics:
await self.push(topic)

await self.flush()

return pipeline


class PipelineMainWaveform(PipelineMainFromTopics):
"""
Expand Down
46 changes: 26 additions & 20 deletions server/reflector/processors/summary/summary_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def print_content(self, role, content):


class SummaryBuilder:
def __init__(self, llm, filename: str | None = None, logger=None):
def __init__(self, llm, filename: str | None = None, logger=None, room=None):
self.transcript: str | None = None
self.recap: str | None = None
self.summaries: list[dict] = []
Expand All @@ -147,6 +147,7 @@ def __init__(self, llm, filename: str | None = None, logger=None):
self.llm_instance: LLM = llm
self.model_name: str = llm.model_name
self.logger = logger or structlog.get_logger()
self.room = room
self.m = Messages(model_name=self.model_name, logger=self.logger)
if filename:
self.read_transcript_from_file(filename)
Expand Down Expand Up @@ -465,26 +466,31 @@ async def generate_summary(self, only_subjects=False):
self.logger.debug("--- extract main subjects")

m = Messages(model_name=self.model_name, logger=self.logger)
m.add_system(
(
"You are an advanced transcription summarization assistant."
"Your task is to summarize discussions by focusing only on the main ideas contributed by participants."
# Prevent generating another transcription
"Exclude direct quotes and unnecessary details."
# Do not mention others participants just because they didn't contributed
"Only include participant names if they actively contribute to the subject."
# Prevent generation of summary with "no others participants contributed" etc
"Keep summaries concise and focused on main subjects without adding conclusions such as 'no other participant contributed'. "
# Avoid: In the discussion, they talked about...
"Do not include contextual preface. "
# Prevention to have too long summary
"Summary should fit in a single paragraph. "
# Using other pronouns that the participants or the group
'Mention the participants or the group using "they".'
# Avoid finishing the summary with "No conclusions were added by the summarizer"
"Do not mention conclusion if there is no conclusion"
)

system_prompt = (
"You are an advanced transcription summarization assistant."
"Your task is to summarize discussions by focusing only on the main ideas contributed by participants."
# Prevent generating another transcription
"Exclude direct quotes and unnecessary details."
# Do not mention others participants just because they didn't contributed
"Only include participant names if they actively contribute to the subject."
# Prevent generation of summary with "no others participants contributed" etc
"Keep summaries concise and focused on main subjects without adding conclusions such as 'no other participant contributed'. "
# Avoid: In the discussion, they talked about...
"Do not include contextual preface. "
# Prevention to have too long summary
"Summary should fit in a single paragraph. "
# Using other pronouns that the participants or the group
'Mention the participants or the group using "they".'
# Avoid finishing the summary with "No conclusions were added by the summarizer"
"Do not mention conclusion if there is no conclusion"
)

# Add room context if available
if self.room and self.room.background_information:
system_prompt += f"\n\nContext about this meeting room: {self.room.background_information}"

m.add_system(system_prompt)
m.add_user(
f"# Transcript\n\n{self.transcript}\n\n"
+ (
Expand Down
5 changes: 3 additions & 2 deletions server/reflector/processors/transcript_final_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ class TranscriptFinalSummaryProcessor(Processor):
INPUT_TYPE = TitleSummary
OUTPUT_TYPE = FinalLongSummary

def __init__(self, transcript=None, **kwargs):
def __init__(self, transcript=None, room=None, **kwargs):
super().__init__(**kwargs)
self.transcript = transcript
self.room = room
self.chunks: list[TitleSummary] = []
self.llm = LLM.get_instance(model_name="NousResearch/Hermes-3-Llama-3.1-8B")
self.builder = None
Expand All @@ -23,7 +24,7 @@ async def _push(self, data: TitleSummary):
self.chunks.append(data)

async def get_summary_builder(self, text) -> SummaryBuilder:
builder = SummaryBuilder(self.llm)
builder = SummaryBuilder(self.llm, room=self.room)
builder.set_transcript(text)
await builder.identify_participants()
await builder.generate_summary()
Comment on lines +27 to 30
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggestion: The builder variable is being used before it's assigned. You need to assign the result of SummaryBuilder() to the builder variable and also assign it to self.builder for later reference. [possible issue, importance: 8]

Suggested change
builder = SummaryBuilder(self.llm, room=self.room)
builder.set_transcript(text)
await builder.identify_participants()
await builder.generate_summary()
async def get_summary_builder(self, text) -> SummaryBuilder:
builder = SummaryBuilder(self.llm, room=self.room)
self.builder = builder
builder.set_transcript(text)
await builder.identify_participants()
await builder.generate_summary()

Expand Down
4 changes: 4 additions & 0 deletions server/reflector/views/rooms.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Room(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str


class Meeting(BaseModel):
Expand All @@ -55,6 +56,7 @@ class CreateRoom(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str = ""


class UpdateRoom(BaseModel):
Expand All @@ -67,6 +69,7 @@ class UpdateRoom(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str = ""


class DeletionStatus(BaseModel):
Expand Down Expand Up @@ -108,6 +111,7 @@ async def rooms_create(
recording_type=room.recording_type,
recording_trigger=room.recording_trigger,
is_shared=room.is_shared,
background_information=room.background_information,
)


Expand Down
25 changes: 21 additions & 4 deletions www/app/(app)/rooms/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {
Input,
Select,
Spinner,
Textarea,
createListCollection,
useDisclosure,
} from "@chakra-ui/react";
import { useEffect, useState } from "react";
import useApi from "../../lib/useApi";
import useRoomList from "./useRoomList";
import { ApiError, Room } from "../../api";
import { Room } from "../../api";
import { RoomList } from "./_components/RoomList";

interface SelectOption {
Expand Down Expand Up @@ -54,6 +55,7 @@ const roomInitialState = {
recordingType: "cloud",
recordingTrigger: "automatic-2nd-participant",
isShared: false,
backgroundInformation: "",
};

export default function RoomsList() {
Expand Down Expand Up @@ -170,6 +172,7 @@ export default function RoomsList() {
recording_type: room.recordingType,
recording_trigger: room.recordingTrigger,
is_shared: room.isShared,
background_information: room.backgroundInformation,
};

if (isEditing) {
Expand All @@ -189,11 +192,10 @@ export default function RoomsList() {
setNameError("");
refetch();
onClose();
} catch (err) {
} catch (err: any) {
if (
err instanceof ApiError &&
err.status === 400 &&
(err.body as any).detail == "Room name is not unique"
err.body?.detail === "Room name is not unique"
) {
setNameError(
"This room name is already taken. Please choose a different name.",
Expand All @@ -215,6 +217,7 @@ export default function RoomsList() {
recordingType: roomData.recording_type,
recordingTrigger: roomData.recording_trigger,
isShared: roomData.is_shared,
backgroundInformation: roomData.background_information || "",
});
setEditRoomId(roomId);
setIsEditing(true);
Expand Down Expand Up @@ -323,6 +326,20 @@ export default function RoomsList() {
{nameError && <Field.ErrorText>{nameError}</Field.ErrorText>}
</Field.Root>

<Field.Root mt={4}>
<Field.Label>Background Information</Field.Label>
<Textarea
name="backgroundInformation"
placeholder="Provide context about this room's purpose, meeting type, or any relevant background information that will help generate better summaries..."
value={room.backgroundInformation}
onChange={handleRoomChange}
rows={3}
/>
<Field.HelperText>
This information will be used to provide context for AI-generated summaries
</Field.HelperText>
</Field.Root>

<Field.Root mt={4}>
<Checkbox.Root
name="isLocked"
Expand Down
6 changes: 3 additions & 3 deletions www/app/(app)/rooms/useRoomList.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { useEffect, useState } from "react";
import { useError } from "../../(errors)/errorContext";
import useApi from "../../lib/useApi";
import { Page_Room_ } from "../../api";
import { PageRoom } from "../../api";

type RoomList = {
response: Page_Room_ | null;
response: PageRoom | null;
loading: boolean;
error: Error | null;
refetch: () => void;
};

//always protected
const useRoomList = (page: number): RoomList => {
const [response, setResponse] = useState<Page_Room_ | null>(null);
const [response, setResponse] = useState<PageRoom | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setErrorState] = useState<Error | null>(null);
const { setError } = useError();
Expand Down
6 changes: 3 additions & 3 deletions www/app/(app)/transcripts/useTranscriptList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useEffect, useState } from "react";
import { useError } from "../../(errors)/errorContext";
import useApi from "../../lib/useApi";
import { Page_GetTranscriptMinimal_, SourceKind } from "../../api";
import { PageGetTranscriptMinimal, SourceKind } from "../../api";

type TranscriptList = {
response: Page_GetTranscriptMinimal_ | null;
response: PageGetTranscriptMinimal | null;
loading: boolean;
error: Error | null;
refetch: () => void;
Expand All @@ -16,7 +16,7 @@ const useTranscriptList = (
roomId: string | null,
searchTerm: string | null,
): TranscriptList => {
const [response, setResponse] = useState<Page_GetTranscriptMinimal_ | null>(
const [response, setResponse] = useState<PageGetTranscriptMinimal | null>(
null,
);
const [loading, setLoading] = useState<boolean>(true);
Expand Down
37 changes: 0 additions & 37 deletions www/app/api/OpenApi.ts

This file was deleted.

9 changes: 0 additions & 9 deletions www/app/api/auth/[...nextauth]/route.ts

This file was deleted.

Loading
Loading