feat: refine voice studio attention workflow
This commit is contained in:
@@ -388,6 +388,12 @@ def _session_to_summary(
|
||||
story_patch=latest_turn.story_patch or {},
|
||||
)
|
||||
latest_safety_state = _resolve_turn_safety_state(latest_turn.story_patch or {})
|
||||
attention_reasons = _build_session_attention_reasons(
|
||||
latest_requires_confirmation=latest_confirmation_state["requires_confirmation"],
|
||||
latest_safety_flags=latest_safety_state["safety_flags"],
|
||||
last_turn_status=latest_turn.status if latest_turn else None,
|
||||
last_error=session.last_error,
|
||||
)
|
||||
|
||||
return VoiceSessionSummaryResponse(
|
||||
id=session.id,
|
||||
@@ -413,12 +419,55 @@ def _session_to_summary(
|
||||
session_audio_exists(latest_turn.assistant_audio_path) if latest_turn else False
|
||||
),
|
||||
last_turn_status=latest_turn.status if latest_turn else None,
|
||||
attention_reasons=attention_reasons,
|
||||
transcription_mode_hint=settings.voice_transcription_mode,
|
||||
can_continue=_session_can_continue(session),
|
||||
can_finalize=_can_finalize_with_latest_turn(session, latest_turn),
|
||||
last_error=session.last_error,
|
||||
created_at=session.created_at,
|
||||
updated_at=session.updated_at,
|
||||
)
|
||||
|
||||
|
||||
def _build_session_attention_reasons(
|
||||
*,
|
||||
latest_requires_confirmation: bool,
|
||||
latest_safety_flags: list[str] | None,
|
||||
last_turn_status: str | None,
|
||||
last_error: str | None,
|
||||
) -> list[str]:
|
||||
reasons: list[str] = []
|
||||
if latest_requires_confirmation:
|
||||
reasons.append("pending_confirmation")
|
||||
if latest_safety_flags:
|
||||
reasons.append("safety_intervention")
|
||||
if last_turn_status == "failed" or last_error:
|
||||
reasons.append("failed_turn")
|
||||
return reasons
|
||||
|
||||
|
||||
def _session_summary_needs_attention(summary: VoiceSessionSummaryResponse) -> bool:
|
||||
return bool(summary.attention_reasons)
|
||||
|
||||
|
||||
def _session_summary_matches_attention_reason(
|
||||
summary: VoiceSessionSummaryResponse,
|
||||
attention_reason: str | None,
|
||||
) -> bool:
|
||||
if attention_reason is None:
|
||||
return True
|
||||
return attention_reason in summary.attention_reasons
|
||||
|
||||
|
||||
async def _build_session_summary(
|
||||
db: AsyncSession,
|
||||
session: VoiceSession,
|
||||
) -> VoiceSessionSummaryResponse:
|
||||
latest_turn = await _get_latest_turn(db, session_id=session.id)
|
||||
return _session_to_summary(
|
||||
session,
|
||||
latest_turn=latest_turn,
|
||||
total_turns=session.current_turn_index,
|
||||
)
|
||||
|
||||
|
||||
@@ -1082,6 +1131,8 @@ async def list_voice_sessions_service(
|
||||
*,
|
||||
limit: int | None = None,
|
||||
active_only: bool = False,
|
||||
needs_attention: bool = False,
|
||||
attention_reason: str | None = None,
|
||||
active_first: bool = False,
|
||||
) -> list[VoiceSessionSummaryResponse]:
|
||||
resolved_limit = limit or settings.voice_session_default_list_limit
|
||||
@@ -1102,19 +1153,20 @@ async def list_voice_sessions_service(
|
||||
)
|
||||
else:
|
||||
query = query.order_by(desc(VoiceSession.updated_at), desc(VoiceSession.created_at))
|
||||
query = query.limit(resolved_limit)
|
||||
if not needs_attention and attention_reason is None:
|
||||
query = query.limit(resolved_limit)
|
||||
|
||||
sessions = (await db.execute(query)).scalars().all()
|
||||
summaries: list[VoiceSessionSummaryResponse] = []
|
||||
for session in sessions:
|
||||
latest_turn = await _get_latest_turn(db, session_id=session.id)
|
||||
summaries.append(
|
||||
_session_to_summary(
|
||||
session,
|
||||
latest_turn=latest_turn,
|
||||
total_turns=session.current_turn_index,
|
||||
)
|
||||
)
|
||||
summary = await _build_session_summary(db, session)
|
||||
if needs_attention and not _session_summary_needs_attention(summary):
|
||||
continue
|
||||
if not _session_summary_matches_attention_reason(summary, attention_reason):
|
||||
continue
|
||||
summaries.append(summary)
|
||||
if (needs_attention or attention_reason is not None) and len(summaries) >= resolved_limit:
|
||||
break
|
||||
return summaries
|
||||
|
||||
|
||||
@@ -1134,12 +1186,7 @@ async def get_latest_active_voice_session_service(
|
||||
session = (await db.execute(query)).scalar_one_or_none()
|
||||
if session is None:
|
||||
return None
|
||||
latest_turn = await _get_latest_turn(db, session_id=session.id)
|
||||
return _session_to_summary(
|
||||
session,
|
||||
latest_turn=latest_turn,
|
||||
total_turns=session.current_turn_index,
|
||||
)
|
||||
return await _build_session_summary(db, session)
|
||||
|
||||
|
||||
async def get_voice_session_analytics_service(
|
||||
@@ -1172,8 +1219,25 @@ async def get_voice_session_analytics_service(
|
||||
sessions = (await db.execute(session_query)).scalars().all()
|
||||
turns = (await db.execute(turn_query)).scalars().all()
|
||||
events = (await db.execute(event_query)).scalars().all()
|
||||
session_summaries = [await _build_session_summary(db, session) for session in sessions]
|
||||
|
||||
total_sessions = len(sessions)
|
||||
attention_sessions = sum(
|
||||
1 for summary in session_summaries if _session_summary_needs_attention(summary)
|
||||
)
|
||||
confirmation_attention_sessions = sum(
|
||||
1
|
||||
for summary in session_summaries
|
||||
if "pending_confirmation" in summary.attention_reasons
|
||||
)
|
||||
safety_attention_sessions = sum(
|
||||
1
|
||||
for summary in session_summaries
|
||||
if "safety_intervention" in summary.attention_reasons
|
||||
)
|
||||
failed_attention_sessions = sum(
|
||||
1 for summary in session_summaries if "failed_turn" in summary.attention_reasons
|
||||
)
|
||||
active_sessions = sum(
|
||||
1 for session in sessions if session.status in CONTINUABLE_SESSION_STATUSES
|
||||
)
|
||||
@@ -1205,6 +1269,10 @@ async def get_voice_session_analytics_service(
|
||||
return VoiceSessionAnalyticsResponse(
|
||||
window_days=days,
|
||||
total_sessions=total_sessions,
|
||||
attention_sessions=attention_sessions,
|
||||
confirmation_attention_sessions=confirmation_attention_sessions,
|
||||
safety_attention_sessions=safety_attention_sessions,
|
||||
failed_attention_sessions=failed_attention_sessions,
|
||||
active_sessions=active_sessions,
|
||||
finalized_sessions=finalized_sessions,
|
||||
abandoned_sessions=abandoned_sessions,
|
||||
|
||||
Reference in New Issue
Block a user