test / _backend /cli /build_tool.py
aiioe's picture
automated upload - fixed missing out/ folder
c1bb824 verified
Raw History Blame Contribute Delete
6.32 kB
import os
import sys
import subprocess
from pathlib import Path
from typing import List
from dotenv import load_dotenv
ROOT = Path(__file__).resolve().parents[2]
load_dotenv(str(Path(__file__).resolve().parents[1] / ".env"))
sys.path.append(str(Path(__file__).resolve().parents[1])) # Add parent directory to path
import typer
try:
import questionary # type: ignore
except ImportError:
questionary = None # Fallback if not installed
from docker_builder import build_dockerfile, write_dockerfile
from graphrag_builder import build_graphrag
from hf_deployer import deploy_space
from vector_db_builder import build_vector_db_for_dir
app = typer.Typer(help="Interactive build & deploy tool")
BACKEND_DIR = ROOT / "_backend"
DATA_DIR = BACKEND_DIR / "data"
DEFAULT_DOCKERFILE = ROOT / "Dockerfile.generated"
def list_data_files() -> List[Path]:
# Listing all the directories in the data directory
folders = [p for p in DATA_DIR.iterdir() if p.is_dir()]
if not folders:
return []
return sorted(folders)
def run_command(cmd: List[str], cwd: Path | None = None):
typer.echo(f"Running: {' '.join(cmd)}")
res = subprocess.run(cmd, cwd=cwd, text=True)
if res.returncode != 0:
raise typer.Exit(code=res.returncode)
def ensure_questionary():
if questionary is None:
typer.echo("questionary not installed. Please add it to requirements and install.")
raise typer.Exit(code=1)
@app.command()
def interactive(
graphrag: bool = typer.Option(False, help="Build GraphRAG artifacts"),
deploy: bool = typer.Option(False, help="Deploy to Hugging Face after build"),
dockerfile_out: Path = typer.Option(DEFAULT_DOCKERFILE, help="Path for generated Dockerfile"),
non_interactive: bool = typer.Option(False, help="Skip confirmations where possible")
):
"""Run the interactive (or semi-interactive) build workflow."""
if not non_interactive:
ensure_questionary()
files = list_data_files()
if not files:
typer.echo("No data files found under _backend/data.")
raise typer.Exit(code=1)
if non_interactive:
selected_paths = files # take all
else:
choices = [f.relative_to(DATA_DIR).as_posix() for f in files]
selected_rel = questionary.checkbox(
"Select data files to include:", choices=choices
).ask()
if not selected_rel:
typer.echo("No files selected.")
raise typer.Exit()
selected_paths = [DATA_DIR / r for r in selected_rel]
# Optional vector DB build using new utility
do_reset = True if non_interactive else questionary.confirm(
"Build/reset vector database(s) for selected data directories?"
).ask()
if do_reset:
for data_path in selected_paths:
# Prompt for file suffix and collection name
if non_interactive:
file_suffix = None
collection_name = data_path.name
else:
file_suffix = questionary.text(
f"File suffix to include for {data_path.name} (e.g. .md, .srt, leave blank for all):",
default=""
).ask()
file_suffix = file_suffix if file_suffix.strip() else None
collection_name = questionary.text(
f"Collection name for {data_path.name}:",
default=data_path.name
).ask()
build_vector_db_for_dir(
data_dir=str(data_path),
collection_name=collection_name,
file_suffix=file_suffix,
verbose=True
)
graphrag_artifact_paths: List[str] = []
build_graphrag_flag = graphrag or (False if non_interactive else questionary.confirm(
"Build GraphRAG artifacts?"
).ask())
if build_graphrag_flag:
out_dir = BACKEND_DIR / "graphrag_artifacts"
build_graphrag(selected_paths, out_dir)
graphrag_artifact_paths.append(str(out_dir.relative_to(ROOT)))
frontend_dir = Path(os.getenv("FRONTEND_DIR", "frontend"))
build_cmd = os.getenv("FRONTEND_BUILD_CMD", "npm run build")
build_out = os.getenv("FRONTEND_BUILD_OUTPUT", "dist")
prebuild_frontend = True if non_interactive else questionary.confirm(
"Pre-build frontend locally first?"
).ask()
if prebuild_frontend:
if frontend_dir.exists():
run_command(build_cmd.split(), cwd=ROOT / frontend_dir)
else:
typer.echo(f"Frontend directory {frontend_dir} not found; skipping pre-build.")
docker_content = build_dockerfile(
frontend_build_cmd=build_cmd,
frontend_build_output=build_out,
graphrag_artifact_paths=graphrag_artifact_paths or None,
)
write_dockerfile(dockerfile_out, docker_content)
typer.echo(f"Wrote Dockerfile to {dockerfile_out}")
build_image = True if non_interactive else questionary.confirm(
"Build Docker image locally?"
).ask()
if build_image:
tag = os.getenv("IMAGE_TAG", "interview-synth:latest") if non_interactive else questionary.text(
"Image tag:", default="interview-synth:latest"
).ask()
run_command(["docker", "build", "-f", str(dockerfile_out), "-t", tag, "."], cwd=ROOT)
do_deploy = deploy or (False if non_interactive else questionary.confirm(
"Deploy to Hugging Face Spaces?"
).ask())
if do_deploy:
space_id = os.getenv("HF_SPACE_ID") or ("" if non_interactive else questionary.text(
"Enter HF Space (user/space):"
).ask())
token = os.getenv("HF_TOKEN") or ("" if non_interactive else questionary.password(
"Enter Hugging Face token:"
).ask())
if not space_id or not token:
typer.echo("Space ID or token missing; aborting deploy.")
else:
# Write the generated Dockerfile to project root for Spaces
(ROOT / "Dockerfile").write_text(docker_content, encoding="utf-8")
deploy_space(space_id, ROOT, token)
typer.echo("Done.")
if __name__ == "__main__":
app()