newmeasure-cli 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: newmeasure-cli
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: NewMeasure eval CLI
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: httpx>=0.28
@@ -16,7 +16,7 @@ Description-Content-Type: text/markdown
16
16
 
17
17
  *Upload eval tasks, start cloud runs, and download results with `newm`*
18
18
 
19
- [Install](#install) • [Usage](#usage) • [Browser tasks](#browser-tasks) • [Commands](#commands) • [Development](#development)
19
+ [Install](#install) • [Usage](#usage) • [Browser tasks](#browser-tasks) • [Edit tasks](#edit-tasks) • [Commands](#commands) • [Development](#development)
20
20
 
21
21
  </div>
22
22
 
@@ -120,6 +120,28 @@ Downloads include `outputs/` (the files the product generated, such as PDF and H
120
120
 
121
121
  A browser task bundle contains `task.toml`, `instruction.md`, and, when present, `scenario.yaml`, `product.md` (or `../../shared/product.md`), and the `environment/` and `tests/` folders.
122
122
 
123
+ ## Edit tasks
124
+
125
+ Turn a Solstice edit session into an Edit-suite task in the app. This uses the personal CLI token
126
+ from `newm login --evals`. Pass the session's `operation_id` or its asset URL
127
+ (`https://<tenant>.solstice.co/home/assets/<operation_id>`):
128
+
129
+ ```bash
130
+ # List the operation's turns, with their surface, whether starting HTML is ready, and the request
131
+ newm edit-task 0b7f2a7e-1111-4222-8333-444455556666
132
+
133
+ # Create a task from turn 2 (a turn number or turn ID)
134
+ newm edit-task 0b7f2a7e-1111-4222-8333-444455556666 --turn 2
135
+
136
+ # Merge consecutive turns into one task, with your own title
137
+ newm edit-task https://acme.solstice.co/home/assets/0b7f2a7e-1111-4222-8333-444455556666 \
138
+ -t 2 -t 3 --title "Swap headline and CTA"
139
+ ```
140
+
141
+ The task's checks and title are drafted from the chosen turns; refine them in the app's Edits tab.
142
+ Turns listed as `awaiting` have no starting HTML yet: the task is created, but stays
143
+ `awaiting_html` until the HTML is imported.
144
+
123
145
  ## Commands
124
146
 
125
147
  ```bash
@@ -130,6 +152,8 @@ newm result <run-id> # run details and trial results
130
152
  newm artifacts <run-id> <trial-id> [--output DIR]
131
153
  newm tasks # list uploaded tasks
132
154
  newm runs [<task-id>] # list runs
155
+ newm edit-task <operation> [-t <turn> ...] # list turns or create an edit task
156
+ newm logins # list saved logins
133
157
  newm harbor ... # pass through to the Harbor CLI
134
158
  newm --help
135
159
  ```
@@ -5,7 +5,7 @@
5
5
 
6
6
  *Upload eval tasks, start cloud runs, and download results with `newm`*
7
7
 
8
- [Install](#install) • [Usage](#usage) • [Browser tasks](#browser-tasks) • [Commands](#commands) • [Development](#development)
8
+ [Install](#install) • [Usage](#usage) • [Browser tasks](#browser-tasks) • [Edit tasks](#edit-tasks) • [Commands](#commands) • [Development](#development)
9
9
 
10
10
  </div>
11
11
 
@@ -109,6 +109,28 @@ Downloads include `outputs/` (the files the product generated, such as PDF and H
109
109
 
110
110
  A browser task bundle contains `task.toml`, `instruction.md`, and, when present, `scenario.yaml`, `product.md` (or `../../shared/product.md`), and the `environment/` and `tests/` folders.
111
111
 
112
+ ## Edit tasks
113
+
114
+ Turn a Solstice edit session into an Edit-suite task in the app. This uses the personal CLI token
115
+ from `newm login --evals`. Pass the session's `operation_id` or its asset URL
116
+ (`https://<tenant>.solstice.co/home/assets/<operation_id>`):
117
+
118
+ ```bash
119
+ # List the operation's turns, with their surface, whether starting HTML is ready, and the request
120
+ newm edit-task 0b7f2a7e-1111-4222-8333-444455556666
121
+
122
+ # Create a task from turn 2 (a turn number or turn ID)
123
+ newm edit-task 0b7f2a7e-1111-4222-8333-444455556666 --turn 2
124
+
125
+ # Merge consecutive turns into one task, with your own title
126
+ newm edit-task https://acme.solstice.co/home/assets/0b7f2a7e-1111-4222-8333-444455556666 \
127
+ -t 2 -t 3 --title "Swap headline and CTA"
128
+ ```
129
+
130
+ The task's checks and title are drafted from the chosen turns; refine them in the app's Edits tab.
131
+ Turns listed as `awaiting` have no starting HTML yet: the task is created, but stays
132
+ `awaiting_html` until the HTML is imported.
133
+
112
134
  ## Commands
113
135
 
114
136
  ```bash
@@ -119,6 +141,8 @@ newm result <run-id> # run details and trial results
119
141
  newm artifacts <run-id> <trial-id> [--output DIR]
120
142
  newm tasks # list uploaded tasks
121
143
  newm runs [<task-id>] # list runs
144
+ newm edit-task <operation> [-t <turn> ...] # list turns or create an edit task
145
+ newm logins # list saved logins
122
146
  newm harbor ... # pass through to the Harbor CLI
123
147
  newm --help
124
148
  ```
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+
5
+ import typer
6
+ from rich.console import Console
7
+ from rich.table import Table
8
+
9
+ from cli.api_client import evals_client
10
+
11
+ console = Console()
12
+
13
+
14
+ def _turn_ids(turns: list[dict], picks: list[str]) -> list[str]:
15
+ """Map 1-based turn numbers to turn IDs; pass turn IDs through."""
16
+ by_seq = {str(t["seq"]): t["turn_id"] for t in turns}
17
+ known = {t["turn_id"] for t in turns}
18
+ out = []
19
+ for pick in picks:
20
+ tid = by_seq.get(pick, pick)
21
+ if tid not in known:
22
+ raise typer.BadParameter(
23
+ f"Turn {pick!r} is not in this operation; run without --turn to list turns"
24
+ )
25
+ out.append(tid)
26
+ return out
27
+
28
+
29
+ def _show_turns(inspected: dict) -> None:
30
+ console.print(
31
+ f"Operation {inspected['operation_id']} {inspected.get('brand_label') or ''}"
32
+ f" {inspected.get('kind') or ''}"
33
+ )
34
+ table = Table()
35
+ for col in ("#", "Turn ID", "Surface", "HTML", "Request"):
36
+ table.add_column(col)
37
+ for t in inspected["turns"]:
38
+ request = " ".join((t.get("user_request") or "").split())
39
+ table.add_row(
40
+ str(t["seq"]),
41
+ t["turn_id"],
42
+ t.get("surface") or "",
43
+ ("ready" if t.get("html_ready") else "awaiting")
44
+ + (" (reverted)" if t.get("reverted") else ""),
45
+ request[:80] + ("…" if len(request) > 80 else ""),
46
+ )
47
+ console.print(table)
48
+ for task in inspected.get("existing_tasks") or []:
49
+ console.print(
50
+ f"Existing task {task['id']}: {task['title']} (turns {', '.join(task['turn_ids'])})"
51
+ )
52
+ console.print("Create a task with: newm edit-task <operation> --turn <#>")
53
+
54
+
55
+ def edit_task(operation: str, turns: list[str], title: str) -> None:
56
+ """List an operation's turns, or create an edit task from the chosen turns."""
57
+ with evals_client() as c:
58
+ r = c.post("edit-tasks/inspect", json={"operation": operation})
59
+ r.raise_for_status()
60
+ inspected = r.json()
61
+ if not turns:
62
+ _show_turns(inspected)
63
+ return
64
+ r = c.post(
65
+ "edit-tasks",
66
+ json={
67
+ "operation": inspected["operation_id"],
68
+ "turn_ids": _turn_ids(inspected["turns"], turns),
69
+ "title": title,
70
+ },
71
+ )
72
+ r.raise_for_status()
73
+ typer.echo(json.dumps(r.json(), indent=1))
@@ -9,6 +9,7 @@ import typer
9
9
 
10
10
  from cli.commands.admin import admin_app
11
11
  from cli.commands.artifacts import artifacts as artifacts_cmd
12
+ from cli.commands.edit_task import edit_task as edit_task_cmd
12
13
  from cli.commands.login import login as login_cmd
13
14
  from cli.commands.result import result as result_cmd
14
15
  from cli.commands.run import run as run_cmd
@@ -130,6 +131,23 @@ def result(run_id: str = typer.Argument(..., help="Run ID")):
130
131
  result_cmd(run_id)
131
132
 
132
133
 
134
+ @app.command("edit-task")
135
+ def edit_task(
136
+ operation: str = typer.Argument(
137
+ ..., help="Solstice operation ID or /home/assets/<id> URL"
138
+ ),
139
+ turn: list[str] = typer.Option(
140
+ [],
141
+ "--turn",
142
+ "-t",
143
+ help="Turn number or turn ID to convert; repeat to merge turns. Omit to list turns",
144
+ ),
145
+ title: str = typer.Option("", "--title", help="Task title (default: drafted)"),
146
+ ):
147
+ """Create an Edit-suite task from a Solstice operation."""
148
+ edit_task_cmd(operation, turn, title)
149
+
150
+
133
151
  @app.command(
134
152
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
135
153
  )
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "newmeasure-cli"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "NewMeasure eval CLI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"