Skip to content

Commit e69fad1

Browse files
committed
file cleanup
1 parent 04a5c23 commit e69fad1

File tree

83 files changed

+6750
-1348
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+6750
-1348
lines changed

QUICK-FIX.md

Lines changed: 0 additions & 190 deletions
This file was deleted.

stringsight/api.py

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,69 @@ def _startup_init_db() -> None:
409409
app.mount("/final_results", StaticFiles(directory=str(final_results_path)), name="final_results")
410410
logger.info(f"Mounted /final_results from {final_results_path.absolute()}")
411411

412+
# -------------------------------------------------------------------------
413+
# Frontend serving (auto-enabled if frontend_dist exists)
414+
# -------------------------------------------------------------------------
415+
def find_frontend_dist() -> Path | None:
416+
"""Find the frontend dist directory in the package installation."""
417+
# Try inside the stringsight package (installed location)
418+
import stringsight
419+
package_dir = Path(stringsight.__file__).parent
420+
dist_path = package_dir / "frontend_dist"
421+
422+
if dist_path.exists() and (dist_path / "index.html").exists():
423+
return dist_path
424+
425+
# Try relative to current working directory (for development)
426+
cwd_dist = Path.cwd() / "stringsight" / "frontend_dist"
427+
if cwd_dist.exists() and (cwd_dist / "index.html").exists():
428+
return cwd_dist
429+
430+
return None
431+
432+
# Auto-detect and mount frontend if available
433+
frontend_dist = find_frontend_dist()
434+
if frontend_dist:
435+
from fastapi import Request
436+
from fastapi.responses import FileResponse
437+
from starlette.exceptions import HTTPException as StarletteHTTPException
438+
439+
# Mount assets directory for static files (JS, CSS, etc.)
440+
assets_path = frontend_dist / "assets"
441+
if assets_path.exists():
442+
app.mount("/assets", StaticFiles(directory=str(assets_path)), name="assets")
443+
logger.info(f"Mounted frontend assets from {assets_path}")
444+
445+
# Root route to serve index.html
446+
@app.get("/")
447+
async def serve_frontend_root():
448+
"""Serve the frontend application root."""
449+
return FileResponse(frontend_dist / "index.html")
450+
451+
# Exception handler for SPA routing (serve index.html for 404s on non-API routes)
452+
@app.exception_handler(StarletteHTTPException)
453+
async def spa_exception_handler(request: Request, exc: StarletteHTTPException):
454+
"""Handle 404s by serving index.html for frontend SPA routing."""
455+
# If it's a 404 and not an API route, serve the SPA
456+
if exc.status_code == 404 and not request.url.path.startswith("/api"):
457+
# Check if it's a static file in the frontend dist
458+
file_path = frontend_dist / request.url.path.lstrip("/")
459+
if file_path.is_file():
460+
return FileResponse(file_path)
461+
# Otherwise serve index.html for SPA routing
462+
return FileResponse(frontend_dist / "index.html")
463+
464+
# For API routes or other errors, return JSON error
465+
from fastapi.responses import JSONResponse
466+
return JSONResponse(
467+
status_code=exc.status_code,
468+
content={"detail": exc.detail}
469+
)
470+
471+
logger.info(f"Frontend auto-mounted from {frontend_dist}")
472+
else:
473+
logger.info("Frontend not found - API-only mode")
474+
412475
# NOTE:
413476
# All of the primary API endpoints are implemented in `stringsight/routers/*` and
414477
# are registered above via `app.include_router(...)`.
@@ -475,12 +538,12 @@ async def _run_cluster_job_async(job: ClusterJob, req: ClusterRunRequest):
475538

476539
# Force-drop any pre-initialized global LMDB caches
477540
from stringsight.core import llm_utils as _llm_utils
478-
from stringsight.clusterers import clustering_utils as _cu
541+
from stringsight.clusterers import embeddings as _embed
479542
from stringsight.core.caching import UnifiedCache
480543

481544
_orig_default_cache: UnifiedCache | None = getattr(_llm_utils, "_default_cache", None)
482545
_orig_default_llm_utils = getattr(_llm_utils, "_default_llm_utils", None)
483-
_orig_embed_cache = getattr(_cu, "_cache", None)
546+
_orig_embed_cache = getattr(_embed, "_cache", None)
484547
try:
485548
if hasattr(_llm_utils, "_default_cache"):
486549
_llm_utils._default_cache = None # type: ignore
@@ -490,8 +553,8 @@ async def _run_cluster_job_async(job: ClusterJob, req: ClusterRunRequest):
490553
# Intentionally silent - cache clearing is best-effort
491554
pass
492555
try:
493-
if hasattr(_cu, "_cache"):
494-
_cu._cache = None # type: ignore
556+
if hasattr(_embed, "_cache"):
557+
_embed._cache = None # type: ignore
495558
except Exception:
496559
# Intentionally silent - cache clearing is best-effort
497560
pass
@@ -949,7 +1012,13 @@ async def _run_cluster_job_async(job: ClusterJob, req: ClusterRunRequest):
9491012
enriched = []
9501013
total_conversations = {}
9511014
for model in all_models:
952-
model_convs = [c for c in conversations if c.model == model]
1015+
# Handle both single model strings and model arrays (for side-by-side)
1016+
if isinstance(conversations[0].model, list) if conversations else False:
1017+
# Side-by-side: check if model is in the array
1018+
model_convs = [c for c in conversations if model in c.model]
1019+
else:
1020+
# Single model: direct comparison
1021+
model_convs = [c for c in conversations if c.model == model]
9531022
total_conversations[model] = len(model_convs)
9541023

9551024
total_unique_conversations = len({c.question_id for c in conversations})

stringsight/clusterers/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,17 @@ def get_clusterer(
6161
from .dummy_clusterer import DummyClusterer
6262
from .base import BaseClusterer
6363

64+
# Import utility modules (commonly used functions)
65+
from . import embeddings
66+
from . import llm_clustering
67+
from . import persistence
68+
6469
__all__ = [
6570
"get_clusterer",
6671
"HDBSCANClusterer",
6772
"DummyClusterer",
6873
"BaseClusterer",
74+
"embeddings",
75+
"llm_clustering",
76+
"persistence",
6977
]

stringsight/clusterers/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def save(self, df: pd.DataFrame, clusters: List[Cluster]) -> Dict[str, str]:
257257
if not self.output_dir:
258258
return {}
259259

260-
from .clustering_utils import save_clustered_results
260+
from .persistence import save_clustered_results
261261

262262
base_filename = os.path.basename(self.output_dir.rstrip("/"))
263263
config = self.get_config()

0 commit comments

Comments
 (0)