from fastapi import HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.models import Tenant, TenantConfig, TenantStatus from app.providers import get_provider from app.providers.base import BaseLLMProvider from app.services.crypto import decrypt_api_key, encrypt_api_key async def get_provider_for_tenant( session: AsyncSession, tenant_id: str ) -> BaseLLMProvider: """根据租户配置动态实例化 Provider""" await get_tenant(session, tenant_id) result = await session.execute( select(TenantConfig).where(TenantConfig.tenant_id == tenant_id) ) config = result.scalar_one_or_none() if not config: raise HTTPException(status_code=404, detail="Tenant config not found") api_key = decrypt_api_key(config.llm_api_key) return get_provider( provider_type=config.llm_provider, api_key=api_key, model=config.llm_model, base_url=config.llm_base_url, ) async def create_tenant(session: AsyncSession, name: str, slug: str) -> Tenant: existing = await session.execute(select(Tenant).where(Tenant.slug == slug)) if existing.scalar_one_or_none(): raise HTTPException(status_code=400, detail="Slug already exists") tenant = Tenant(name=name, slug=slug) session.add(tenant) await session.commit() await session.refresh(tenant) return tenant async def get_tenant(session: AsyncSession, tenant_id: str) -> Tenant: result = await session.execute( select(Tenant).where(Tenant.id == tenant_id, Tenant.status != TenantStatus.deleted) ) tenant = result.scalar_one_or_none() if not tenant: raise HTTPException(status_code=404, detail="Tenant not found") return tenant async def list_tenants(session: AsyncSession) -> list[Tenant]: result = await session.execute( select(Tenant).where(Tenant.status != TenantStatus.deleted) ) return list(result.scalars().all()) async def update_tenant( session: AsyncSession, tenant_id: str, name: str | None, slug: str | None ) -> Tenant: tenant = await get_tenant(session, tenant_id) if slug and slug != tenant.slug: existing = await session.execute(select(Tenant).where(Tenant.slug == slug)) if existing.scalar_one_or_none(): raise HTTPException(status_code=400, detail="Slug already exists") tenant.slug = slug if name: tenant.name = name await session.commit() await session.refresh(tenant) return tenant async def delete_tenant(session: AsyncSession, tenant_id: str) -> None: tenant = await get_tenant(session, tenant_id) tenant.status = TenantStatus.deleted await session.commit() async def update_tenant_config( session: AsyncSession, tenant_id: str, llm_provider: str, llm_api_key: str, llm_model: str, llm_base_url: str | None = None, max_tokens_per_month: int = 1000000, ) -> TenantConfig: await get_tenant(session, tenant_id) result = await session.execute( select(TenantConfig).where(TenantConfig.tenant_id == tenant_id) ) config = result.scalar_one_or_none() encrypted_key = encrypt_api_key(llm_api_key) if config: config.llm_provider = llm_provider config.llm_api_key = encrypted_key config.llm_model = llm_model config.llm_base_url = llm_base_url config.max_tokens_per_month = max_tokens_per_month else: config = TenantConfig( tenant_id=tenant_id, llm_provider=llm_provider, llm_api_key=encrypted_key, llm_model=llm_model, llm_base_url=llm_base_url, max_tokens_per_month=max_tokens_per_month, ) session.add(config) await session.commit() await session.refresh(config) return config