diff --git a/backend/pyproject.toml b/backend/pyproject.toml index c0c44e15..0b2ed90a 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -4,10 +4,19 @@ build-backend = "hatchling.build" [project] name = "pyspur" -version = "0.1.4" +version = "0.1.5" description = "PySpur is a Graph UI for building AI Agents in Python" -requires-python = ">=3.12" +requires-python = ">=3.11" license = "Apache-2.0" +classifiers = [ + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Operating System :: Unix", + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] maintainers = [ {name = "Srijan Patel", email = "srijan@pyspur.dev"}, {name = "Jean Kaddour", email = "jean@pyspur.dev"}, diff --git a/backend/pyspur/api/main.py b/backend/pyspur/api/main.py index b4b1a04e..a3bd25fa 100644 --- a/backend/pyspur/api/main.py +++ b/backend/pyspur/api/main.py @@ -1,13 +1,14 @@ -from dotenv import load_dotenv -import tempfile import shutil +import tempfile from contextlib import ExitStack, asynccontextmanager -from importlib.resources import files, as_file +from importlib.resources import as_file, files +from pathlib import Path + +from dotenv import load_dotenv from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles -from pathlib import Path from .api_app import api_app diff --git a/backend/pyspur/cli/main.py b/backend/pyspur/cli/main.py index 6b96c217..436f29e6 100644 --- a/backend/pyspur/cli/main.py +++ b/backend/pyspur/cli/main.py @@ -57,9 +57,14 @@ def init( print("[green]✓[/green] Created .env from template") # add PROJECT_ROOT to .env - with open(env_path, "a") as f: - f.write("""\nDO NOT CHANGE THIS VALUE\n""") - f.write("\nPROJECT_ROOT=" + str(target_dir) + "\n") + # Check if PROJECT_ROOT is already defined in .env + with open(env_path, "r") as f: + if "PROJECT_ROOT=" not in f.read(): + with open(env_path, "a") as f: + f.write("\n# ================================") + f.write("\n# PROJECT_ROOT: DO NOT CHANGE THIS VALUE") + f.write("\n# ================================") + f.write("\nPROJECT_ROOT=" + str(target_dir) + "\n") # Create a data directory data_dir = target_dir / "data" diff --git a/backend/pyspur/cli/utils.py b/backend/pyspur/cli/utils.py index b54fd40c..4d13d3f8 100644 --- a/backend/pyspur/cli/utils.py +++ b/backend/pyspur/cli/utils.py @@ -1,17 +1,17 @@ """Utility functions for the PySpur CLI.""" -from pathlib import Path import shutil -from importlib import resources import tempfile +from importlib import resources +from pathlib import Path -from rich import print import typer -from dotenv import load_dotenv -from sqlalchemy import text from alembic import command from alembic.config import Config from alembic.runtime.migration import MigrationContext +from dotenv import load_dotenv +from rich import print +from sqlalchemy import text def copy_template_file(template_name: str, dest_path: Path) -> None: @@ -39,9 +39,24 @@ def load_environment() -> None: def run_migrations() -> None: """Run database migrations using SQLAlchemy.""" try: - from ..database import engine, database_url + # ruff: noqa: F401 + from ..database import database_url, engine from ..models.base_model import BaseModel + # Import models + from ..models.dataset_model import DatasetModel # type: ignore + from ..models.dc_and_vi_model import ( + DocumentCollectionModel, # type: ignore + VectorIndexModel, # type: ignore + ) + from ..models.eval_run_model import EvalRunModel # type: ignore + from ..models.output_file_model import OutputFileModel # type: ignore + from ..models.run_model import RunModel # type: ignore + from ..models.task_model import TaskModel # type: ignore + from ..models.workflow_model import WorkflowModel # type: ignore + from ..models.workflow_version_model import WorkflowVersionModel # type: ignore + # Import all models to ensure they're registered with SQLAlchemy + # Test connection with engine.connect() as conn: conn.execute(text("SELECT 1")) @@ -51,10 +66,19 @@ def run_migrations() -> None: if database_url.startswith("sqlite"): try: BaseModel.metadata.create_all(engine) + print("[green]✓[/green] Created SQLite database") + print(f"[green]✓[/green] Database URL: {database_url}") + # Print all tables in the database + tables = BaseModel.metadata.tables + if tables: + print("\n[green]✓[/green] Successfully initialized SQLite database") + else: + print("[red]![/red] SQLite database is empty") + raise typer.Exit(1) print("[yellow]![/yellow] SQLite database is not recommended for production") print("[yellow]![/yellow] Please use a postgres instance instead") return - except Exception as e: + except Exception: print("[yellow]![/yellow] SQLite database out of sync, recreating from scratch") # Ask for confirmation before dropping all tables confirm = input( diff --git a/backend/pyspur/database.py b/backend/pyspur/database.py index 428e1819..9a1b47a3 100644 --- a/backend/pyspur/database.py +++ b/backend/pyspur/database.py @@ -1,8 +1,8 @@ import os from typing import Iterator -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker, Session +from sqlalchemy import create_engine +from sqlalchemy.orm import Session, sessionmaker # Get the database URL from the environment POSTGRES_USER = os.getenv("POSTGRES_USER") diff --git a/backend/pyspur/models/management/alembic/env.py b/backend/pyspur/models/management/alembic/env.py index 5983bb39..4aafc836 100644 --- a/backend/pyspur/models/management/alembic/env.py +++ b/backend/pyspur/models/management/alembic/env.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401 from logging.config import fileConfig from alembic import context @@ -8,9 +9,9 @@ from pyspur.models.base_model import BaseModel from pyspur.models.dataset_model import DatasetModel # type: ignore from pyspur.models.dc_and_vi_model import ( - DocumentCollectionModel, - VectorIndexModel, -) # type: ignore + DocumentCollectionModel, # type: ignore + VectorIndexModel, # type: ignore +) from pyspur.models.eval_run_model import EvalRunModel # type: ignore from pyspur.models.output_file_model import OutputFileModel # type: ignore from pyspur.models.run_model import RunModel # type: ignore