"""AI Spending dashboard — read-only aggregation domain. PR #253 (Phase 2) rewrote this domain to a clean 4-bucket model. The domain stitches four upstream data sources into a single finance- facing view, with no dedup ladder required because each bucket is backed by exactly one source table: * SaaS AI seats — ``software_monthly_bills`` for closed periods, live preview from ``software_seat_allocations`` × plan price for the current period. Filtered to ``asset_software_catalog.category='ai'``. * Bailian token spend — ``bailian_usage_daily`` (Aliyun BSS-derived; per-API-key breakdown unavailable — BSS only exposes account-level). * Azure AI Foundry — ``cmdb_cost_facts`` filtered to Azure integrations whose ``service`` matches the Azure entry of the ``ai_spending.service_whitelist`` platform setting. * AWS Bedrock — ``cmdb_cost_facts`` filtered to AWS integrations whose ``service`` matches the AWS whitelist entry. A future ``vertex`` bucket renders when GCP AI facts are present. Bailian's LLM is hooked up as the optional "AI summary" generator — the domain calls qwen-max-latest with the period aggregates and caches the bilingual JSON result in ``ai_spending_summaries``. PII (user emails / display names) is replaced with stable hashes before the prompt leaves the org. Read-mostly. The writes are: * ``ai_spending_summaries`` rows (LLM-generated; auto + admin manual) * ``platform_settings`` rows under the ``ai_spending.*`` namespace (admin settings update + the daily regenerate-quota counters under ``ai_spending.regenerate_counter..``) * ``audit_logs`` rows for every state-changing endpoint above Cross-domain reads are tolerated under the "reporting/coordinator-class service" carve-out documented in PLATFORM.md — the dashboard reads from asset / cmdb / bailian / identity / apikey, but those domains never read from ai_spending. """ from __future__ import annotations from app.domains.ai_spending import models as _models # noqa: F401 __all__ = ["_models"]