Add voice name flow controls and analytics

This commit is contained in:
Yera All
2026-04-05 03:26:18 +05:00
parent d15fcf7129
commit d959c2b2f0
31 changed files with 6410 additions and 152 deletions
+140 -3
View File
@@ -861,6 +861,140 @@ def test_call_started_uses_api_fallback_after_create_failure(monkeypatch, tmp_pa
session.close()
def test_voice_ai_summary_exposes_customer_name_state_for_operator(tmp_path):
now = utc_now_iso()
call_id = f"call_voice_summary_{tmp_path.name}"
session_id = f"avs_voice_summary_{tmp_path.name}"
ai_session_id = f"ais_voice_summary_{tmp_path.name}"
interaction_id = f"int_voice_summary_{tmp_path.name}"
session = get_session()
try:
session.add(
AsteriskCallLinkRow(
call_id=call_id,
linked_id=f"linked_voice_summary_{tmp_path.name}",
queue_code="voice_support",
queue_id="que_voice_support",
interaction_id=interaction_id,
caller_number="+77010001122",
caller_name="Summary Caller",
status="active",
telephony_status="connected",
claimed_by_user=None,
claimed_at=None,
operator_extension=None,
channel_name="PJSIP/1001-000099",
started_at=now,
connected_at=now,
ended_at=None,
updated_at=now,
voice_session_id=session_id,
ai_session_id=ai_session_id,
ai_state="handoff_required",
ai_handoff_reason="customer requested operator",
ai_last_model_at=now,
voice_start_language="ru",
customer_name_status="name_followup_required",
customer_name_value="Айдос",
customer_name_source="voice_start",
customer_name_resolved_at=now,
)
)
session.add(
AISessionRow(
session_id=ai_session_id,
channel="voice",
call_id=call_id,
thread_id=None,
interaction_id=interaction_id,
customer_id="cus_voice_summary",
agent_profile="voice_support",
language="ru",
status="handoff_required",
summary_text="AI collected context and asked for operator handoff.",
last_user_message_id=None,
last_ai_message_id=None,
handoff_reason="customer requested operator",
created_at=now,
updated_at=now,
closed_at=None,
)
)
session.add(
VoiceAISessionRow(
session_id=session_id,
call_id=call_id,
linked_id=f"linked_voice_summary_{tmp_path.name}",
interaction_id=interaction_id,
customer_id="cus_voice_summary",
queue_id="que_voice_support",
ai_session_id=ai_session_id,
agent_profile="voice_support",
language="ru",
asr_provider="openai",
tts_provider="yandex",
status="handoff_requested",
handoff_reason="customer requested operator",
handoff_target_queue_id="que_voice_support",
disclosure_played_at=now,
last_user_utterance_at=now,
last_ai_reply_at=now,
started_at=now,
updated_at=now,
ended_at=None,
voice_start_language="ru",
customer_name_status="name_followup_required",
customer_name_value="Айдос",
customer_name_source="voice_start",
customer_name_resolved_at=now,
)
)
session.add_all(
[
VoiceTranscriptSegmentRow(
segment_id=f"{session_id}_seg_1",
session_id=session_id,
call_id=call_id,
interaction_id=interaction_id,
sequence_no=1,
speaker="caller",
source_type="voice_asr",
text="Соедините с оператором",
confidence=0.96,
is_final=True,
barge_in_interrupted=False,
payload_json="{}",
created_at=now,
),
VoiceTranscriptSegmentRow(
segment_id=f"{session_id}_seg_2",
session_id=session_id,
call_id=call_id,
interaction_id=interaction_id,
sequence_no=2,
speaker="assistant",
source_type="voice_policy",
text="Сейчас переведу вас на оператора.",
confidence=None,
is_final=True,
barge_in_interrupted=False,
payload_json="{}",
created_at=now,
),
]
)
session.commit()
finally:
session.close()
summary = bridge_module._voice_ai_summary_for_call(call_id)
assert summary is not None
assert summary.customer_name_status == "name_followup_required"
assert summary.customer_name_value == "Айдос"
assert summary.customer_name_source == "voice_start"
assert summary.voice_start_language == "ru"
def test_call_started_re_raises_original_create_error_when_all_fallbacks_fail(monkeypatch, tmp_path):
monkeypatch.setenv("ASTERISK_QUEUE_MAP_JSON", '{"lab":"que_lab"}')
@@ -3153,9 +3287,12 @@ def test_voice_ai_handoff_revives_false_ended_call_when_channel_is_resolvable(mo
assert result.call_id == call_id
assert ami_calls
assert ami_calls[0][0] == "Redirect"
assert ami_calls[0][1]["Channel"] == "PJSIP/1001-0000077"
assert ami_calls[0][1]["Exten"] == "2001"
assert [call[0] for call in ami_calls[:4]] == ["Setvar", "Setvar", "Setvar", "Setvar"]
assert ami_calls[0][1]["Variable"] == "MVPCC_START_LANGUAGE"
assert ami_calls[3][1]["Variable"] == "MVPCC_CUSTOMER_NAME_SOURCE"
assert ami_calls[-1][0] == "Redirect"
assert ami_calls[-1][1]["Channel"] == "PJSIP/1001-0000077"
assert ami_calls[-1][1]["Exten"] == "2001"
payload = {
"status_label": "AI передал звонок оператору",
"customer_request_text": "Соедините меня с оператором",