mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 09:14:58 +03:00
Учёт стоимости вызовов остался без потребителя, когда сняли лимиты, и сам по себе не работал: цену присылает только 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>
55 lines
1.8 KiB
Python
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,
|
|
)
|