evalmc 0.0.1__tar.gz → 0.0.3__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.
Files changed (37) hide show
  1. evalmc-0.0.3/PKG-INFO +95 -0
  2. evalmc-0.0.3/README.md +72 -0
  3. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/check_challenge.py +3 -0
  4. evalmc-0.0.3/evalmc/config_paths.py +9 -0
  5. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/host.py +1 -7
  6. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/login.py +1 -8
  7. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/main.py +3 -4
  8. evalmc-0.0.3/evalmc/nominate.py +33 -0
  9. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/push_helpers.py +63 -4
  10. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/set_token.py +1 -7
  11. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/submission.py +14 -28
  12. evalmc-0.0.3/evalmc/submit_onnx.py +49 -0
  13. evalmc-0.0.3/evalmc.egg-info/PKG-INFO +95 -0
  14. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc.egg-info/SOURCES.txt +5 -2
  15. {evalmc-0.0.1 → evalmc-0.0.3}/pyproject.toml +2 -2
  16. evalmc-0.0.3/tests/test_cli_nominate.py +57 -0
  17. evalmc-0.0.3/tests/test_push_helpers.py +160 -0
  18. evalmc-0.0.3/tests/test_submission_api_base.py +92 -0
  19. evalmc-0.0.1/PKG-INFO +0 -89
  20. evalmc-0.0.1/README.md +0 -66
  21. evalmc-0.0.1/evalmc/submit.py +0 -88
  22. evalmc-0.0.1/evalmc/submit_onnx.py +0 -62
  23. evalmc-0.0.1/evalmc.egg-info/PKG-INFO +0 -89
  24. evalmc-0.0.1/tests/test_push_helpers.py +0 -32
  25. {evalmc-0.0.1 → evalmc-0.0.3}/LICENSE +0 -0
  26. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/__init__.py +0 -0
  27. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/__main__.py +0 -0
  28. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/check_registered.py +0 -0
  29. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/register.py +0 -0
  30. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/status.py +0 -0
  31. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc/utils.py +0 -0
  32. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc.egg-info/dependency_links.txt +0 -0
  33. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc.egg-info/entry_points.txt +0 -0
  34. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc.egg-info/requires.txt +0 -0
  35. {evalmc-0.0.1 → evalmc-0.0.3}/evalmc.egg-info/top_level.txt +0 -0
  36. {evalmc-0.0.1 → evalmc-0.0.3}/setup.cfg +0 -0
  37. {evalmc-0.0.1 → evalmc-0.0.3}/tests/test_cli_smoke.py +0 -0
