agent-0 / run_augmentoolkit.py
Leon4gr45's picture
Upload folder using huggingface_hub
1499d83 verified
Raw History Blame Contribute Delete
10.1 kB
# Imports the function named by the config file, and runs it with the arguments inside the config specified
import importlib
import asyncio
import sys
import traceback
import yaml
import argparse
from pathlib import Path
import json
import io
# ==============================================================================
# FORCE UTF-8 FOR ALL I/O
# where the default encoding is not UTF-8. It prevents UnicodeEncodeError
# when printing characters that are not in the default system codepage.
# ==============================================================================
if sys.stdout.encoding != 'utf-8':
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
if sys.stderr.encoding != 'utf-8':
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
# ==============================================================================
from resolve_path import resolve_path
def load_function_from_path(function_path):
"""Dynamically import a function from a module path string"""
module_path, function_name = function_path.rsplit(".", 1)
# Ensure module path uses dots for importlib
module_import_path = module_path.replace("/", ".")
module = importlib.import_module(module_import_path)
return getattr(module, function_name)
def flatten_config(config, no_flatten_keys=None):
"""Flatten config while preserving specified nested structures"""
flattened = {}
no_flatten = set(no_flatten_keys or [])
for key, value in config.items():
if key in no_flatten:
flattened[key] = value
continue
if isinstance(value, dict):
nested_flat = flatten_config(value, no_flatten_keys)
for nested_key, nested_value in nested_flat.items():
if nested_key in flattened:
raise ValueError(f"Key conflict: '{nested_key}'")
flattened[nested_key] = nested_value
else:
if key in flattened:
raise ValueError(f"Key conflict: '{key}'")
flattened[key] = value
print("FLATTENED")
print(flattened)
return flattened
try:
with open("super_config.yaml", "r", encoding="utf-8") as f:
super_config = yaml.safe_load(f)
path_aliases = super_config.get(
"path_aliases", {}
) # Get aliases, default to empty dict if not present
except FileNotFoundError:
print(f"Error: Super config file not found at super_config.yaml")
sys.exit(1)
# Load super config
except yaml.YAMLError as e:
print(f"Error parsing super config file super_config.yaml: {e}")
sys.exit(1)
def run_pipeline(node, config, override_fields={}):
# Resolve node and config paths using aliases
resolved_node_path = resolve_path(node, path_aliases)
# print(f"DEBUG: Resolved node path: {resolved_node_path}")
# Handle optional config key and resolve its path if present
config_path_str = config
resolved_config_path_str = (
resolve_path(config_path_str, path_aliases) if config_path_str else None
)
# print(f"DEBUG: Resolved config path: {resolved_config_path_str}")
resolved_config_path_str = (
resolved_config_path_str
if resolved_config_path_str and resolved_config_path_str.endswith(".yaml")
else (
resolved_config_path_str + ".yaml"
if resolved_config_path_str
else resolved_config_path_str
)
)
# Load pipeline-specific config if a path is provided
config = {}
if resolved_config_path_str:
# Resolve config path relative to the script's directory
script_dir = Path(__file__).parent
config_path = (script_dir / resolved_config_path_str).resolve()
# print(f"DEBUG: Final resolved config path: {config_path}")
try:
with open(config_path, "r", encoding="utf-8") as f:
config = (
yaml.safe_load(f) or {}
) # Ensure config is at least an empty dict
except FileNotFoundError:
print(
f"Warning: Config file not found at {config_path}. Proceeding without it."
)
except Exception as e:
print(f"Error loading config file {config_path}: {e}")
# Decide if you want to raise the error or continue
# raise e
print("Proceeding with empty configuration for this pipeline.")
# print("DEBUG: CONFIG")
# print(config)
run_pipeline_config(
config=config,
resolved_node_path=resolved_node_path,
override_fields=override_fields,
)
def run_pipeline_config(
config, resolved_node_path, override_fields={}
): # the second half of run_pipeline, extracted so that it is easier to use in isolation as an api.
# Merge pipeline config from super_config with loaded config file parameters
# Parameters defined directly in the pipeline entry override those in the loaded config file.
# Parameters passed in as overrides override those in either.
# Flatten the nested configuration
no_flatten_keys = config.get(
"no_flatten", []
) # Get no_flatten from loaded/merged config
flattened_config = flatten_config(config, no_flatten_keys=no_flatten_keys)
flattened_config.update(override_fields)
# Import the target function using the resolved node path
try:
function = load_function_from_path(resolved_node_path)
except (ImportError, AttributeError, ValueError) as e:
print(f"Error loading function from node path '{resolved_node_path}': {e}")
print(f"Skipping pipeline: resolved_node_path") # Use name if available
return # Skip this pipeline if function cannot be loaded
if asyncio.iscoroutinefunction(function):
print(f"Running async pipeline: {resolved_node_path}")
# print("DEBUG: Flattened config")
# print(flattened_config)
try:
asyncio.run(function(**flattened_config))
except Exception as e:
print(f"Error running async pipeline {resolved_node_path}: {e}")
traceback.print_exc()
raise
# Optionally re-raise or handle error reporting
else:
print(f"Running sync pipeline: {resolved_node_path}")
try:
function(**flattened_config)
except Exception as e:
print(f"Error running sync pipeline {resolved_node_path}: {e}")
# Optionally re-raise or handle error reporting
raise
print(f"Completed pipeline: {resolved_node_path}")
def main():
parser = argparse.ArgumentParser(description="Run Augmentoolkit pipelines.")
parser.add_argument(
"--node",
type=str,
help="Path (potentially aliased) to the pipeline node function (e.g., 'pipelines/my_pipeline.run').",
)
parser.add_argument(
"--config",
type=str,
help="Path (potentially aliased) to the pipeline-specific configuration YAML file.",
)
parser.add_argument(
"--override-json",
type=str,
help="JSON string of parameters to override pipeline config.",
)
args = parser.parse_args()
# Always load path aliases
# path_aliases are already loaded globally before main()
override_params = {}
if args.override_json:
try:
override_params = json.loads(args.override_json)
if not isinstance(override_params, dict):
print(
"Error: --override-json must be a valid JSON object (dictionary)."
)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"Error parsing --override-json: {e}")
sys.exit(1)
# Decide execution path based on provided arguments
if args.node:
print(f"Running single pipeline specified via command line arguments:")
print(f" Node: {args.node}")
print(f" Config: {args.config}")
print(f" Overrides: {override_params}")
run_pipeline(args.node, args.config, override_params)
else:
print("Running pipelines defined in super_config.yaml 'pipeline_order'.")
# Run pipelines in order specified in super_config
pipelines_to_run = super_config.get("pipeline_order", [])
if not pipelines_to_run:
print(
"No pipelines specified in 'pipeline_order' and no specific pipeline provided via args. Exiting."
)
return
for pipeline in pipelines_to_run:
if not isinstance(pipeline, dict) or "node" not in pipeline:
print(
f"Warning: Skipping invalid pipeline entry in super_config: {pipeline}. Must be a dictionary with a 'node' key."
)
continue
# Merge super_config parameters with any CLI overrides (though CLI overrides usually imply single pipeline run)
pipeline_params = pipeline.get("parameters", {})
# Note: If running from super_config, CLI overrides are *not* typically used per-pipeline.
# The logic here prioritizes the CLI override if BOTH --override-json and super_config parameters exist,
# but this scenario is less common when running the whole sequence.
# If you intended CLI overrides to *only* apply when --node is used, keep override_params empty here.
# If CLI overrides should *globally* apply even to super_config runs, update pipeline_params:
# pipeline_params.update(override_params) # Uncomment this if CLI overrides should apply globally
print(f"Running pipeline from super_config:")
print(f" Node: {pipeline['node']}")
print(f" Config: {pipeline.get('config')}") # Use .get for optional config
print(f" Parameters: {pipeline_params}")
run_pipeline(
pipeline["node"], pipeline.get("config"), pipeline_params
) # Use .get for config key
if __name__ == "__main__":
main()