hte-cli 0.2.8__py3-none-any.whl → 0.2.16__py3-none-any.whl

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.
hte_cli/cli.py CHANGED
@@ -311,41 +311,20 @@ def session_join(ctx, session_id: str, force_setup: bool):
311
311
  continue
312
312
 
313
313
  # Need to pull - show progress
314
- last_status = ["connecting..."] # Use list for closure mutability
314
+ last_status = ["connecting..."]
315
315
  with console.status(f"[yellow]↓[/yellow] {short_name} [dim]connecting...[/dim]") as status:
316
316
  def show_progress(image: str, line: str):
317
- # Parse docker pull output for layer progress
318
- # Lines look like: "abc123: Downloading [====> ] 10MB/50MB"
319
- # Or: "abc123: Extracting [====> ] 10MB/50MB"
320
- # Or: "abc123: Pull complete", "Digest: sha256:...", "Status: ..."
321
- display_text = None
317
+ # Show docker output directly - includes MB progress from PTY
318
+ # Lines look like: "abc123: Downloading 360.9MB/4.075GB"
322
319
  if ": " in line:
323
320
  parts = line.split(": ", 1)
324
321
  if len(parts) == 2:
325
- layer_id = parts[0][-8:] # Last 8 chars of layer ID
326
- layer_status = parts[1] # Don't truncate - keep full progress
327
- # Show progress bars for Downloading/Extracting with MB info
328
- if "Downloading" in layer_status or "Extracting" in layer_status:
329
- # Keep progress: "[====> ] 10.5MB/50MB"
330
- display_text = f"{layer_id}: {layer_status[:50]}"
331
- elif "Pull complete" in layer_status:
332
- display_text = f"{layer_id}: done"
333
- elif "Download complete" in layer_status:
334
- display_text = f"{layer_id}: download done"
335
- elif "Already exists" in layer_status:
336
- display_text = f"{layer_id}: cached"
337
- elif "Waiting" in layer_status:
338
- display_text = f"{layer_id}: waiting"
339
- elif "Verifying" in layer_status:
340
- display_text = f"{layer_id}: verifying"
341
- else:
342
- display_text = line[:55]
343
- elif line.strip():
344
- display_text = line[:55]
345
-
346
- if display_text and display_text != last_status[0]:
347
- last_status[0] = display_text
348
- status.update(f"[yellow]↓[/yellow] {short_name} [dim]{display_text}[/dim]")
322
+ layer_id = parts[0][-8:]
323
+ layer_status = parts[1][:45]
324
+ display = f"{layer_id}: {layer_status}"
325
+ if display != last_status[0]:
326
+ last_status[0] = display
327
+ status.update(f"[yellow]↓[/yellow] {short_name} [dim]{display}[/dim]")
349
328
 
350
329
  success = pull_image_with_progress(img, on_progress=show_progress)
351
330
 
@@ -375,6 +354,15 @@ def session_join(ctx, session_id: str, force_setup: bool):
375
354
  console.print("[dim]Skipping setup (use --force-setup to re-run)[/dim]")
376
355
  console.print()
377
356
 
357
+ # Check if session was cancelled during setup
358
+ try:
359
+ updated_session = api.join_session(session_id)
360
+ if updated_session.get("status") == "cancelled":
361
+ console.print("[yellow]Session was cancelled. Exiting.[/yellow]")
362
+ sys.exit(0)
363
+ except APIError:
364
+ pass # Continue if we can't check - server might be temporarily unavailable
365
+
378
366
  # Step 4: Show instructions
379
367
  if session_info.get("instructions"):
380
368
  console.print(Panel(session_info["instructions"], title="Task Instructions"))
@@ -383,18 +371,19 @@ def session_join(ctx, session_id: str, force_setup: bool):
383
371
  # Step 3: Run the task using TaskRunner
384
372
  step_num = "3" if (not is_reconnect or force_setup) and images else "2" if (not is_reconnect or force_setup) else "1"
385
373
  console.print(f"[bold]Step {step_num}:[/bold] Starting task environment...")
374
+ console.print("[dim]Launching Docker containers...[/dim]")
375
+ console.print()
386
376
 
387
377
  events.docker_started()
388
378
 
