Files
chatballs/apps/backend/chatballs/platform/validation.py
T
AndreyandClaude Opus 5 09fb0177b1 🔥 refactor(ai): убрать учёт расхода на модель и валюту организации
Учёт стоимости вызовов остался без потребителя, когда сняли лимиты, и сам
по себе не работал: цену присылает только OpenRouter, для остальных
провайдеров была прайс-таблица из двух моделей и ноль для всех прочих.
Ни одна цифра расхода в интерфейсе не показывалась. Удалены ai/pricing.py,
ai/limits.py, поля LlmInvocation.cost_micros и currency, статус BLOCKED
вместе со строками журнала, ChatResult.cost_micros и чтение usage.cost,
настройки CHATBALLS_AI_PRICING и CHATBALLS_AI_GLOBAL_DAILY_COST_LIMIT_MICROS,
поля aiCostMicros/aiCostPrevMicros в сводке и стоимость в демо-данных.
Обработка входящих ловит теперь только сбой провайдера — лимиту неоткуда
сработать.

Валюта организации удалена целиком: поле принимало единственное значение
RUB, не читалось нигде и ни одна сумма в продукте в ней не считалась.
Убраны колонка с миграцией, валидация «только рубль», payload настроек и
платформенной выдачи, обязательное поле currency в API провижининга,
типы и транспорт на фронте.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 09:02:01 +03:00

55 lines
1.8 KiB
Python

from __future__ import annotations
from typing import Any
from chatballs.platform.provisioning_command import ProvisioningCommand
from chatballs.platform.provisioning_models import ProvisioningSource
_REQUIRED_FIELDS = (
"name",
"slug",
"owner_email",
"timezone",
)
def parse_provisioning_body(
body: Any, *, idempotency_key: str, source: str
) -> tuple[ProvisioningCommand, str | None]:
"""Map the API body (SPEC-HUB-0021 §12) to a ProvisioningCommand. Returns
(command, error_message). Validation is intentionally explicit (no DRF
serializers), mirroring identity/access_payloads.py."""
if not isinstance(body, dict):
return _empty_command(idempotency_key, source), "Request body must be an object"
for field in _REQUIRED_FIELDS:
if field not in body:
return _empty_command(idempotency_key, source), f"{field} is required"
command = ProvisioningCommand(
organization_name=str(body.get("name", "")),
organization_slug=str(body.get("slug", "")),
owner_email=str(body.get("owner_email", "")),
source=source,
idempotency_key=idempotency_key,
timezone=str(body.get("timezone", "Europe/Moscow")),
locale=str(body.get("locale", "")),
legal_name=str(body.get("legal_name", "")),
tax_profile=body.get("tax_profile") if isinstance(body.get("tax_profile"), dict) else None,
)
if not _valid_source(source):
return command, f"Unsupported source: {source}"
return command, None
def _valid_source(source: str) -> bool:
return source in ProvisioningSource.values
def _empty_command(idempotency_key: str, source: str) -> ProvisioningCommand:
return ProvisioningCommand(
organization_name="",
organization_slug="",
owner_email="",
source=source,
idempotency_key=idempotency_key,
)