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 +26 -37
- hte_cli/image_utils.py +62 -13
- {hte_cli-0.2.8.dist-info → hte_cli-0.2.16.dist-info}/METADATA +1 -1
- {hte_cli-0.2.8.dist-info → hte_cli-0.2.16.dist-info}/RECORD +6 -6
- {hte_cli-0.2.8.dist-info → hte_cli-0.2.16.dist-info}/WHEEL +0 -0
- {hte_cli-0.2.8.dist-info → hte_cli-0.2.16.dist-info}/entry_points.txt +0 -0
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..."]
|
|
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
|
-
#
|
|
318
|
-
# Lines look like: "abc123: Downloading
|
|
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:]
|
|
326
|
-
layer_status = parts[1]
|
|
327
|
-
|
|
328
|
-
if
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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=
|
|
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
|
|
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=
|
|
78
|
-
stderr=
|
|
79
|
-
|
|
80
|
-
|
|
84
|
+
stdout=slave_fd,
|
|
85
|
+
stderr=slave_fd,
|
|
86
|
+
stdin=slave_fd,
|
|
87
|
+
close_fds=True,
|
|
81
88
|
)
|
|
82
89
|
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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 (
|
|
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,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=
|
|
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=
|
|
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.
|
|
13
|
-
hte_cli-0.2.
|
|
14
|
-
hte_cli-0.2.
|
|
15
|
-
hte_cli-0.2.
|
|
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,,
|
|
File without changes
|
|
File without changes
|