fix(teamsbot): Fix Invalid Date + pass language to browser bot
- Add getIsoTimestamp() to timeUtils for JS-compatible ISO 8601 strings - Replace getUtcTimestamp() (epoch float) with getIsoTimestamp() for all teamsbot session/transcript/response timestamp fields (startedAt, endedAt, creationDate, lastModified, SSE event timestamps) - Pass config.language to browser bot in join request for captions spoken language Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
294f1a001c
commit
5d987e72fe
4 changed files with 32 additions and 15 deletions
|
|
@ -35,6 +35,7 @@ class BrowserBotConnector:
|
||||||
botName: str,
|
botName: str,
|
||||||
instanceId: str,
|
instanceId: str,
|
||||||
gatewayWsUrl: str,
|
gatewayWsUrl: str,
|
||||||
|
language: str = "de-DE",
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Send join command to the Browser Bot service.
|
Send join command to the Browser Bot service.
|
||||||
|
|
@ -49,6 +50,7 @@ class BrowserBotConnector:
|
||||||
Args:
|
Args:
|
||||||
gatewayWsUrl: Full WebSocket URL for the bot to connect back to
|
gatewayWsUrl: Full WebSocket URL for the bot to connect back to
|
||||||
(e.g. wss://gateway-int.poweron-center.net/api/teamsbot/{instanceId}/bot/ws/{sessionId})
|
(e.g. wss://gateway-int.poweron-center.net/api/teamsbot/{instanceId}/bot/ws/{sessionId})
|
||||||
|
language: BCP-47 language code for captions spoken language (e.g. "de-DE", "en-US")
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict with 'success' bool and optional 'error' string.
|
Dict with 'success' bool and optional 'error' string.
|
||||||
|
|
@ -66,6 +68,7 @@ class BrowserBotConnector:
|
||||||
"botName": botName,
|
"botName": botName,
|
||||||
"instanceId": instanceId,
|
"instanceId": instanceId,
|
||||||
"gatewayWsUrl": gatewayWsUrl, # Full WebSocket URL for bot to connect back
|
"gatewayWsUrl": gatewayWsUrl, # Full WebSocket URL for bot to connect back
|
||||||
|
"language": language, # Spoken language for Teams captions
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from typing import Dict, Any, List, Optional
|
||||||
|
|
||||||
from modules.datamodels.datamodelUam import User
|
from modules.datamodels.datamodelUam import User
|
||||||
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
from modules.connectors.connectorDbPostgre import DatabaseConnector
|
||||||
from modules.shared.timeUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getIsoTimestamp
|
||||||
from modules.shared.configuration import APP_CONFIG
|
from modules.shared.configuration import APP_CONFIG
|
||||||
|
|
||||||
from .datamodelTeamsbot import (
|
from .datamodelTeamsbot import (
|
||||||
|
|
@ -98,13 +98,13 @@ class TeamsbotObjects:
|
||||||
|
|
||||||
def createSession(self, sessionData: Dict[str, Any]) -> Dict[str, Any]:
|
def createSession(self, sessionData: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Create a new session."""
|
"""Create a new session."""
|
||||||
sessionData["creationDate"] = getUtcTimestamp()
|
sessionData["creationDate"] = getIsoTimestamp()
|
||||||
sessionData["lastModified"] = getUtcTimestamp()
|
sessionData["lastModified"] = getIsoTimestamp()
|
||||||
return self.db.recordCreate(TeamsbotSession, sessionData)
|
return self.db.recordCreate(TeamsbotSession, sessionData)
|
||||||
|
|
||||||
def updateSession(self, sessionId: str, updates: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
def updateSession(self, sessionId: str, updates: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||||
"""Update session fields."""
|
"""Update session fields."""
|
||||||
updates["lastModified"] = getUtcTimestamp()
|
updates["lastModified"] = getIsoTimestamp()
|
||||||
return self.db.recordModify(TeamsbotSession, sessionId, updates)
|
return self.db.recordModify(TeamsbotSession, sessionId, updates)
|
||||||
|
|
||||||
def deleteSession(self, sessionId: str) -> bool:
|
def deleteSession(self, sessionId: str) -> bool:
|
||||||
|
|
@ -143,7 +143,7 @@ class TeamsbotObjects:
|
||||||
|
|
||||||
def createTranscript(self, transcriptData: Dict[str, Any]) -> Dict[str, Any]:
|
def createTranscript(self, transcriptData: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Create a new transcript segment."""
|
"""Create a new transcript segment."""
|
||||||
transcriptData["creationDate"] = getUtcTimestamp()
|
transcriptData["creationDate"] = getIsoTimestamp()
|
||||||
return self.db.recordCreate(TeamsbotTranscript, transcriptData)
|
return self.db.recordCreate(TeamsbotTranscript, transcriptData)
|
||||||
|
|
||||||
def _deleteTranscriptsBySession(self, sessionId: str) -> int:
|
def _deleteTranscriptsBySession(self, sessionId: str) -> int:
|
||||||
|
|
@ -170,7 +170,7 @@ class TeamsbotObjects:
|
||||||
|
|
||||||
def createBotResponse(self, responseData: Dict[str, Any]) -> Dict[str, Any]:
|
def createBotResponse(self, responseData: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Create a new bot response record."""
|
"""Create a new bot response record."""
|
||||||
responseData["creationDate"] = getUtcTimestamp()
|
responseData["creationDate"] = getIsoTimestamp()
|
||||||
return self.db.recordCreate(TeamsbotBotResponse, responseData)
|
return self.db.recordCreate(TeamsbotBotResponse, responseData)
|
||||||
|
|
||||||
def _deleteResponsesBySession(self, sessionId: str) -> int:
|
def _deleteResponsesBySession(self, sessionId: str) -> int:
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from fastapi import WebSocket
|
||||||
|
|
||||||
from modules.datamodels.datamodelUam import User
|
from modules.datamodels.datamodelUam import User
|
||||||
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, PriorityEnum
|
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, PriorityEnum
|
||||||
from modules.shared.timeUtils import getUtcTimestamp
|
from modules.shared.timeUtils import getUtcTimestamp, getIsoTimestamp
|
||||||
|
|
||||||
from .datamodelTeamsbot import (
|
from .datamodelTeamsbot import (
|
||||||
TeamsbotSessionStatus,
|
TeamsbotSessionStatus,
|
||||||
|
|
@ -57,7 +57,7 @@ async def _emitSessionEvent(sessionId: str, eventType: str, data: Any):
|
||||||
"""Emit an event to the session's SSE stream."""
|
"""Emit an event to the session's SSE stream."""
|
||||||
eventQueue = _sessionEvents.get(sessionId)
|
eventQueue = _sessionEvents.get(sessionId)
|
||||||
if eventQueue:
|
if eventQueue:
|
||||||
await eventQueue.put({"type": eventType, "data": data, "timestamp": getUtcTimestamp()})
|
await eventQueue.put({"type": eventType, "data": data, "timestamp": getIsoTimestamp()})
|
||||||
|
|
||||||
|
|
||||||
class TeamsbotService:
|
class TeamsbotService:
|
||||||
|
|
@ -126,6 +126,7 @@ class TeamsbotService:
|
||||||
botName=session.get("botName", self.config.botName),
|
botName=session.get("botName", self.config.botName),
|
||||||
instanceId=self.instanceId,
|
instanceId=self.instanceId,
|
||||||
gatewayWsUrl=fullGatewayWsUrl,
|
gatewayWsUrl=fullGatewayWsUrl,
|
||||||
|
language=self.config.language,
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.get("success"):
|
if result.get("success"):
|
||||||
|
|
@ -164,7 +165,7 @@ class TeamsbotService:
|
||||||
|
|
||||||
interface.updateSession(sessionId, {
|
interface.updateSession(sessionId, {
|
||||||
"status": TeamsbotSessionStatus.ENDED.value,
|
"status": TeamsbotSessionStatus.ENDED.value,
|
||||||
"endedAt": getUtcTimestamp(),
|
"endedAt": getIsoTimestamp(),
|
||||||
})
|
})
|
||||||
await _emitSessionEvent(sessionId, "statusChange", {"status": "ended"})
|
await _emitSessionEvent(sessionId, "statusChange", {"status": "ended"})
|
||||||
|
|
||||||
|
|
@ -178,7 +179,7 @@ class TeamsbotService:
|
||||||
interface.updateSession(sessionId, {
|
interface.updateSession(sessionId, {
|
||||||
"status": TeamsbotSessionStatus.ERROR.value,
|
"status": TeamsbotSessionStatus.ERROR.value,
|
||||||
"errorMessage": str(e),
|
"errorMessage": str(e),
|
||||||
"endedAt": getUtcTimestamp(),
|
"endedAt": getIsoTimestamp(),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Cleanup event queue
|
# Cleanup event queue
|
||||||
|
|
@ -269,9 +270,9 @@ class TeamsbotService:
|
||||||
if errorMessage:
|
if errorMessage:
|
||||||
updates["errorMessage"] = errorMessage
|
updates["errorMessage"] = errorMessage
|
||||||
if dbStatus == TeamsbotSessionStatus.ACTIVE.value:
|
if dbStatus == TeamsbotSessionStatus.ACTIVE.value:
|
||||||
updates["startedAt"] = getUtcTimestamp()
|
updates["startedAt"] = getIsoTimestamp()
|
||||||
elif dbStatus in [TeamsbotSessionStatus.ENDED.value, TeamsbotSessionStatus.ERROR.value]:
|
elif dbStatus in [TeamsbotSessionStatus.ENDED.value, TeamsbotSessionStatus.ERROR.value]:
|
||||||
updates["endedAt"] = getUtcTimestamp()
|
updates["endedAt"] = getIsoTimestamp()
|
||||||
|
|
||||||
interface.updateSession(sessionId, updates)
|
interface.updateSession(sessionId, updates)
|
||||||
await _emitSessionEvent(sessionId, "statusChange", {"status": status, "errorMessage": errorMessage})
|
await _emitSessionEvent(sessionId, "statusChange", {"status": status, "errorMessage": errorMessage})
|
||||||
|
|
@ -301,7 +302,7 @@ class TeamsbotService:
|
||||||
sessionId=sessionId,
|
sessionId=sessionId,
|
||||||
speaker=speaker,
|
speaker=speaker,
|
||||||
text=text,
|
text=text,
|
||||||
timestamp=str(getUtcTimestamp()),
|
timestamp=getIsoTimestamp(),
|
||||||
confidence=1.0, # Captions don't have confidence scores
|
confidence=1.0, # Captions don't have confidence scores
|
||||||
language=self.config.language,
|
language=self.config.language,
|
||||||
isFinal=isFinal,
|
isFinal=isFinal,
|
||||||
|
|
@ -326,7 +327,7 @@ class TeamsbotService:
|
||||||
"speaker": speaker,
|
"speaker": speaker,
|
||||||
"text": text,
|
"text": text,
|
||||||
"confidence": 1.0,
|
"confidence": 1.0,
|
||||||
"timestamp": getUtcTimestamp(),
|
"timestamp": getIsoTimestamp(),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Update session transcript count
|
# Update session transcript count
|
||||||
|
|
@ -499,7 +500,7 @@ class TeamsbotService:
|
||||||
modelName=response.modelName,
|
modelName=response.modelName,
|
||||||
processingTime=response.processingTime,
|
processingTime=response.processingTime,
|
||||||
priceCHF=response.priceCHF,
|
priceCHF=response.priceCHF,
|
||||||
timestamp=str(getUtcTimestamp()),
|
timestamp=getIsoTimestamp(),
|
||||||
).model_dump()
|
).model_dump()
|
||||||
|
|
||||||
createdResponse = interface.createBotResponse(botResponseData)
|
createdResponse = interface.createBotResponse(botResponseData)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,19 @@ def getUtcTimestamp() -> float:
|
||||||
"""
|
"""
|
||||||
return time.time()
|
return time.time()
|
||||||
|
|
||||||
|
def getIsoTimestamp() -> str:
|
||||||
|
"""
|
||||||
|
Get current UTC timestamp as ISO 8601 string.
|
||||||
|
|
||||||
|
Use this for fields declared as 'ISO timestamp' strings (e.g. Pydantic str fields
|
||||||
|
that will be parsed by JavaScript's new Date()). JavaScript cannot parse epoch
|
||||||
|
floats from getUtcTimestamp() as dates.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Current UTC time in ISO 8601 format (e.g. "2026-02-15T00:08:32.070000+00:00")
|
||||||
|
"""
|
||||||
|
return datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
def createExpirationTimestamp(expiresInSeconds: int) -> float:
|
def createExpirationTimestamp(expiresInSeconds: int) -> float:
|
||||||
"""
|
"""
|
||||||
Create a new expiration timestamp from seconds until expiration.
|
Create a new expiration timestamp from seconds until expiration.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue