diff --git a/packages/ragbits-cli/README.md b/packages/ragbits-cli/README.md new file mode 100644 index 000000000..e9f62f634 --- /dev/null +++ b/packages/ragbits-cli/README.md @@ -0,0 +1 @@ +# Ragbits CLI diff --git a/packages/ragbits-cli/pyproject.toml b/packages/ragbits-cli/pyproject.toml new file mode 100644 index 000000000..66fbeaa9e --- /dev/null +++ b/packages/ragbits-cli/pyproject.toml @@ -0,0 +1,53 @@ +[project] +name = "ragbits-cli" +version = "0.1.0" +description = "A CLI application for ragbits - building blocks for rapid development of GenAI applications" +readme = "README.md" +requires-python = ">=3.10" +license = "MIT" +authors = [ + { name = "deepsense.ai", email = "contact@deepsense.ai"} +] +keywords = [ + "Retrieval Augmented Generation", + "RAG", + "Large Language Models", + "LLMs", + "Generative AI", + "GenAI", + "Prompt Management" +] +classifiers = [ + "Development Status :: 1 - Planning", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Software Development :: Libraries :: Python Modules", + "Private :: Do Not Upload" +] +dependencies = [ + "typer>=0.12.5", +] + +[project.scripts] +ragbits = "ragbits.cli:main" +rbts = "ragbits.cli:main" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.build.targets.wheel] +packages = ["src/ragbits"] + +[tool.pytest.ini_options] +asyncio_mode = "auto" diff --git a/packages/ragbits-cli/src/ragbits/cli/__init__.py b/packages/ragbits-cli/src/ragbits/cli/__init__.py new file mode 100644 index 000000000..21fc531ed --- /dev/null +++ b/packages/ragbits-cli/src/ragbits/cli/__init__.py @@ -0,0 +1,31 @@ +import importlib.util +import pkgutil + +from typer import Typer + +import ragbits + +app = Typer() + + +def main() -> None: + """ + Main entry point for the CLI. + + This function registers all the CLI modules in the ragbits packages: + - iterates over every package in the ragbits.* namespace + - it looks for `cli` package / module + - if found it imports the `register` function from the `cli` module and calls it with the `app` object + - register function should add the CLI commands to the `app` object + """ + + cli_enabled_modules = [ + module + for module in pkgutil.iter_modules(ragbits.__path__) + if module.ispkg and module.name != "cli" and importlib.util.find_spec(f"ragbits.{module.name}.cli") + ] + for module in cli_enabled_modules: + register_func = importlib.import_module(f"ragbits.{module.name}.cli").register + register_func(app) + + app() diff --git a/packages/ragbits-core/src/ragbits/core/cli.py b/packages/ragbits-core/src/ragbits/core/cli.py new file mode 100644 index 000000000..46945b99e --- /dev/null +++ b/packages/ragbits-core/src/ragbits/core/cli.py @@ -0,0 +1,19 @@ +from typer import Typer + +prompts_app = Typer() + + +@prompts_app.command() +def placeholder() -> None: + """Placeholder command""" + print("foo") + + +def register(app: Typer) -> None: + """ + Register the CLI commands for the ragbits-core package. + + Args: + app: The Typer object to register the commands with. + """ + app.add_typer(prompts_app, name="prompts") diff --git a/pyproject.toml b/pyproject.toml index f54194a6c..2a9d974bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,7 @@ dependencies = [ "ragbits[litellm,local]", "ragbits-dev-kit", "ragbits-document-search[chromadb]" + "ragbits-cli" ] [tool.uv] @@ -23,12 +24,14 @@ dev-dependencies = [ ragbits = { workspace = true } ragbits-dev-kit = { workspace = true } ragbits-document-search = { workspace = true } +ragbits-cli = { workspace = true } [tool.uv.workspace] members = [ "packages/ragbits-core", "packages/ragbits-dev-kit", - "packages/ragbits-document-search" + "packages/ragbits-document-search", + "packages/ragbits-cli" ] [tool.isort] @@ -119,6 +122,7 @@ mypy_path = [ 'packages/ragbits-core/src', 'packages/ragbits-dev-kit/src', 'packages/ragbits-document-search/src', + 'packages/ragbits-cli/src', ] [[tool.mypy.overrides]] diff --git a/scripts/create_ragbits_package.py b/scripts/create_ragbits_package.py new file mode 100644 index 000000000..56b9e0031 --- /dev/null +++ b/scripts/create_ragbits_package.py @@ -0,0 +1,74 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "tomlkit", +# "inquirer", +# ] +# /// +# To run this script and create a new package, run the following command: +# +# uv run scripts/create_ragbits_package.py +# + +from pathlib import Path + +import tomlkit +from inquirer.shortcuts import text + +PACKAGES_DIR = Path(__file__).parent.parent / "packages" + + +def run() -> None: + """ + Create a new Ragbits package. + """ + package_name: str = text("Enter the package name", default="ragbits-") + + package_dir = PACKAGES_DIR / package_name + + if package_dir.exists(): + print(f"Package {package_name} already exists at {package_dir}") + return + + package_dir.mkdir(exist_ok=True, parents=True) + + src_dir = package_dir / "src" / "ragbits" / package_name.removeprefix("ragbits-").replace("-", "_") + src_dir.mkdir(exist_ok=True, parents=True) + (src_dir / "__init__.py").touch() + + examples_dir = package_dir / "examples" + examples_dir.mkdir(exist_ok=True) + + tests_dir = package_dir / "tests" + tests_dir.mkdir(exist_ok=True) + + pkg_pyproject = tomlkit.parse((PACKAGES_DIR / "ragbits-core" / "pyproject.toml").read_text()) + + pkg_pyproject["project"]["name"] = package_name + pkg_pyproject["project"]["dependencies"] = [] + pkg_pyproject["project"]["optional-dependencies"] = {} + + (package_dir / "pyproject.toml").write_text(tomlkit.dumps(pkg_pyproject)) + + print(f"Package {package_name} created at {package_dir}") + + workspace_pyproject_path = PACKAGES_DIR.parent / "pyproject.toml" + workspace_pyproject = tomlkit.parse(workspace_pyproject_path.read_text()) + + workspace_pyproject["project"]["dependencies"].append(package_name) + + workspace_info = tomlkit.inline_table() + workspace_info.update({"workspace": True}) + + workspace_pyproject["tool"]["uv"]["sources"][package_name] = workspace_info + + workspace_pyproject["tool"]["uv"]["workspace"]["members"].append(f"packages/{package_name}") + workspace_pyproject["tool"]["mypy"]["mypy_path"].append(f"packages/{package_name}/src") + + workspace_pyproject_path.write_text(tomlkit.dumps(workspace_pyproject), encoding="utf-8") + + print(f"Package {package_name} added to the workspace") + + +if __name__ == "__main__": + run() diff --git a/uv.lock b/uv.lock index 4fcfd7759..c50473075 100644 --- a/uv.lock +++ b/uv.lock @@ -10,6 +10,7 @@ resolution-markers = [ [manifest] members = [ "ragbits", + "ragbits-cli", "ragbits-dev-kit", "ragbits-document-search", "ragbits-workspace", @@ -2410,6 +2411,17 @@ dev = [ { name = "pytest-cov", specifier = "~=5.0.0" }, ] +[[package]] +name = "ragbits-cli" +version = "0.1.0" +source = { editable = "packages/ragbits-cli" } +dependencies = [ + { name = "typer" }, +] + +[package.metadata] +requires-dist = [{ name = "typer", specifier = ">=0.12.5" }] + [[package]] name = "ragbits-dev-kit" version = "0.1.0" @@ -2492,6 +2504,7 @@ version = "0.1.0" source = { virtual = "." } dependencies = [ { name = "ragbits", extra = ["litellm", "local"] }, + { name = "ragbits-cli" }, { name = "ragbits-dev-kit" }, { name = "ragbits-document-search", extra = ["chromadb"] }, ] @@ -2508,6 +2521,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "ragbits", extras = ["litellm", "local"], editable = "packages/ragbits-core" }, + { name = "ragbits-cli", editable = "packages/ragbits-cli" }, { name = "ragbits-dev-kit", editable = "packages/ragbits-dev-kit" }, { name = "ragbits-document-search", extras = ["chromadb"], editable = "packages/ragbits-document-search" }, ]