everestapi 0.3.2__tar.gz → 0.3.5__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 (33) hide show
  1. {everestapi-0.3.2/src/everestapi.egg-info → everestapi-0.3.5}/PKG-INFO +1 -1
  2. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/__init__.py +1 -1
  3. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/client.py +13 -6
  4. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/mcp/server.py +84 -15
  5. {everestapi-0.3.2 → everestapi-0.3.5/src/everestapi.egg-info}/PKG-INFO +1 -1
  6. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_client.py +1 -1
  7. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_eve1087_cpu_tier.py +1 -1
  8. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_eve959_toolsets.py +17 -0
  9. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_mcp_and_models.py +6 -6
  10. {everestapi-0.3.2 → everestapi-0.3.5}/LICENSE +0 -0
  11. {everestapi-0.3.2 → everestapi-0.3.5}/README.md +0 -0
  12. {everestapi-0.3.2 → everestapi-0.3.5}/pyproject.toml +0 -0
  13. {everestapi-0.3.2 → everestapi-0.3.5}/setup.cfg +0 -0
  14. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/__main__.py +0 -0
  15. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/cli.py +0 -0
  16. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/mcp/__init__.py +0 -0
  17. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/mcp/__main__.py +0 -0
  18. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/plots.py +0 -0
  19. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/scoring.py +0 -0
  20. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi/types.py +0 -0
  21. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi.egg-info/SOURCES.txt +0 -0
  22. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi.egg-info/dependency_links.txt +0 -0
  23. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi.egg-info/entry_points.txt +0 -0
  24. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi.egg-info/requires.txt +0 -0
  25. {everestapi-0.3.2 → everestapi-0.3.5}/src/everestapi.egg-info/top_level.txt +0 -0
  26. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_cli.py +0 -0
  27. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_diagnostics.py +0 -0
  28. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_eve953_mcp_progress.py +0 -0
  29. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_eve957_mcp_annotations_resources.py +0 -0
  30. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_eve967_mcp_discoverability.py +0 -0
  31. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_json_or_raise.py +0 -0
  32. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_prediction_range.py +0 -0
  33. {everestapi-0.3.2 → everestapi-0.3.5}/tests/test_scoring.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.3.2
3
+ Version: 0.3.5
4
4
  Summary: Python SDK for the Everesteer prediction tournament platform
5
5
  Author-email: Everesteer <support@everesteer.ai>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  """EverestAPI — Python SDK for the Everesteer prediction tournament platform."""
2
2
 
3
- __version__ = "0.3.2"
3
+ __version__ = "0.3.5"
4
4
 
5
5
  from everestapi.client import EverestAPI, EverestError
