"""Prompt management commands — list and show prompts.""" import click from tabulate import tabulate from solar_eval.cli.commands.projects import _resolve_project_id @click.group("prompts") def prompts_group() -> None: """Manage evaluation prompts.""" @prompts_group.command("list") @click.argument("project") @click.pass_context def list_prompts(ctx, project): """List available prompt YAML files.""" cfg = ctx.obj["config"] if cfg.is_remote: from solar_eval.cli.client import EvalClient from solar_eval.cli.formatters import format_prompts_table client = EvalClient(cfg.remote_url, cfg.timeout) project_id = _resolve_project_id(client, project) prompts = client.get(f"/api/projects/{project_id}/prompts") if not prompts: click.secho("No prompts found.", fg="yellow") return click.secho(format_prompts_table(prompts), fg="blue") return # Local mode: list YAML files + TXT directories in prompts/ prompts_dir = cfg.prompts_dir(project) if not prompts_dir.exists(): click.secho("No prompts directory found.", fg="yellow") return entries = [] # YAML files for f in sorted(prompts_dir.glob("*.yaml")): entries.append(("yaml", f.stem, f)) # TXT directories (exclude _library, __pycache__) for d in sorted(prompts_dir.iterdir()): if d.is_dir() and not d.name.startswith("_") and list(d.glob("*.txt")): entries.append(("txt_dir", d.name, d)) if not entries: click.secho("No prompt files found.", fg="yellow") return def _classify_type(name: str) -> str: if name.startswith("prod_"): return "prod" if name.startswith("dev_"): return "dev" if name.startswith("ci_"): return "ci" if name.startswith("exp"): return "exp" return "-" rows = [] for fmt, name, path in entries: ptype = _classify_type(name) if fmt == "yaml": size_kb = path.stat().st_size / 1024 rows.append([name, ptype, "yaml", f"{size_kb:.1f}KB"]) else: txt_files = list(path.glob("*.txt")) total = sum(f.stat().st_size for f in txt_files) rows.append([name, ptype, f"txt({len(txt_files)})", f"{total / 1024:.1f}KB"]) click.secho(f"\nPrompts: {project} ({len(rows)} entries)", fg="blue", bold=True) click.echo(tabulate(rows, headers=["Name", "Type", "Format", "Size"], tablefmt="simple")) @prompts_group.command("show") @click.argument("project") @click.option("--prompt", "-p", "prompt_name", required=True, help="Prompt name") @click.pass_context def show_prompt(ctx, project, prompt_name): """Show prompt content.""" 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, project) prompt = client.get(f"/api/projects/{project_id}/prompts/{prompt_name}") click.secho(f"Prompt: {prompt_name}", fg="blue", bold=True) click.echo(prompt.get("system_prompt", "")) return # Local mode from solar_eval.cli.commands.runs import _resolve_prompt from solar_eval.models.prompt_version import ( detect_prompt_format, load_prompt_messages, load_step_prompts, ) prompts_dir = cfg.prompts_dir(project) prompt_path = _resolve_prompt(prompts_dir, prompt_name) is_dir = prompt_path.is_dir() click.secho(f"Prompt: {prompt_name}", fg="blue", bold=True) click.echo(f" {'Directory' if is_dir else 'File'}: {prompt_path}") click.echo() fmt = detect_prompt_format(prompt_path) if fmt == "multi_step": step_prompts = load_step_prompts(prompt_path) for step_name, content in step_prompts.items(): click.secho(f"[{step_name}]", fg="cyan", bold=True) lines = content.strip().split("\n") preview = "\n".join(lines[:5]) click.echo(preview) if len(lines) > 5: click.secho(f" ... ({len(lines) - 5} more lines)", fg="yellow") click.echo() else: messages = load_prompt_messages(prompt_path) for msg in messages: click.secho(f"[{msg['role']}]", fg="cyan", bold=True) click.echo(msg["content"]) click.echo() @prompts_group.command("create") @click.argument("project") @click.option("--task", "-t", required=True) @click.option("--prompt-file", "-f", type=click.Path(exists=True), required=True) @click.option("--model", "-m", default="solar-pro2") @click.option("--temperature", default=0.0, type=float) @click.option("--max-tokens", default=8000, type=int) @click.pass_context def create_prompt(ctx, project, task, prompt_file, model, temperature, max_tokens): """Create a new prompt version (remote mode).""" cfg = ctx.obj["config"] if not cfg.is_remote: raise click.ClickException("Use 'prompts add' for local mode") from solar_eval.cli.client import EvalClient client = EvalClient(cfg.remote_url, cfg.timeout) project_id = _resolve_project_id(client, project) with open(prompt_file) as f: system_prompt = f.read() result = client.post( f"/api/projects/{project_id}/prompts", json={ "project_id": project_id, "task": task, "system_prompt": system_prompt, "model": model, "temperature": temperature, "max_tokens": max_tokens, }, ) click.secho(f"Created prompt v{result.get('version', '?')} (ID: {result['id']})", fg="green")