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:
patrick-motsch 2026-02-15 01:26:27 +01:00
parent 294f1a001c
commit 5d987e72fe
4 changed files with 32 additions and 15 deletions

View file

@ -35,6 +35,7 @@ class BrowserBotConnector:
botName: str,
instanceId: str,
gatewayWsUrl: str,
language: str = "de-DE",
) -> Dict[str, Any]:
"""
Send join command to the Browser Bot service.
@ -49,6 +50,7 @@ class BrowserBotConnector:
Args:
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})
language: BCP-47 language code for captions spoken language (e.g. "de-DE", "en-US")
Returns:
Dict with 'success' bool and optional 'error' string.
@ -66,6 +68,7 @@ class BrowserBotConnector:
"botName": botName,
"instanceId": instanceId,
"gatewayWsUrl": gatewayWsUrl, # Full WebSocket URL for bot to connect back
"language": language, # Spoken language for Teams captions
}
try:

View file

@ -10,7 +10,7 @@ from typing import Dict, Any, List, Optional
from modules.datamodels.datamodelUam import User
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 .datamodelTeamsbot import (
@ -98,13 +98,13 @@ class TeamsbotObjects:
def createSession(self, sessionData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new session."""
sessionData["creationDate"] = getUtcTimestamp()
sessionData["lastModified"] = getUtcTimestamp()
sessionData["creationDate"] = getIsoTimestamp()
sessionData["lastModified"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotSession, sessionData)
def updateSession(self, sessionId: str, updates: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""Update session fields."""
updates["lastModified"] = getUtcTimestamp()
updates["lastModified"] = getIsoTimestamp()
return self.db.recordModify(TeamsbotSession, sessionId, updates)
def deleteSession(self, sessionId: str) -> bool:
@ -143,7 +143,7 @@ class TeamsbotObjects:
def createTranscript(self, transcriptData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new transcript segment."""
transcriptData["creationDate"] = getUtcTimestamp()
transcriptData["creationDate"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotTranscript, transcriptData)
def _deleteTranscriptsBySession(self, sessionId: str) -> int:
@ -170,7 +170,7 @@ class TeamsbotObjects:
def createBotResponse(self, responseData: Dict[str, Any]) -> Dict[str, Any]:
"""Create a new bot response record."""
responseData["creationDate"] = getUtcTimestamp()
responseData["creationDate"] = getIsoTimestamp()
return self.db.recordCreate(TeamsbotBotResponse, responseData)
def _deleteResponsesBySession(self, sessionId: str) -> int:

View file

@ -16,7 +16,7 @@ from fastapi import WebSocket
from modules.datamodels.datamodelUam import User
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 (
TeamsbotSessionStatus,
@ -57,7 +57,7 @@ async def _emitSessionEvent(sessionId: str, eventType: str, data: Any):
"""Emit an event to the session's SSE stream."""
eventQueue = _sessionEvents.get(sessionId)
if eventQueue:
await eventQueue.put({"type": eventType, "data": data, "timestamp": getUtcTimestamp()})
await eventQueue.put({"type": eventType, "data": data, "timestamp": getIsoTimestamp()})
class TeamsbotService:
@ -126,6 +126,7 @@ class TeamsbotService:
botName=session.get("botName", self.config.botName),
instanceId=self.instanceId,
gatewayWsUrl=fullGatewayWsUrl,
language=self.config.language,
)
if result.get("success"):
@ -164,7 +165,7 @@ class TeamsbotService:
interface.updateSession(sessionId, {
"status": TeamsbotSessionStatus.ENDED.value,
"endedAt": getUtcTimestamp(),
"endedAt": getIsoTimestamp(),
})
await _emitSessionEvent(sessionId, "statusChange", {"status": "ended"})
@ -178,7 +179,7 @@ class TeamsbotService:
interface.updateSession(sessionId, {
"status": TeamsbotSessionStatus.ERROR.value,
"errorMessage": str(e),
"endedAt": getUtcTimestamp(),
"endedAt": getIsoTimestamp(),
})
# Cleanup event queue
@ -269,9 +270,9 @@ class TeamsbotService:
if errorMessage:
updates["errorMessage"] = errorMessage
if dbStatus == TeamsbotSessionStatus.ACTIVE.value:
updates["startedAt"] = getUtcTimestamp()
updates["startedAt"] = getIsoTimestamp()
elif dbStatus in [TeamsbotSessionStatus.ENDED.value, TeamsbotSessionStatus.ERROR.value]:
updates["endedAt"] = getUtcTimestamp()
updates["endedAt"] = getIsoTimestamp()
interface.updateSession(sessionId, updates)
await _emitSessionEvent(sessionId, "statusChange", {"status": status, "errorMessage": errorMessage})
@ -301,7 +302,7 @@ class TeamsbotService:
sessionId=sessionId,
speaker=speaker,
text=text,
timestamp=str(getUtcTimestamp()),
timestamp=getIsoTimestamp(),
confidence=1.0, # Captions don't have confidence scores
language=self.config.language,
isFinal=isFinal,
@ -326,7 +327,7 @@ class TeamsbotService:
"speaker": speaker,
"text": text,
"confidence": 1.0,
"timestamp": getUtcTimestamp(),
"timestamp": getIsoTimestamp(),
})
# Update session transcript count
@ -499,7 +500,7 @@ class TeamsbotService:
modelName=response.modelName,
processingTime=response.processingTime,
priceCHF=response.priceCHF,
timestamp=str(getUtcTimestamp()),
timestamp=getIsoTimestamp(),
).model_dump()
createdResponse = interface.createBotResponse(botResponseData)

View file

@ -31,6 +31,19 @@ def getUtcTimestamp() -> float:
"""
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:
"""
Create a new expiration timestamp from seconds until expiration.