Spaces:
Running
Running
nothex commited on
Commit ·
723ce57
1
Parent(s): 67a6408
fix: deployment readiness — auth, naming, Dockerfile, render config
Browse files- Dockerfile +2 -2
- backend/api/admin.py +1 -1
- backend/api/corpus.py +1 -1
- backend/api/ingest.py +1 -1
- backend/api/query.py +1 -1
- backend/core/build_ml_assets.py +3 -3
- backend/core/config.py +1 -1
- backend/core/distill_reranker.py +1 -2
- backend/core/intent_classifier.py +7 -3
- backend/core/tasks.py +1 -5
- backend/eval/run_eval.py +1 -1
- docs/model_migration_roadmap.md +4 -4
- vercel.json +1 -1
Dockerfile
CHANGED
|
@@ -29,9 +29,9 @@ COPY --chown=user:user . .
|
|
| 29 |
|
| 30 |
# 7. Pre-build ML assets (downloads models to cache, trains intent classifier)
|
| 31 |
ARG PREBUILD_ML_ASSETS=1
|
| 32 |
-
ARG
|
| 33 |
RUN if [ "$PREBUILD_ML_ASSETS" = "1" ]; then \
|
| 34 |
-
|
| 35 |
else \
|
| 36 |
echo "Skipping ML asset pre-build"; \
|
| 37 |
fi
|
|
|
|
| 29 |
|
| 30 |
# 7. Pre-build ML assets (downloads models to cache, trains intent classifier)
|
| 31 |
ARG PREBUILD_ML_ASSETS=1
|
| 32 |
+
ARG MORPHEUS_BUILD_ASSETS_MODE=light
|
| 33 |
RUN if [ "$PREBUILD_ML_ASSETS" = "1" ]; then \
|
| 34 |
+
MORPHEUS_BUILD_ASSETS_MODE=$MORPHEUS_BUILD_ASSETS_MODE python -m backend.core.build_ml_assets ; \
|
| 35 |
else \
|
| 36 |
echo "Skipping ML asset pre-build"; \
|
| 37 |
fi
|
backend/api/admin.py
CHANGED
|
@@ -7,7 +7,7 @@ from backend.core.warmup_classifier import warmup, warmup_cross_encoder
|
|
| 7 |
from datetime import datetime, timedelta, timezone
|
| 8 |
from collections import Counter
|
| 9 |
|
| 10 |
-
log = logging.getLogger("
|
| 11 |
router = APIRouter()
|
| 12 |
|
| 13 |
|
|
|
|
| 7 |
from datetime import datetime, timedelta, timezone
|
| 8 |
from collections import Counter
|
| 9 |
|
| 10 |
+
log = logging.getLogger("morpheus.api.admin")
|
| 11 |
router = APIRouter()
|
| 12 |
|
| 13 |
|
backend/api/corpus.py
CHANGED
|
@@ -14,7 +14,7 @@ from shared.types import (
|
|
| 14 |
CorpusFile, CorpusResponse, RenameRequest,
|
| 15 |
)
|
| 16 |
|
| 17 |
-
log
|
| 18 |
router = APIRouter()
|
| 19 |
|
| 20 |
|
|
|
|
| 14 |
CorpusFile, CorpusResponse, RenameRequest,
|
| 15 |
)
|
| 16 |
|
| 17 |
+
log = logging.getLogger("morpheus.api.corpus")
|
| 18 |
router = APIRouter()
|
| 19 |
|
| 20 |
|
backend/api/ingest.py
CHANGED
|
@@ -6,7 +6,7 @@ from backend.core.auth_utils import require_auth_token
|
|
| 6 |
from backend.core.tasks import process_pdf_task
|
| 7 |
from backend.core.tasks import celery_app
|
| 8 |
|
| 9 |
-
log = logging.getLogger("
|
| 10 |
router = APIRouter()
|
| 11 |
|
| 12 |
|
|
|
|
| 6 |
from backend.core.tasks import process_pdf_task
|
| 7 |
from backend.core.tasks import celery_app
|
| 8 |
|
| 9 |
+
log = logging.getLogger("morpheus.api.ingest")
|
| 10 |
router = APIRouter()
|
| 11 |
|
| 12 |
|
backend/api/query.py
CHANGED
|
@@ -9,7 +9,7 @@ from backend.core.pipeline import retrieve_chunks, generate_answer_stream, analy
|
|
| 9 |
from backend.core.auth_utils import require_auth_token
|
| 10 |
from backend.main import limiter
|
| 11 |
|
| 12 |
-
log
|
| 13 |
router = APIRouter()
|
| 14 |
|
| 15 |
|
|
|
|
| 9 |
from backend.core.auth_utils import require_auth_token
|
| 10 |
from backend.main import limiter
|
| 11 |
|
| 12 |
+
log = logging.getLogger("morpheus.api.query")
|
| 13 |
router = APIRouter()
|
| 14 |
|
| 15 |
|
backend/core/build_ml_assets.py
CHANGED
|
@@ -11,7 +11,7 @@ import os
|
|
| 11 |
import logging
|
| 12 |
|
| 13 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
| 14 |
-
log = logging.getLogger("
|
| 15 |
|
| 16 |
def build_assets():
|
| 17 |
log.info("Starting ML asset pre-build...")
|
|
@@ -19,14 +19,14 @@ def build_assets():
|
|
| 19 |
# Build-time safety:
|
| 20 |
# Prevent intent_classifier singleton from starting background bootstrap
|
| 21 |
# threads while we run deterministic synchronous training below.
|
| 22 |
-
os.environ["
|
| 23 |
|
| 24 |
# In CI/build environments we may not have real Supabase credentials.
|
| 25 |
# Keep train/upload logic local-only in that case.
|
| 26 |
os.environ.setdefault("SUPABASE_URL", "")
|
| 27 |
os.environ.setdefault("SUPABASE_SERVICE_KEY", "")
|
| 28 |
|
| 29 |
-
mode = os.getenv("
|
| 30 |
log.info("Build asset mode: %s", mode)
|
| 31 |
|
| 32 |
# 1. Optional pre-download sentence-transformers (used by Intent Classifier)
|
|
|
|
| 11 |
import logging
|
| 12 |
|
| 13 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
| 14 |
+
log = logging.getLogger("morpheus.build_assets")
|
| 15 |
|
| 16 |
def build_assets():
|
| 17 |
log.info("Starting ML asset pre-build...")
|
|
|
|
| 19 |
# Build-time safety:
|
| 20 |
# Prevent intent_classifier singleton from starting background bootstrap
|
| 21 |
# threads while we run deterministic synchronous training below.
|
| 22 |
+
os.environ["MORPHEUS_DISABLE_INTENT_BOOTSTRAP"] = "true"
|
| 23 |
|
| 24 |
# In CI/build environments we may not have real Supabase credentials.
|
| 25 |
# Keep train/upload logic local-only in that case.
|
| 26 |
os.environ.setdefault("SUPABASE_URL", "")
|
| 27 |
os.environ.setdefault("SUPABASE_SERVICE_KEY", "")
|
| 28 |
|
| 29 |
+
mode = os.getenv("MORPHEUS_BUILD_ASSETS_MODE", "light").strip().lower()
|
| 30 |
log.info("Build asset mode: %s", mode)
|
| 31 |
|
| 32 |
# 1. Optional pre-download sentence-transformers (used by Intent Classifier)
|
backend/core/config.py
CHANGED
|
@@ -29,7 +29,7 @@ COHERE_API_KEY = os.getenv("COHERE_API_KEY")
|
|
| 29 |
MASTER_ADMIN_KEY = os.getenv("MASTER_ADMIN_KEY")
|
| 30 |
|
| 31 |
# ==================== PROVIDER SELECTION ====================
|
| 32 |
-
LLM_PROVIDER = os.getenv("
|
| 33 |
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
| 34 |
OLLAMA_MODELS = ["llama3.2", "mistral"]
|
| 35 |
|
|
|
|
| 29 |
MASTER_ADMIN_KEY = os.getenv("MASTER_ADMIN_KEY")
|
| 30 |
|
| 31 |
# ==================== PROVIDER SELECTION ====================
|
| 32 |
+
LLM_PROVIDER = os.getenv("MORPHEUS_LLM_PROVIDER", "openrouter").strip().lower()
|
| 33 |
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
|
| 34 |
OLLAMA_MODELS = ["llama3.2", "mistral"]
|
| 35 |
|
backend/core/distill_reranker.py
CHANGED
|
@@ -23,11 +23,10 @@ you can optionally promote the local model to Path 1 and make Cohere the fallbac
|
|
| 23 |
|
| 24 |
import os
|
| 25 |
import logging
|
| 26 |
-
import json
|
| 27 |
from dotenv import load_dotenv
|
| 28 |
|
| 29 |
load_dotenv()
|
| 30 |
-
log = logging.getLogger("
|
| 31 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
| 32 |
|
| 33 |
DISTILLED_MODEL_PATH = "backend/core/local_reranker"
|
|
|
|
| 23 |
|
| 24 |
import os
|
| 25 |
import logging
|
|
|
|
| 26 |
from dotenv import load_dotenv
|
| 27 |
|
| 28 |
load_dotenv()
|
| 29 |
+
log = logging.getLogger("morpheus.distill")
|
| 30 |
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
| 31 |
|
| 32 |
DISTILLED_MODEL_PATH = "backend/core/local_reranker"
|
backend/core/intent_classifier.py
CHANGED
|
@@ -31,12 +31,14 @@ from pathlib import Path
|
|
| 31 |
from typing import Optional
|
| 32 |
from supabase.client import create_client
|
| 33 |
|
| 34 |
-
log = logging.getLogger("
|
| 35 |
|
| 36 |
|
| 37 |
def _bootstrap_disabled() -> bool:
|
| 38 |
from backend.core import config
|
| 39 |
-
force_disabled = os.getenv(
|
|
|
|
|
|
|
| 40 |
# Default production-safe posture: do not train on import unless explicitly enabled.
|
| 41 |
return force_disabled or (not config.INTENT_BOOTSTRAP_ON_STARTUP)
|
| 42 |
|
|
@@ -326,7 +328,9 @@ class IntentClassifier:
|
|
| 326 |
log.info("No intent model found — will use fallback until trained.")
|
| 327 |
self._ready = False
|
| 328 |
if _bootstrap_disabled():
|
| 329 |
-
log.info(
|
|
|
|
|
|
|
| 330 |
elif not getattr(self, "_bootstrap_started", False):
|
| 331 |
self._bootstrap_started = True
|
| 332 |
threading.Thread(target=train_initial_model, daemon=True).start()
|
|
|
|
| 31 |
from typing import Optional
|
| 32 |
from supabase.client import create_client
|
| 33 |
|
| 34 |
+
log = logging.getLogger("morpheus.intent")
|
| 35 |
|
| 36 |
|
| 37 |
def _bootstrap_disabled() -> bool:
|
| 38 |
from backend.core import config
|
| 39 |
+
force_disabled = os.getenv(
|
| 40 |
+
"MORPHEUS_DISABLE_INTENT_BOOTSTRAP", "false"
|
| 41 |
+
).lower() in {"1", "true", "yes"}
|
| 42 |
# Default production-safe posture: do not train on import unless explicitly enabled.
|
| 43 |
return force_disabled or (not config.INTENT_BOOTSTRAP_ON_STARTUP)
|
| 44 |
|
|
|
|
| 328 |
log.info("No intent model found — will use fallback until trained.")
|
| 329 |
self._ready = False
|
| 330 |
if _bootstrap_disabled():
|
| 331 |
+
log.info(
|
| 332 |
+
"Intent bootstrap disabled by MORPHEUS_DISABLE_INTENT_BOOTSTRAP."
|
| 333 |
+
)
|
| 334 |
elif not getattr(self, "_bootstrap_started", False):
|
| 335 |
self._bootstrap_started = True
|
| 336 |
threading.Thread(target=train_initial_model, daemon=True).start()
|
backend/core/tasks.py
CHANGED
|
@@ -5,11 +5,7 @@ from backend.core.pipeline import run_ingestion
|
|
| 5 |
# Initialize Celery pointing to your Redis broker
|
| 6 |
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
| 7 |
|
| 8 |
-
celery_app = Celery(
|
| 9 |
-
"nexus_worker",
|
| 10 |
-
broker=REDIS_URL,
|
| 11 |
-
backend=REDIS_URL
|
| 12 |
-
)
|
| 13 |
|
| 14 |
@celery_app.task(bind=True)
|
| 15 |
def process_pdf_task(self, tmp_path: str, original_filename: str, access_token: str):
|
|
|
|
| 5 |
# Initialize Celery pointing to your Redis broker
|
| 6 |
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
| 7 |
|
| 8 |
+
celery_app = Celery("morpheus_worker", broker=REDIS_URL, backend=REDIS_URL)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
@celery_app.task(bind=True)
|
| 11 |
def process_pdf_task(self, tmp_path: str, original_filename: str, access_token: str):
|
backend/eval/run_eval.py
CHANGED
|
@@ -36,7 +36,7 @@ from backend.core.pipeline import retrieve_chunks
|
|
| 36 |
from backend.eval.metrics import score_example, _doc_text_for_scoring
|
| 37 |
from backend.eval.openrouter_eval import ModelOrchestrator
|
| 38 |
|
| 39 |
-
log = logging.getLogger("
|
| 40 |
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s — %(message)s")
|
| 41 |
|
| 42 |
# Calibration: relevance_proxy must stay BELOW this for unanswerable questions
|
|
|
|
| 36 |
from backend.eval.metrics import score_example, _doc_text_for_scoring
|
| 37 |
from backend.eval.openrouter_eval import ModelOrchestrator
|
| 38 |
|
| 39 |
+
log = logging.getLogger("morpheus.eval.run")
|
| 40 |
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s — %(message)s")
|
| 41 |
|
| 42 |
# Calibration: relevance_proxy must stay BELOW this for unanswerable questions
|
docs/model_migration_roadmap.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
## Step 0: Preserve vector schema compatibility
|
| 4 |
- Do not change embedding dimensions in `public.documents.embedding` until you are ready for a full re-embed migration.
|
|
@@ -7,8 +7,8 @@
|
|
| 7 |
## Step 1: Switch generation to a local provider (Ollama)
|
| 8 |
What’s implemented:
|
| 9 |
- Generation calls in `backend/core/pipeline.py` now support a provider toggle:
|
| 10 |
-
- `
|
| 11 |
-
- `
|
| 12 |
- Ollama models are configured via:
|
| 13 |
- `OLLAMA_MODELS` (comma-separated) or `OLLAMA_MODEL` (single model)
|
| 14 |
- `OLLAMA_BASE_URL` (defaults to `http://localhost:11434`)
|
|
@@ -16,7 +16,7 @@ What’s implemented:
|
|
| 16 |
How to run:
|
| 17 |
1. Ensure Ollama is running locally and can load your target model.
|
| 18 |
2. Set environment variables:
|
| 19 |
-
- `
|
| 20 |
- `OLLAMA_MODELS=llama3`
|
| 21 |
|
| 22 |
Notes:
|
|
|
|
| 1 |
+
# MORPHEUS Model Migration Roadmap (Prototype -> Enterprise)
|
| 2 |
|
| 3 |
## Step 0: Preserve vector schema compatibility
|
| 4 |
- Do not change embedding dimensions in `public.documents.embedding` until you are ready for a full re-embed migration.
|
|
|
|
| 7 |
## Step 1: Switch generation to a local provider (Ollama)
|
| 8 |
What’s implemented:
|
| 9 |
- Generation calls in `backend/core/pipeline.py` now support a provider toggle:
|
| 10 |
+
- `MORPHEUS_LLM_PROVIDER=openrouter` (default)
|
| 11 |
+
- `MORPHEUS_LLM_PROVIDER=ollama`
|
| 12 |
- Ollama models are configured via:
|
| 13 |
- `OLLAMA_MODELS` (comma-separated) or `OLLAMA_MODEL` (single model)
|
| 14 |
- `OLLAMA_BASE_URL` (defaults to `http://localhost:11434`)
|
|
|
|
| 16 |
How to run:
|
| 17 |
1. Ensure Ollama is running locally and can load your target model.
|
| 18 |
2. Set environment variables:
|
| 19 |
+
- `MORPHEUS_LLM_PROVIDER=ollama`
|
| 20 |
- `OLLAMA_MODELS=llama3`
|
| 21 |
|
| 22 |
Notes:
|
vercel.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
{
|
| 2 |
"version": 2,
|
| 3 |
-
"name": "
|
| 4 |
"builds": [{ "src": "frontend/**", "use": "@vercel/static" }],
|
| 5 |
"routes": [{ "src": "/(.*)", "dest": "frontend/$1" }]
|
| 6 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"version": 2,
|
| 3 |
+
"name": "morpheus-frontend",
|
| 4 |
"builds": [{ "src": "frontend/**", "use": "@vercel/static" }],
|
| 5 |
"routes": [{ "src": "/(.*)", "dest": "frontend/$1" }]
|
| 6 |
}
|