Spaces:
Sleeping
Sleeping
| import argparse | |
| from trackio import show, sync | |
| from trackio.cli_helpers import ( | |
| error_exit, | |
| format_json, | |
| format_list, | |
| format_metric_values, | |
| format_project_summary, | |
| format_run_summary, | |
| format_system_metric_names, | |
| format_system_metrics, | |
| ) | |
| from trackio.markdown import Markdown | |
| from trackio.sqlite_storage import SQLiteStorage | |
| from trackio.ui.main import get_project_summary, get_run_summary | |
| def _handle_status(): | |
| print("Reading local Trackio projects...\n") | |
| projects = SQLiteStorage.get_projects() | |
| if not projects: | |
| print("No Trackio projects found.") | |
| return | |
| local_projects = [] | |
| synced_projects = [] | |
| unsynced_projects = [] | |
| for project in projects: | |
| space_id = SQLiteStorage.get_space_id(project) | |
| if space_id is None: | |
| local_projects.append(project) | |
| elif SQLiteStorage.has_pending_data(project): | |
| unsynced_projects.append(project) | |
| else: | |
| synced_projects.append(project) | |
| print("Finished reading Trackio projects") | |
| if local_projects: | |
| print(f" * {len(local_projects)} local trackio project(s) [OK]") | |
| if synced_projects: | |
| print(f" * {len(synced_projects)} trackio project(s) synced to Spaces [OK]") | |
| if unsynced_projects: | |
| print( | |
| f" * {len(unsynced_projects)} trackio project(s) with unsynced changes [WARNING]:" | |
| ) | |
| for p in unsynced_projects: | |
| print(f" - {p}") | |
| if unsynced_projects: | |
| print( | |
| f"\nRun `trackio sync --project {unsynced_projects[0]}` to sync. " | |
| "Or run `trackio sync --all` to sync all unsynced changes." | |
| ) | |
| def _handle_sync(args): | |
| from trackio.deploy import sync_incremental | |
| if args.sync_all and args.project: | |
| error_exit("Cannot use --all and --project together.") | |
| if not args.sync_all and not args.project: | |
| error_exit("Must provide either --project or --all.") | |
| if args.sync_all: | |
| projects = SQLiteStorage.get_projects() | |
| synced_any = False | |
| for project in projects: | |
| space_id = SQLiteStorage.get_space_id(project) | |
| if space_id and SQLiteStorage.has_pending_data(project): | |
| sync_incremental( | |
| project, space_id, private=args.private, pending_only=True | |
| ) | |
| synced_any = True | |
| if not synced_any: | |
| print("No projects with unsynced data found.") | |
| else: | |
| space_id = args.space_id | |
| if space_id is None: | |
| space_id = SQLiteStorage.get_space_id(args.project) | |
| sync( | |
| project=args.project, | |
| space_id=space_id, | |
| private=args.private, | |
| force=args.force, | |
| ) | |
| def _extract_reports( | |
| run: str, logs: list[dict], report_name: str | None = None | |
| ) -> list[dict]: | |
| reports = [] | |
| for log in logs: | |
| timestamp = log.get("timestamp") | |
| step = log.get("step") | |
| for key, value in log.items(): | |
| if report_name is not None and key != report_name: | |
| continue | |
| if isinstance(value, dict) and value.get("_type") == Markdown.TYPE: | |
| content = value.get("_value") | |
| if isinstance(content, str): | |
| reports.append( | |
| { | |
| "run": run, | |
| "report": key, | |
| "step": step, | |
| "timestamp": timestamp, | |
| "content": content, | |
| } | |
| ) | |
| return reports | |
| def main(): | |
| parser = argparse.ArgumentParser(description="Trackio CLI") | |
| subparsers = parser.add_subparsers(dest="command") | |
| ui_parser = subparsers.add_parser( | |
| "show", help="Show the Trackio dashboard UI for a project" | |
| ) | |
| ui_parser.add_argument( | |
| "--project", required=False, help="Project name to show in the dashboard" | |
| ) | |
| ui_parser.add_argument( | |
| "--theme", | |
| required=False, | |
| default="default", | |
| help="A Gradio Theme to use for the dashboard instead of the default, can be a built-in theme (e.g. 'soft', 'citrus'), or a theme from the Hub (e.g. 'gstaff/xkcd').", | |
| ) | |
| ui_parser.add_argument( | |
| "--mcp-server", | |
| action="store_true", | |
| help="Enable MCP server functionality. The Trackio dashboard will be set up as an MCP server and certain functions will be exposed as MCP tools.", | |
| ) | |
| ui_parser.add_argument( | |
| "--footer", | |
| action="store_true", | |
| default=True, | |
| help="Show the Gradio footer. Use --no-footer to hide it.", | |
| ) | |
| ui_parser.add_argument( | |
| "--no-footer", | |
| dest="footer", | |
| action="store_false", | |
| help="Hide the Gradio footer.", | |
| ) | |
| ui_parser.add_argument( | |
| "--color-palette", | |
| required=False, | |
| help="Comma-separated list of hex color codes for plot lines (e.g. '#FF0000,#00FF00,#0000FF'). If not provided, the TRACKIO_COLOR_PALETTE environment variable will be used, or the default palette if not set.", | |
| ) | |
| ui_parser.add_argument( | |
| "--host", | |
| required=False, | |
| help="Host to bind the server to (e.g. '0.0.0.0' for remote access). If not provided, defaults to '127.0.0.1' (localhost only).", | |
| ) | |
| subparsers.add_parser( | |
| "status", | |
| help="Show the status of all local Trackio projects, including sync status.", | |
| ) | |
| sync_parser = subparsers.add_parser( | |
| "sync", | |
| help="Sync a local project's database to a Hugging Face Space. If the Space does not exist, it will be created.", | |
| ) | |
| sync_parser.add_argument( | |
| "--project", | |
| required=False, | |
| help="The name of the local project.", | |
| ) | |
| sync_parser.add_argument( | |
| "--space-id", | |
| required=False, | |
| help="The Hugging Face Space ID where the project will be synced (e.g. username/space_id). If not provided, uses the previously-configured Space.", | |
| ) | |
| sync_parser.add_argument( | |
| "--all", | |
| action="store_true", | |
| dest="sync_all", | |
| help="Sync all projects that have unsynced data to their configured Spaces.", | |
| ) | |
| sync_parser.add_argument( | |
| "--private", | |
| action="store_true", | |
| help="Make the Hugging Face Space private if creating a new Space. By default, the repo will be public unless the organization's default is private. This value is ignored if the repo already exists.", | |
| ) | |
| sync_parser.add_argument( | |
| "--force", | |
| action="store_true", | |
| help="Overwrite the existing database without prompting for confirmation.", | |
| ) | |
| list_parser = subparsers.add_parser( | |
| "list", | |
| help="List projects, runs, or metrics", | |
| ) | |
| list_subparsers = list_parser.add_subparsers(dest="list_type", required=True) | |
| list_projects_parser = list_subparsers.add_parser( | |
| "projects", | |
| help="List all projects", | |
| ) | |
| list_projects_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| list_runs_parser = list_subparsers.add_parser( | |
| "runs", | |
| help="List runs for a project", | |
| ) | |
| list_runs_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| list_runs_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| list_metrics_parser = list_subparsers.add_parser( | |
| "metrics", | |
| help="List metrics for a run", | |
| ) | |
| list_metrics_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| list_metrics_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| list_metrics_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| list_system_metrics_parser = list_subparsers.add_parser( | |
| "system-metrics", | |
| help="List system metrics for a run", | |
| ) | |
| list_system_metrics_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| list_system_metrics_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| list_system_metrics_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| list_reports_parser = list_subparsers.add_parser( | |
| "reports", | |
| help="List markdown reports for a project or run", | |
| ) | |
| list_reports_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| list_reports_parser.add_argument( | |
| "--run", | |
| required=False, | |
| help="Run name (optional)", | |
| ) | |
| list_reports_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| get_parser = subparsers.add_parser( | |
| "get", | |
| help="Get project, run, or metric information", | |
| ) | |
| get_subparsers = get_parser.add_subparsers(dest="get_type", required=True) | |
| get_project_parser = get_subparsers.add_parser( | |
| "project", | |
| help="Get project summary", | |
| ) | |
| get_project_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| get_project_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| get_run_parser = get_subparsers.add_parser( | |
| "run", | |
| help="Get run summary", | |
| ) | |
| get_run_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| get_run_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| get_run_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| get_metric_parser = get_subparsers.add_parser( | |
| "metric", | |
| help="Get metric values for a run", | |
| ) | |
| get_metric_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| get_metric_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| get_metric_parser.add_argument( | |
| "--metric", | |
| required=True, | |
| help="Metric name", | |
| ) | |
| get_metric_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| get_system_metric_parser = get_subparsers.add_parser( | |
| "system-metric", | |
| help="Get system metric values for a run", | |
| ) | |
| get_system_metric_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| get_system_metric_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| get_system_metric_parser.add_argument( | |
| "--metric", | |
| required=False, | |
| help="System metric name (optional, if not provided returns all system metrics)", | |
| ) | |
| get_system_metric_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| get_report_parser = get_subparsers.add_parser( | |
| "report", | |
| help="Get markdown report entries for a run", | |
| ) | |
| get_report_parser.add_argument( | |
| "--project", | |
| required=True, | |
| help="Project name", | |
| ) | |
| get_report_parser.add_argument( | |
| "--run", | |
| required=True, | |
| help="Run name", | |
| ) | |
| get_report_parser.add_argument( | |
| "--report", | |
| required=True, | |
| help="Report metric name", | |
| ) | |
| get_report_parser.add_argument( | |
| "--json", | |
| action="store_true", | |
| help="Output in JSON format", | |
| ) | |
| args = parser.parse_args() | |
| if args.command == "show": | |
| color_palette = None | |
| if args.color_palette: | |
| color_palette = [color.strip() for color in args.color_palette.split(",")] | |
| show( | |
| project=args.project, | |
| theme=args.theme, | |
| mcp_server=args.mcp_server, | |
| footer=args.footer, | |
| color_palette=color_palette, | |
| host=args.host, | |
| ) | |
| elif args.command == "status": | |
| _handle_status() | |
| elif args.command == "sync": | |
| _handle_sync(args) | |
| elif args.command == "list": | |
| if args.list_type == "projects": | |
| projects = SQLiteStorage.get_projects() | |
| if args.json: | |
| print(format_json({"projects": projects})) | |
| else: | |
| print(format_list(projects, "Projects")) | |
| elif args.list_type == "runs": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.json: | |
| print(format_json({"project": args.project, "runs": runs})) | |
| else: | |
| print(format_list(runs, f"Runs in '{args.project}'")) | |
| elif args.list_type == "metrics": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| metrics = SQLiteStorage.get_all_metrics_for_run(args.project, args.run) | |
| if args.json: | |
| print( | |
| format_json( | |
| {"project": args.project, "run": args.run, "metrics": metrics} | |
| ) | |
| ) | |
| else: | |
| print( | |
| format_list( | |
| metrics, f"Metrics for '{args.run}' in '{args.project}'" | |
| ) | |
| ) | |
| elif args.list_type == "system-metrics": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| system_metrics = SQLiteStorage.get_all_system_metrics_for_run( | |
| args.project, args.run | |
| ) | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "system_metrics": system_metrics, | |
| } | |
| ) | |
| ) | |
| else: | |
| print(format_system_metric_names(system_metrics)) | |
| elif args.list_type == "reports": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run and args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| target_runs = [args.run] if args.run else runs | |
| all_reports = [] | |
| for run_name in target_runs: | |
| logs = SQLiteStorage.get_logs(args.project, run_name) | |
| all_reports.extend(_extract_reports(run_name, logs)) | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "reports": all_reports, | |
| } | |
| ) | |
| ) | |
| else: | |
| report_lines = [ | |
| f"{entry['run']} | {entry['report']} | step={entry['step']} | {entry['timestamp']}" | |
| for entry in all_reports | |
| ] | |
| if args.run: | |
| print( | |
| format_list( | |
| report_lines, | |
| f"Reports for '{args.run}' in '{args.project}'", | |
| ) | |
| ) | |
| else: | |
| print(format_list(report_lines, f"Reports in '{args.project}'")) | |
| elif args.command == "get": | |
| if args.get_type == "project": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| summary = get_project_summary(args.project) | |
| if args.json: | |
| print(format_json(summary)) | |
| else: | |
| print(format_project_summary(summary)) | |
| elif args.get_type == "run": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| summary = get_run_summary(args.project, args.run) | |
| if args.json: | |
| print(format_json(summary)) | |
| else: | |
| print(format_run_summary(summary)) | |
| elif args.get_type == "metric": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| metrics = SQLiteStorage.get_all_metrics_for_run(args.project, args.run) | |
| if args.metric not in metrics: | |
| error_exit( | |
| f"Metric '{args.metric}' not found in run '{args.run}' of project '{args.project}'." | |
| ) | |
| values = SQLiteStorage.get_metric_values( | |
| args.project, args.run, args.metric | |
| ) | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "metric": args.metric, | |
| "values": values, | |
| } | |
| ) | |
| ) | |
| else: | |
| print(format_metric_values(values)) | |
| elif args.get_type == "system-metric": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| if args.metric: | |
| system_metrics = SQLiteStorage.get_system_logs(args.project, args.run) | |
| all_system_metric_names = SQLiteStorage.get_all_system_metrics_for_run( | |
| args.project, args.run | |
| ) | |
| if args.metric not in all_system_metric_names: | |
| error_exit( | |
| f"System metric '{args.metric}' not found in run '{args.run}' of project '{args.project}'." | |
| ) | |
| filtered_metrics = [ | |
| { | |
| k: v | |
| for k, v in entry.items() | |
| if k == "timestamp" or k == args.metric | |
| } | |
| for entry in system_metrics | |
| if args.metric in entry | |
| ] | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "metric": args.metric, | |
| "values": filtered_metrics, | |
| } | |
| ) | |
| ) | |
| else: | |
| print(format_system_metrics(filtered_metrics)) | |
| else: | |
| system_metrics = SQLiteStorage.get_system_logs(args.project, args.run) | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "system_metrics": system_metrics, | |
| } | |
| ) | |
| ) | |
| else: | |
| print(format_system_metrics(system_metrics)) | |
| elif args.get_type == "report": | |
| db_path = SQLiteStorage.get_project_db_path(args.project) | |
| if not db_path.exists(): | |
| error_exit(f"Project '{args.project}' not found.") | |
| runs = SQLiteStorage.get_runs(args.project) | |
| if args.run not in runs: | |
| error_exit(f"Run '{args.run}' not found in project '{args.project}'.") | |
| logs = SQLiteStorage.get_logs(args.project, args.run) | |
| reports = _extract_reports(args.run, logs, report_name=args.report) | |
| if not reports: | |
| error_exit( | |
| f"Report '{args.report}' not found in run '{args.run}' of project '{args.project}'." | |
| ) | |
| if args.json: | |
| print( | |
| format_json( | |
| { | |
| "project": args.project, | |
| "run": args.run, | |
| "report": args.report, | |
| "values": reports, | |
| } | |
| ) | |
| ) | |
| else: | |
| output = [] | |
| for idx, entry in enumerate(reports, start=1): | |
| output.append( | |
| f"Entry {idx} | step={entry['step']} | timestamp={entry['timestamp']}" | |
| ) | |
| output.append(entry["content"]) | |
| if idx < len(reports): | |
| output.append("-" * 80) | |
| print("\n".join(output)) | |
| else: | |
| parser.print_help() | |
| if __name__ == "__main__": | |
| main() | |