Skip to content
Merged
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
72 changes: 0 additions & 72 deletions backend/models/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,78 +114,6 @@ def __init__(self, **data):
self.plugins_results = [PluginResult(plugin_id=app.app_id, content=app.content) for app in self.apps_results]
self.processing_memory_id = self.processing_conversation_id

@staticmethod
def conversations_to_string(
conversations: List['Conversation'],
use_transcript: bool = False,
include_timestamps: bool = False,
people: List[Person] = None,
user_name: str = None,
) -> str:
result = []
people_map = {p.id: p for p in people} if people else {}
for i, conversation in enumerate(conversations):
if isinstance(conversation, dict):
conversation = Conversation(**conversation)
formatted_date = conversation.created_at.astimezone(timezone.utc).strftime("%d %b %Y at %H:%M") + " UTC"
conversation_str = (
f"Conversation #{i + 1}\n"
f"{formatted_date} ({str(conversation.structured.category.value).capitalize()})\n"
)

# Add started_at and finished_at if available
if conversation.started_at:
formatted_started = (
conversation.started_at.astimezone(timezone.utc).strftime("%d %b %Y at %H:%M") + " UTC"
)
conversation_str += f"Started: {formatted_started}\n"
if conversation.finished_at:
formatted_finished = (
conversation.finished_at.astimezone(timezone.utc).strftime("%d %b %Y at %H:%M") + " UTC"
)
conversation_str += f"Finished: {formatted_finished}\n"

conversation_str += f"{str(conversation.structured.title).capitalize()}\n"

if (
conversation.apps_results
and len(conversation.apps_results) > 0
and conversation.apps_results[0].content.strip()
):
conversation_str += f"{conversation.apps_results[0].content}\n"
else:
conversation_str += f"{str(conversation.structured.overview).capitalize()}\n"

# attendees
if people_map:
conv_person_ids = set(conversation.get_person_ids())
if conv_person_ids:
attendees_names = [people_map[pid].name for pid in conv_person_ids if pid in people_map]
if attendees_names:
attendees = ", ".join(attendees_names)
conversation_str += f"Attendees: {attendees}\n"

if conversation.structured.action_items:
conversation_str += "Action Items:\n"
for item in conversation.structured.action_items:
conversation_str += f"- {item.description}\n"

if conversation.structured.events:
conversation_str += "Events:\n"
for event in conversation.structured.events:
conversation_str += f"- {event.title} ({event.start} - {event.duration} minutes)\n"

if use_transcript:
conversation_str += f"\nTranscript:\n{conversation.get_transcript(include_timestamps=include_timestamps, people=people, user_name=user_name)}\n"
# photos
photo_descriptions = conversation.get_photos_descriptions(include_timestamps=include_timestamps)
if photo_descriptions != 'None':
conversation_str += f"Photo Descriptions from a wearable camera:\n{photo_descriptions}\n"

result.append(conversation_str.strip())

return "\n\n---------------------\n\n".join(result).strip()

def get_transcript(self, include_timestamps: bool, people: List[Person] = None, user_name: str = None) -> str:
# Warn: missing transcript for workflow source, external integration source
return TranscriptSegment.segments_as_string(
Expand Down
39 changes: 17 additions & 22 deletions backend/routers/conversations.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
UpdateActionItemDescriptionRequest,
UpdateSegmentTextRequest,
)
from utils.conversations.factory import deserialize_conversation
from utils.conversations.render import redact_conversations_for_list
from utils.conversations.render import conversation_to_dict
from models.conversation_enums import ConversationStatus, ConversationVisibility
from models.conversation_photo import ConversationPhoto
from models.geolocation import Geolocation
Expand Down Expand Up @@ -71,7 +74,7 @@ def process_in_progress_conversation(
raise HTTPException(status_code=404, detail="Conversation in progress not found")
redis_db.remove_in_progress_conversation_id(uid)

conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)