389
379
  runner = TaskRunner()
390
380
  eval_log_bytes = None
391
381
  try:
392
- with console.status("[dim]Launching Docker containers (this may take a minute)...[/dim]"):
393
- result = runner.run_from_assignment(
394
- assignment=assignment,
395
- compose_yaml=compose_yaml,
396
- files_zip=files_zip,
397
- )
382
+ result = runner.run_from_assignment(
383
+ assignment=assignment,
384
+ compose_yaml=compose_yaml,
385
+ files_zip=files_zip,
386
+ )
398
387
  # Read eval log before cleanup
399
388
  if result.eval_log_path and result.eval_log_path.exists():
400
389
  eval_log_bytes = result.eval_log_path.read_bytes()
@@ -439,7 +428,7 @@ def session_join(ctx, session_id: str, force_setup: bool):
439
428
  eval_log_bytes=eval_log_bytes,
440
429
  score=result.score,
441
430
  score_binarized=result.score_binarized,
442
- agent_id=result.agent_id,
431
+ agent_id=None, # TODO: extract from task files if needed
443
432
  )
444
433
  except APIError as e:
445
434
  console.print(f"[red]Failed to upload result: {e}[/red]")
hte_cli/image_utils.py CHANGED
@@ -1,6 +1,10 @@
1
1
  """Docker image utilities for pre-pulling compose images."""
2
2
 
3
3
  import logging
4
+ import os
5
+ import pty
6
+ import re
7
+ import select
4
8
  import subprocess
5
9
  from collections.abc import Callable
6
10
 
@@ -61,32 +65,77 @@ def pull_image_with_progress(
61
65
  on_complete: Callable[[str, bool], None] | None = None,
62
66
  ) -> bool:
63
67
  """
64
- Pull a Docker image with progress callbacks.
68
+ Pull a Docker image with progress callbacks using PTY for real progress output.
65
69
 
66
70
  Args:
67
71
  image: Image name to pull
68
- on_progress: Callback(image, status_line) called for each line of output
72
+ on_progress: Callback(image, status_line) called for each progress update
69
73
  on_complete: Callback(image, success) called when pull completes
70
74
 
71
75
  Returns:
72
76
  True if pull succeeded, False otherwise
73
77
  """
74
78
  try:
79
+ # Use PTY to get real progress output from docker
80
+ master_fd, slave_fd = pty.openpty()
81
+
75
82
  process = subprocess.Popen(
76
83
  ["docker", "pull", image],
77
- stdout=subprocess.PIPE,
78
- stderr=subprocess.STDOUT,
79
- text=True,
80
- bufsize=1,
84
+ stdout=slave_fd,
85
+ stderr=slave_fd,
86
+ stdin=slave_fd,
87
+ close_fds=True,
81
88
  )
82
89
 
83
- # Stream output line by line
84
- for line in iter(process.stdout.readline, ""):
85
- line = line.strip()
86
- if line and on_progress:
87
- on_progress(image, line)
90
+ os.close(slave_fd) # Close slave in parent
91
+
92
+ # Read output from master with timeout
93
+ output_buffer = ""
94
+ # Regex to parse docker progress: "abc123: Downloading [===> ] 10.5MB/50MB"
95
+ progress_pattern = re.compile(
96
+ r"([a-f0-9]+):\s*(Downloading|Extracting|Verifying Checksum|Download complete|Pull complete|Already exists|Waiting)(?:\s+\[.*?\]\s+)?(\d+\.?\d*\s*[kMG]?B)?(?:/(\d+\.?\d*\s*[kMG]?B))?"
97
+ )
88
98
 
