Ashmi Banerjee
Initial deployment: Sustainable Tourism CRS Chatbot
c3674d7
Raw
History Blame Contribute Delete
5.57 kB
"""
Database Configuration
Factory functions that fall back to in-memory stores when Firestore is not configured
or when credentials are invalid/expired.
The connection is probed eagerly so auth failures surface at startup rather than
mid-request (which would produce a cryptic gRPC traceback).
"""
import logging
import os
from pathlib import Path
from typing import Optional
from .session_store import SessionStore
logger = logging.getLogger(__name__)
def _resolve_credentials_path(credentials_path: Optional[str]) -> Optional[str]:
if not credentials_path:
return None
if os.path.isabs(credentials_path):
return credentials_path
project_root = Path(__file__).parent.parent
abs_path = str(project_root / credentials_path)
print(f"[Config] Resolved credentials path: {abs_path}")
print(f"[Config] Credentials file exists: {os.path.exists(abs_path)}")
return abs_path
def _probe_firestore(db) -> bool:
"""
Test credentials by refreshing the OAuth2 token directly (plain HTTPS, no gRPC).
This fails in ~1 s instead of waiting for gRPC's 60 s retry timeout.
Returns False on any auth or network error.
"""
try:
import google.auth.transport.requests
request = google.auth.transport.requests.Request()
creds = getattr(db, "_credentials", None)
if creds is None:
return True # no credentials object β€” assume ADC / Cloud Run, let it through
if hasattr(creds, "refresh"):
creds.refresh(request)
return True
except Exception as exc:
logger.warning("[Database] Firestore credential probe failed: %s", exc)
return False
_session_store_instance = None
_conversation_store_instance = None
def get_session_store(backend: Optional[str] = None) -> SessionStore:
global _session_store_instance
if _session_store_instance is not None:
return _session_store_instance
from config import settings
project_id = (settings.firebase_project_id or settings.google_cloud_project) if settings.use_firestore else None
if not project_id:
logger.warning("[Database] No Firestore project ID β€” using in-memory session store.")
from .memory_store import MemorySessionStore
_session_store_instance = MemorySessionStore()
return _session_store_instance
credentials_path = _resolve_credentials_path(settings.firebase_google_application_credentials)
try:
from .firestore_store import FirestoreSessionStore
store = FirestoreSessionStore(
project_id=project_id,
credentials_path=credentials_path,
collection_name="sessions",
)
if not _probe_firestore(store.db):
raise RuntimeError("Firestore credential probe failed")
_session_store_instance = store
except Exception as e:
logger.warning("[Database] Firestore unavailable (%s) β€” using in-memory session store.", e)
from .memory_store import MemorySessionStore
_session_store_instance = MemorySessionStore()
return _session_store_instance
def get_conversation_store():
"""
Priority order for conversation persistence:
1. Firestore β€” if FIREBASE_PROJECT_ID is set and credentials are valid
2. HF Dataset β€” if HF_TOKEN + HF_DATASET_REPO are set
3. In-memory β€” fallback (data lost on restart, fine for demo)
Returns the same singleton instance on every call so all callers share state.
"""
global _conversation_store_instance
if _conversation_store_instance is not None:
return _conversation_store_instance
from config import settings
# --- 1. Firestore (only when explicitly opted in) ---
project_id = (settings.firebase_project_id or settings.google_cloud_project) if settings.use_firestore else None
if project_id:
credentials_path = _resolve_credentials_path(settings.firebase_google_application_credentials)
try:
from .conversation_store import ConversationStore
store = ConversationStore(
project_id=project_id,
credentials_path=credentials_path,
collection_name="conversations",
)
if not _probe_firestore(store.db):
raise RuntimeError("Firestore credential probe failed")
logger.info("[Database] Using Firestore conversation store.")
_conversation_store_instance = store
return _conversation_store_instance
except Exception as e:
logger.warning("[Database] Firestore unavailable (%s), trying next option.", e)
# --- 2. HF Dataset repo ---
hf_token = os.getenv("HF_TOKEN") or settings.hf_token
hf_repo = os.getenv("HF_DATASET_REPO") or settings.hf_dataset_repo
if hf_token and hf_repo:
try:
from .hf_store import HFConversationStore
_conversation_store_instance = HFConversationStore(repo_id=hf_repo, token=hf_token)
logger.info("[Database] Using HF Dataset conversation store (%s).", hf_repo)
return _conversation_store_instance
except Exception as e:
logger.warning("[Database] HF store unavailable (%s), falling back to in-memory.", e)
# --- 3. In-memory ---
logger.warning("[Database] No persistent store configured β€” using in-memory conversation store.")
from .memory_store import MemoryConversationStore
_conversation_store_instance = MemoryConversationStore()
return _conversation_store_instance