feat: add background information field to room model

- Add background_information field to Room database table and model
- Create database migration for the new field
- Update API schemas (CreateRoom, UpdateRoom) to handle background_information
- Integrate room context into AI summarization prompts
- Add background_information field to frontend room form
- Update TypeScript types from regenerated OpenAPI spec

The background information will be used to provide context for AI-generated
summaries, helping create more appropriate and relevant meeting summaries.

🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <noreply@opencode.ai>
This commit is contained in:
opencode
2025-07-29 01:53:13 +00:00
parent db3beae5cd
commit 47cb75ee39
36 changed files with 4248 additions and 3687 deletions

View File

@@ -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')

View File

@@ -39,6 +39,7 @@ rooms = sqlalchemy.Table(
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"),
)
@@ -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:
@@ -106,6 +108,7 @@ class RoomController:
recording_type: str,
recording_trigger: str,
is_shared: bool,
background_information: str = "",
):
"""
Add a new room
@@ -121,6 +124,7 @@ class RoomController:
recording_type=recording_type,
recording_trigger=recording_trigger,
is_shared=is_shared,
background_information=background_information,
)
query = rooms.insert().values(**room.model_dump())
try:

View File

@@ -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):
"""

View File

@@ -135,7 +135,7 @@ class Messages:
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] = []
@@ -147,6 +147,7 @@ class SummaryBuilder:
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)
@@ -465,26 +466,31 @@ class SummaryBuilder:
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"
+ (

View File

@@ -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
@@ -23,7 +24,7 @@ class TranscriptFinalSummaryProcessor(Processor):
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()

View File

@@ -33,6 +33,7 @@ class Room(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str
class Meeting(BaseModel):
@@ -55,6 +56,7 @@ class CreateRoom(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str = ""
class UpdateRoom(BaseModel):
@@ -67,6 +69,7 @@ class UpdateRoom(BaseModel):
recording_type: str
recording_trigger: str
is_shared: bool
background_information: str = ""
class DeletionStatus(BaseModel):
@@ -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,
)