89
- process.wait()
99
+ while True:
100
+ # Check if process is done
101
+ ret = process.poll()
102
+ if ret is not None:
103
+ # Read any remaining output
104
+ try:
105
+ while True:
106
+ ready, _, _ = select.select([master_fd], [], [], 0.1)
107
+ if not ready:
108
+ break
109
+ chunk = os.read(master_fd, 4096)
110
+ if not chunk:
111
+ break
112
+ except OSError:
113
+ pass
114
+ break
115
+
116
+ # Read available output
117
+ try:
118
+ ready, _, _ = select.select([master_fd], [], [], 0.1)
119
+ if ready:
120
+ chunk = os.read(master_fd, 4096)
121
+ if chunk:
122
+ output_buffer += chunk.decode("utf-8", errors="replace")
123
+
124
+ # Parse and report progress
125
+ # Docker uses carriage returns to update lines in place
126
+ lines = output_buffer.replace("\r", "\n").split("\n")
127
+ output_buffer = lines[-1] # Keep incomplete line
128
+
129
+ for line in lines[:-1]:
130
+ line = line.strip()
131
+ # Strip ANSI escape codes
132
+ line = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line)
133
+ if line and on_progress:
134
+ on_progress(image, line)
135
+ except OSError:
136
+ break
137
+
138
+ os.close(master_fd)
90
139
  success = process.returncode == 0
91
140
 
92
141
  if on_complete:
@@ -94,7 +143,7 @@ def pull_image_with_progress(
94
143
 
95
144
  return success
96
145
 
97
- except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
146
+ except (FileNotFoundError, OSError) as e:
98
147
  logger.error(f"Failed to pull {image}: {e}")
99
148
  if on_complete:
100
149
  on_complete(image, False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hte-cli
3
- Version: 0.2.8
3
+ Version: 0.2.16
4
4
  Summary: Human Time-to-Completion Evaluation CLI
5
5
  Project-URL: Homepage, https://github.com/sean-peters-au/lyptus-mono
6
6
  Author: Lyptus Research
@@ -1,15 +1,15 @@
1
1
  hte_cli/__init__.py,sha256=fDGXp-r8bIoLtlQnn5xJ_CpwMhonvk9bGjZQsjA2mDI,914
2
2
  hte_cli/__main__.py,sha256=63n0gNGfskidWDU0aAIF2N8lylVCLYKVIkrN9QiORoo,107
3
3
  hte_cli/api_client.py,sha256=m42kfFZS72Nu_VuDwxRsLNy4ziCcvgk7KNWBh9gwqy0,9257
4
- hte_cli/cli.py,sha256=W9R_jHBLhLho2GyroKzCCg6EhBluCrFJdZ9zCaKFGuo,42745
4
+ hte_cli/cli.py,sha256=XpV7x2HcHlVg-ynr-Ih8MxbHc74PT9mQzCICavtpV_0,41623
5
5
  hte_cli/config.py,sha256=42Xv__YMSeRLs2zhGukJkIXFKtnBtYCHnONfViGyt2g,3387
6
6
  hte_cli/errors.py,sha256=1J5PpxcUKBu6XjigMMCPOq4Zc12tnv8LhAsiaVFWLQM,2762
7
7
  hte_cli/events.py,sha256=Zn-mroqaLHNzdT4DFf8st1Qclglshihdc09dBfCN070,5522
8
- hte_cli/image_utils.py,sha256=454yoZEI1duNYrZC8UjhfZzDRP4Nxdrf2TvnZ_54G1k,4439
8
+ hte_cli/image_utils.py,sha256=TLwJdswUQrSD2bQcAXW03R8j8WG2pbHzd12TWcE7zy4,6418
9
9
  hte_cli/runner.py,sha256=DhC8FMjHwfLR193iP4thLDRZrNssYA9KH1WYKU2JKeg,13535
10
10
  hte_cli/scorers.py,sha256=sFoPJePRt-K191-Ga4cVmrldruJclYXTOLkU_C9nCDI,6025
11
11
  hte_cli/version_check.py,sha256=WVZyGy2XfAghQYdd2N9-0Qfg-7pgp9gt4761-PnmacI,1708
12
- hte_cli-0.2.8.dist-info/METADATA,sha256=XT1WLIfvC2PRQtcFB19qTpBdMApP_UKabzal-Fcn8Cw,3767
13
- hte_cli-0.2.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- hte_cli-0.2.8.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
- hte_cli-0.2.8.dist-info/RECORD,,
12
+ hte_cli-0.2.16.dist-info/METADATA,sha256=pNGPVDfi_RV-IWuC3pGzrqJCUzXgA2rf9-jXI2tNk3s,3768
13
+ hte_cli-0.2.16.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ hte_cli-0.2.16.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
+ hte_cli-0.2.16.dist-info/RECORD,,