mirror of
http://88.130.71.182:3000/BlitTech/contexta_be.git
synced 2026-06-12 23:23:21 +00:00
fixed the RAg in test pipeline issue
This commit is contained in:
54
app/services/cache.py
Normal file
54
app/services/cache.py
Normal file
@@ -0,0 +1,54 @@
|
||||
"""
|
||||
In-memory TTL cache for RAG responses.
|
||||
Keyed on (collection_name, normalised query).
|
||||
Only used for stateless queries (no conversation history).
|
||||
"""
|
||||
import hashlib
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
|
||||
_store: dict[str, tuple[Any, float]] = {}
|
||||
_index: dict[str, set[str]] = {} # collection_name → set of cache keys
|
||||
_MAX_ENTRIES = 500
|
||||
TTL = 6 * 3600 # 6 hours
|
||||
_set_ctor = set # save builtin before it's shadowed by our module-level `set` function
|
||||
|
||||
|
||||
def _make_key(collection_name: str, query: str) -> str:
|
||||
raw = f"{collection_name}::{query.lower().strip()}"
|
||||
return hashlib.sha256(raw.encode()).hexdigest()
|
||||
|
||||
|
||||
def get(collection_name: str, query: str) -> Optional[Any]:
|
||||
k = _make_key(collection_name, query)
|
||||
entry = _store.get(k)
|
||||
if entry is None:
|
||||
return None
|
||||
value, expires_at = entry
|
||||
if time.monotonic() > expires_at:
|
||||
_store.pop(k, None)
|
||||
_index.get(collection_name, _set_ctor()).discard(k)
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
def set(collection_name: str, query: str, value: Any) -> None:
|
||||
if len(_store) >= _MAX_ENTRIES:
|
||||
now = time.monotonic()
|
||||
expired = [k for k, (_, exp) in _store.items() if exp < now]
|
||||
for k in expired:
|
||||
_store.pop(k, None)
|
||||
if len(_store) >= _MAX_ENTRIES:
|
||||
oldest = min(_store, key=lambda k: _store[k][1])
|
||||
_store.pop(oldest, None)
|
||||
k = _make_key(collection_name, query)
|
||||
_store[k] = (value, time.monotonic() + TTL)
|
||||
_index.setdefault(collection_name, _set_ctor()).add(k)
|
||||
|
||||
|
||||
def invalidate(collection_name: str) -> int:
|
||||
"""Drop all cached entries for a collection — call after any KB update."""
|
||||
keys = _index.pop(collection_name, _set_ctor())
|
||||
for k in keys:
|
||||
_store.pop(k, None)
|
||||
return len(keys)
|
||||
69
app/services/notification_service.py
Normal file
69
app/services/notification_service.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
from app.services.telegram_service import send_message as tg_send
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def send_handoff_alert(
|
||||
chatbot_id: str,
|
||||
chatbot_name: str,
|
||||
conversation_history: List[dict],
|
||||
trigger_message: str,
|
||||
conversation_id: str,
|
||||
low_confidence: bool,
|
||||
supabase,
|
||||
) -> bool:
|
||||
"""
|
||||
Notify the chatbot owner via their connected Telegram bot.
|
||||
Owner must have sent /owner to their bot to register their chat_id.
|
||||
Returns True if notification was sent.
|
||||
"""
|
||||
try:
|
||||
conn = (
|
||||
supabase.table("channel_connections")
|
||||
.select("bot_token, owner_chat_id")
|
||||
.eq("chatbot_id", chatbot_id)
|
||||
.eq("channel", "telegram")
|
||||
.eq("is_active", True)
|
||||
.execute()
|
||||
)
|
||||
if not conn.data:
|
||||
logger.info(f"No Telegram connection for chatbot {chatbot_id}")
|
||||
return False
|
||||
|
||||
bot_token: Optional[str] = conn.data[0].get("bot_token")
|
||||
owner_chat_id = conn.data[0].get("owner_chat_id")
|
||||
|
||||
if not bot_token or not owner_chat_id:
|
||||
logger.info(
|
||||
f"Owner has not registered for notifications on chatbot {chatbot_id}. "
|
||||
"They should send /owner to their Telegram bot."
|
||||
)
|
||||
return False
|
||||
|
||||
from app.config import settings
|
||||
recent = conversation_history[-4:]
|
||||
history_lines = "\n".join(
|
||||
f"{'👤' if m['role'] == 'user' else '🤖'} {m['content'][:120]}"
|
||||
for m in recent
|
||||
)
|
||||
|
||||
reason = "❓ Bot couldn't answer confidently" if low_confidence else "🙋 User requested a human"
|
||||
inbox_url = f"{settings.app_url}/inbox"
|
||||
|
||||
text = (
|
||||
f"🔔 *Handoff — {chatbot_name}*\n"
|
||||
f"Reason: {reason}\n\n"
|
||||
f"Last messages:\n{history_lines}\n\n"
|
||||
f"▶ [Open in inbox]({inbox_url})"
|
||||
)
|
||||
|
||||
sent = await tg_send(bot_token, owner_chat_id, text)
|
||||
if sent:
|
||||
logger.info(f"Handoff alert sent to owner for chatbot {chatbot_id}")
|
||||
return sent
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send handoff alert for chatbot {chatbot_id}: {e}")
|
||||
return False
|
||||
Reference in New Issue
Block a user