everestapi 0.2.8__tar.gz → 0.2.9__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 (32) hide show
  1. {everestapi-0.2.8/src/everestapi.egg-info → everestapi-0.2.9}/PKG-INFO +13 -6
  2. {everestapi-0.2.8 → everestapi-0.2.9}/README.md +12 -5
  3. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/__init__.py +1 -1
  4. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/client.py +101 -25
  5. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/mcp/server.py +59 -48
  6. {everestapi-0.2.8 → everestapi-0.2.9/src/everestapi.egg-info}/PKG-INFO +13 -6
  7. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_mcp_and_models.py +7 -0
  8. {everestapi-0.2.8 → everestapi-0.2.9}/LICENSE +0 -0
  9. {everestapi-0.2.8 → everestapi-0.2.9}/pyproject.toml +0 -0
  10. {everestapi-0.2.8 → everestapi-0.2.9}/setup.cfg +0 -0
  11. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/__main__.py +0 -0
  12. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/cli.py +0 -0
  13. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/mcp/__init__.py +0 -0
  14. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/mcp/__main__.py +0 -0
  15. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/plots.py +0 -0
  16. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/scoring.py +0 -0
  17. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi/types.py +0 -0
  18. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi.egg-info/SOURCES.txt +0 -0
  19. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi.egg-info/dependency_links.txt +0 -0
  20. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi.egg-info/entry_points.txt +0 -0
  21. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi.egg-info/requires.txt +0 -0
  22. {everestapi-0.2.8 → everestapi-0.2.9}/src/everestapi.egg-info/top_level.txt +0 -0
  23. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_cli.py +0 -0
  24. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_client.py +0 -0
  25. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_diagnostics.py +0 -0
  26. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_eve953_mcp_progress.py +0 -0
  27. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_eve957_mcp_annotations_resources.py +0 -0
  28. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_eve959_toolsets.py +0 -0
  29. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_eve967_mcp_discoverability.py +0 -0
  30. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_json_or_raise.py +0 -0
  31. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_prediction_range.py +0 -0
  32. {everestapi-0.2.8 → everestapi-0.2.9}/tests/test_scoring.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Python SDK for the Everesteer prediction tournament platform
5
5
  Author-email: Everesteer <support@everesteer.ai>
6
6
  License-Expression: MIT
@@ -164,11 +164,18 @@ df = pd.DataFrame(lb["entries"])
164
164
  ### Serverless compute
165
165
 
166
166
  ```python
167
- # Tier 1 — quick-train with built-in templates
168
- job = api.quick_train(model="lightgbm", features="small", target="target_everest_20")
169
-
170
- # Tier 2 — custom script on GPU
171
- job = api.custom_train(script_path="train.py", gpu="A100", max_hours=2.0)
167
+ # Built-in preset (lightgbm/xgboost/ridge/mlp/random_forest) — no data upload,
168
+ # the platform trains against the same obfuscated dataset you download.
169
+ job = api.train(model="lightgbm", features="small", target="target_everest_20")
170
+
171
+ # model="custom" — your own model factory, run server-side in an isolated,
172
+ # network-denied sandbox (no filesystem access, never sees held-out targets)
173
+ job = api.train(
174
+ model="custom",
175
+ custom_model_fn="def build_model(params):\n from sklearn.linear_model import Ridge\n return Ridge(**params)",
176
+ gpu="A100",
177
+ max_hours=2.0,
178
+ )
172
179
 
173
180
  # Wait and download
174
181
  result = api.wait_for_job(job["job_id"])
@@ -125,11 +125,18 @@ df = pd.DataFrame(lb["entries"])
125
125
  ### Serverless compute
126
126
 
127
127
  ```python
128
- # Tier 1 — quick-train with built-in templates
129
- job = api.quick_train(model="lightgbm", features="small", target="target_everest_20")
130
-
131
- # Tier 2 — custom script on GPU
132
- job = api.custom_train(script_path="train.py", gpu="A100", max_hours=2.0)
128
+ # Built-in preset (lightgbm/xgboost/ridge/mlp/random_forest) — no data upload,
129
+ # the platform trains against the same obfuscated dataset you download.
130
+ job = api.train(model="lightgbm", features="small", target="target_everest_20")
131
+
132
+ # model="custom" — your own model factory, run server-side in an isolated,
133
+ # network-denied sandbox (no filesystem access, never sees held-out targets)
134
+ job = api.train(
135
+ model="custom",
136
+ custom_model_fn="def build_model(params):\n from sklearn.linear_model import Ridge\n return Ridge(**params)",
137
+ gpu="A100",
138
+ max_hours=2.0,
139
+ )
133
140
 
