Skip to content

Commit bd969ec

Browse files
authored
Merge pull request #39 from mezonai/feature/record_Agent_session
fix bug update status final room
2 parents 615a678 + 1e8b6cd commit bd969ec

9 files changed

Lines changed: 52 additions & 70 deletions

File tree

Architect_MultiClient_Server/orchestrator_service/api/room_registry_api.py

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,22 +73,21 @@ async def register_room(
7373
try:
7474
registry = get_room_registry()
7575

76-
# Tạo ISO string timestamp duy nhất tại thời điểm này
76+
# create actual_start_time string in ISO format
7777
if request.start_time is not None:
78-
# Nếu được cung cấp, convert float timestamp sang ISO string
79-
actual_start_time = datetime.fromtimestamp(request.start_time).isoformat()
78+
# if provided, use the given start_time
79+
actual_start_time = datetime.fromtimestamp(request.start_time).strftime("%Y%m%d_%H%M%S")
8080
else:
81-
# Nếu không, dùng thời gian hiện tại
82-
actual_start_time = datetime.utcnow().isoformat()
83-
84-
# Register room vào registry với ISO string
81+
# If not provided, use the current time
82+
actual_start_time = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
83+
84+
# Register room in registry
8585
if not registry.register_room(request.room_name, actual_start_time):
8686
raise HTTPException(
8787
status_code=409,
8888
detail=f"Room '{request.room_name}' is already registered"
8989
)
90-
print(f"Room '{request.room_name}' registered with start_time: {registry.get_room_start_time(request.room_name)}")
91-
# Lấy tất cả tracks đang có trong room và bắt đầu recording
90+
# get all tracks in the room and start recording for audio tracks
9291
tracks_started = 0
9392
try:
9493
livekit_service = get_livekit_service()
@@ -177,12 +176,12 @@ async def unregister_room(
177176
auth: Dict[str, Any] = Depends(verify_api_key)
178177
):
179178
"""
180-
Unregister một room khỏi registry.
179+
Unregister a room from the registry.
181180
182-
Sau khi unregister, webhook sẽ không xử lý các events của room này nữa.
183-
Đồng thời:
184-
- Dừng tất cả egress recordings đang chạy
185-
- Finalize room status trong STT service
181+
After unregistering, the webhook will no longer process events for this room.
182+
Additionally:
183+
- Stop all running egress recordings
184+
- Finalize room status in the STT service
186185
187186
**Example:**
188187
```json
@@ -194,7 +193,7 @@ async def unregister_room(
194193
try:
195194
registry = get_room_registry()
196195

197-
# Lấy start_session_time trước khi unregister (ISO string, luôn có giá trị)
196+
# get start_session_time after unregister (ISO string)
198197
start_session_time = registry.get_room_start_time(request.room_name)
199198

200199
# Unregister room from registry
@@ -254,9 +253,9 @@ async def get_room_status(
254253
auth: Dict[str, Any] = Depends(verify_api_key)
255254
):
256255
"""
257-
Kiểm tra trạng thái registration của một room.
256+
Check status registration for a room.
258257
259-
Returns thông tin về room bao gồm start_time duration nếu room đã được register.
258+
Returns information about the room including start_time and duration if the room is registered.
260259
"""
261260
try:
262261
registry = get_room_registry()
@@ -284,9 +283,9 @@ async def list_registered_rooms(
284283
auth: Dict[str, Any] = Depends(verify_api_key)
285284
):
286285
"""
287-
Lấy danh sách tất cả rooms đang được register.
286+
Get a list of all currently registered rooms.
288287
289-
Returns dictionary với key là room_name và value là start_time.
288+
Returns a dictionary with keys as room_name and values as start_time.
290289
"""
291290
try:
292291
registry = get_room_registry()
@@ -310,9 +309,9 @@ async def clear_all_rooms(
310309
auth: Dict[str, Any] = Depends(verify_api_key)
311310
):
312311
"""
313-
Clear tất cả rooms khỏi registry (dùng cho testing hoặc cleanup).
312+
clear all registered rooms from the registry.
314313
315-
**Cảnh báo**: Action này sẽ xóa tất cả rooms đang được register.
314+
**Warning**: This action will delete all currently registered rooms.
316315
"""
317316
try:
318317
registry = get_room_registry()

Architect_MultiClient_Server/orchestrator_service/api/stream_message_api.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class PushMessageRequest(BaseModel):
1818

1919
@router.post("/push_message")
2020
async def push_message_api(req: PushMessageRequest):
21-
# Kiểm tra có client nào đang listen không
21+
# check room has active connections
2222
if not manager.has_active_connections(req.room_name):
2323
logger.warning(f"No active connections for room {req.room_name}, message may be lost")
2424

@@ -34,25 +34,26 @@ async def push_message_api(req: PushMessageRequest):
3434

3535
async def event_generator(room_name: str, connection_id: str):
3636
"""
37-
Generator SSE với:
38-
- Timeout để detect client disconnect
39-
- Proper cleanup khi connection đóng
40-
- Heartbeat để giữ connection alive
37+
Generator SSE events for a specific room and connection.
38+
Features:
39+
- Timeout to detect client disconnect
40+
- Proper cleanup when connection closes
41+
- Heartbeat to keep connection alive
4142
"""
4243
logger.info(f"[SSE] Connection started: {connection_id} for room: {room_name}")
4344
q = manager.get_queue(room_name)
4445
loop = asyncio.get_event_loop()
4546

46-
# Gửi event đầu tiên để confirm connection
47+
# Send initial event to confirm connection
4748
yield f"event: connected\ndata: {connection_id}\n\n"
4849

49-
heartbeat_interval = 15 # Gửi heartbeat mỗi 15 giây
50+
heartbeat_interval = 15 # Send heartbeat every 15 seconds
5051
last_heartbeat = asyncio.get_event_loop().time()
5152

5253
try:
5354
while True:
5455
try:
55-
# Sử dụng timeout để có thể check connection và gửi heartbeat
56+
# Use timeout to check connection and send heartbeat
5657
text = await asyncio.wait_for(
5758
loop.run_in_executor(None, lambda: q.get(timeout=1.0)),
5859
timeout=2.0
@@ -61,7 +62,7 @@ async def event_generator(room_name: str, connection_id: str):
6162
yield f"data: {text}\n\n"
6263

6364
except (asyncio.TimeoutError, queue.Empty):
64-
# Không có message mới - kiểm tra có cần gửi heartbeat không
65+
# No new message - check if heartbeat needs to be sent
6566
current_time = asyncio.get_event_loop().time()
6667
if current_time - last_heartbeat >= heartbeat_interval:
6768
yield f"event: heartbeat\ndata: ping\n\n"
@@ -76,7 +77,7 @@ async def event_generator(room_name: str, connection_id: str):
7677
except GeneratorExit:
7778
logger.info(f"[SSE] Generator exit: {connection_id}")
7879
finally:
79-
# Cleanup khi connection đóng
80+
# Cleanup when connection closes
8081
manager.unregister_connection(room_name, connection_id)
8182
logger.info(f"[SSE] Connection closed and unregistered: {connection_id}")
8283

Architect_MultiClient_Server/orchestrator_service/api/stream_message_manager.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ def __init__(self):
2121
self._connection_counter = 0
2222

2323
def get_queue(self, room_id: str) -> queue.Queue:
24-
"""Lấy queue cho room, tạo mới nếu chưa có"""
24+
"""Get queue for room, and create if not exists"""
2525
with self._lock:
2626
if room_id not in self.queues:
2727
self.queues[room_id] = queue.Queue()
2828
return self.queues[room_id]
2929

3030
def register_connection(self, room_id: str) -> str:
31-
"""Đăng ký một connection mới, trả về connection_id"""
31+
"""Register a new connection, return connection_id"""
3232
with self._lock:
3333
self._connection_counter += 1
3434
connection_id = f"{room_id}_{self._connection_counter}_{int(time.time())}"
@@ -40,16 +40,16 @@ def register_connection(self, room_id: str) -> str:
4040
return connection_id
4141

4242
def unregister_connection(self, room_id: str, connection_id: str):
43-
"""Hủy đăng ký connection khi client disconnect"""
43+
"""Unregister connection when client disconnects"""
4444
with self._lock:
4545
if room_id in self.connections:
4646
self.connections[room_id].discard(connection_id)
4747

48-
# Nếu không còn connection nào, xóa queue để tránh memory leak
48+
# If no more connections, delete queue to avoid memory leak
4949
if not self.connections[room_id]:
5050
del self.connections[room_id]
5151
if room_id in self.queues:
52-
# Clear queue trước khi xóa
52+
# Clear queue before deleting
5353
q = self.queues[room_id]
5454
while not q.empty():
5555
try:
@@ -59,12 +59,12 @@ def unregister_connection(self, room_id: str, connection_id: str):
5959
del self.queues[room_id]
6060

6161
def has_active_connections(self, room_id: str) -> bool:
62-
"""Kiểm tra room có connection active không"""
62+
"""Check if room has active connections"""
6363
with self._lock:
6464
return room_id in self.connections and len(self.connections[room_id]) > 0
6565

6666
def get_connection_count(self, room_id: str) -> int:
67-
"""Lấy số connection active của room"""
67+
"""Get number of active connections for room"""
6868
with self._lock:
6969
return len(self.connections.get(room_id, set()))
7070

Architect_MultiClient_Server/orchestrator_service/controller/webhook_handler.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ async def _handle_room_finished(self, event: Dict) -> WebhookResponse:
126126
room_name = event.get("room", {}).get("name", "unknown")
127127
logger.info(f" Room finished: {room_name}")
128128

129-
# Get start_session_time from registry (ISO string)
130-
start_session_time = self.room_registry.get_room_start_time(room_name)
131-
132-
await self.transcription_service.final_room(room_name, start_session_time)
133129
return WebhookResponse(received=True, action="room_finished_logged")
134130

135131
async def _handle_egress_ended(self, event: Dict) -> WebhookResponse:
@@ -165,7 +161,7 @@ def _build_egress_info(self, egress: Dict, file_data: Dict,
165161
"""Build EgressInfo object from event data"""
166162
egress_data = {
167163
"egressId": egress.get("egressId", "unknown"),
168-
"room": {"name": egress.get("roomName", ""), "start_session_time": parsed.get("timestamp_iso")},
164+
"room": {"name": egress.get("roomName", ""), "start_session_time": parsed.get("timestamp")},
169165
"participant": {"identity": parsed.get("identity", "unknown")},
170166
"track": {
171167
"id": egress.get("track", {}).get("trackId", "unknown"),

Architect_MultiClient_Server/orchestrator_service/services/egress_service.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,8 @@ def _build_filepath(self, room_name: str, identity: str, source: str,
2929
ext = "ogg" if track_type == "AUDIO" else "webm"
3030

3131
# Parse ISO string và format lại thành YYYYmmdd_HHMMSS
32-
try:
33-
dt = datetime.fromisoformat(room_start_time)
34-
timestamp = dt.strftime("%Y%m%d_%H%M%S")
35-
except (ValueError, AttributeError):
36-
# Fallback nếu parse thất bại
37-
logger.warning(f"Failed to parse room_start_time: {room_start_time}, using current time")
38-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
3932

40-
return f"{room_name}/{identity}-{source}-{track_type.lower()}-{timestamp}.{ext}"
33+
return f"{room_name}/{identity}-{source}-{track_type.lower()}-{room_start_time}.{ext}"
4134

4235
def _get_s3_upload(self) -> api.S3Upload:
4336
if self._s3_upload is None:

Architect_MultiClient_Server/orchestrator_service/services/transcription_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ async def enqueue(self, egress_info: Dict) -> bool:
4747
logger.error(f"✗ Error sending to queue: {e}")
4848
return False
4949

50-
async def final_room(self, room_name: str, start_session_time: str = None) -> bool:
50+
async def final_room(self, room_name: str, start_session_time: str ) -> bool:
5151
"""
5252
Notify transcription service to finalize room
5353
5454
Args:
5555
room_name: Name of the room to finalize
56-
start_session_time: Optional start session time
56+
start_session_time: start session time
5757
5858
Returns:
5959
True if successful, False if failed

Architect_MultiClient_Server/orchestrator_service/utils/filepath_parser.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,4 @@ def parse(cls, filepath: str) -> Dict[str, str]:
3232

3333
result = match.groupdict()
3434

35-
# Convert timestamp from "YYYYmmdd_HHMMSS" to ISO format "YYYY-MM-DDTHH:MM:SS"
36-
try:
37-
timestamp_str = result.get("timestamp", "")
38-
dt = datetime.strptime(timestamp_str, "%Y%m%d_%H%M%S")
39-
result["timestamp_iso"] = dt.isoformat()
40-
except (ValueError, AttributeError):
41-
result["timestamp_iso"] = None
42-
4335
return result

Architect_MultiClient_Server/stt_service/controller/transcription_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
class RoomInfo(BaseModel):
2727
name: str
28-
start_session_time: Optional[str]
28+
start_session_time: str
2929

3030
class ParticipantInfo(BaseModel):
3131
identity: str

Architect_MultiClient_Server/stt_service/service/mongodb_service.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,12 @@ async def get_room_by_name(self, room_name: str) -> Optional[Dict]:
345345
return None
346346
return await self.rooms_collection.find_one({"room_name": room_name})
347347

348+
async def get_room_session_by_name(self, room_name: str, start_session_time: str) -> Optional[Dict]:
349+
"""Get room by name"""
350+
if not self.connected:
351+
return None
352+
return await self.rooms_collection.find_one({"room_name": room_name, "start_session_time": start_session_time})
353+
348354
async def update_room_status(
349355
self,
350356
room_ref_id: ObjectId,
@@ -370,18 +376,18 @@ async def update_room_status(
370376
logger.error(f"Failed to update room status: {e}")
371377
return False
372378

373-
async def final_room_status(self, room_name: str, start_session_time: Optional[str]) -> bool:
379+
async def final_room_status(self, room_name: str, start_session_time: str) -> bool:
374380

375381
if not self.connected:
376382
return False
377383

378384
try:
379-
room = await self.get_room_by_name(room_name)
385+
room = await self.get_room_session_by_name(room_name, start_session_time)
380386
if not room:
381387
logger.error(f"Room not found: {room_name}")
382388
return False
383389

384-
# Chỉ update nếu chưa finalized
390+
# only update when status not finalized
385391
if room["status"] in ["final_room", "completed"]:
386392
logger.warning(f"Room already finalized: {room_name}")
387393
return True
@@ -394,11 +400,6 @@ async def final_room_status(self, room_name: str, start_session_time: Optional[s
394400
}
395401
}
396402

397-
# Chỉ update start_session_time nếu được cung cấp VÀ room chưa có
398-
if start_session_time and not room.get("start_session_time"):
399-
update_doc["$set"]["start_session_time"] = start_session_time
400-
logger.info(f"Setting start_session_time for room {room_name}: {start_session_time}")
401-
402403
await self.rooms_collection.update_one(
403404
{"_id": room["_id"]},
404405
update_doc

0 commit comments

Comments
 (0)