Files
call-center/services/shared/ai_operator_config.py
T

85 lines
3.9 KiB
Python

from __future__ import annotations
import json
from typing import Any
from sqlalchemy import select
from services.shared.core import utc_now_iso
from services.shared.models import AIOperatorConfig, AIOperatorConfigOut
from services.shared.sql_models import AIOperatorSettingsRow
AI_OPERATOR_SETTINGS_KEY = "global"
def ai_operator_default_config() -> AIOperatorConfig:
return AIOperatorConfig(
agent_name="Айнур",
company_name="DigiOps",
base_system_prompt=(
"Ты Айнур, единый ИИ-оператор контакт-центра DigiOps для звонков, Telegram и других каналов. "
"Всегда сохраняй одну и ту же личность: тебя зовут Айнур. Если клиент спрашивает, кто ты или как "
"тебя зовут, отвечай, что ты Айнур. Отвечай естественно, кратко и по делу. Когда говоришь о себе, "
"используй женский род: могла, смогла, сделала, готова, проверила, нашла. Не завершай диалог "
"самостоятельно и говори «до свидания» только если клиент явно попрощался или попросил завершить "
"разговор. Телефонные номера читай по цифрам. Не используй Markdown, URL или таблицы."
),
identity_reply_ru="Я Айнур, оператор контакт-центра DigiOps. Чем могу помочь?",
identity_reply_kz="Мен Айнурмын, DigiOps байланыс орталығының операторымын. Қалай көмектесе аламын?",
voice_greeting_ru="Здравствуйте. Я Айнур. Подскажите, пожалуйста, чем помочь.",
voice_greeting_kz="Сәлеметсіз бе. Мен Айнурмын. Қалай көмектесе аламын?",
)
def _settings_row(session) -> AIOperatorSettingsRow | None:
return session.execute(
select(AIOperatorSettingsRow).where(AIOperatorSettingsRow.settings_key == AI_OPERATOR_SETTINGS_KEY)
).scalar_one_or_none()
def _normalize_config_payload(raw: Any) -> AIOperatorConfig:
if isinstance(raw, AIOperatorConfig):
return raw
if not isinstance(raw, dict):
raise ValueError("AI operator config payload must be an object")
return AIOperatorConfig.model_validate(raw)
def load_ai_operator_config(session) -> AIOperatorConfigOut:
default_config = ai_operator_default_config()
row = _settings_row(session)
if row is None:
return AIOperatorConfigOut(config=default_config, updated_at=None, source="defaults")
try:
parsed = json.loads(row.config_json or "{}")
config = _normalize_config_payload(parsed)
return AIOperatorConfigOut(config=config, updated_at=row.updated_at, source="database")
except Exception:
return AIOperatorConfigOut(config=default_config, updated_at=row.updated_at, source="defaults")
def load_effective_ai_operator_config(session) -> AIOperatorConfig:
return load_ai_operator_config(session).config
def save_ai_operator_config(
session,
config_payload: AIOperatorConfig | dict[str, Any],
) -> AIOperatorConfigOut:
config = _normalize_config_payload(config_payload)
row = _settings_row(session)
now = utc_now_iso()
if row is None:
row = AIOperatorSettingsRow(
settings_key=AI_OPERATOR_SETTINGS_KEY,
config_json="{}",
updated_at=now,
)
session.add(row)
row.config_json = json.dumps(config.model_dump(), ensure_ascii=False)
row.updated_at = now
session.commit()
session.refresh(row)
return AIOperatorConfigOut(config=config, updated_at=row.updated_at, source="database")