Add voice analytics filters and metrics
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -12,6 +12,10 @@ def _uuid() -> str:
|
||||
return str(uuid4())
|
||||
|
||||
|
||||
def _utcnow() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class Provider(Base):
|
||||
"""Model provider registry."""
|
||||
|
||||
@@ -34,9 +38,9 @@ class Provider(Base):
|
||||
nullable=True,
|
||||
) # 存储额外配置(speed, vol, etc)
|
||||
config_ref: Mapped[str] = mapped_column(String(100), nullable=True) # 环境变量 key 名称(回退)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
updated_by: Mapped[str] = mapped_column(String(100), nullable=True)
|
||||
|
||||
@@ -51,7 +55,7 @@ class ProviderMetrics(Base):
|
||||
String(36), ForeignKey("providers.id", ondelete="CASCADE"), nullable=False, index=True
|
||||
)
|
||||
timestamp: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, index=True
|
||||
DateTime(timezone=True), default=_utcnow, index=True
|
||||
)
|
||||
success: Mapped[bool] = mapped_column(Boolean, nullable=False)
|
||||
latency_ms: Mapped[int] = mapped_column(Integer, nullable=True)
|
||||
@@ -82,9 +86,9 @@ class ProviderSecret(Base):
|
||||
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
|
||||
name: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
|
||||
encrypted_value: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
|
||||
@@ -97,10 +101,10 @@ class CostRecord(Base):
|
||||
user_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True)
|
||||
provider_id: Mapped[str] = mapped_column(String(36), nullable=True) # 可能是环境变量配置
|
||||
provider_name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
capability: Mapped[str] = mapped_column(String(50), nullable=False) # text/image/tts/storybook/asr
|
||||
capability: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
estimated_cost: Mapped[Decimal] = mapped_column(Numeric(10, 6), nullable=False)
|
||||
timestamp: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, index=True
|
||||
DateTime(timezone=True), default=_utcnow, index=True
|
||||
)
|
||||
|
||||
|
||||
@@ -116,7 +120,7 @@ class UserBudget(Base):
|
||||
Numeric(3, 2), default=Decimal("0.8")
|
||||
) # 80% 时告警
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.utcnow)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow
|
||||
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow
|
||||
)
|
||||
|
||||
@@ -6,7 +6,7 @@ from app.core.config import settings
|
||||
|
||||
_engine = None
|
||||
_session_factory: async_sessionmaker[AsyncSession] | None = None
|
||||
_lock = threading.Lock()
|
||||
_lock = threading.RLock()
|
||||
|
||||
|
||||
def _get_engine():
|
||||
@@ -34,6 +34,25 @@ def _get_session_factory():
|
||||
return _session_factory
|
||||
|
||||
|
||||
async def dispose_engine():
|
||||
"""Dispose the async engine and reset cached DB handles.
|
||||
|
||||
Celery tasks run async code through ``asyncio.run()``, which creates and closes
|
||||
one event loop per task. Asyncpg connections are bound to the loop that created
|
||||
them, so worker tasks must not keep pooled connections across task runs.
|
||||
"""
|
||||
global _engine, _session_factory
|
||||
|
||||
engine = _engine
|
||||
if engine is not None:
|
||||
await engine.dispose()
|
||||
|
||||
with _lock:
|
||||
if _engine is engine:
|
||||
_engine = None
|
||||
_session_factory = None
|
||||
|
||||
|
||||
async def init_db():
|
||||
"""Create tables if they do not exist."""
|
||||
from app.db.models import Base # main models
|
||||
|
||||
Reference in New Issue
Block a user