Конфигурация и интеграции
Этот раздел описывает настройки 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) напрямую. Это означает, что база данных должна называться ailogist (и MYSQL_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‑раннером.