evalmc-0.0.3/PKG-INFO ADDED
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: evalmc
3
+ Version: 0.0.3
4
+ Summary: Submit ONNX policies and Docker agents to MyoChallenge
5
+ Author-email: MyoChallenge Team <caggiano@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/MyoHub/mcEvalProd-CLI
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.0.0
19
+ Requires-Dist: docker>=6.0.0
20
+ Requires-Dist: requests>=2.25.0
21
+ Requires-Dist: boto3>=1.26.0
22
+ Dynamic: license-file
23
+
24
+ # evalmc
25
+
26
+ MyoChallenge submission CLI — upload ONNX policies or Docker agent images to
27
+ [mc26.myosuite.org](https://mc26.myosuite.org).
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/evalmc.svg)](https://pypi.org/project/evalmc/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/evalmc.svg)](https://pypi.org/project/evalmc/)
31
+ [![License](https://img.shields.io/pypi/l/evalmc.svg)](https://pypi.org/project/evalmc/)
32
+
33
+ ```bash
34
+ pip install -U evalmc
35
+ ```
36
+
37
+ Default API: `https://api.mc.myosuite.org`
38
+
39
+ ---
40
+
41
+ ## Quick start
42
+
43
+ ```bash
44
+ evalmc host --set-host https://api.mc.myosuite.org
45
+ evalmc login
46
+ evalmc status
47
+ evalmc submit-onnx model.onnx -t chasetag -p 1
48
+ ```
49
+
50
+ `evalmc login` stores the email and auth token from the dashboard.
51
+ Credentials live in `~/.myochallenge/`.
52
+
53
+ ---
54
+
55
+ ## Commands
56
+
57
+ | Command | Purpose |
58
+ |---|---|
59
+ | `evalmc host` | Show or set the API host |
60
+ | `evalmc login` | Save email + auth token |
61
+ | `evalmc status` | Confirm host and team |
62
+ | `evalmc submit-onnx` | Upload an ONNX policy |
63
+ | `evalmc nominate` | Pick a scored artifact for title-cycle finals |
64
+ | `evalmc push` | Submit a Docker / ECR agent image |
65
+
66
+ ### ONNX
67
+
68
+ Recommended for ChaseTag. The graph is `obs → action`; no `policy.py` required.
69
+
70
+ ```bash
71
+ evalmc submit-onnx model.onnx -t chasetag -p 1
72
+ ```
73
+
74
+ ### Title-cycle nomination
75
+
76
+ Before freeze, choose which scored in-window submission plays in finals.
77
+ If you skip this, the platform uses your best score.
78
+
79
+ ```bash
80
+ evalmc nominate -t chasetag -p 1 --submission-id 123
81
+ ```
82
+
83
+ ### Docker
84
+
85
+ For custom Python around the policy. The image must ship `policy.py` and/or
86
+ `model.onnx` under `/policy` (or `/app`).
87
+
88
+ ```bash
89
+ evalmc push my-agent:tag -t chasetag -p 1
90
+ ```
91
+
92
+ ---
93
+
94
+ Some tracks are invitation-only. Registration and submission are rejected until
95
+ the team is on the whitelist. Leaderboards stay public.
evalmc-0.0.3/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # evalmc
2
+
3
+ MyoChallenge submission CLI — upload ONNX policies or Docker agent images to
4
+ [mc26.myosuite.org](https://mc26.myosuite.org).
5
+
6
+ [![PyPI](https://img.shields.io/pypi/v/evalmc.svg)](https://pypi.org/project/evalmc/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/evalmc.svg)](https://pypi.org/project/evalmc/)
8
+ [![License](https://img.shields.io/pypi/l/evalmc.svg)](https://pypi.org/project/evalmc/)
9
+
10
+ ```bash
11
+ pip install -U evalmc
12
+ ```
13
+
14
+ Default API: `https://api.mc.myosuite.org`
15
+
16
+ ---
17
+
18
+ ## Quick start
19
+
20
+ ```bash
21
+ evalmc host --set-host https://api.mc.myosuite.org
22
+ evalmc login
23
+ evalmc status
24
+ evalmc submit-onnx model.onnx -t chasetag -p 1
25
+ ```
26
+
27
+ `evalmc login` stores the email and auth token from the dashboard.
28
+ Credentials live in `~/.myochallenge/`.
29
+
30
+ ---
31
+
32
+ ## Commands
33
+
34
+ | Command | Purpose |
35
+ |---|---|
36
+ | `evalmc host` | Show or set the API host |
37
+ | `evalmc login` | Save email + auth token |
38
+ | `evalmc status` | Confirm host and team |
39
+ | `evalmc submit-onnx` | Upload an ONNX policy |
40
+ | `evalmc nominate` | Pick a scored artifact for title-cycle finals |
41
+ | `evalmc push` | Submit a Docker / ECR agent image |
42
+
43
+ ### ONNX
44
+
45
+ Recommended for ChaseTag. The graph is `obs → action`; no `policy.py` required.
46
+
47
+ ```bash
48
+ evalmc submit-onnx model.onnx -t chasetag -p 1
49
+ ```
50
+
51
+ ### Title-cycle nomination
52
+
53
+ Before freeze, choose which scored in-window submission plays in finals.
54
+ If you skip this, the platform uses your best score.
55
+
56
+ ```bash
57
+ evalmc nominate -t chasetag -p 1 --submission-id 123
58
+ ```
59
+
60
+ ### Docker
61
+
62
+ For custom Python around the policy. The image must ship `policy.py` and/or
63
+ `model.onnx` under `/policy` (or `/app`).
64
+
65
+ ```bash
66
+ evalmc push my-agent:tag -t chasetag -p 1
67
+ ```
68
+
69
+ ---
70
+
71
+ Some tracks are invitation-only. Registration and submission are rejected until
72
+ the team is on the whitelist. Leaderboards stay public.
@@ -18,6 +18,8 @@ def check_task_phase(api_url, task, phase):
18
18
  "score_names": data.get("score_names"),
19
19
  "display_name": data.get("display_name"),
20
20
  "visible": data.get("visible"),
21
+ "evaluation_mode": data.get("evaluation_mode", "absolute"),
22
+ "access_mode": data.get("access_mode", "open"),
21
23
  "created_at": data.get("created_at"),
22
24
  }
23
25
  else:
@@ -112,6 +114,7 @@ def main():
112
114
  total_score_metrics += tp.get("num_scores", 0)
113
115
  label = tp.get("display_name") or f"{tp['task']} phase {tp['phase']}"
114
116
  print(f"✅ {label}: {tp['num_scores']} score metrics")
117
+ print(f" Format: {tp.get('evaluation_mode', 'absolute')} | Access: {tp.get('access_mode', 'open')}")
115
118
  print(f" Score Names: {', '.join(tp.get('score_names', []))}")
116
119
  print()
117
120
 
@@ -0,0 +1,9 @@
1
+ """Shared local config file locations for the evalmc CLI."""
2
+
3
+ from pathlib import Path
4
+
5
+ CONFIG_DIR = Path.home() / ".myochallenge"
6
+ HOST_URL_FILE = CONFIG_DIR / "host_url"
7
+ AUTH_TOKEN_FILE = CONFIG_DIR / "auth_token.json"
8
+
9
+ CONFIG_DIR.mkdir(exist_ok=True)
@@ -4,16 +4,10 @@ MyoChallenge Host - Configure API host URL
4
4
  """
5
5
 
6
6
  import sys
7
- from pathlib import Path
8
7
 
9
8
  import click
10
9
 
11
- # Configuration file paths
12
- CONFIG_DIR = Path.home() / ".myochallenge"
13
- HOST_URL_FILE = CONFIG_DIR / "host_url"
14
-
15
- # Ensure config directory exists
16
- CONFIG_DIR.mkdir(exist_ok=True)
10
+ from .config_paths import HOST_URL_FILE
17
11
 
18
12
 
19
13
  @click.command()
@@ -5,18 +5,11 @@ MyoChallenge Login - Interactive authentication and credential storage
5
5
 
6
6
  import json
7
7
  import sys
8
- from pathlib import Path
9
8
 
10
9
  import click
11
10
  import requests
12
11
 
13
- # Configuration file paths
14
- CONFIG_DIR = Path.home() / ".myochallenge"
15
- HOST_URL_FILE = CONFIG_DIR / "host_url"
16
- AUTH_TOKEN_FILE = CONFIG_DIR / "auth_token.json"
17
-
18
- # Ensure config directory exists
19
- CONFIG_DIR.mkdir(exist_ok=True)
12
+ from .config_paths import AUTH_TOKEN_FILE, HOST_URL_FILE
20
13
 
21
14
 
22
15
  def get_default_host():
@@ -10,8 +10,8 @@ from .login import login
10
10
  from .set_token import set_token
11
11
  from .status import status
12
12
  from .submission import push
13
- from .submit import submit
14
13
  from .submit_onnx import submit_onnx
14
+ from .nominate import nominate
15
15
 
16
16
 
17
17
  @click.group()
@@ -23,8 +23,7 @@ def main():
23
23
  Two submission paths are available:
24
24
 
25
25
  \b
26
- evalmc submit policy.zip -p 1 -t tabletennis # zip upload (recommended)
27
- evalmc submit-onnx model.onnx -p 1 -t elbowflexion # ONNX upload
26
+ evalmc submit-onnx model.onnx -p 1 -t elbowflexion # ONNX (elbow / ChaseTag)
28
27
  evalmc push my-image:tag -p 1 -t tabletennis # Docker/ECR image push
29
28
  """
30
29
  pass
@@ -34,6 +33,6 @@ main.add_command(host)
34
33
  main.add_command(login)
35
34
  main.add_command(set_token)
36
35
  main.add_command(push)
37
- main.add_command(submit)
38
36
  main.add_command(submit_onnx)
37
+ main.add_command(nominate)
39
38
  main.add_command(status)
@@ -0,0 +1,33 @@
1
+ """Select a scored title-cycle submission for the upcoming finals."""
2
+
3
+ import click
4
+ import requests
5
+
6
+ from .login import get_default_host
7
+ from .push_helpers import http_error_detail, resolve_credentials
8
+
9
+
10
+ @click.command()
11
+ @click.option("-t", "--task", required=True)
12
+ @click.option("-p", "--phase", required=True, type=int)
13
+ @click.option("--submission-id", required=True, type=int)
14
+ @click.option("-e", "--email", default=None)
15
+ @click.option("-a", "--auth-token", default=None)
16
+ def nominate(task, phase, submission_id, email, auth_token):
17
+ """Use SUBMISSION_ID for this title cycle's finals."""
18
+ email, auth_token = resolve_credentials(email, auth_token)
19
+ try:
20
+ response = requests.put(
21
+ f"{get_default_host()}/title-cycle/{task}/{phase}/nominate",
22
+ json={"submission_id": submission_id},
23
+ headers={"X-User-Email": email, "X-Auth-Token": auth_token},
24
+ timeout=20,
25
+ )
26
+ response.raise_for_status()
27
+ except requests.exceptions.HTTPError as exc:
28
+ raise click.ClickException(
29
+ f"Could not nominate submission: {http_error_detail(exc.response)}"
30
+ )
31
+ except requests.exceptions.RequestException as exc:
32
+ raise click.ClickException(f"Could not reach the API: {exc}")
33
+ click.echo(f"Submission {submission_id} will be used for finals.")
@@ -4,6 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  from urllib.parse import urlparse
6
6
 
7
+ import click
8
+ import requests
9
+
7
10
  POLICY_PATHS = (
8
11
  "/policy/policy.py",
9
12
  "/policy/model.onnx",
@@ -51,7 +54,7 @@ def image_not_found_help(image: str, build_hint: str | None = None) -> str:
51
54
  lines.extend(
52
55
  [
53
56
  "",
54
- "For elbow flexion, zip/ONNX submission is usually simpler:",
57
+ "For elbow flexion, ONNX submission is usually simpler:",
55
58
  " evalmc submit-onnx model.onnx -t elbowflexion -p 1",
56
59
  ]
57
60
  )
@@ -95,9 +98,8 @@ def local_push_remote_warning(api_base: str, local_tag: str) -> str:
95
98
  f"\n"
96
99
  f"Expected outcome: submission status 'submitted' → 'failed' (no leaderboard entry).\n"
97
100
  f"\n"
98
- f"Recommended alternatives for elbow flexion:\n"
101
+ f"Recommended alternative for elbow flexion:\n"
99
102
  f" evalmc submit-onnx model.onnx -t elbowflexion -p 1\n"
100
- f" evalmc submit policy.zip -t elbowflexion -p 1\n"
101
103
  f"\n"
102
104
  f"For Docker on production, the server needs USE_LOCAL_STORAGE=false and ECR push.\n"
103
105
  f"Contact the organizers if ECR upload is required."
@@ -121,6 +123,63 @@ def policy_missing_help(image: str) -> str:
121
123
  )
122
124
 
123
125
 
126
+ def resolve_credentials(email: str | None, auth_token: str | None) -> tuple[str, str]:
127
+ """Fill in email/auth_token from stored credentials when omitted.
128
+
129
+ Raises click.ClickException if neither the args nor stored credentials
130
+ provide both values.
131
+ """
132
+ from .login import get_stored_auth
133
+
134
+ if not email or not auth_token:
135
+ stored = get_stored_auth()
136
+ if not stored:
137
+ raise click.ClickException(
138
+ "No credentials found. Run 'evalmc login' or pass --email and --auth-token."
139
+ )
140
+ email = email or stored["email"]
141
+ auth_token = auth_token or stored["auth_token"]
142
+ return email, auth_token
143
+
144
+
145
+ def post_submission_file(
146
+ api_base: str,
147
+ endpoint: str,
148
+ task: str,
149
+ phase: int,
150
+ email: str,
151
+ auth_token: str,
152
+ file_field: str,
153
+ file_name: str,
154
+ file_obj,
155
+ content_type: str,
156
+ ) -> dict:
157
+ """POST a submission file to the API; raise ClickException on failure."""
158
+ try:
159
+ resp = requests.post(
160
+ f"{api_base}{endpoint}",
161
+ data={"task": task, "phase": phase, "email": email, "auth_token": auth_token},
162
+ files={file_field: (file_name, file_obj, content_type)},
163
+ timeout=120,
164
+ )
165
+ resp.raise_for_status()
166
+ except requests.exceptions.HTTPError as e:
167
+ detail = http_error_detail(e.response)
168
+ if e.response.status_code == 403 and "invitation-only" in detail.lower():
169
+ detail = f"{detail} Ask an organizer to approve your team for this track."
170
+ raise click.ClickException(f"Server error {e.response.status_code}: {detail}")
171
+ except requests.exceptions.RequestException as e:
172
+ raise click.ClickException(f"Request failed: {e}")
173
+ return resp.json()
174
+
175
+
176
+ def echo_submission_result(data: dict) -> None:
177
+ click.echo("Submitted successfully!")
178
+ click.echo(f" Submission ID : {data['submission_id']}")
179
+ click.echo(f" Type : {data['submission_type']}")
180
+ click.echo(f" Status : {data['status']}")
181
+
182
+
124
183
  def post_submit_local_remote_note(submission_id, local_tag: str, api_base: str) -> str:
125
184
  return (
126
185
  f"\n⚠️ Submission {submission_id} recorded, but evaluation may fail on the server.\n"
@@ -132,5 +191,5 @@ def post_submit_local_remote_note(submission_id, local_tag: str, api_base: str)
132
191
  f" curl -s {api_base}/submissions/team/YOUR_TEAM | python3 -m json.tool\n"
133
192
  f"\n"
134
193
  f"If status becomes 'failed', the server could not run your local Docker tag.\n"
135
- f"Use evalmc submit-onnx or evalmc submit (zip) instead."
194
+ f"Use evalmc submit-onnx instead."
136
195
  )
@@ -5,16 +5,10 @@ MyoChallenge Set Token - Directly configure authentication token
5
5
 
6
6
  import json
7
7
  import sys
8
- from pathlib import Path
9
8
 
10
9
  import click
11
10
 
12
- # Configuration file paths
13
- CONFIG_DIR = Path.home() / ".myochallenge"
14
- AUTH_TOKEN_FILE = CONFIG_DIR / "auth_token.json"
15
-
16
- # Ensure config directory exists
17
- CONFIG_DIR.mkdir(exist_ok=True)
11
+ from .config_paths import AUTH_TOKEN_FILE
18
12
 
19
13
 
20
14
  @click.command()
@@ -3,10 +3,13 @@
3
3
  MyoChallenge Submission Script - ECR-based Docker image submission
4
4
  """
5
5
 
6
+ import json
7
+ import shutil
6
8
  import subprocess
7
9
  import sys
10
+ import tempfile
11
+ import time
8
12
  import uuid
9
- from pathlib import Path
10
13
 
11
14
  import click
12
15
  import docker
@@ -25,14 +28,6 @@ from .push_helpers import (
25
28
  upload_mode_summary,
26
29
  )
27
30
 
28
- # Configuration file paths
29
- CONFIG_DIR = Path.home() / ".myochallenge"
30
- HOST_URL_FILE = CONFIG_DIR / "host_url"
31
- AUTH_TOKEN_FILE = CONFIG_DIR / "auth_token.json"
32
-
33
- # Ensure config directory exists
34
- CONFIG_DIR.mkdir(exist_ok=True)
35
-
36
31
 
37
32
  # Configuration
38
33
  LOCAL_DOCKER_REGISTRY_URI = "localhost:5000"
@@ -134,8 +129,8 @@ def _run_subprocess(cmd: list[str], step: str) -> None:
134
129
  @click.option(
135
130
  "--api-base",
136
131
  help="API base URL",
137
- default="https://api.mc.myosuite.org",
138
- show_default=True,
132
+ default=None,
133
+ show_default="stored host, or https://api.mc.myosuite.org",
139
134
  )
140
135
  @click.option(
141
136
  "--skip-policy-check",
@@ -177,7 +172,7 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
177
172
  if not public and not private:
178
173
  private = True # Default to private
179
174
 
180
- api_base = get_default_host()
175
+ api_base = api_base or get_default_host()
181
176
 
182
177
  click.echo(f"🚀 Starting submission for {image} to {task} phase {phase}")
183
178
  click.echo(f"🌐 API host: {api_base}")
@@ -250,15 +245,6 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
250
245
  except requests.exceptions.RequestException as e:
251
246
  _fail(f"Authentication request failed: {e}")
252
247
 
253
- # Collect submission metadata (not used in ECR flow)
254
- # submission_metadata = {
255
- # "is_public": json.dumps(public),
256
- # "method_name": "",
257
- # "method_description": "",
258
- # "project_url": "",
259
- # "publication_url": "",
260
- # }
261
-
262
248
  # Generate unique tag for submission
263
249
  submission_tag = str(uuid.uuid4())
264
250
  click.echo(f"🏷️ Generated submission tag: {submission_tag}")
@@ -290,7 +276,7 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
290
276
  if not click.confirm("Continue with local Docker tag anyway?", default=False):
291
277
  _fail(
292
278
  "Submission cancelled",
293
- "Use evalmc submit-onnx or evalmc submit (zip), or re-run with --yes.",
279
+ "Use evalmc submit-onnx, or re-run with --yes.",
294
280
  )
295
281
  elif not is_local_api(api_base):
296
282
  click.echo(local_push_remote_warning(api_base, local_tag))
@@ -327,6 +313,8 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
327
313
  "task": task,
328
314
  "phase": phase,
329
315
  "image_uri": image_uri,
316
+ "email": email,
317
+ "auth_token": auth_token,
330
318
  },
331
319
  timeout=10,
332
320
  )
@@ -376,6 +364,8 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
376
364
  "task": task,
377
365
  "phase": phase,
378
366
  "image_uri": image_uri,
367
+ "email": email,
368
+ "auth_token": auth_token,
379
369
  },
380
370
  timeout=10,
381
371
  )
@@ -412,8 +402,6 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
412
402
  f"Server returned: {ecr_data}",
413
403
  )
414
404
 
415
- import time
416
-
417
405
  submission_id = str(uuid.uuid4())[
418
406
  :8
419
407
  ] # Use first 8 chars of UUID for uniqueness
@@ -425,10 +413,6 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
425
413
  click.echo(f"⏰ Timestamp: {timestamp}")
426
414
 
427
415
  try:
428
- import json
429
- import shutil
430
- import tempfile
431
-
432
416
  temp_dir = tempfile.mkdtemp()
433
417
  dockerfile_path = f"{temp_dir}/Dockerfile"
434
418
 
@@ -485,6 +469,8 @@ def push(image, phase, task, email, auth_token, url, public, private, api_base,
485
469
  "task": task,
486
470
  "phase": phase,
487
471
  "image_uri": image_uri,
472
+ "email": email,
473
+ "auth_token": auth_token,
488
474
  },
489
475
  timeout=10,
490
476
  )
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ """evalmc submit-onnx — upload a standalone ONNX model to MyoChallenge."""
3
+
4
+ from pathlib import Path
5
+
6
+ import click
7
+
8
+ from .login import get_default_host
9
+ from .push_helpers import echo_submission_result, post_submission_file, resolve_credentials
10
+
11
+
12
+ @click.command("submit-onnx")
13
+ @click.argument("onnx_path", type=click.Path(exists=True, path_type=Path))
14
+ @click.option("-p", "--phase", required=True, type=int, help="Challenge phase number")
15
+ @click.option("-t", "--task", required=True, help="Challenge task name (e.g. elbowflexion)")
16
+ @click.option("-e", "--email", default=None, help="Team email")
17
+ @click.option("-a", "--auth-token", default=None, help="Auth token")
18
+ def submit_onnx(onnx_path, phase, task, email, auth_token):
19
+ """Submit a standalone ONNX model (no policy.py required).
20
+
21
+ Works for elbow flexion and ChaseTag (obs→action graphs).
22
+
23
+ Example:
24
+ evalmc submit-onnx my_model.onnx -t elbowflexion -p 1
25
+ evalmc submit-onnx chase.onnx -t chasetag -p 1
26
+ """
27
+ if onnx_path.suffix.lower() != ".onnx":
28
+ raise click.ClickException("File must have .onnx extension")
29
+
30
+ api_base = get_default_host()
31
+ email, auth_token = resolve_credentials(email, auth_token)
32
+
33
+ click.echo(f"Submitting {onnx_path.name} → task={task} phase={phase}")
34
+
35
+ with open(onnx_path, "rb") as f:
36
+ data = post_submission_file(
37
+ api_base,
38
+ "/submit/onnx",
39
+ task,
40
+ phase,
41
+ email,
42
+ auth_token,
43
+ file_field="onnx_model",
44
+ file_name=onnx_path.name,
45
+ file_obj=f,
46
+ content_type="application/octet-stream",
47
+ )
48
+
49
+ echo_submission_result(data)
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: evalmc
3
+ Version: 0.0.3
4
+ Summary: Submit ONNX policies and Docker agents to MyoChallenge
5
+ Author-email: MyoChallenge Team <caggiano@gmail.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/MyoHub/mcEvalProd-CLI
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: click>=8.0.0
19
+ Requires-Dist: docker>=6.0.0
20
+ Requires-Dist: requests>=2.25.0
21
+ Requires-Dist: boto3>=1.26.0
22
+ Dynamic: license-file
23
+
24
+ # evalmc
25
+
26
+ MyoChallenge submission CLI — upload ONNX policies or Docker agent images to
27
+ [mc26.myosuite.org](https://mc26.myosuite.org).
28
+
29
+ [![PyPI](https://img.shields.io/pypi/v/evalmc.svg)](https://pypi.org/project/evalmc/)
30
+ [![Python](https://img.shields.io/pypi/pyversions/evalmc.svg)](https://pypi.org/project/evalmc/)
31
+ [![License](https://img.shields.io/pypi/l/evalmc.svg)](https://pypi.org/project/evalmc/)
32
+
33
+ ```bash
34
+ pip install -U evalmc
35
+ ```
36
+
37
+ Default API: `https://api.mc.myosuite.org`
38
+
39
+ ---
40
+
41
+ ## Quick start
42
+
43
+ ```bash
44
+ evalmc host --set-host https://api.mc.myosuite.org
45
+ evalmc login
46
+ evalmc status
47
+ evalmc submit-onnx model.onnx -t chasetag -p 1
48
+ ```
49
+
50
+ `evalmc login` stores the email and auth token from the dashboard.
51
+ Credentials live in `~/.myochallenge/`.
52
+
53
+ ---
54
+
55
+ ## Commands
56
+
57
+ | Command | Purpose |
58
+ |---|---|
59
+ | `evalmc host` | Show or set the API host |
60
+ | `evalmc login` | Save email + auth token |
61
+ | `evalmc status` | Confirm host and team |
62
+ | `evalmc submit-onnx` | Upload an ONNX policy |
63
+ | `evalmc nominate` | Pick a scored artifact for title-cycle finals |
64
+ | `evalmc push` | Submit a Docker / ECR agent image |
65
+
66
+ ### ONNX
67
+
68
+ Recommended for ChaseTag. The graph is `obs → action`; no `policy.py` required.
69
+
70
+ ```bash
71
+ evalmc submit-onnx model.onnx -t chasetag -p 1
72
+ ```
73
+
74
+ ### Title-cycle nomination
75
+
76
+ Before freeze, choose which scored in-window submission plays in finals.
77
+ If you skip this, the platform uses your best score.
78
+
79
+ ```bash
80
+ evalmc nominate -t chasetag -p 1 --submission-id 123
81
+ ```
82
+
83
+ ### Docker
84
+
85
+ For custom Python around the policy. The image must ship `policy.py` and/or
86
+ `model.onnx` under `/policy` (or `/app`).
87
+
88
+ ```bash
89
+ evalmc push my-agent:tag -t chasetag -p 1
90
+ ```
91
+
92
+ ---
93
+
94
+ Some tracks are invitation-only. Registration and submission are rejected until
95
+ the team is on the whitelist. Leaderboards stay public.