Commit Graph
91 Commits
Author SHA1 Message Date
arys fe8598e09f fix: TTS ack-bank served stale voice after voice-config changes; add website/English pronunciation rules
RuntimeConfiguredTTSProvider (the live 'dynamic' TTS provider, resolves
voice from DB-backed config) never overrode cache_fingerprint(), so it
fell back to the base class's empty string. PrebakedAckBank keys its
on-disk cache on that fingerprint, so short filler phrases like
'Секунду' kept serving audio baked with the previous ElevenLabs voice
even after a voice change, while full LLM replies (cached inside the
resolved provider itself, keyed on its own voice id) already used the
new voice — explaining why callers heard two different voices in the
same call. Fix: delegate cache_fingerprint() to the resolved provider.

Also extend the voice delivery_hint so the model transliterates website
addresses and English words/abbreviations into spoken Cyrillic instead
of leaving raw Latin text for the TTS engine to mangle (egov.kz was
coming out as 'эговкз').
2026-08-30 11:39:14 +05:00
didar 9e4c47eddd feat: enhance AudioSocketMediaRuntime to skip filler acks for closing intents and throttle repeated filler acks
deploy / deploy (push) Successful in 30s
2026-08-30 11:30:55 +05:00
arys d876a67b3a fix: instruct voice AI to spell out phone numbers and dates in natural spoken Russian
TTS reads raw text digit-by-digit with no number/date normalization layer.
Short numbers like 1414 were read as a single 4-digit number (tysyacha
chetyresta chetyrnadtsat) instead of a spoken code, and dates like '25
chisla' were read in the wrong grammatical case (dvadtsat pyat chislo
instead of dvadtsat pyatogo chisla). Extend the voice delivery_hint with
explicit spell-out rules so the model itself produces already-correct
spoken-form text.
2026-08-30 11:21:42 +05:00
Hermes Agent 92095ff5d6 fix: release reserved L2 agent when AMI redirect fails during escalation
deploy / deploy (push) Successful in 32s
Found during production smoke test: if the AMI Redirect call in
create_escalation() raises (channel gone, AMI hiccup), the agent stays
RESERVED forever with no owning call — orphaned out of the pool until
someone fixes it by hand. Now releases the agent and marks the
escalation failed before re-raising as a 502.
2026-08-29 14:01:40 +05:00
arystanbek 16ce9659db Merge pull request 'feat: L1->L2 agent pool and routing engine for voice escalation' (#4) from feature/l1-l2-routing-engine into main
deploy / deploy (push) Successful in 32s
2026-08-29 08:31:40 +00:00
Hermes Agent 1ad4e1ec6e feat: surface L2 agent pool and escalations on supervisor screen
- routing-service: GET /escalations (list recent escalation attempts)
- supervisor UI: new panel showing the real agent pool (status, level,
  tenant, skills, calls handled) fed by GET /agents, with a form to add
  operators to the pool
- supervisor UI: new live escalation feed (AI->L2 handoffs, status,
  assigned operator), auto-refreshed every 5s alongside live calls
2026-08-29 13:28:14 +05:00
didar a651b9c086 fix: add name correction action in _voice_downstream_name_update function
deploy / deploy (push) Successful in 30s
2026-08-29 01:53:58 +05:00
didar 78007dae38 fix: improve name matching logic in _voice_reply_with_name function
deploy / deploy (push) Successful in 31s
2026-08-29 01:32:08 +05:00
didar d2a9df36d1 feat: add cache_fingerprint method to TTSProvider and its subclasses for voice/model configuration
deploy / deploy (push) Successful in 30s
2026-08-29 01:23:23 +05:00
didar 09bcf7457c feat: add AI_VOICE_AI_TIMEOUT_SECONDS for configurable voice timeout
deploy / deploy (push) Successful in 30s
2026-08-29 00:50:08 +05:00
Hermes Agent 2243f305b8 feat: L1->L2 agent pool and routing engine for voice escalation
Replaces the hardcoded single-extension redirect for AI->human call
escalation with a real Agent Pool + Routing Engine:

- agents/escalations/routing_rules tables (migration 0031), asterisk_call_links
  gains tenant_id/current_level/required_skills_json/priority.
- services/routing_service/engine.py: level/tenant/skill filtered agent
  selection with atomic (CAS) reservation, no double-booking.
- routing-service: /agents CRUD + /internal/routing/reserve-agent and
  /internal/routing/release-agent.
- asterisk-bridge-service: voice_ai.request_handoff now uses the Routing
  Engine automatically for any queue_code configured in
  ASTERISK_QUEUE_LEVEL_MAP_JSON (all other queue_codes keep the existing
  static ASTERISK_TRANSFER_TARGET_MAP_JSON behavior unchanged); new
  POST /asterisk/live-calls/{call_id}/escalations entrypoint; agent is
  released back to AVAILABLE and the escalation closed when the call ends.

Targets the Tele2 Kazgaz DID +77476456048 (from-tele2-kazgaz context) as the
first queue wired to real L2 routing instead of AI-only.

Known gap (documented in docs/architecture/l1-l2-routing-engine.md):
automatic no-answer retry-to-next-agent needs a small, separately reviewed
dialplan change and is left for a follow-up MR rather than guessed at blind.

Tests: services/routing_service/engine.py covered by
tests/test_routing_engine.py (selection filtering, atomic reservation,
release); existing test_asterisk_bridge_service.py and
test_routing_service_pg_counter.py suites still pass unmodified.
2026-08-28 16:22:32 +05:00
didar b474c35608 feat: enhance acknowledgment responses for Kazakh and Russian languages
deploy / deploy (push) Successful in 33s
2026-08-28 15:12:19 +05:00
didar f9793dc5f4 feat: enable emotive acknowledgments and update ElevenLabs TTS model ID for enhanced audio responses
deploy / deploy (push) Successful in 30s
2026-08-25 02:00:26 +05:00
didar 2ea6e6f4bb feat: update AI voice settings for improved responsiveness and pacing
deploy / deploy (push) Successful in 31s
2026-08-25 01:42:10 +05:00
didar 6ccdaf9167 feat: update ElevenLabs TTS model ID and add prebuffering for improved audio streaming
deploy / deploy (push) Successful in 31s
2026-08-25 01:20:52 +05:00
didar c399296165 feat: implement no-speech reprompt functionality with configurable parameters
deploy / deploy (push) Successful in 33s
2026-08-24 14:17:00 +05:00
didar c590528694 feat: add debug audio dump functionality for ElevenLabs ASR provider
deploy / deploy (push) Successful in 30s
2026-08-23 13:38:56 +05:00
didar 9a09a39849 feat: enhance ElevenLabs ASR provider to accumulate autonomous VAD commits
deploy / deploy (push) Successful in 31s
2026-08-23 13:20:31 +05:00
didar 1a91dc4489 feat: add tenant ID handling for sales voice and telegram sync requests
deploy / deploy (push) Successful in 30s
2026-08-23 12:55:25 +05:00
didar 8dd3e238f2 feat: enhance low signal transcript handling with finalization checks and logging
deploy / deploy (push) Successful in 31s
2026-08-23 12:40:59 +05:00
didarandClaude Sonnet 5 c5dd87ee32 feat: make ai_operator_settings config code the source of truth
Add sync_ai_operator_config_from_code() which overwrites the DB-cached
ai_operator_settings row from ai_operator_default_config() on startup
of ai_orchestrator_service and ai_voice_runtime_service. Greeting and
system prompt changes now go through git + deploy instead of manual
psql/API edits to prod. Also adds a root README pointing to existing docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 21:00:34 +05:00
didar 13ef87eb2c Fix voice greeting test marker to hit the actually-served default
deploy / deploy (push) Canceled after 25h17m2s
The previous marker was added to operator_persona.py's inline fallback,
which only fires when config is None. Every real call path loads a
populated AIOperatorConfig via load_effective_ai_operator_config(),
so ai_operator_default_config() in services/shared/ai_operator_config.py
is the default that's actually served.
2026-08-20 22:53:27 +05:00
didar c5e11fde3d Add test marker to voice greeting to verify Gitea CI/CD deploy
deploy / deploy (push) Canceled after 25h22m40s
2026-08-20 22:47:34 +05:00
didar 4e1039ab58 Merge pull request 'Play a filler ack during slow voice decisions and cache KB search rows' (#2) from voice-latency-llm-tts-improvements into main
deploy / deploy (push) Canceled after 27h22m42s
Reviewed-on: #2
2026-08-20 09:47:45 +00:00
Didar KozhikovandClaude Sonnet 5 49489f89b5 Play a filler ack during slow voice decisions and cache KB search rows
Two independent latency fixes for the voice-assistant reply pipeline,
both scoped to the parts of the flow that run regardless of whether
voice_v2 is enabled for a queue:

1. media_runtime._process_utterance: the v1/fallback turn path (used by
   any queue not covered by AI_VOICE_V2_QUEUE_CODES) silently awaited the
   full LLM decision with no audio playing at all, unlike the v2 path
   which already has a decision-timeout ack. Give v1 the same behavior:
   wait up to 600ms (_v1_ack_wait_seconds) for the decision, and if it's
   still not ready, play a short "Секунду." filler via the existing
   _emit_early_ack before the real reply, instead of leaving the caller
   in silence for the full LLM+TTS round trip. Reuses the same ack
   selection/playback code path v2 already exercises, so no new failure
   modes - just an added timeout branch mirroring the existing v2 one.

2. ai_orchestrator_service._kb_search: every voice/chat turn re-ran a
   full-table scan of kb_articles (all columns, including body text) and
   rescored every row in Python, even though the KB rarely changes
   mid-conversation. Added an in-process cache keyed by language, gated
   on a cheap content fingerprint (row count + max id + max updated_at +
   summed title/body/tags length, all computed server-side without
   transferring the text columns). A fingerprint mismatch always
   triggers a fresh fetch, so this can never serve stale results after
   an insert/update/delete - unlike a naive TTL cache, which would have
   been be wrong the moment a test (or a real KB edit) changed the table
   within the cache window.

   Note the first fingerprint design (count + max id + max updated_at
   only) was insufficient: utc_now_iso() truncates to whole seconds and
   SQLite reuses primary keys after a full-table delete, so two
   different row sets written in the same wall-clock second could share
   a fingerprint. Caught this via a real test failure
   (test_ai_whatsapp_relaxed_kb_search_answers_phrase_query breaking
   only when run after test_ai_orchestrator_service.py in the same
   process) before it could reach production; the summed content-length
   term closes the gap.

Added test_media_runtime_plays_filler_ack_when_v1_decision_is_slow
(asserts greeting -> ack -> reply delivery order when process_turn is
slow) and verified the KB cache against the full
test_ai_orchestrator_service.py + test_ai_whatsapp_orchestrator_service.py
suite plus a wider kb/orchestrator/whatsapp/telegram/voice-filtered run:
only the same pre-existing, already-documented failures remain (unrelated
sales_service test-isolation ordering, one known persona-prompt
assertion) - no new failures from either change.

Streaming the LLM decision itself (start speaking reply_text before the
full structured JSON response finishes generating) was scoped but
deliberately deferred: it needs incremental JSON parsing on top of SSE
streaming to detect when just the reply_text field is complete, shared
across both voice and text-channel decision paths - a separate,
higher-risk change that deserves its own PR and testing pass rather than
being bundled here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 21:54:02 +05:00
Didar KozhikovandClaude Sonnet 5 a9be976845 Implement real streaming TTS for ElevenLabs to cut voice-assistant reply latency
The AudioSocket/media_runtime playback pipeline already supports chunked
TTS streaming (voice_v2_streaming_tts), but every provider inherited the
base TTSProvider.synthesize_chunks(), which just called the blocking
synthesize() and yielded the entire finished audio as a single "chunk" -
so the caller waited for full-utterance synthesis before any playback
could start regardless of the flag.

ElevenLabs is the production default (AI_VOICE_TTS_PROVIDER=elevenlabs in
deployment/docker-compose.server.yml), so give it a real implementation
that POSTs to the /stream endpoint and yields audio as network chunks
arrive, instead of waiting for the whole response body. Chunk boundaries
are re-aligned to whole 16-bit PCM samples so a split sample at a network
read boundary can't corrupt playback. The full synthesized audio is still
written to the on-disk cache afterwards so repeat phrases stay fast and
skip the vendor call entirely, matching the existing synthesize() cache
behavior.

Added test_elevenlabs_tts_provider_streams_chunks_and_caches_full_audio to
cover: chunk splitting mid-sample gets re-aligned, all yielded chunks are
sample-aligned, the full audio round-trips through the cache, and a
cached synthesis is replayed without invoking the streaming endpoint
again.

Verified via tests/test_ai_voice_tts_provider.py (9/9 pass) and a wider
voice/tts-filtered run across the suite: the only failures present are
the same pre-existing, already-documented ones (sales_service test
cross-file isolation ordering, one known persona-prompt assertion) -
identical set to before this change, no new failures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 21:40:35 +05:00
Didar KozhikovandClaude Sonnet 5 cdabe61bc2 Add architecture longread and remove dead code found during review
- docs: longread.md — deep architecture/flow review of the whole
  platform (services, event bus reality vs docs, AI/ML stack honesty
  check, tech debt inventory)
- ai_orchestrator_service/voice.py: drop _voice_decision_legacy
  (unreferenced) and the shadowed first _voice_decision definition
  (silently overwritten by the real one, dead code)
- ui/analyst/app.js: drop duplicate dead definitions of
  loadSavedAnalyticsViews/saveAnalyticsView/deleteAnalyticsView and
  the first loadAnalyticsTrend implementation, all shadowed by later
  declarations in the same file; kept the intentional AI-mode
  drilldown wrapper layer (openAnalyticsDrilldown/exportAnalyticsDrilldownCsv/etc.)
  since that duplication is deliberate delegation, not dead code
- ui/operator/vendor/sip-0.21.2.min.js: remove byte-identical orphaned
  duplicate of ui/operator/sip-0.21.2.min.js (unreferenced anywhere)

Verified via full pytest run: identical set of 97 pre-existing
failures before and after (sales_* test-isolation ordering issue and
one known persona-prompt test), no new regressions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 22:25:27 +05:00
Magzhan Zhumabayev 5236ee74ac Autostage Telegram sales leads 2026-05-12 19:41:04 +05:00
Magzhan Zhumabayev fbc77ba8a7 Expand sales leads and customer contracts 2026-05-12 19:09:31 +05:00
Magzhan Zhumabayev ce3ba270aa Add configurable AI operator persona 2026-05-11 17:37:26 +05:00
Magzhan Zhumabayev 67a0255c0d Fix sales post-repair acceptance gaps 2026-05-11 17:33:08 +05:00
Magzhan Zhumabayev 64862dd49a Add sales contract repair routes 2026-05-11 16:57:30 +05:00
Magzhan Zhumabayev ebba6c3f82 Allow tenant header for bearer-authenticated requests 2026-05-11 16:02:16 +05:00
Admin 8577d97356 Implement sales CRM workflow foundation 2026-05-10 20:54:01 +05:00
Magzhan Zhumabayev 866d96e560 Implement sales tenant pipeline events 2026-05-10 18:24:06 +05:00
arys f53af1f7dc sales fix 2026-05-09 09:16:06 +05:00
Yera All aa64b6b94c Revert "feat(voice): start early replies before final asr"
This reverts commit a44a1b97a1.
2026-04-19 01:30:26 +05:00
Yera All 224973d840 Revert "fix(voice): race slow streaming asr finalize"
This reverts commit 77df41bce0.
2026-04-19 01:30:26 +05:00
Yera All 93002ab86a Revert "fix(voice): stop repeating failed clarifications"
This reverts commit 294d30a797.
2026-04-19 01:30:26 +05:00
Yera All 294d30a797 fix(voice): stop repeating failed clarifications 2026-04-19 01:19:54 +05:00
Yera All 77df41bce0 fix(voice): race slow streaming asr finalize 2026-04-19 01:06:03 +05:00
Yera All a44a1b97a1 feat(voice): start early replies before final asr 2026-04-19 00:46:16 +05:00
Yera All 9cd553bf92 feat(voice): start replies from stable streaming partials 2026-04-19 00:13:58 +05:00
Yera All 770ba4e925 feat(voice): add elevenlabs realtime streaming asr 2026-04-18 20:15:44 +05:00
Yera All 34b807e460 feat(voice): add elevenlabs stt and transcript truth fixes 2026-04-18 18:25:07 +05:00
Yera All ac2269b6a4 fix(voice): correct yandex asr endpoints 2026-04-17 13:39:26 +05:00
Yera All feb0ce01e2 fix(asterisk): update telecom route and recording fallback 2026-04-17 12:16:51 +05:00
Yera All 4adaf192fb fix(voice): avoid silence on ASR runtime failures 2026-04-17 01:28:06 +05:00
Yera All f66b289371 feat(voice): add Yandex streaming ASR 2026-04-17 01:06:45 +05:00
Yera All 5d40cfafd8 feat(voice): switch voice ASR to Yandex SpeechKit 2026-04-17 00:53:20 +05:00