134
141
  # Wait and download
135
142
  result = api.wait_for_job(job["job_id"])
@@ -1,6 +1,6 @@
1
1
  """EverestAPI — Python SDK for the Everesteer prediction tournament platform."""
2
2
 
3
- __version__ = "0.2.8"
3
+ __version__ = "0.2.9"
4
4
 
5
5
  from everestapi.client import EverestAPI, EverestError
6
6
  from everestapi.types import (
@@ -26,6 +26,7 @@ Usage::
26
26
  from __future__ import annotations
27
27
 
28
28
  import os
29
+ import warnings
29
30
  from pathlib import PurePath
30
31
  from typing import Any
31
32
 
@@ -786,50 +787,125 @@ class EverestAPI:
786
787
 
787
788
  # -- compute ----------------------------------------------------------
788
789
 
789
- def quick_train(
790
+ def train(
790
791
  self,
791
792
  model: str = "lightgbm",
792
- features: str = "small",
793
- target: str = "target_everest_20",
793
+ *,
794
+ custom_model_fn: str | None = None,
795
+ custom_feature_fn: str | None = None,
796
+ features: str | list[str] = "small",
797
+ target: str | list[str] = "target_everest_20",
794
798
  universe: str = "futures",
795
799
  params: dict | None = None,
800
+ cv: dict | None = None,
801
+ train_filter: dict | None = None,
802
+ val_filter: dict | None = None,
803
+ transforms: dict | None = None,
804
+ gpu: str = "T4",
805
+ max_hours: float = 1.0,
796
806
  ) -> dict:
797
- """Submit Tier 1 serverless training job."""
807
+ """Submit a unified training job (EVE-1013). Non-blocking.
808
+
809
+ train only — returns downloadable artifacts + metrics; does NOT
810
+ submit/host (use submit_futures_predictions / upload_model). Supply
811
+ a built-in ``model`` preset (lightgbm/xgboost/ridge/mlp/random_forest)
812
+ or ``model="custom"`` with a ``custom_model_fn`` factory; optionally
813
+ add ``custom_feature_fn`` for server-side feature engineering on the
814
+ resident data (no upload). The platform always runs exped-purged
815
+ cross-validation and computes canonical CORR20v2/AIMC/FNC/EAC — note
816
+ the pre-round AIMC is an ESTIMATE vs a static consensus proxy, not
817
+ the live stake-weighted crowd score used at round close.
818
+
819
+ Futures payout uses target_everest_20 (20-day forward return).
820
+ Final score = 0.75 * CORR + 2.25 * AIMC.
821
+
822
+ ``train_filter``/``val_filter`` (RowFilter) and ``transforms`` (fit-only
823
+ target transform + sample_weight override) are honored server-side,
824
+ diagnostic-only — never affects payout; see API_CONTRACT.md for the
825
+ full shape and the ``data_spec``/``canonical``/``slice_leak_note``
826
+ fields returned when a slice is applied.
827
+ """
798
828
  body: dict = {
799
829
  "model": model,
800
830
  "features": features,
801
831
  "target": target,
802
832
  "universe": universe,
833
+ "gpu": gpu,
834
+ "max_hours": max_hours,
803
835
  }
804
- if params:
836
+ if custom_model_fn is not None:
837
+ body["custom_model_fn"] = custom_model_fn
838
+ if custom_feature_fn is not None:
839
+ body["custom_feature_fn"] = custom_feature_fn
840
+ if params is not None:
805
841
  body["params"] = params
806
- return self._request("POST", "/api/v1/compute/quick-train", json=body)
842
+ if cv is not None:
843
+ body["cv"] = cv
844
+ if train_filter is not None:
845
+ body["train_filter"] = train_filter
846
+ if val_filter is not None:
847
+ body["val_filter"] = val_filter
848
+ if transforms is not None:
849
+ body["transforms"] = transforms
850
+ return self._request("POST", "/api/v1/compute/train", json=body)
807
851
 
808
- def custom_train(
852
+ def quick_train(
809
853
  self,
810
- script: str | None = None,
811
- script_path: str | None = None,
812
- files: dict[str, str] | None = None,
813
- gpu: str = "T4",
814
- max_hours: float = 1.0,
815
- requirements: list[str] | None = None,
854
+ model: str = "lightgbm",
855
+ features: str = "small",
856
+ target: str = "target_everest_20",
857
+ universe: str = "futures",
858
+ params: dict | None = None,
816
859
  ) -> dict:
817
- """Submit Tier 2 custom training job."""
818
- if script_path and not script:
819
- with open(script_path) as f:
820
- script = f.read()
821
- if files and not script:
822
- parts = [f"# --- FILE: {name} ---\n{content}" for name, content in files.items()]
823
- script = "\n\n".join(parts)
824
- body: dict = {"script": script, "gpu": gpu, "max_hours": max_hours}
825
- if requirements:
826
- body["requirements"] = requirements
827
- return self._request("POST", "/api/v1/compute/custom-train", json=body)
860
+ """Deprecated since 0.2.9 — forwards to :meth:`train`.
861
+
862
+ The Tier 1/Tier 2 split (``quick_train``/``custom_train``) was
863
+ replaced server-side by a single unified ``train`` tool (EVE-1013).
864
+ This shim keeps the old call signature working but always submits
865
+ through the new ``/api/v1/compute/train`` endpoint.
866
+ """
867
+ warnings.warn(
868
+ "quick_train() is deprecated; use train() instead (EVE-1013).",
869
+ DeprecationWarning,
870
+ stacklevel=2,
871
+ )
872
+ return self.train(
873
+ model=model, features=features, target=target, universe=universe, params=params
874
+ )
875
+
876
+ def custom_train(self, *args: Any, **kwargs: Any) -> dict:
877
+ """Removed since 0.2.9 — the Tier-2 script-based training path no longer exists.
878
+
879
+ Use :meth:`train` with ``model="custom"`` and a ``custom_model_fn``
880
+ source string instead; it now runs inside a sandboxed, network-denied
881
+ environment rather than a dedicated GPU pod running an arbitrary script.
882
+ """
883
+ warnings.warn(
884
+ "custom_train() is removed; use train(model='custom', custom_model_fn=...).",
885
+ DeprecationWarning,
886
+ stacklevel=2,
887
+ )
888
+ raise EverestError(
889
+ 410,
890
+ "custom_train (Tier-2 script) was removed. Use "
891
+ "train(model='custom', custom_model_fn='def build_model(params): ...').",
892
+ )
828
893
 
829
894
  def get_job_status(self, job_id: str) -> dict:
830
895
  """Poll compute job status."""
831
896
  return self._request("GET", f"/api/v1/compute/jobs/{job_id}")
832
897
 
898
+ def list_compute_jobs(self, limit: int = 50, status: str | None = None) -> dict:
899
+ """List your compute jobs, newest first (rows exclude config/output payloads)."""
900
+ params: dict = {"limit": limit}
901
+ if status is not None:
902
+ params["status"] = status
903
+ return self._request("GET", "/api/v1/compute/jobs", params=params)
904
+
905
+ def get_job_log(self, job_id: str) -> dict:
906
+ """Lifecycle event trail for one compute job — debug a failed/stuck job."""
907
+ return self._request("GET", f"/api/v1/compute/jobs/{job_id}/logs")
908
+
833
909
  def get_model_download_url(self, job_id: str) -> dict:
834
910
  """Get a presigned download URL for a trained model."""
835
911
  return self._request("GET", f"/api/v1/compute/jobs/{job_id}/model")
@@ -875,7 +951,7 @@ class EverestAPI:
875
951
  return self._request("GET", "/api/v1/compute/credits")
876
952
 
877
953
  def list_model_templates(self) -> dict:
878
- """List available quick-train model templates."""
954
+ """List available train model templates."""
879
955
  return self._request("GET", "/api/v1/compute/models")
880
956
 
881
957
  # -- registration (no auth) ------------------------------------------
@@ -270,42 +270,64 @@ TOOLS = [
270
270
  },
271
271
  },
272
272
  {
273
- "name": "quick_train",
274
- "description": "Train a model on Everesteer data using serverless GPU. Returns job_id. Poll with get_job_status(). Models: lightgbm, xgboost, ridge, mlp. Costs $0.005-0.05. Futures payout target: target_everest_20 (20-day forward return). Final score = 0.75*CORR + 2.25*AIMC.",
273
+ "name": "train",
274
+ "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 (0.75*CORR + 2.25*AIMC; 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().",
275
275
  "inputSchema": {
276
276
  "type": "object",
277
277
  "properties": {
278
- "model": {"type": "string", "enum": ["lightgbm", "xgboost", "ridge", "mlp"]},
279
- "features": {
278
+ "model": {
279
+ "type": "string",
280
+ "enum": ["lightgbm", "xgboost", "ridge", "mlp", "random_forest", "custom"],
281
+ "default": "lightgbm",
282
+ },
283
+ "custom_model_fn": {
284
+ "type": "string",
285
+ "description": "Python source defining build_model(params)->estimator, required when model='custom'. Runs in an isolated, network-denied sandbox with no filesystem access and never sees held-out targets. Rejected by the static safety scanner (returns 400) if it references disallowed modules/builtins.",
286
+ },
287
+ "custom_feature_fn": {
280
288
  "type": "string",
281
- "enum": ["small", "medium", "all"],
289
+ "description": "OPTIONAL Python source defining build_feature_transformer(params)->sklearn transformer for server-side feature engineering on the resident features. Usable with any model (preset or custom), in the same isolated sandbox as custom_model_fn. Same static-scanner gate (returns 400 on rejection).",
290
+ },
291
+ "params": {"type": "object", "description": "Model hyperparameters (optional)"},
292
+ "features": {
293
+ "oneOf": [
294
+ {"type": "string", "enum": ["small", "medium", "all"]},
295
+ {"type": "array", "items": {"type": "string"}},
296
+ ],
282
297
  "default": "small",
298
+ "description": "Preset (small/medium/all) or an explicit list of feature_* names.",
283
299
  },
284
300
  "target": {
285
- "type": "string",
301
+ "oneOf": [
302
+ {"type": "string"},
303
+ {"type": "array", "items": {"type": "string"}},
304
+ ],
286
305
  "default": "target_everest_20",
287
- "description": "Target column. Futures payout uses target_everest_20 (20-day forward return). Multiple target types available. See get_dataset_schema for full list.",
306
+ "description": "Target column. Futures payout uses target_everest_20 (20-day forward return).",
288
307
  },
289
308
  "universe": {"type": "string", "default": "futures"},
290
- "params": {"type": "object", "description": "Model hyperparameters (optional)"},
309
+ "cv": {
310
+ "type": "object",
311
+ "description": "Cross-validation spec: {scheme, folds, embargo}. scheme is exped_purged (default), chain_group, or combined; embargo defaults to the label horizon when null.",
312
+ },
313
+ "train_filter": {
314
+ "type": "object",
315
+ "description": "Row filter (exped/climb_difficulty/sample/feature_filter) applied to the training split server-side, no re-upload — diagnostic-only, never affects payout. Full RowFilter shape in API_CONTRACT.md.",
316
+ },
317
+ "val_filter": {
318
+ "type": "object",
319
+ "description": "Same RowFilter shape as train_filter, applied to the validation split (layered under the platform's own validation clamp, if any) — diagnostic-only, never affects payout.",
320
+ },
321
+ "transforms": {
322
+ "type": "object",
323
+ "description": "Fit-only target transform (residualize_to_benchmark/subtract_benchmark_zscore) and/or sample_weight override — scoring always uses the untouched canonical target. Full shape in API_CONTRACT.md.",
324
+ },
325
+ "gpu": {"type": "string", "enum": ["T4", "A10G", "A100"], "default": "T4"},
326
+ "max_hours": {"type": "number", "minimum": 0.1, "maximum": 4.0, "default": 1.0},
291
327
  },
292
328
  "required": ["model"],
293
329
  },
294
330
  },
295
- {
296
- "name": "custom_train",
297
- "description": "Run a custom Python script on a GPU pod. Pod has EverestAPI SDK + PyTorch + ML libs pre-installed. Costs $0.50-1.79/hr.",
298
- "inputSchema": {
299
- "type": "object",
300
- "properties": {
301
- "script": {"type": "string", "description": "Python script content"},
302
- "gpu": {"type": "string", "enum": ["T4", "A40", "A100"], "default": "T4"},
303
- "max_hours": {"type": "number", "default": 1},
304
- "requirements": {"type": "array", "items": {"type": "string"}},
305
- },
306
- "required": ["script"],
307
- },
308
- },
309
331
  {
310
332
  "name": "get_job_status",
311
333
  "description": "Check status of a compute job. Returns status, cost, output files when complete.",
@@ -314,7 +336,7 @@ TOOLS = [
314
336
  "properties": {
315
337
  "job_id": {
316
338
  "type": "string",
317
- "description": "Job ID from quick_train or custom_train",
339
+ "description": "Job ID from a train job",
318
340
  },
319
341
  },
320
342
  "required": ["job_id"],
@@ -328,7 +350,7 @@ TOOLS = [
328
350
  "properties": {
329
351
  "job_id": {
330
352
  "type": "string",
331
- "description": "Job ID from quick_train or custom_train",
353
+ "description": "Job ID from a train job",
332
354
  },
333
355
  },
334
356
  "required": ["job_id"],
@@ -903,6 +925,13 @@ TOOLSETS: dict[str, set[str]] = {
903
925
  # of the diagnostics lifecycle stays in "diagnostics".
904
926
  "submit_validation_diagnostics",
905
927
  "get_diagnostics_leaderboard",
928
+ # EVE-970: hosted training is a first-class flow now that every account
929
+ # carries a compute grant — advertise train + get_compute_credits by
930
+ # default so agents discover the hosted-training entry point without
931
+ # setting EIQ_MCP_TOOLSETS. get_job_status stays in "compute" (hidden
932
+ # tools remain callable by name). Mirrors the platform MCP server.
933
+ "train",
934
+ "get_compute_credits",
906
935
  },
907
936
  "data": {
908
937
  "get_universe",
@@ -925,10 +954,7 @@ TOOLSETS: dict[str, set[str]] = {
925
954
  "run_validation_diagnostics",
926
955
  },
927
956
  "compute": {
928
- "quick_train",
929
- "custom_train",
930
957
  "get_job_status",
931
- "get_compute_credits",
932
958
  },
933
959
  "staking": {
934
960
  "stake_on_model",
@@ -1090,21 +1116,8 @@ def _dispatch(name: str, arguments: dict) -> str:
1090
1116
  output_path=arguments.get("output_path"),
1091
1117
  )
1092
1118
  result = {"file_path": path, "status": "downloaded"}
1093
- elif name == "quick_train":
1094
- result = client.quick_train(
1095
- model=arguments["model"],
1096
- features=arguments.get("features", "small"),
1097
- target=arguments.get("target", "target_everest_20"),
1098
- universe=arguments.get("universe", "futures"),
1099
- params=arguments.get("params"),
1100
- )
1101
- elif name == "custom_train":
1102
- result = client.custom_train(
1103
- script=arguments.get("script"),
1104
- gpu=arguments.get("gpu", "T4"),
1105
- max_hours=arguments.get("max_hours", 1),
1106
- requirements=arguments.get("requirements"),
1107
- )
1119
+ elif name == "train":
1120
+ result = client.train(**arguments)
1108
1121
  elif name == "get_job_status":
1109
1122
  result = client.get_job_status(job_id=arguments["job_id"])
1110
1123
  elif name == "get_model_download_url":
@@ -1463,8 +1476,7 @@ _NEXT_ACTIONS: dict[str, list[str]] = {
1463
1476
  "download_dataset": ["create_model", "submit_futures_predictions"],
1464
1477
  "download_benchmark": ["submit_futures_predictions"],
1465
1478
  "get_benchmarks": ["download_benchmark"],
1466
- "quick_train": ["get_job_status"],
1467
- "custom_train": ["get_job_status"],
1479
+ "train": ["get_job_status"],
1468
1480
  "get_job_status": ["get_model_download_url"], # conditional
1469
1481
  "get_model_download_url": ["upload_model", "create_model"],
1470
1482
  "create_model": ["submit_futures_predictions", "upload_model"],
@@ -1496,7 +1508,7 @@ _NEXT_ACTIONS: dict[str, list[str]] = {
1496
1508
  "get_deposit_address": ["get_forwarder_balance", "relay_stake"],
1497
1509
  "get_forwarder_balance": ["relay_stake"],
1498
1510
  "withdraw_usdc": ["get_forwarder_balance"],
1499
- "get_compute_credits": ["quick_train"],
1511
+ "get_compute_credits": ["train"],
1500
1512
  "get_notifications": [],
1501
1513
  "get_badges": [],
1502
1514
  }
@@ -1521,8 +1533,7 @@ _NARRATION: dict[str, str] = {
1521
1533
  "submit_predictions": (
1522
1534
  "Submitted predictions for model '{model_id}'. Scoring starts at round close."
1523
1535
  ),
1524
- "quick_train": "Training job queued.",
1525
- "custom_train": "Custom training job queued.",
1536
+ "train": "Training job queued — returns downloadable artifacts + metrics, not a submission.",
1526
1537
  "get_job_status": "Training job status: {status}.",
1527
1538
  "get_model_download_url": "Generated a download URL for the trained model (valid ~1h).",
1528
1539
  "upload_model": "Model artifact uploaded; validation runs asynchronously.",
@@ -1582,7 +1593,7 @@ def _next_actions_for(name: str, arguments: dict, data: dict) -> list[str]:
1582
1593
  if status in ("completed", "done", "succeeded", "success"):
1583
1594
  return ["get_model_download_url", "create_model"]
1584
1595
  if status in ("failed", "error"):
1585
- return ["quick_train", "custom_train"]
1596
+ return ["train"]
1586
1597
  return ["get_job_status"] # still running — keep polling
1587
1598
  if name == "get_upload_status":
1588
1599
  if status in ("validated", "done", "success", "ready", "active"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Summary: Python SDK for the Everesteer prediction tournament platform
5
5
  Author-email: Everesteer <support@everesteer.ai>
6
6
  License-Expression: MIT
@@ -164,11 +164,18 @@ df = pd.DataFrame(lb["entries"])
164
164
  ### Serverless compute
165
165
 
166
166
  ```python
167
- # Tier 1 — quick-train with built-in templates
168
- job = api.quick_train(model="lightgbm", features="small", target="target_everest_20")
169
-
170
- # Tier 2 — custom script on GPU
171
- job = api.custom_train(script_path="train.py", gpu="A100", max_hours=2.0)
167
+ # Built-in preset (lightgbm/xgboost/ridge/mlp/random_forest) — no data upload,
168
+ # the platform trains against the same obfuscated dataset you download.
169
+ job = api.train(model="lightgbm", features="small", target="target_everest_20")
170
+
171
+ # model="custom" — your own model factory, run server-side in an isolated,
172
+ # network-denied sandbox (no filesystem access, never sees held-out targets)
173
+ job = api.train(
174
+ model="custom",
175
+ custom_model_fn="def build_model(params):\n from sklearn.linear_model import Ridge\n return Ridge(**params)",
176
+ gpu="A100",
177
+ max_hours=2.0,
178
+ )
172
179
 
173
180
  # Wait and download
174
181
  result = api.wait_for_job(job["job_id"])
@@ -514,6 +514,8 @@ _SDK_ONLY_SANCTIONED = {
514
514
  "get_multipliers",
515
515
  "get_current_season",
516
516
  "get_job_output",
517
+ "list_compute_jobs", # EVE-1013 — job lookup, same category as get_job_output
518
+ "get_job_log", # EVE-1013 — job lookup, same category as get_job_output
517
519
  "get_diagnostics_run",
518
520
  "get_validation_panel",
519
521
  "get_validation_diagnostics",
@@ -530,6 +532,11 @@ _SDK_ONLY_SANCTIONED = {
530
532
  "unstake",
531
533
  "confirm_stake",
532
534
  "cancel_job",
535
+ # EVE-1013 — quick_train/custom_train replaced by the unified `train` tool;
536
+ # both remain on the client as deprecated shims (quick_train forwards to
537
+ # train(), custom_train raises) but are no longer registered as MCP tools.
538
+ "quick_train",
539
+ "custom_train",
533
540
  }
534
541
 
535
542
 
File without changes
File without changes
File without changes
File without changes