File size: 3,302 Bytes
9c84f9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""Project management commands — local YAML or remote API."""

import click

from solar_eval.cli.config import config
from solar_eval.cli.formatters import format_projects_table


def _resolve_project_id(client, name_or_id: str) -> str:
    """Resolve a project name to its API ID (remote mode only)."""
    if len(name_or_id) == 24:
        try:
            int(name_or_id, 16)
            return name_or_id
        except ValueError:
            pass
    projects = client.get("/api/projects")
    for p in projects:
        if p["name"] == name_or_id:
            return p["id"]
    raise click.ClickException(f"Project not found: {name_or_id}")


@click.group("projects")
def projects_group() -> None:
    """Manage evaluation projects."""


@projects_group.command("list")
@click.pass_context
def list_projects(ctx: click.Context) -> None:
    """List projects (from YAML locally, or from API remotely)."""
    cfg = ctx.obj["config"]
    if cfg.is_remote:
        from solar_eval.cli.client import EvalClient
        client = EvalClient(cfg.remote_url, cfg.timeout)
        projects = client.get("/api/projects")
        if not projects:
            click.secho("No projects found.", fg="yellow")
            return
        click.secho(format_projects_table(projects), fg="blue")
    else:
        from solar_eval.core.project_loader import load_all_project_configs
        configs = load_all_project_configs(cfg.projects_dir, cfg.config_dirs)
        if not configs:
            click.secho(f"No project YAML files in {cfg.projects_dir}", fg="yellow")
            return
        rows = [{
            "name": c["name"],
            "display_name": c.get("display_name", c["name"]),
            "description": c.get("description", ""),
            "task_count": len(c.get("tasks", [])),
        } for c in configs]
        click.secho(format_projects_table(rows), fg="blue")


@projects_group.command("show")
@click.argument("name")
@click.pass_context
def show_project(ctx: click.Context, name: str) -> None:
    """Show project details by name."""
    cfg = ctx.obj["config"]
    if cfg.is_remote:
        from solar_eval.cli.client import EvalClient
        client = EvalClient(cfg.remote_url, cfg.timeout)
        project_id = _resolve_project_id(client, name)
        project = client.get(f"/api/projects/{project_id}")
    else:
        from solar_eval.core.project_loader import load_all_project_configs
        configs = load_all_project_configs(cfg.projects_dir, cfg.config_dirs)
        project = next((c for c in configs if c["name"] == name), None)
        if not project:
            raise click.ClickException(f"Project not found: {name}")
        project["display_name"] = project.get("display_name", name)
        project["config"] = project

    click.secho(f"Project: {project.get('display_name', name)}", fg="blue", bold=True)
    click.echo(f"  Name:        {project.get('name', name)}")
    click.echo(f"  Description: {project.get('description', '-')}")
    tasks = project.get("config", {}).get("tasks", project.get("tasks", []))
    if tasks:
        click.echo(f"  Tasks ({len(tasks)}):")
        for t in tasks:
            ev = t.get("evaluator", {})
            click.echo(f"    - {t['name']} (pipeline: {t.get('pipeline', '?')}, evaluator: {ev.get('type', '?')})")