6
6
  from everestapi.types import (
@@ -662,9 +662,10 @@ class EverestAPI:
662
662
  The download route is version-scoped
663
663
  (``/api/v1/data/download/{version}/{universe}/{split}``). Pass
664
664
  ``version="latest"`` (the default) to resolve the current version from
665
- ``/api/v1/data/versions`` automatically, or an explicit version
666
- (e.g. ``"bregen"``) to pin it. A stale pinned version self-heals: on a
667
- 404 the current version is resolved and the download retried once.
665
+ ``/api/v1/data/versions`` automatically, or an explicit version string
666
+ (as returned by :meth:`list_versions`) to pin it. A stale pinned
667
+ version self-heals: on a 404 the current version is resolved and the
668
+ download retried once.
668
669
 
669
670
  Splits: ``train`` / ``validation`` (features + targets), ``live``
670
671
  (features only, no targets). In hackathon mode, ``train`` is the LABELED
@@ -677,8 +678,8 @@ class EverestAPI:
677
678
  404s for hackathon keys.
678
679
 
679
680
  Futures is served by the futures endpoint (``version`` is a no-op):
680
- it returns the real bregen tree to full-scope keys and the hackathon
681
- tree (labeled ``train`` + blank-target ``validation``) to
681
+ it returns the current production dataset to full-scope keys and the
682
+ hackathon tree (labeled ``train`` + blank-target ``validation``) to
682
683
  hackathon-scoped keys.
683
684
 
684
685
  Value semantics: observed feature values are cross-sectional
@@ -895,12 +896,18 @@ class EverestAPI:
895
896
 
896
897
  # -- data discovery ---------------------------------------------------
897
898
 
898
- def get_dataset_schema(self, version: str = "bregen", verbose: bool = False) -> dict:
899
+ def get_dataset_schema(self, version: str = "latest", verbose: bool = False) -> dict:
899
900
  """Get the dataset schema for a version.
900
901
 
902
+ ``version="latest"`` (the default) resolves the current version from
903
+ ``/api/v1/data/versions`` automatically, or pass an explicit version
904
+ string (as returned by :meth:`list_versions`) to pin it.
905
+
901
906
  Default is a compact summary (feature-set counts, target list, which splits
902
907
  are labeled, the CV embargo, distinct feature count, active version). Pass
903
908
  ``verbose=True`` for the full per-feature listing."""
909
+ if version in (None, "latest", "current"):
910
+ version = self._current_dataset_version("futures") or "v0"
904
911
  path = f"/api/v1/data/{version}/schema"
905
912
  if verbose:
906
913
  path += "?verbose=true"
@@ -298,8 +298,8 @@ TOOLS = [
298
298
  "properties": {
299
299
  "version": {
300
300
  "type": "string",
301
- "description": "Dataset version (default: bregen)",
302
- "default": "bregen",
301
+ "description": "Dataset version (default: latest, resolved from /api/v1/data/versions)",
302
+ "default": "latest",
303
303
  },
304
304
  },
305
305
  "required": [],
@@ -340,7 +340,7 @@ TOOLS = [
340
340
  },
341
341
  {
342
342
  "name": "train",
343
- "description": "Train a model on Everesteer data (no data upload — the platform uses the same obfuscated dataset you download). The PLATFORM loads data, runs exped-purged/embargoed cross-validation, computes canonical tournament metrics (a weighted combination of CORR and AIMC, AIMC-dominant, with per-model weights read from the API; AIMC before round close is a PRE-SUBMISSION ESTIMATE vs a static consensus proxy — real AIMC is scored vs the live stake-weighted crowd at round close), pickles the final model, and returns DOWNLOADABLE artifacts (model .pkl + validation/live prediction files) plus the metrics. Supply a built-in `model` preset (lightgbm/xgboost/ridge/mlp/random_forest), or `model='custom'` with a `custom_model_fn` (and optional `custom_feature_fn`) to run your own code — it executes inside an isolated, network-denied sandbox with no filesystem access and never sees held-out targets; a script rejected by the static safety scanner returns 400. `train` does NOT submit predictions or host a model on your behalf — use submit_futures_predictions/submit_predictions_file to submit and upload_model to host the .pkl. Poll with get_job_status() — the completed result carries the metrics + a predictions download URL; fetch the model file separately via get_model_download_url(). Call get_compute_credits for the live per-hour GPU rate card and your balance.",
343
+ "description": "Train a model REMOTELY on Everesteer's hosted compute — nothing executes on your machine. The call returns immediately with a job id and may run for up to 45 minutes. Poll get_job_status and get_job_log every 10–15 seconds for a reconnect-safe estimated percentage, stage, and fold-level messages. The platform loads its obfuscated data, runs purged CV, computes canonical metrics, and returns downloadable model/prediction artifacts; it does not submit or host the model for you. Custom code runs in an isolated network-denied sandbox without filesystem access or held-out targets. Call get_compute_credits for the live rate card and balance.",
344
344
  "inputSchema": {
345
345
  "type": "object",
346
346
  "properties": {
@@ -404,7 +404,7 @@ TOOLS = [
404
404
  },
405
405
  {
406
406
  "name": "get_job_status",
407
- "description": "Check status of a compute job. Returns status, cost, output files when complete.",
407
+ "description": "Check status plus reconnect-safe estimated percentage, stage, message, and update time. Poll every 10–15 seconds while running; use get_job_log for the full fold-level trail.",
408
408
  "inputSchema": {
409
409
  "type": "object",
410
410
  "properties": {
@@ -416,6 +416,17 @@ TOOLS = [
416
416
  "required": ["job_id"],
417
417
  },
418
418
  },
419
+ {
420
+ "name": "get_job_log",
421
+ "description": "Return the estimated progress summary and lifecycle/per-fold event trail for one training job. Poll every 10–15 seconds while running. Percentages are workflow estimates, not an ETA.",
422
+ "inputSchema": {
423
+ "type": "object",
424
+ "properties": {
425
+ "job_id": {"type": "string", "description": "Job ID returned by train"},
426
+ },
427
+ "required": ["job_id"],
428
+ },
429
+ },
419
430
  {
420
431
  "name": "get_model_download_url",
421
432
  "description": "Get a presigned download URL for a trained model from a completed job. URL valid for 1 hour. Only you can download your own models.",
@@ -430,6 +441,38 @@ TOOLS = [
430
441
  "required": ["job_id"],
431
442
  },
432
443
  },
444
+ {
445
+ "name": "get_job_predictions_url",
446
+ "description": "Get a presigned download URL for the prediction files (validation + live parquet) produced by a completed train job. URL valid for ~1 hour. Only you can download your own job's predictions. Fetch the model .pkl separately with get_model_download_url.",
447
+ "inputSchema": {
448
+ "type": "object",
449
+ "properties": {
450
+ "job_id": {
451
+ "type": "string",
452
+ "description": "Job ID from a train job",
453
+ },
454
+ },
455
+ "required": ["job_id"],
456
+ },
457
+ },
458
+ {
459
+ "name": "list_compute_jobs",
460
+ "description": "List your compute training jobs, newest first. Rows carry status, GPU, cost and the provider job id but exclude the large config/output payloads — use get_job_status for one job's full record. Optionally filter by status.",
461
+ "inputSchema": {
462
+ "type": "object",
463
+ "properties": {
464
+ "limit": {
465
+ "type": "integer",
466
+ "description": "Max rows (default 50, capped at 200)",
467
+ },
468
+ "status": {
469
+ "type": "string",
470
+ "description": "Filter to one status",
471
+ "enum": ["pending", "running", "completed", "failed", "timeout", "cancelled"],
472
+ },
473
+ },
474
+ },
475
+ },
433
476
  {
434
477
  "name": "get_compute_credits",
435
478
  "description": "Check your compute credit balance and usage. Purchase credits with USDC before using GPU resources.",
@@ -994,7 +1037,10 @@ READ_ONLY_TOOLS = frozenset(
994
1037
  "download_dataset",
995
1038
  "download_benchmark",
996
1039
  "get_job_status",
1040
+ "get_job_log",
1041
+ "list_compute_jobs",
997
1042
  "get_model_download_url",
1043
+ "get_job_predictions_url",
998
1044
  "get_compute_credits",
999
1045
  "get_rounds",
1000
1046
  "get_current_round",
@@ -1077,10 +1123,19 @@ TOOLSETS: dict[str, set[str]] = {
1077
1123
  # EVE-970: hosted training is a first-class flow now that every account
1078
1124
  # carries a compute grant — advertise train + get_compute_credits by
1079
1125
  # default so agents discover the hosted-training entry point without
1080
- # setting EIQ_MCP_TOOLSETS. get_job_status stays in "compute" (hidden
1081
- # tools remain callable by name). Mirrors the platform MCP server.
1126
+ # setting EIQ_MCP_TOOLSETS. EVE-1145 (platform parity): the whole
1127
+ # job-lifecycle group is core too. train's description tells the caller
1128
+ # to poll get_job_status()/get_job_log() and fetch artifacts via
1129
+ # get_model_download_url()/get_job_predictions_url() — hiding those in a
1130
+ # non-default group made hosted training an unfinishable dead end for an
1131
+ # agent that only sees the default toolset. Mirrors the platform MCP server.
1082
1132
  "train",
1083
1133
  "get_compute_credits",
1134
+ "get_job_status",
1135
+ "get_job_log",
1136
+ "list_compute_jobs",
1137
+ "get_model_download_url",
1138
+ "get_job_predictions_url",
1084
1139
  },
1085
1140
  "data": {
1086
1141
  "get_universe",
@@ -1093,7 +1148,6 @@ TOOLSETS: dict[str, set[str]] = {
1093
1148
  "get_benchmarks",
1094
1149
  "download_benchmark",
1095
1150
  "get_models",
1096
- "get_model_download_url",
1097
1151
  "get_model_per_exped_breakdown",
1098
1152
  },
1099
1153
  "submit": {"submit_predictions", "upload_model", "get_upload_status"},
@@ -1102,9 +1156,10 @@ TOOLSETS: dict[str, set[str]] = {
1102
1156
  # "core" (EVE-967) — every name still lives in exactly one group.
1103
1157
  "run_validation_diagnostics",
1104
1158
  },
1105
- "compute": {
1106
- "get_job_status",
1107
- },
1159
+ # EVE-1145: the job-lifecycle tools (get_job_status/get_job_log/
1160
+ # list_compute_jobs/get_model_download_url/get_job_predictions_url) were
1161
+ # promoted to "core". Kept as a stable, addressable EIQ_MCP_TOOLSETS name.
1162
+ "compute": set(),
1108
1163
  "staking": {
1109
1164
  "stake_on_model",
1110
1165
  "unstake_from_model",
@@ -1263,7 +1318,7 @@ def _dispatch(name: str, arguments: dict) -> str:
1263
1318
  elif name == "get_leaderboard":
1264
1319
  result = client.get_leaderboard(period=arguments.get("period", "30d"))
1265
1320
  elif name == "get_dataset_schema":
1266
- result = client.get_dataset_schema(version=arguments.get("version", "bregen"))
1321
+ result = client.get_dataset_schema(version=arguments.get("version", "latest"))
1267
1322
  elif name == "download_dataset":
1268
1323
  path = client.download_dataset(
1269
1324
  universe=arguments.get("universe", "futures"),
@@ -1282,8 +1337,17 @@ def _dispatch(name: str, arguments: dict) -> str:
1282
1337
  result = client.train(**arguments)
1283
1338
  elif name == "get_job_status":
1284
1339
  result = client.get_job_status(job_id=arguments["job_id"])
1340
+ elif name == "get_job_log":
1341
+ result = client.get_job_log(job_id=arguments["job_id"])
1285
1342
  elif name == "get_model_download_url":
1286
1343
  result = client.get_model_download_url(job_id=arguments["job_id"])
1344
+ elif name == "get_job_predictions_url":
1345
+ result = client.get_job_predictions_url(job_id=arguments["job_id"])
1346
+ elif name == "list_compute_jobs":
1347
+ result = client.list_compute_jobs(
1348
+ limit=arguments.get("limit", 50),
1349
+ status=arguments.get("status"),
1350
+ )
1287
1351
  elif name == "get_compute_credits":
1288
1352
  result = client.get_compute_credits()
1289
1353
  elif name == "get_rounds":
@@ -1649,9 +1713,12 @@ _NEXT_ACTIONS: dict[str, list[str]] = {
1649
1713
  "download_dataset": ["create_model", "submit_futures_predictions"],
1650
1714
  "download_benchmark": ["submit_futures_predictions"],
1651
1715
  "get_benchmarks": ["download_benchmark"],
1652
- "train": ["get_job_status"],
1653
- "get_job_status": ["get_model_download_url"], # conditional
1716
+ "train": ["get_job_status", "get_job_log"],
1717
+ "get_job_status": ["get_model_download_url", "get_job_predictions_url"], # conditional
1718
+ "get_job_log": ["get_job_status", "train"],
1719
+ "list_compute_jobs": ["get_job_status", "get_job_log"],
1654
1720
  "get_model_download_url": ["upload_model", "create_model"],
1721
+ "get_job_predictions_url": ["submit_futures_predictions", "create_model"],
1655
1722
  "create_model": ["submit_futures_predictions", "upload_model"],
1656
1723
  "upload_model": ["get_upload_status"],
1657
1724
  "get_upload_status": ["submit_futures_predictions"], # conditional
@@ -1714,6 +1781,8 @@ _NARRATION: dict[str, str] = {
1714
1781
  "train": "Training job queued — returns downloadable artifacts + metrics, not a submission.",
1715
1782
  "get_job_status": "Training job status: {status}.",
1716
1783
  "get_model_download_url": "Generated a download URL for the trained model (valid ~1h).",
1784
+ "get_job_predictions_url": "Generated a download URL for the job's predictions (valid ~1h).",
1785
+ "list_compute_jobs": "Returned your compute jobs.",
1717
1786
  "upload_model": "Model artifact uploaded; validation runs asynchronously.",
1718
1787
  "get_upload_status": "Model upload status: {status}.",
1719
1788
  "get_scores": "Returned the score history for model '{model_id}'.",
@@ -1771,8 +1840,8 @@ def _next_actions_for(name: str, arguments: dict, data: dict) -> list[str]:
1771
1840
  if status in ("completed", "done", "succeeded", "success"):
1772
1841
  return ["get_model_download_url", "create_model"]
1773
1842
  if status in ("failed", "error"):
1774
- return ["train"]
1775
- return ["get_job_status"] # still running — keep polling
1843
+ return ["get_job_log", "train"]
1844
+ return ["get_job_status", "get_job_log"]
1776
1845
  if name == "get_upload_status":
1777
1846
  if status in ("validated", "done", "success", "ready", "active"):
1778
1847
  return ["submit_futures_predictions", "get_scores"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.3.2
3
+ Version: 0.3.5
4
4
  Summary: Python SDK for the Everesteer prediction tournament platform
5
5
  Author-email: Everesteer <support@everesteer.ai>
6
6
  License-Expression: MIT
@@ -130,7 +130,7 @@ def test_download_dataset_futures_uses_futures_endpoint(httpx_mock, api, tmp_pat
130
130
  universe="futures",
131
131
  split="validation",
132
132
  output_path=str(tmp_path / "f.parquet"),
133
- version="bregen",
133
+ version="some-version",
134
134
  )
135
135
  assert out == str(tmp_path / "f.parquet")
136
136
  paths = [r.url.path for r in httpx_mock.get_requests()]
@@ -17,4 +17,4 @@ def test_train_gpu_enum_includes_cpu():
17
17
 
18
18
 
19
19
  def test_version_bumped():
20
- assert everestapi.__version__ == "0.3.2"
20
+ assert everestapi.__version__ == "0.3.5"
@@ -32,6 +32,23 @@ def test_toolset_coverage_both_directions():
32
32
  assert len(grouped) == len(grouped_set), "a tool name appears in more than one toolset"
33
33
 
34
34
 
35
+ def test_job_lifecycle_tools_are_core():
36
+ """Regression: the hosted-train lifecycle tools must be discoverable in the
37
+ DEFAULT toolset. train's own description points the caller at get_job_status/
38
+ get_job_log/get_model_download_url/get_job_predictions_url, so hiding them in
39
+ a non-default group made hosted training an unfinishable dead end — the very
40
+ gap that the everestapi package lagged eiq-platform (EVE-1145) on."""
41
+ core = S.TOOLSETS["core"]
42
+ for name in (
43
+ "get_job_status",
44
+ "get_job_log",
45
+ "list_compute_jobs",
46
+ "get_model_download_url",
47
+ "get_job_predictions_url",
48
+ ):
49
+ assert name in core, f"{name} must be in core (default toolset) to finish a train job"
50
+
51
+
35
52
  def test_default_advertises_core_only(monkeypatch):
36
53
  monkeypatch.delenv("EIQ_MCP_TOOLSETS", raising=False)
37
54
  assert S._enabled_tool_names() == set(S.TOOLSETS["core"])
@@ -79,16 +79,16 @@ def test_get_models(httpx_mock, api):
79
79
  # via the futures endpoint, EVE-931 — see test_client.py).
80
80
  def test_download_dataset_uses_versioned_route(httpx_mock, api, tmp_path):
81
81
  httpx_mock.add_response(
82
- url="http://test/api/v1/data/download/bregen/equities/train",
82
+ url="http://test/api/v1/data/download/test-version/equities/train",
83
83
  content=b"PAR1data",
84
84
  )
85
85
  out = api.download_dataset(
86
86
  universe="equities",
87
87
  split="train",
88
- version="bregen",
88
+ version="test-version",
89
89
  output_path=str(tmp_path / "t.parquet"),
90
90
  )
91
- assert httpx_mock.get_request().url.path == "/api/v1/data/download/bregen/equities/train"
91
+ assert httpx_mock.get_request().url.path == "/api/v1/data/download/test-version/equities/train"
92
92
  with open(out, "rb") as f:
93
93
  assert f.read() == b"PAR1data"
94
94
 
@@ -96,10 +96,10 @@ def test_download_dataset_uses_versioned_route(httpx_mock, api, tmp_path):
96
96
  def test_download_dataset_latest_resolves_version(httpx_mock, api, tmp_path):
97
97
  httpx_mock.add_response(
98
98
  url="http://test/api/v1/data/versions",
99
- json={"versions": [{"version": "bregen", "universes": ["equities"]}]},
99
+ json={"versions": [{"version": "test-version", "universes": ["equities"]}]},
100
100
  )
101
101
  httpx_mock.add_response(
102
- url="http://test/api/v1/data/download/bregen/equities/live",
102
+ url="http://test/api/v1/data/download/test-version/equities/live",
103
103
  content=b"PAR1live",
104
104
  )
105
105
  out = api.download_dataset(
@@ -327,7 +327,7 @@ def test_enrich_next_actions_are_result_conditional():
327
327
  _, running = server._enrich_result(
328
328
  "get_job_status", {"job_id": "j"}, json.dumps({"status": "running"})
329
329
  )
330
- assert running["next_actions"] == ["get_job_status"]
330
+ assert running["next_actions"] == ["get_job_status", "get_job_log"]
331
331
  _, done = server._enrich_result(
332
332
  "get_job_status", {"job_id": "j"}, json.dumps({"status": "completed"})
333
333
  )
File without changes
File without changes
File without changes
File without changes
File without changes