Files
aitbc/apps/pool-hub/src/poolhub/repositories/feedback_repository.py
oib 15427c96c0 chore: update file permissions to executable across repository
- Change file mode from 644 to 755 for all project files
- Add chain_id parameter to get_balance RPC endpoint with default "ait-devnet"
- Rename Miner.extra_meta_data to extra_metadata for consistency
2026-03-06 22:17:54 +01:00

82 lines
2.5 KiB
Python
Executable File

from __future__ import annotations
import datetime as dt
import json
from aitbc.logging import get_logger
from typing import Iterable, List, Optional
from uuid import UUID
from redis.asyncio import Redis
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from ..models import Feedback
from ..storage.redis_keys import RedisKeys
logger = get_logger(__name__)
class FeedbackRepository:
"""Persists coordinator feedback and emits Redis notifications."""
def __init__(self, session: AsyncSession, redis: Redis) -> None:
self._session = session
self._redis = redis
async def add_feedback(
self,
*,
job_id: str,
miner_id: str,
outcome: str,
latency_ms: Optional[int] = None,
fail_code: Optional[str] = None,
tokens_spent: Optional[float] = None,
) -> Feedback:
feedback = Feedback(
job_id=job_id,
miner_id=miner_id,
outcome=outcome,
latency_ms=latency_ms,
fail_code=fail_code,
tokens_spent=tokens_spent,
created_at=dt.datetime.utcnow(),
)
self._session.add(feedback)
await self._session.flush()
payload = {
"job_id": job_id,
"miner_id": miner_id,
"outcome": outcome,
"latency_ms": latency_ms,
"fail_code": fail_code,
"tokens_spent": tokens_spent,
"created_at": feedback.created_at.isoformat() if feedback.created_at else None,
}
try:
await self._redis.publish(RedisKeys.feedback_channel(), json.dumps(payload))
except Exception as exc: # pragma: no cover - defensive
logger.warning("Failed to publish feedback event for job %s: %s", job_id, exc)
return feedback
async def list_feedback_for_miner(self, miner_id: str, limit: int = 50) -> List[Feedback]:
stmt = (
select(Feedback)
.where(Feedback.miner_id == miner_id)
.order_by(Feedback.created_at.desc())
.limit(limit)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())
async def list_feedback_for_job(self, job_id: str, limit: int = 50) -> List[Feedback]:
stmt = (
select(Feedback)
.where(Feedback.job_id == job_id)
.order_by(Feedback.created_at.desc())
.limit(limit)
)
result = await self._session.execute(stmt)
return list(result.scalars().all())