О проекте
Команда: DeTeam
Кейс HackAlem.ai: Коммуникации — Halyk Bank · кейс 2
Стадия: Доведённый продукт
Задача кейса: «Voice Router»: голосовой AI, маршрутизирующий звонки в контакт-центре страховой компании
Суть
Голосовой робот контакт-центра страховой: понимает русскую, казахскую и смешанную речь, выбирает один из 40 сценариев через LLM и выполняет его на моках. Работает в вебе и по настоящему телефону через Asterisk. Есть панель супервизора с трассировкой и редактор каталога.
Как устроено
Аудио из браузера (WS, PCM 16 kHz) или из Asterisk (ARI + AudioSocket) проходит через Silero VAD с семантическим концом реплики. Затем STT gpt-transcribe (два параллельных запроса) и fast path без LLM для типовых ответов. Если fast path не сработал, LLM-роутер gpt-5.6-luna со строгой JSON-схемой по каталогу из 40 сценариев определяет язык, намерения, уверенность и слоты. Запросы хеджируются, есть резервная модель; при уверенности 0.45–0.75 спрашивается второе мнение gpt-6-sol. Исполнитель ведёт фреймы и стек тем и требует явного «да» для необратимых действий. Ответ строится по шаблону или gpt-6-luna и озвучивается gpt-4o-mini-tts, около 200 фраз синтезированы заранее.
Ключевые решения
- LLM routing со строгой JSON-схемой
- Silero VAD
- hedged requests / fallback model
- second opinion при средней уверенности
- стек тем (topic stack)
- deterministic fast path
- pre-synthesized TTS cache
- эволюция каталога: патч + регрессия + откат
- Asterisk ARI + AudioSocket
- нормализация слотов (ИИН, телефоны словами, даты)
Метрики
Dev-набор: 99–100% верный основной сценарий, все 40 ходов 10 эталонных диалогов; медиана задержки хода 1.0 с против 2.1 с без оптимизаций
Особенности
Ошибки маршрутизации исправляются правкой человекочитаемого каталога сценариев, а не переобучением. LLM-патчер меняет только описания и примеры, фильтрует хардкод по сходству >85% и прогоняет регрессию на 104 dev-репликах до применения. Задержка снижена за счёт хеджирования запросов, спекулятивного STT, раннего колбэка с заранее озвученным подтверждением и настоящей телефонии с записью звонков в MinIO.
Стек
Python · FastAPI · React · TypeScript · Vite · Docker · Asterisk · MinIO · WebSocket
Модели
gpt-5.6-luna · gpt-6-sol · gpt-6-luna · gpt-transcribe · gpt-4o-mini-tts
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-5351b533-deteam.
README
README.mdVoice Router — голосовой AI‑агент Saqta Insurance (DeTeam)
HackAlem AI, трек Halyk Bank, кейс 2 «Voice Router».
Голосовой робот контакт‑центра, у которого сценарий разговора выбирает LLM, а не классификатор намерений. Он понимает живую русскую, казахскую и смешанную речь, переключается между темами, не теряя контекста, и выполняет 40 сценариев страховой компании на данных. Агент работает в вебе и по настоящему телефону через Asterisk. Супервизор видит трассировку каждого решения. Ошибки маршрутизации исправляются правкой каталога за полминуты с регрессионной проверкой, без переобучения модели.
Содержание
- Запуск
- Что умеет
- Как устроен один ход
- Сильные стороны
- Главная фича: эволюция каталога
- Технологический стек
- Измеренные результаты
- Как проверить самому
- Структура репозитория
- Ограничения
- Документы
Запуск
Бэкенд одной командой (нужны Docker и ключ OpenAI):
echo "OPENAI_API_KEY=sk-..." > backend/.env
docker compose up --build # http://127.0.0.1:8000
Полный стенд с фронтом, телефонией и записью звонков. Пошаговая инструкция с чек‑листом проверки лежит в docs/RUNBOOK.md:
make venv && npm ci --prefix frontend # один раз
docker compose -f infra/asterisk/compose.yaml up -d --build # Asterisk + MinIO + загрузчик записей
make run # бэкенд :8000, AudioSocket :9092
make web # фронт http://127.0.0.1:5173
Звонок: софтфон на аккаунт 1001, номер 757507 звонит от имени клиента Сергея, 9999 включает эхо‑тест.
Что умеет
Обязательные требования кейса:
| Требование | Как сделано |
|---|---|
| Голос в вебе | Микрофон браузера, WebSocket /ws/voice, серверный VAD, ответ голосом. Текст как запасной канал |
| Выбор сценария на LLM | gpt-5.6-luna со строгой JSON‑схемой по каталогу из 40 сценариев. Классификаторов намерений нет |
| Корректность выбора | Dev‑набор: 99–100% основного сценария, все 40 ходов десяти эталонных диалогов кита |
| Панель трассировки | После каждой реплики: транскрипт, язык, сценарии с уверенностью и причиной, альтернативы, слоты, действия, задержка по этапам |
| Русский и казахский | Включая смену языка посреди диалога и смешанную речь в одной фразе |
Опциональные пункты ТЗ, все реализованы:
| Опция | Реализация |
|---|---|
| Гибридная архитектура с измеримым выигрышем | Детерминированный fast path без LLM и шаблонные ответы с готовым аудио. Медиана задержки хода 1.0 с против 2.1 с без этих приёмов |
| Удержание контекста и возврат к теме | Стек тем: прерванная тема откладывается, после новой бот предлагает вернуться. Слоты не теряются |
| Переспросить вместо угадывания | Уверенность 0.45–0.75 уходит на второе мнение gpt-6-sol, при сомнении бот задаёт уточняющий вопрос с двумя вариантами |
| Передача оператору с контекстом | По просьбе, после трёх неясных реплик подряд и по правилам сценария. Оператор получает резюме, слоты и хвост диалога |
| Извлечение параметров из речи | Телефоны словами на русском и казахском, ИИН, госномера, даты «ертең», «три дня назад», суммы «двадцать лямов» |
| Потоковая обработка | Стриминг роутера с ранним решением, стриминг ответа по предложениям, стриминг синтеза |
| Эмоция и подстройка тона | Роутер оценивает эмоцию, ответ начинается с эмпатии при жалобе или ДТП |
| Панель супервизора со статистикой ошибок | Сессии, трассировки, распределение уверенности, спорные ходы, запись звонка с плеером |
| Редактирование каталога без разработчиков | Эволюция каталога: патч, регрессия, применение, откат. Подробнее ниже |
Плюс сверх ТЗ: настоящая телефония через Asterisk, полная запись каждого звонка в MinIO, экран оператора, генератор каверзных реплик для поиска ошибок роутера.
Как устроен один ход
Браузер (WS, PCM 16 kHz) Софтфон → Asterisk → ARI + External Media (AudioSocket, 8 kHz)
└──────────────────────┬──────────────────────┘
▼
1. VAD Silero + конец реплики семантический (≈0.3 с тишины и законченная фраза) / по тишине 0.8 с / push‑to‑talk
▼ распознавание стартует спекулятивно уже на паузе
2. STT gpt-transcribe два запроса параллельно (казахский + автоопределение), берётся первый непустой
▼
3. Понимание a) fast path без LLM: ожидаемый слот по шаблону, «да/нет», «оператор», «алло, слышите?», прощание
b) LLM-роутер gpt-5.6-luna: 2 одинаковых стрима, побеждает первый; запасной стрим на gpt-6-luna,
если первого токена нет 1.8 с; каталог 40 сценариев в кэше промпта; строгая JSON-схема
c) ранний колбэк: первый scenario_id в потоке → сразу играем подтверждение из кэша аудио
d) уверенность 0.45–0.75 → второе мнение gpt-6-sol
▼ решение: run / continue / confirm / cancel / clarify / handoff / goodbye
4. Исполнитель сценариев фреймы, стек тем, план на каждый из 40 сценариев: идентификация → слоты по одному →
действия над моками → для необратимых: озвучить данные → явное «да» → выполнить
▼ план ответа: факты + вопрос
5. Ответ шаблон без LLM (вопросы слотов, уточнения, прощание) или gpt-6-luna стримом строго по фактам;
числа словами, email маскируется, один вопрос за ход
▼
6. TTS gpt-4o-mini-tts, голос marin ≈200 фраз заранее синтезированы; следующее предложение синтезируется, пока звучит текущее
▼
PCM обратно в браузер / кадрами по 20 мс в Asterisk; события и trace → фронт, /ws/supervisor, журнал сессии
Сильные стороны
Маршрутизация, которая понимает, а не сопоставляет
- Роутер видит весь каталог: описания, правила разграничения
not_this_ifи примеры. Кроме того, он видит состояние диалога: активный сценарий, ожидаемый слот, стек тем, последнюю реплику бота. - В одном вызове роутер определяет язык, режет реплику на несколько намерений, выбирает сценарии с уверенностью и причиной, извлекает и нормализует слоты. Он же распознаёт продолжение сценария и ответ на подтверждение.
- Срочные сценарии идут первыми: ДТП прямо сейчас, болезнь за границей, мошенничество.
- Честная уверенность и второе мнение: модель не угадывает, в зоне сомнения бот переспрашивает.
Задержка, которую можно проверить секундомером
- Мгновенное подтверждение понимания. Как только роутер выдал первый сценарий, звучит первая фраза opening этого сценария из кита, аудио для неё готово заранее. Правила кейса сами требуют начинать с понимания.
- Хеджирование: два одинаковых запроса к роутеру и запасной на другую модель при всплеске задержки у API.
- Спекулятивное распознавание, семантический конец реплики, прогретые соединения к OpenAI.
- Задержка замеряется честно: от последнего озвученного кадра клиента до отправки первого кадра ответа. Все этапы видны в трассировке.
Безопасность и качество ответа
- Необратимые действия выполняются только после явного «да»: оформление, продление, изменение, расторжение полиса, заявления, записи. Перед этим бот зачитывает данные.
- Ответы строятся только из фактов плана: результаты моков и разделы базы знаний. Цены считаются по формулам кита, а не придумываются моделью.
- Персональные данные при повторном озвучивании маскируются. На вопрос «вы робот?» бот отвечает честно.
Живой диалог
- Бот переспрашивает, если значение не подходит, и называет допустимые варианты с ценами.
- Держит контекст: «а если дом, а не квартира?» он пересчитывает по уже известной сумме.
- Спокойно реагирует на грубость и остаётся в теме. Бота можно перебить голосом.
- Опознаёт клиента по номеру звонящего или по телефону, продиктованному словами на русском или казахском.
Инженерия
- Контракты раньше кода: JSON‑схемы событий, трассировки, вывода роутера и эволюции, OpenAPI. По ним параллельно работали фронт, телефония и бэкенд.
- Проверки на каждом уровне: dev‑набор, 10 эталонных диалогов, 80 сценарных диалогов, сквозные голосовой и телефонный тесты, валидация событий по схемам.
Главная фича: эволюция каталога
Знание о маршрутизации живёт не в весах модели, а в человекочитаемом каталоге сценариев. Поэтому ошибку роутера можно исправить за полминуты с регрессионной проверкой. У классификатора намерений тот же цикл занимает дни: разметка, переобучение, релиз.
ошибка в трассировке ─► кейс ─► патч каталога (gpt-6-sol) ─► валидатор ─► регрессия до/после ─► супервизор применяет ─► новая версия
▲ │
└───────────────────────────── откат в один клик, заморозка на время зачётного прогона ◄────────────────────────────┘
- Кейсы. Кнопка «Неверно» у любого хода или генератор каверзных реплик на стыках похожих сценариев. Генератор сохраняет только те реплики, на которых роутер реально ошибся.
- Патчер. Меняет только
description,not_this_ifи примеры: не больше 4 операций и 2 сценариев. Идентификаторы, слоты и действия не трогает, поэтому оценка по исходным 40 сценариям остаётся честной. - Защита от хардкода. Пример, похожий на текст кейса или dev‑реплику больше чем на 85%, отклоняется, и патчер переписывает его обобщённо.
- Регрессия. Роутер прогоняется по 104 dev‑репликам и всем кейсам на старой и новой версии каталога. Спорные пункты перепроверяются пятью прогонами. Показываются починенные и сломанные кейсы.
- Версии. Каждое применение даёт новую версию с хэшем. Промпт роутера пересобирается и прогревается за 2 секунды.
Технологический стек
| Слой | Технологии |
|---|---|
| Модели, только OpenAI | роутер gpt-5.6-luna; запасной роутер gpt-6-luna; второе мнение и патчер gpt-6-sol; ответы gpt-6-luna; STT gpt-transcribe; TTS gpt-4o-mini-tts-2025-12-15, голос marin. Везде priority tier и кэш промпта |
| Бэкенд | Python 3.12, FastAPI, asyncio, OpenAI SDK (Responses API, structured outputs strict, streaming), onnxruntime + Silero VAD, numpy, num2words |
| Голос в вебе | WebSocket, PCM16 16 kHz на вход и 24 kHz на выход, AudioWorklet в браузере, перебивание |
| Телефония | Asterisk 20 (Docker), ARI‑приложение voice-ai, External Media с инкапсуляцией AudioSocket, PJSIP, перевод на оператора через ARI; запасной режим через диалплан |
| Записи звонков | MixMonitor → MinIO (S3) → загрузчик → callback в бэкенд → плеер в карточке сессии |
| Фронтенд | React, Vite, TypeScript: экраны звонка, супервизора, сессии, эволюции, оператора и каталога |
| Инфраструктура | Docker Compose, Makefile, JSON Schema + OpenAPI контракты, журналы сессий в JSONL |
| Данные | Стартовый кит кейса: сценарии, слоты, действия, база знаний, мок‑бэкенд, эталонные диалоги, dev‑набор |
Почему такая схема, а не speech‑to‑speech модель. В gpt-realtime и gpt-live выбор сценария происходит внутри модели. Тогда нельзя показать слой решения, замерить этапы и проверить маршрутизацию на dev‑наборе. Каскад STT → роутер → ответ → TTS даёт прозрачную трассировку и дословный контроль над тем, что произносит бот.
Выбор модели роутера по замеру на нашем промпте (8 реплик на модель, один запрос без хеджа):
| Модель | Первый токен, медиана / p90 | Ответ целиком, медиана / p90 | Верно |
|---|---|---|---|
| gpt-5.6-luna | 1.03 / 1.33 с | 1.63 / 1.91 с | 8/8 |
| gpt-6-luna | 0.95 / 1.29 с | 1.65 / 2.82 с | 8/8 |
| gpt-6-sol | 1.05 / 1.33 с | 1.86 / 2.16 с | 8/8 |
| gpt-5.6-sol | 1.05 / 1.23 с | 1.89 / 2.29 с | 8/8 |
gpt-5.6-luna даёт лучший ответ целиком и стабильный хвост, на полном dev‑наборе у неё выше точность. gpt-6-luna стоит запасной на случай всплеска задержки.
Измеренные результаты
| Метрика | Значение | Как воспроизвести |
|---|---|---|
| Dev‑набор, основной сценарий | 0.99–1.00 | make bench |
| Dev‑набор, полное совпадение и намерения в мульти‑запросах | 0.99–1.00 | make bench |
| Эталонные диалоги кита | 40 из 40 ходов | make replay |
| Сценарные диалоги: 40 основных и 40 краевых | 80 из 80 | make test |
| Задержка хода в текстовом режиме, медиана | ≈1.0 с | make replay |
| Голос в вебе, от конца речи до первого звука | 1.5–2.5 с | make probe-voice |
| Телефон, ход с роутером / fast path | 1.9–2.6 с / ≈1 с | звонок или make probe-phone |
| Патч эволюции от запроса до готовности | 27–38 с | вкладка «Эволюция» |
| События WS по JSON‑схемам контракта | 0 ошибок | make validate, голосовой тест |
Как проверить самому
make validate # фикстуры и схемы контрактов
make replay # 10 эталонных диалогов кита, печатает каждый ход с решением и задержкой
make test # 80 сценарных диалогов, без сервера
make bench # dev-набор + официальный data/evaluate.py
make probe-voice # сквозной голосовой тест без микрофона (сервер запущен)
make probe-phone # сквозной тест телефонии: притворяемся Asterisk по AudioSocket
Реплики для проверки голосом лежат в docs/RUNBOOK.md, раздел 4. Там же чек‑лист телефонии и таблица «если что‑то не так».
Структура репозитория
| Путь | Что внутри |
|---|---|
| backend/ | голосовой агент: VAD, STT, роутер, исполнитель, ответы, TTS, телефония, эволюция, тесты |
| frontend/ | веб: звонок, супервизор, сессии, эволюция, оператор, каталог |
| infra/asterisk/ | Asterisk в Docker (PJSIP, ARI), MinIO и загрузчик полных записей звонков |
| contracts/ | JSON‑схемы событий, трассировки, роутера и эволюции, OpenAPI, конфиги Asterisk |
| fixtures/ | шаблонные сессии и данные супервизора для фронта |
| tools/ | мок‑сервер, проверка фикстур против схем |
| data/ | стартовый кит кейса и evaluate.py |
| docs/ | runbook, концепт, контракты фронта и телефонии, условия кейса |
Ограничения
- Задержка зависит от OpenAI. Медиана укладывается в 3 с, но при всплесках API отдельные ходы бывают дольше. Запасной запрос режет большую часть таких хвостов.
- Бенчи параллельно со звонком нельзя. Все компоненты работают на одном ключе, бенч съедает лимит токенов в минуту и замедляет живой разговор.
- Моки вместо реальных систем. Все действия работают поверх
mock_backend.jsonи базы знаний из кита. - Сценарий SC16 не проходит до конца. В данных кита ни у одного клиента нет полиса от несчастного случая, поэтому бот переводит такого клиента на оператора.
- Роутер недетерминирован. Между прогонами dev‑набора разница около одного процентного пункта.
- Качество казахского синтеза и распознавания ниже русского. Для казахского распознавание идёт двумя вариантами параллельно.
Документы
| Кому | Куда смотреть |
|---|---|
| Запуск и проверка | docs/RUNBOOK.md |
| Архитектура | ARCHITECTURE.md, ROUTER.md, EVOLUTION.md |
| Бэкенд | backend/README.md |
| Фронтенд | docs/frontend/README.md, DIALOG_EXAMPLES.md, frontend/README.md |
| Телефония | docs/telephony/README.md, infra/asterisk/README.md |
| Контракты | contracts/ |
| Условия кейса | docs/case/README.ru.md, ТЗ |
Авторы коммитов
История коммитов
- 2026-09-23 17:57Merge branch 'frontend'Bekzat Uteulin
- 2026-09-23 17:57README: case requirements mapping, strengths, full stack, model speed comparison, measured results, limitationsBekzat Uteulin
- 2026-09-23 17:57Correct transparent logo and align interface colorstsukilun
- 2026-09-23 17:50Slot hints: prices only for the client's property typeBekzat Uteulin
- 2026-09-23 17:49Merge branch 'frontend'Bekzat Uteulin
- 2026-09-23 17:49Repeated slot asks explain allowed values with prices; unclear/rude reply mid-scenario stays on topicBekzat Uteulin
- 2026-09-23 17:48Refresh NotSoFar frontend visual designtsukilun
- 2026-09-23 17:42Softphone 1001 is an unknown caller (personas via 7575NN); client name once per call; no repeated lead-in on revisited topicsBekzat Uteulin
Все коммиты (36)
- 2026-09-23 17:36Dialog quality from live phone test: hearing check, unclear policy, buy offer, lead-in, STT and router tailsBekzat Uteulin
- 2026-09-23 17:18Runbook for setup and verification; UTF-8 file IO for Windows; make web reuses node_modulesBekzat Uteulin
- 2026-09-23 17:04Merge branch 'frontend': /ws/voice call, evolution, operator, recording playerBekzat Uteulin
- 2026-09-23 17:04infra: extra_hosts for recording-uploader so the backend callback resolves on LinuxBekzat Uteulin
- 2026-09-23 17:02Improve supervisor UX and add recording playertsukilun
- 2026-09-23 16:57Telephony: full call recording callback from MinIO uploaderBekzat Uteulin
- 2026-09-23 16:55Merge branch 'diDocker': ARI smoke stack, call recording upload to MinIOBekzat Uteulin
- 2026-09-23 16:53Add full call recording upload and backend callback contractSolidiguana
- 2026-09-23 16:42Merge remote-tracking branch 'origin/main'Bekzat Uteulin
- 2026-09-23 16:41Backend: voice, latency, evolution, telephony (AudioSocket + ARI), scenario QA, Docker, docsBekzat Uteulin
- 2026-09-23 16:36Prepare local ARI AudioSocket telephony smoke stackSolidiguana
- 2026-09-23 15:55Telephony contract: switch Asterisk integration to ARI + External Media (AudioSocket)Bekzat Uteulin
- 2026-09-23 15:45Add Silero VAD onnx model (MIT)Bekzat Uteulin
- 2026-09-23 15:45Supervisor: keep four headline numbers, move engineering metrics downtsukilun
- 2026-09-23 15:44Backend: voice pipeline (Silero VAD, gpt-transcribe, cached TTS player), hub, FastAPI app, feature module stubsBekzat Uteulin
- 2026-09-23 15:38Backend: mocks for 31 actions, scenario plans, dialog session, responder, kit dialog replayBekzat Uteulin
- 2026-09-23 15:34Frontend: call over /ws/voice, evolution and operator screenstsukilun
- 2026-09-23 15:26Backend: kit loader, versioned catalog, LLM router with second opinion, dev benchBekzat Uteulin
- 2026-09-23 15:22Frontend: switch supervisor to team contracts and mock servertsukilun
- 2026-09-23 15:07Restructure repository layoutBekzat Uteulin
- 2026-09-23 15:06Merge branch 'diDocker' (Asterisk)Bekzat Uteulin
- 2026-09-23 15:05Merge branch 'frontend'Bekzat Uteulin
- 2026-09-23 15:05Add concept, contracts, fixtures and mock server for Voice RouterBekzat Uteulin
- 2026-09-23 14:42Add Docker Asterisk setup with AMI and ARISolidiguana
- 2026-09-23 14:21Fix blank call page: effect returned scrollIntoView promisetsukilun
- 2026-09-23 14:17Supervisor: journal of all calls and call detail pagetsukilun
- 2026-09-23 13:53Frontend skeleton: call simulator, trace panel, supervisor, scenario catalogtsukilun
- 2026-09-23 13:16Set up frontend: Vite + React + TypeScripttsukilun
Структура репозитория (181 файлов)
.dockerignore .gitignore .nvmrc Makefile README.md backend/.dockerignore backend/Dockerfile backend/Dockerfile.dockerignore backend/README.md backend/app/__init__.py backend/app/ari.py backend/app/assets/scenario_phrases.json backend/app/assets/silero_vad.onnx backend/app/audio.py backend/app/bench.py backend/app/call.py backend/app/catalog.py backend/app/config.py backend/app/dialog.py backend/app/evolution.py backend/app/hub.py backend/app/kit.py backend/app/main.py backend/app/mocks.py backend/app/normalize.py backend/app/plans.py backend/app/policy.py backend/app/replay.py backend/app/responder.py backend/app/router.py backend/app/session.py backend/app/speech.py backend/app/telephony.py backend/requirements.txt backend/tests/__init__.py backend/tests/scenario_dialogs.py backend/tools/phone_probe.py backend/tools/voice_probe.py contracts/README.md contracts/asterisk/ari.conf contracts/asterisk/extensions.conf contracts/asterisk/http.conf contracts/asterisk/manager.conf contracts/asterisk/pjsip.conf contracts/dialog-state.schema.json contracts/evolution.schema.json contracts/openapi.yaml contracts/router-output.schema.json contracts/trace.schema.json contracts/ws-events.schema.json data/actions.json data/dev_utterances.json data/dialogs_sample.json data/evaluate.py data/knowledge_base.json data/mock_backend.json data/scenarios.json data/slots.json docker-compose.yml docs/RUNBOOK.md docs/case/README.kz.md docs/case/README.md docs/case/README.ru.md docs/case/TZ_Voice_Router.pdf docs/concept/ARCHITECTURE.md docs/concept/EVOLUTION.md docs/concept/ROUTER.md docs/frontend/DIALOG_EXAMPLES.md docs/frontend/README.md docs/telephony/README.md fixtures/README.md fixtures/catalog/actions.min.json fixtures/catalog/clients.min.json fixtures/catalog/scenarios.min.json fixtures/sessions/index.json fixtures/sessions/phone-p01.jsonl fixtures/sessions/web-d03.jsonl fixtures/sessions/web-d04.jsonl fixtures/supervisor/bench_runs.json fixtures/supervisor/cases.json fixtures/supervisor/catalog_versions.json fixtures/supervisor/patches.json fixtures/supervisor/sessions.json fixtures/supervisor/stats.json frontend/.env.example frontend/.gitignore frontend/.oxlintrc.json frontend/README.md frontend/index.html frontend/package-lock.json frontend/package.json frontend/public/favicon.svg frontend/public/fonts/OFL.txt frontend/public/fonts/Onest-Variable.woff2 frontend/public/notsofar-mark.png frontend/public/pcm-capture-worklet.js frontend/src/App.tsx frontend/src/api/http.ts frontend/src/api/index.ts frontend/src/api/types.ts frontend/src/assets/icons/arrow-right.svg frontend/src/assets/icons/check.svg frontend/src/assets/icons/chevron-down.svg frontend/src/assets/icons/close.svg frontend/src/assets/icons/lock.svg frontend/src/assets/icons/microphone-off.svg frontend/src/assets/icons/microphone.svg frontend/src/assets/icons/phone-off.svg frontend/src/assets/icons/phone.svg frontend/src/assets/icons/search.svg frontend/src/assets/icons/sound.svg frontend/src/assets/icons/status-dot.svg frontend/src/assets/icons/stop.svg frontend/src/assets/icons/unlock.svg frontend/src/audio/capture.ts frontend/src/audio/player.ts frontend/src/components/Icon.tsx frontend/src/components/call/DialogStatePanel.tsx frontend/src/components/call/LiveTranscript.tsx frontend/src/components/evolution/BenchPanel.tsx frontend/src/components/evolution/CasesPanel.tsx frontend/src/components/evolution/PatchCard.tsx frontend/src/components/evolution/PatchProgress.tsx frontend/src/components/evolution/VersionsPanel.tsx frontend/src/components/supervisor/CaseForm.tsx frontend/src/components/supervisor/FlaggedQueue.tsx frontend/src/components/supervisor/SessionInspector.css frontend/src/components/supervisor/SessionInspector.tsx frontend/src/components/supervisor/SessionJournal.tsx frontend/src/components/supervisor/SessionOutcome.tsx frontend/src/components/supervisor/SessionRecordingPlayer.css frontend/src/components/supervisor/SessionRecordingPlayer.tsx frontend/src/components/supervisor/SessionTimeline.css frontend/src/components/supervisor/SessionTimeline.tsx frontend/src/components/supervisor/StatsPanel.tsx frontend/src/components/trace/LatencyWaterfall.tsx frontend/src/components/trace/TracePanel.tsx frontend/src/components/ui.tsx frontend/src/hooks/useResource.ts frontend/src/hooks/useVoiceSession.ts frontend/src/hooks/voiceState.ts frontend/src/index.css frontend/src/lib/catalog-context.ts frontend/src/lib/catalog.tsx frontend/src/lib/format.ts frontend/src/lib/live-context.ts frontend/src/lib/live.tsx frontend/src/main.tsx frontend/src/pages/CallPage.tsx frontend/src/pages/EvolutionPage.tsx frontend/src/pages/OperatorPage.tsx frontend/src/pages/ScenariosPage.tsx frontend/src/pages/SessionPage.tsx frontend/src/pages/SupervisorPage.tsx frontend/tsconfig.app.json frontend/tsconfig.json frontend/tsconfig.node.json frontend/vite.config.ts infra/asterisk/.dockerignore infra/asterisk/.env.example infra/asterisk/.gitignore infra/asterisk/Dockerfile infra/asterisk/Dockerfile.smoke infra/asterisk/README.md infra/asterisk/VOICE_ROUTER.md infra/asterisk/compose.smoke.yaml infra/asterisk/compose.yaml infra/asterisk/config/pjsip_trunk.conf.example infra/asterisk/docker/entrypoint.py infra/asterisk/docker/recording_finished.py infra/asterisk/recording/Dockerfile infra/asterisk/recording/requirements.txt infra/asterisk/recording/uploader.py infra/asterisk/scripts/ari_smoke.py infra/asterisk/scripts/requirements-smoke.txt infra/asterisk/scripts/setup.ps1 infra/asterisk/scripts/test-ami.ps1 tools/README.md tools/mock-server/requirements.txt tools/mock-server/server.py tools/validate_contracts.py
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.