Conversation
| dynamic = ["dependencies"] | ||
|
|
||
| [project.urls] | ||
| Homepage = "https://github.com/flagos-ai/FlagScale" |
There was a problem hiding this comment.
Not sure why we changed the url to FlagOpen.
setup.py
Outdated
| return result | ||
|
|
||
| # Version is defined in pyproject.toml - keep in sync | ||
| FLAGSCALE_VERSION = "1.0.0" |
There was a problem hiding this comment.
It's better to get the version from pyproject.toml
|
|
||
| setup( | ||
| name="flag_scale", | ||
| name="flagscale", |
There was a problem hiding this comment.
Pull request overview
This PR refactors the FlagScale installation process to streamline pip-based installations and modernize the CLI framework. The changes enable users to install FlagScale with FLAGSCALE_PLATFORM=cuda FLAGSCALE_TASK=train pip install --no-build-isolation ., providing a more standard Python packaging experience.
Changes:
- Renamed package from "flag_scale" to "flagscale" with simplified package structure
- Migrated CLI framework from click to typer for better type safety and modern Python practices
- Introduced
--only-pipinstallation mode to skip apt packages and source builds - Refactored setup.py to conditionally run install.sh based on build isolation detection
- Consolidated version management (now defined in pyproject.toml, setup.py, and flagscale/init.py)
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| version.py | Removed - version now managed in pyproject.toml and flagscale/init.py |
| init.py | Removed - no longer needed at repository root |
| .coveragerc | Removed configuration file |
| pyproject.toml | Updated package name to "flagscale", removed nonexistent pip version constraint, simplified configuration |
| setup.py | Complete rewrite to support conditional installation based on build isolation, now only includes minimal dependencies |
| flagscale/init.py | New file managing version with fallback mechanism using importlib.metadata |
| flagscale/cli.py | Complete rewrite using typer instead of click, added new run command and improved command structure |
| flagscale/run.py | New file extracting core logic from root run.py for modular imports |
| run.py | Refactored as backward compatibility wrapper importing from flagscale.run |
| requirements/common.txt | Added typer>=0.9.0 for CLI framework |
| tools/install/install.sh | Added --only-pip flag with documentation and examples |
| tools/install/utils/pkg_utils.sh | Implemented is_only_pip() and updated should_install_src() to respect --only-pip mode |
| tools/install/install_dev.sh | Added only-pip mode check in install_src() |
| tools/install/cuda/install_base.sh | Added only-pip mode check to skip apt packages |
| tools/install/cuda/install_train.sh | Added only-pip mode check in install_src() |
| tools/install/cuda/install_serve.sh | Added only-pip mode check in install_src() |
| flagscale/train/megatron/training/training.py | Added eval_iters parameter to evaluate() function signature |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
setup.py
Outdated
| result = subprocess.run(cmd, check=False) | ||
| if result.returncode != 0: | ||
| raise RuntimeError(f"Installation failed with exit code {result.returncode}") |
There was a problem hiding this comment.
The subprocess.run() call uses check=False and then manually raises an exception if returncode != 0. This bypasses Python's standard error handling. Either use check=True to let CalledProcessError be raised automatically, or if you need custom error handling, consider capturing stdout/stderr for better error messages.
| result = subprocess.run(cmd, check=False) | |
| if result.returncode != 0: | |
| raise RuntimeError(f"Installation failed with exit code {result.returncode}") | |
| try: | |
| subprocess.run(cmd, check=True) | |
| except subprocess.CalledProcessError as exc: | |
| raise RuntimeError(f"Installation failed with exit code {exc.returncode}") from exc |
| args = [ | ||
| "run.py", | ||
| f"--config-path={config_path}", | ||
| f"--config-name={config_name}", | ||
| f"action={action}", | ||
| ] | ||
| if extra_args: | ||
| args.extend(extra_args) | ||
| sys.argv = args |
There was a problem hiding this comment.
The run_task function sets sys.argv[0] to "run.py" which doesn't match the actual command being invoked ("flagscale"). This could cause issues with error messages or help text that reference sys.argv[0]. Consider using a more accurate argv[0] value like "flagscale" or the actual task name.
| args = [ | ||
| "flagscale", | ||
| f"--config-path={config_path}", | ||
| f"--config-name={config_name}", | ||
| f"action={action.value}", | ||
| ] | ||
| if overrides: | ||
| args.extend(overrides) | ||
|
|
||
| typer.echo(f"Running: {' '.join(args)}") | ||
| sys.argv = args | ||
| run_main() |
There was a problem hiding this comment.
The "run" command uses sys.argv[0] = "flagscale" at line 116, which is inconsistent with the run_task helper function that uses "run.py". This inconsistency could lead to confusion. Standardize on one approach throughout the CLI implementation.
flagscale/cli.py
Outdated
| stop: bool = typer.Option(False, "--stop", help="Stop serving"), | ||
| test: bool = typer.Option(False, "--test", help="Test serving"), | ||
| tune: bool = typer.Option(False, "--tune", help="Auto-tune"), | ||
| port: int | None = typer.Option(None, "--port", "-p", help="Server port"), |
There was a problem hiding this comment.
The parameter name "port" conflicts with the typer.Option() name. In the serve() function, the parameter is named "port" but the option flag is also "-p" (short for --port), which could be confused with --config's "-c" flag. Consider using a different short flag like "-P" for port to avoid confusion, or remove the short flag entirely since --port is self-explanatory.
| port: int | None = typer.Option(None, "--port", "-p", help="Server port"), | |
| port: int | None = typer.Option(None, "--port", help="Server port"), |
| packages=["flagscale"], | ||
| package_dir={"flagscale": "flagscale"}, |
There was a problem hiding this comment.
The setup function only declares a single package "flagscale", but this may not include all subpackages. Consider using setuptools.find_packages() or explicitly listing all subpackages (like flagscale.train, flagscale.serve, etc.) to ensure all modules are properly packaged. The current configuration relies on pyproject.toml's package-finding, but setup.py should be self-contained for backward compatibility.
| verbose=False, | ||
| non_loss_data_func=None, | ||
| extra_valid_index=None, | ||
| eval_iters=None, |
There was a problem hiding this comment.
The eval_iters parameter is added to the function signature but never used in the function body. This creates dead code and potential confusion. Either implement the logic to use eval_iters within the function, or remove the parameter if it's not needed.
pyproject.toml
Outdated
| include = ["flagscale*"] | ||
|
|
||
| [tool.setuptools.package-data] | ||
| "*" = ["*.yaml", "*.yml"] |
There was a problem hiding this comment.
The package_data configuration in pyproject.toml uses a wildcard "" to match all packages and include ".yaml" and ".yml" files. This is overly broad and will include YAML files from all packages, not just flagscale. Consider being more specific with the package selector, e.g., "flagscale" = [".yaml", "*.yml"] to only include YAML files from the flagscale package.
| "*" = ["*.yaml", "*.yml"] | |
| "flagscale" = ["*.yaml", "*.yml"] |
flagscale/__init__.py
Outdated
| from importlib.metadata import version as get_version | ||
|
|
||
| __version__ = get_version("flagscale") | ||
| except Exception: | ||
| pass # Use hardcoded version above |
There was a problem hiding this comment.
The bare except clause on line 9 catches all exceptions including SystemExit and KeyboardInterrupt, which is generally not recommended. This could mask important errors during import. Consider catching specific exceptions like PackageNotFoundError or ImportError instead, or at minimum use "except Exception:" to avoid catching system-level exceptions.
| from importlib.metadata import version as get_version | |
| __version__ = get_version("flagscale") | |
| except Exception: | |
| pass # Use hardcoded version above | |
| from importlib.metadata import PackageNotFoundError, version as get_version | |
| except ImportError: | |
| # importlib.metadata not available; use hardcoded version above | |
| pass | |
| else: | |
| try: | |
| __version__ = get_version("flagscale") | |
| except PackageNotFoundError: | |
| # Package metadata not found; use hardcoded version above | |
| pass |
| def get_action(stop: bool, dryrun: bool, test: bool, query: bool, tune: bool) -> str: | ||
| """Determine action from flags (mutually exclusive)""" | ||
| if stop: | ||
| return "stop" | ||
| if dryrun: | ||
| return "dryrun" | ||
| if test: | ||
| return "test" | ||
| if query: | ||
| return "query" | ||
| if tune: | ||
| return "auto_tune" | ||
| return "run" # default |
There was a problem hiding this comment.
The get_action() function doesn't validate that the boolean flags are mutually exclusive. If multiple flags are set (e.g., both --stop and --dryrun), the function will just return the first matching one based on the if-chain order. Consider adding validation to ensure only one action flag is set at a time, or document the priority order clearly.
setup.py
Outdated
| # Version is defined in pyproject.toml - keep in sync | ||
| FLAGSCALE_VERSION = "1.0.0" |
There was a problem hiding this comment.
The version is hardcoded in three places (setup.py line 7, pyproject.toml line 10, and flagscale/init.py line 2), which creates a maintenance burden. Consider having setup.py and flagscale/init.py read the version from pyproject.toml to maintain a single source of truth. This could use importlib.metadata or parse pyproject.toml directly.
|
Instead of having users to customize an installation by specifying environment variables, we can leverage the builtin mechanism in [project]
dependencies = [
"foo==1.2.3",
"core==2.4.0",
]
[project.optional-dependencies]
inference = [
"vllm==3.4.0",
"blah==2.3",
]
serve = [
"flag-scale[inference]",
"flask==1.0.3"
]
train = [
"megatron-lm-fl==0.1.0",
"..."
]You can easily install flag-scale for different scenarios using the following commands:
This is much simpler than writing our own Python code. |
|
PR #1078 was proposed exactly for simplify the installation process using declared dependencies. It can serve as a starting point for further revisions such as a better grouping of dependencies based on usage scenarios; a clearer dependency on the various |
This is our ultimate goal. |
PR Category
Tools
PR Types
Improvements
PR Description
Refactor the installation process for FlagScale to make it easier for users to install with pip.
FLAGSCALE_PLATFORM=cuda FLAGSCALE_TASK=train pip install --no-build-isolation . -vvv