Перейти к содержанию

Конфигурация и интеграции

Этот раздел описывает настройки SIP Hook: переменные окружения, работу с OpenAI, MCP и MariaDB. В примерах ниже используются фрагменты кода из модулей sip_hook_app/config.py, db.py и mcp.py.

Основные настройки

Файл sip-hook/sip_hook_app/config.py при импорте вызывает load_dotenv(), чтобы прочитать .env, и проверяет обязательные переменные:

  • OPENAI_API_KEY — ключ OpenAI;
  • OPENAI_WEBHOOK_SECRET — секрет для проверки подписи webhook.

Условно базовая структура модуля конфигурации выглядит так:

load_dotenv()

OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
OPENAI_WEBHOOK_SECRET = os.environ["OPENAI_WEBHOOK_SECRET"]
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
ONLINE_TRANSCRIPTION_MODEL = os.getenv("ONLINE_TRANSCRIPTION_MODEL", "whisper-1")

Realtime и транскрипция

Параметры Realtime в текущей версии зафиксированы константами в sip-hook/sip_hook_app/config.py (не читаются из env):

  • REALTIME_MODEL = "gpt-realtime";
  • REALTIME_AUDIO_FORMAT = "pcm16";
  • REALTIME_VOICE = "marin";
  • REALTIME_VAD_TYPE = "server_vad";
  • REALTIME_SILENCE_MS = 1200.

Параметры онлайн‑транскрипции:

  • ONLINE_TRANSCRIPTION_MODEL — модель (по умолчанию whisper-1);
  • ONLINE_TRANSCRIPTION_LANGUAGE — язык (по умолчанию uk);
  • ONLINE_TRANSCRIPTION_PROMPT — подсказка для распознавания речи.

Эти значения передаются в Realtime‑модель через функции build_model_settings() и build_call_accept_payload() в модуле model_setup.py.

MCP

Параметры подключения к MCP читаются из окружения и используются в sip_hook_app/mcp.py:

  • MCP_SERVER_URL — URL MCP‑сервера (если пустой — MCP отключён);
  • MCP_AUTH_TOKEN — JWT‑токен (если включена авторизация на MCP‑сервере).

Tip

Если вы поднимаете SIP Hook локально и хотите сначала проверить базовую связность Realtime → webhook → раннер, можно оставить MCP_SERVER_URL пустым.
В этом режиме голосовой агент не сможет вызывать MCP‑инструменты, а verify_user будет идти по резервному пути через БД (если она настроена).

Остальные значения (MCP_SERVER_LABEL="prime-utils", MCP_SERVER_TIMEOUT=60.0, MCP_SERVER_MAX_RETRIES=3) сейчас заданы константами в sip_hook_app/config.py.

При подключении SIP Hook передаёт заголовки:

  • MCP-Protocol-Version: 2025-06-18;
  • Authorization: Bearer <MCP_AUTH_TOKEN> (если токен задан).

Пример подключения:

headers = {"MCP-Protocol-Version": "2025-06-18"}
if MCP_AUTH_TOKEN:
    headers["Authorization"] = f"Bearer {MCP_AUTH_TOKEN}"

async with MCPServerStreamableHttp(
    name=MCP_SERVER_LABEL,
    params={"url": MCP_SERVER_URL, "timeout": MCP_SERVER_TIMEOUT, "headers": headers},
    cache_tools_list=True,
    max_retry_attempts=MCP_SERVER_MAX_RETRIES,
) as mcp_server:
    ...

База данных и верификация

  • MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE — параметры подключения к MariaDB;
  • DEFAULT_USER_ROLE — роль, которую SIP Hook пытается проставить при автосоздании пользователя по телефону (по умолчанию "sip"; при несовместимой схеме вставка может упасть и будет использован fallback без роли);
  • VERIFY_USER_RETRIES, VERIFY_USER_RETRY_DELAY, VERIFY_USER_TOOL_NAME — настройки вызова MCP‑инструмента verify_user;
  • DIALOGUE_HISTORY_LIMIT — сколько строк истории загружать при начале звонка;
  • PHONE_DEFAULT_REGION — регион для парсинга телефонов с помощью phonenumbers (по умолчанию UA).

Warning

В sip-hook/db.py SQL‑запросы используют схему ailogist.* (например, ailogist.users, ailogist.dialogue_history) напрямую. Это означает, что база данных должна называться ailogistMYSQL_DATABASE имеет смысл выставлять в ailogist).

Уведомления /cargo-assigned (SMS через Dinstar)

Эндпоинт POST /cargo-assigned использует модуль sip_hook_app/notifications.py: он генерирует текст SMS через OpenAI и отправляет SMS через HTTP API шлюза Dinstar UC2000.

  • DINSTAR_GATEWAY_URL, DINSTAR_GATEWAY_USER, DINSTAR_GATEWAY_PASS — доступ к шлюзу Dinstar (обязательны для успешной отправки SMS);
  • CARGO_ASSIGNED_OPENAI_MODEL — модель Responses API для генерации текста SMS (по умолчанию gpt-4.1).

