Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Tests

on:
push:
branches: [ main, dev ]
pull_request:
branches: [ main, dev ]

jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install uv
run: pip install uv

- name: Install dependencies
# Install minimal dependencies to avoid issues with heavy dev tools like python-magic
run: |
uv pip install --system -e .
uv pip install --system pytest pytest-asyncio pytest-mock pytest-loguru

- name: Run tests
run: |
pytest tests/unit/test_lazy_imports.py
# We run only the new tests first to ensure our changes are good.
# Ideally, we would run all tests, but existing tests might require more setup (e.g. Docker).
52 changes: 52 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Contributing to ROMA

Thank you for your interest in contributing to ROMA (Recursive Open Meta-Agents)! We welcome contributions from the community to help make this framework better.

## getting Started

1. **Fork the repository** on GitHub.
2. **Clone your fork** locally:
```bash
git clone https://github.com/your-username/ROMA.git
cd ROMA
```
3. **Set up your environment**. We recommend using `uv` for fast dependency management:
```bash
pip install uv
uv pip install -e ".[dev]"
```

## Development Workflow

1. Create a new branch for your feature or fix:
```bash
git checkout -b feature/my-new-feature
```
2. Make your changes.
3. Run tests to ensure everything is working:
```bash
pytest
```
4. Format your code (if applicable):
```bash
ruff format .
```

## Pull Request Process

1. Push your changes to your fork:
```bash
git push origin feature/my-new-feature
```
2. Open a Pull Request against the `main` branch of the upstream repository.
3. Provide a clear description of your changes and why they are necessary.

## Code of Conduct

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

## Questions?

If you have any questions, feel free to open an issue or reach out to the maintainers.

Happy coding!
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,10 @@ executor = Executor(

ROMA will ensure both constructor and per-call tools are available to the strategy.

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to get started, set up your development environment, and submit pull requests.

## 🧪 Testing

```bash
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name = "roma-dspy"
version = "0.1.0"
description = "Recursive agentic problem solver"
readme = "README.md"
keywords = ["agents", "ai", "dspy", "llm", "multi-agent", "framework", "recursive"]
authors = [
{ name = "salzubi401", email = "salaheddin@sentient.xyz" },
{ name = "baran nama", email = "baran@sentient.xyz" }
Expand Down
9 changes: 8 additions & 1 deletion src/roma_dspy/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
"""Core runtime components for ROMA-DSPy."""
"""
Core runtime components for ROMA-DSPy.

This module exposes the main building blocks of the framework, including:
- Engine components (Solver, DAG)
- Modules (Atomizer, Planner, Executor, Aggregator, Verifier)
- Signatures and Data Structures
"""

from .engine import (
TaskDAG,
Expand Down
31 changes: 28 additions & 3 deletions src/roma_dspy/utils/lazy_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def is_available(module_name: str) -> bool:
module_name: Name of the module to check (e.g., "sqlalchemy", "mlflow")

Returns:
True if module can be imported, False otherwise
bool: True if module can be imported, False otherwise

Example:
>>> if is_available("mlflow"):
Expand Down Expand Up @@ -78,7 +78,7 @@ def require_module(
raise ImportError(error_msg)


def lazy_import(module_name: str, package: Optional[str] = None):
def lazy_import(module_name: str, package: Optional[str] = None) -> Optional[object]:
"""
Lazily import a module only when accessed.

Expand All @@ -90,7 +90,7 @@ def lazy_import(module_name: str, package: Optional[str] = None):
package: Package name for relative imports

Returns:
Module if available, None otherwise
Optional[object]: The imported module if available, None otherwise

Example:
>>> mlflow = lazy_import("mlflow")
Expand Down Expand Up @@ -159,6 +159,31 @@ def get_available_features() -> dict[str, bool]:
}


def get_missing_features() -> dict[str, str]:
"""
Get a dictionary of missing optional features and their install commands.

Returns:
Dictionary mapping missing feature names to their install commands.
"""
features = {
"persistence": ("persistence", HAS_PERSISTENCE),
"observability": ("observability", HAS_MLFLOW),
"s3": ("s3", HAS_S3),
"code_execution": ("e2b", HAS_CODE_EXECUTION),
"api_server": ("api", HAS_API_SERVER),
"tui": ("tui", HAS_TUI),
"wandb": ("wandb", HAS_WANDB),
}

missing = {}
for feature, (extra, available) in features.items():
if not available:
missing[feature] = f"uv pip install roma-dspy[{extra}]"

return missing


def print_available_features() -> None:
"""
Print a formatted list of available optional features.
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/test_lazy_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import sys
from unittest.mock import patch, MagicMock
import pytest
from roma_dspy.utils.lazy_imports import is_available, require_module, get_missing_features, lazy_import

def test_is_available_existing_module():
"""Test is_available returns True for existing standard library module."""
assert is_available("os") is True
assert is_available("sys") is True

def test_is_available_non_existing_module():
"""Test is_available returns False for non-existing module."""
assert is_available("non_existent_module_xyz_123") is False

def test_require_module_success():
"""Test require_module does not raise error when module exists."""
# 'os' should always exist
require_module("os", "OS Feature", "os-extra")

def test_require_module_failure():
"""Test require_module raises ImportError with helpful message when module missing."""
with pytest.raises(ImportError) as excinfo:
require_module("non_existent_module_xyz_123", "Cool Feature", "cool-extra")

error_msg = str(excinfo.value)
assert "Cool Feature requires non_existent_module_xyz_123" in error_msg
assert "uv pip install roma-dspy[cool-extra]" in error_msg

def test_get_missing_features():
"""Test get_missing_features returns a dict."""
missing = get_missing_features()
assert isinstance(missing, dict)
# We can't guarantee what's missing in the test environment, but we can check the values structure if any exist
for install_cmd in missing.values():
assert "uv pip install" in install_cmd

def test_lazy_import_success():
"""Test lazy_import returns module if it exists."""
os_mod = lazy_import("os")
assert os_mod is not None
assert os_mod.__name__ == "os"

def test_lazy_import_failure():
"""Test lazy_import returns None if module does not exist."""
mod = lazy_import("non_existent_module_xyz_123")
assert mod is None