# Inject calendar context if provided
if request and request.calendar_meeting_context:
Expand Down Expand Up @@ -107,7 +110,7 @@ def reprocess_conversation(
:return: The updated conversation after reprocessing.
"""
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
if not language_code:
language_code = conversation.language or 'en'

Expand Down Expand Up @@ -147,15 +150,7 @@ def get_conversations(
starred=starred,
)

for conv in conversations:
if conv.get('is_locked', False):
if 'structured' in conv:
conv['structured']['action_items'] = []
conv['structured']['events'] = []
conv['apps_results'] = []
conv['plugins_results'] = []
conv['suggested_summarization_apps'] = []
conv['transcript_segments'] = []
redact_conversations_for_list(conversations)
return conversations


Expand Down Expand Up @@ -253,7 +248,7 @@ def set_conversation_events_state(
conversation_id: str, data: SetConversationEventsStateRequest, uid: str = Depends(auth.get_current_user_uid)
):
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
events = conversation.structured.events
for i, event_idx in enumerate(data.events_idx):
if event_idx >= len(events):
Expand All @@ -269,7 +264,7 @@ def set_action_item_status(
data: SetConversationActionItemsStateRequest, conversation_id: str, uid=Depends(auth.get_current_user_uid)
):
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
action_items = conversation.structured.action_items
for i, action_item_idx in enumerate(data.items_idx):
if action_item_idx >= len(action_items):
Expand Down Expand Up @@ -330,7 +325,7 @@ def update_action_item_description(
conversation_id: str, data: UpdateActionItemDescriptionRequest, uid=Depends(auth.get_current_user_uid)
):
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
action_items = conversation.structured.action_items

found_item = False
Expand Down Expand Up @@ -361,7 +356,7 @@ def update_action_item_description(
@router.delete("/v1/conversations/{conversation_id}/action-items", response_model=dict, tags=['conversations'])
def delete_action_item(data: DeleteActionItemRequest, conversation_id: str, uid=Depends(auth.get_current_user_uid)):
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
action_items = conversation.structured.action_items
updated_action_items = [item for item in action_items if not (item.description == data.description)]
conversations_db.update_conversation_action_items(
Expand Down Expand Up @@ -414,7 +409,7 @@ def set_assignee_conversation_segment(
f'set_assignee_conversation_segment {conversation_id} {segment_idx} {assign_type} {value} {use_for_speech_training} {uid}'
)
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)

if value == 'null':
value = None
Expand Down Expand Up @@ -483,7 +478,7 @@ def set_assignee_conversation_segment(
f'set_assignee_conversation_segment {conversation_id} {speaker_id} {assign_type} {value} {use_for_speech_training} {uid}'
)
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)

if value == 'null':
value = None
Expand Down Expand Up @@ -542,7 +537,7 @@ def assign_segments_bulk(
uid: str = Depends(auth.get_current_user_uid),
):
conversation = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)

value = data.value
if value == 'null':
Expand Down Expand Up @@ -619,7 +614,7 @@ def get_shared_conversation_by_id(conversation_id: str):
visibility = conversation.get('visibility', ConversationVisibility.private)
if not visibility or visibility == ConversationVisibility.private:
raise HTTPException(status_code=404, detail="Conversation is private")
conversation = Conversation(**conversation)
conversation = deserialize_conversation(conversation)
conversation.geolocation = None

# Fetch people data for speaker names
Expand All @@ -630,7 +625,7 @@ def get_shared_conversation_by_id(conversation_id: str):
people = [Person(**p) for p in people_data]

# Return conversation with people data
response_dict = conversation.as_dict_cleaned_dates()
response_dict = conversation_to_dict(conversation)
response_dict['people'] = [p.dict() for p in people]
return response_dict

Expand Down Expand Up @@ -684,7 +679,7 @@ def get_conversation_suggested_apps(conversation_id: str, uid: str = Depends(aut
from models.app import App

conversation_data = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation_data)
conversation = deserialize_conversation(conversation_data)

# Get suggested app models with full data (similar to /v1/apps endpoint)
suggested_apps = []
Expand Down Expand Up @@ -719,7 +714,7 @@ def test_prompt(
uid: str = Depends(auth.with_rate_limit(auth.get_current_user_uid, "test:prompt")),
):
conversation_data = _get_valid_conversation_by_id(uid, conversation_id)
conversation = Conversation(**conversation_data)
conversation = deserialize_conversation(conversation_data)

full_transcript = "\n".join([seg.text for seg in conversation.transcript_segments if seg.text])

Expand Down
58 changes: 6 additions & 52 deletions backend/routers/developer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import database.action_items as action_items_db
import database.goals as goals_db
import database.users as users_db
import database.folders as folders_db
from database.vector_db import upsert_memory_vectors_batch

from models.memories import MemoryCategory, Memory, MemoryDB
Expand All @@ -24,6 +23,7 @@
ExternalIntegrationConversationSource,
)
from models.geolocation import Geolocation
from utils.conversations.render import populate_speaker_names, populate_folder_names
from models.conversation import Conversation as ConversationModel
from models.transcript_segment import TranscriptSegment
from dependencies import (
Expand Down Expand Up @@ -732,52 +732,6 @@ class CreateConversationFromTranscriptRequest(BaseModel):
geolocation: Optional[Geolocation] = Field(default=None, description="Geolocation where conversation occurred")


def _add_speaker_names_to_segments(uid, conversations: list):
"""Add speaker_name to transcript segments based on person_id mappings."""
user_profile = users_db.get_user_profile(uid)
user_name = user_profile.get('name') or 'User'

all_person_ids = set()
for conv in conversations:
for seg in conv.get('transcript_segments', []):
if seg.get('person_id'):
all_person_ids.add(seg['person_id'])

people_map = {}
if all_person_ids:
people_data = users_db.get_people_by_ids(uid, list(all_person_ids))
people_map = {p['id']: p['name'] for p in people_data}

for conv in conversations:
for seg in conv.get('transcript_segments', []):
if seg.get('is_user'):
seg['speaker_name'] = user_name
elif seg.get('person_id') and seg['person_id'] in people_map:
seg['speaker_name'] = people_map[seg['person_id']]
else:
seg['speaker_name'] = f"Speaker {seg.get('speaker_id', 0)}"


def _add_folder_names_to_conversations(uid, conversations: list):
"""Add folder_name to conversations based on folder_id mappings."""
folder_ids = set()
for conv in conversations:
if conv.get('folder_id'):
folder_ids.add(conv['folder_id'])

if not folder_ids:
for conv in conversations:
conv['folder_name'] = None
return

all_folders = folders_db.get_folders(uid)
folder_map = {f['id']: f['name'] for f in all_folders}

for conv in conversations:
folder_id = conv.get('folder_id')
conv['folder_name'] = folder_map.get(folder_id) if folder_id else None


@router.get("/v1/dev/user/conversations", response_model=List[Conversation], tags=["developer"])
def get_conversations(
start_date: Optional[datetime] = None,
Expand Down Expand Up @@ -817,9 +771,9 @@ def get_conversations(
for conv in unlocked_conversations:
conv.pop('transcript_segments', None)
else:
_add_speaker_names_to_segments(uid, unlocked_conversations)
populate_speaker_names(uid, unlocked_conversations)

_add_folder_names_to_conversations(uid, unlocked_conversations)
populate_folder_names(uid, unlocked_conversations)

return unlocked_conversations

Expand Down Expand Up @@ -926,9 +880,9 @@ def get_conversation_endpoint(
if not include_transcript:
conversation.pop('transcript_segments', None)
else:
_add_speaker_names_to_segments(uid, [conversation])
populate_speaker_names(uid, [conversation])

_add_folder_names_to_conversations(uid, [conversation])
populate_folder_names(uid, [conversation])

return conversation

Expand Down Expand Up @@ -1122,7 +1076,7 @@ def update_conversation_endpoint(

conversation = conversations_db.get_conversation(uid, conversation_id)
if conversation:
_add_folder_names_to_conversations(uid, [conversation])
populate_folder_names(uid, [conversation])
return conversation


Expand Down
11 changes: 2 additions & 9 deletions backend/routers/folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
ReorderFoldersRequest,
)
from models.conversation import Conversation
from utils.conversations.render import redact_conversations_for_list
from utils.other import endpoints as auth

router = APIRouter()
Expand Down Expand Up @@ -111,15 +112,7 @@ def get_folder_conversations(
conversations = folders_db.get_conversations_in_folder(
uid, folder_id, limit=limit, offset=offset, include_discarded=include_discarded
)
for conv in conversations:
if conv.get('is_locked', False):
if 'structured' in conv:
conv['structured']['action_items'] = []
conv['structured']['events'] = []
conv['apps_results'] = []
conv['plugins_results'] = []
conv['suggested_summarization_apps'] = []
conv['transcript_segments'] = []
redact_conversations_for_list(conversations)
return conversations


Expand Down
23 changes: 3 additions & 20 deletions backend/routers/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from models.app import App
from routers.conversations import process_conversation, trigger_external_integrations
from utils.conversations.location import get_google_maps_location
from utils.conversations.render import redact_conversation_for_integration
from utils.conversations.memories import process_external_integration_memory
from utils.conversations.search import search_conversations
from utils.app_integrations import send_app_notification
Expand Down Expand Up @@ -347,16 +348,7 @@ async def get_conversations_via_integration(
conversation_items = []
for conv in conversations_data:
try:
if conv.get('is_locked', False):
conv['structured']['title'] = ''
conv['structured']['overview'] = ''
conv['structured']['action_items'] = []
conv['structured']['events'] = []
conv['transcript_segments'] = []
conv['apps_results'] = []
conv['plugins_results'] = []
conv['suggested_summarization_apps'] = []

redact_conversation_for_integration(conv)
item = integration_models.ConversationItem.parse_obj(conv)

# Limit transcript segments
Expand Down Expand Up @@ -480,16 +472,7 @@ async def search_conversations_via_integration(
conversation_items = []
for conv in full_conversations:
try:
if conv.get('is_locked', False):
conv['structured']['title'] = ''
conv['structured']['overview'] = ''
conv['structured']['action_items'] = []
conv['structured']['events'] = []
conv['transcript_segments'] = []
conv['apps_results'] = []
conv['plugins_results'] = []
conv['suggested_summarization_apps'] = []

redact_conversation_for_integration(conv)
item = integration_models.ConversationItem.parse_obj(conv)

# Limit transcript segments
Expand Down
Loading
Loading