Ограничения, которые важно знать (это прямо проверяется в коде):

  • сервис не делает очередь и не хранит «идемпотентность»: один HTTP‑запрос → одна попытка отправить SMS;
  • текст SMS должен быть коротким (до 300 символов) и содержать контакт в Telegram (@nika_logist), иначе генерация считается неуспешной и сервис вернёт 502.

HTTP‑сервер

При запуске через sip-hook/sip_hook.py порт Flask задаётся переменной окружения PORT (по умолчанию 8000).

Файл sip-hook/db.py формирует настройки и открывает соединение через pymysql:

def _get_mysql_settings() -> dict[str, Any] | None:
    host = os.getenv("MYSQL_HOST")
    ...
    return {"host": host, "port": port, "user": user, ...}

Работа с MariaDB

В db.py определены основные функции работы с БД.

  • verify_user_record(phone_digits, auto_create=True) — ищет пользователя по нормализованному телефону в ailogist.users и при auto_create=True создаёт запись, если пользователь не найден. При успешной вставке повторно читает запись и возвращает полный словарь строки. Ошибки подключения и выполнения запросов логируются и пробрасываются наверх.
  • fetch_dialogue_history(user_id, limit, channel=None) — возвращает список словарей с полями user_id, channel, direction, content_type, content, created_at из ailogist.dialogue_history, отсортированный по created_at DESC. При указании channel добавляется фильтр по каналу; при ошибках возвращает пустой список и пишет ошибку в лог.
  • insert_dialogue_entry(...) — вставляет новую строку в ailogist.dialogue_history для канала sip (или другого, если передан) и возвращает id созданной записи. Поддерживает явную установку created_at, если нужно сохранить оригинальное время события.
  • update_dialogue_entry(entry_id, content=None, content_type=None, direction=None) — обновляет существующую строку диалога по id, изменяя переданные поля. Если ни одно поле не задано, функция не выполняет запрос. При ошибках выбрасывает исключение, которое логируется.

Подключение к MCP‑серверу

Файл sip-hook/sip_hook_app/mcp.py использует OpenAI Agents SDK (MCPServerStreamableHttp) для подключения к MCP.

  • connect_mcp_server() — асинхронный контекстный менеджер: если MCP_SERVER_URL не задан, возвращает None и логирует предупреждение; иначе создаёт и возвращает клиент MCP.
  • _extract_tool_payload(result) — пытается извлечь полезные данные из ответа инструмента: сначала из structuredContent, при необходимости — из текстового поля, парся JSON.
  • call_verify_user_tool_async(phone_digits) — вызывает MCP‑инструмент VERIFY_USER_TOOL_NAME (по умолчанию verify_user), обрабатывает ошибки и возвращает распарсенный словарь с данными пользователя.
  • verify_user_for_call(call_id, phone_digits) — делает несколько попыток вызова MCP‑инструмента, при неудаче логирует ошибки и выполняет резервный путь через verify_user_record, а затем возвращает словарь с данными пользователя или None.

Эти функции используются из webhook.py для определения user_id по номеру телефона при входящем звонке.

Высокоуровневый сценарий verify_user_for_call(call_id, phone_digits) выглядит так:

  • несколько раз (до VERIFY_USER_RETRIES) пытается вызвать MCP‑инструмент VERIFY_USER_TOOL_NAME:
  • измеряет время выполнения;
  • при успехе и наличии содержательного ответа логирует результат и возвращает словарь с данными пользователя;
  • при отсутствии данных (пустой payload) логирует предупреждение и возвращает None;
  • при исключениях логирует ошибку, делает задержку VERIFY_USER_RETRY_DELAY и повторяет попытку;
  • если после всех попыток MCP не дал результата, а есть последнее исключение — логирует предупреждение и переходит к резервному пути через verify_user_record(phone_digits, auto_create=True):
  • при успехе возвращает запись из MariaDB;
  • при ошибке БД логирует её и, если было исходное исключение MCP, пробрасывает его выше.

Работа с номерами телефонов и состоянием

phone.py

Файл sip-hook/sip_hook_app/phone.py содержит функции:

  • extract_remote_party_id(event) — извлекает SIP‑заголовок Remote-Party-ID из события Realtime и возвращает его значение в виде строки или None;
  • extract_phone_from_remote(remote_value) — ищет телефон в строке Remote-Party-ID с помощью phonenumbers.PhoneNumberMatcher, выбирает валидный и максимально длинный номер и возвращает его в формате E.164 (с плюсом) или None;
  • normalize_phone_number(phone_value) — парсит переданный номер относительно PHONE_DEFAULT_REGION, проверяет, что номер возможен, и возвращает номер в формате E.164 без плюса (например, 380XXXXXXXXX) или None.

Эти функции используются в _resolve_user для определения номера и сопоставления звонка с записью пользователя.

state.py

Файл sip-hook/sip_hook_app/state.py содержит глобальный словарь:

  • call_metadata: dict[str, dict[str, Any]] — ключом является call_id звонка, а значением — словарь метаданных (номер телефона, user_id, история и служебные поля).

Этот словарь используется в webhook.py и runtime.py для обмена информацией между обработчиками событий и Realtime‑раннером.