everestapi 0.2.5__tar.gz → 0.2.7__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.
- {everestapi-0.2.5/src/everestapi.egg-info → everestapi-0.2.7}/PKG-INFO +60 -6
- {everestapi-0.2.5 → everestapi-0.2.7}/README.md +52 -5
- {everestapi-0.2.5 → everestapi-0.2.7}/pyproject.toml +4 -1
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/__init__.py +12 -1
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/cli.py +1 -1
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/client.py +101 -31
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/mcp/server.py +687 -43
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/plots.py +4 -5
- everestapi-0.2.7/src/everestapi/scoring.py +197 -0
- {everestapi-0.2.5 → everestapi-0.2.7/src/everestapi.egg-info}/PKG-INFO +60 -6
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi.egg-info/SOURCES.txt +6 -1
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi.egg-info/requires.txt +8 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/tests/test_client.py +116 -5
- everestapi-0.2.7/tests/test_eve953_mcp_progress.py +176 -0
- everestapi-0.2.7/tests/test_eve957_mcp_annotations_resources.py +75 -0
- everestapi-0.2.7/tests/test_eve959_toolsets.py +72 -0
- everestapi-0.2.7/tests/test_mcp_and_models.py +570 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/tests/test_prediction_range.py +2 -2
- everestapi-0.2.7/tests/test_scoring.py +112 -0
- everestapi-0.2.5/tests/test_mcp_and_models.py +0 -195
- {everestapi-0.2.5 → everestapi-0.2.7}/LICENSE +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/setup.cfg +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/__main__.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/mcp/__init__.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/mcp/__main__.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi/types.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi.egg-info/dependency_links.txt +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi.egg-info/entry_points.txt +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/src/everestapi.egg-info/top_level.txt +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/tests/test_cli.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/tests/test_diagnostics.py +0 -0
- {everestapi-0.2.5 → everestapi-0.2.7}/tests/test_json_or_raise.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: everestapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Python SDK for the Everesteer prediction tournament platform
|
|
5
5
|
Author-email: Everesteer <support@everesteer.ai>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,10 +24,17 @@ Requires-Dist: click>=8.0
|
|
|
24
24
|
Provides-Extra: dev
|
|
25
25
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
26
26
|
Requires-Dist: pytest-httpx>=0.34.0; extra == "dev"
|
|
27
|
+
Requires-Dist: numpy>=1.26; extra == "dev"
|
|
28
|
+
Requires-Dist: pandas>=2.0; extra == "dev"
|
|
29
|
+
Requires-Dist: scipy>=1.10; extra == "dev"
|
|
27
30
|
Provides-Extra: mcp
|
|
28
31
|
Requires-Dist: mcp>=1.0; extra == "mcp"
|
|
29
32
|
Provides-Extra: viz
|
|
30
33
|
Requires-Dist: plotnine>=0.13; extra == "viz"
|
|
34
|
+
Provides-Extra: scoring
|
|
35
|
+
Requires-Dist: numpy>=1.26; extra == "scoring"
|
|
36
|
+
Requires-Dist: pandas>=2.0; extra == "scoring"
|
|
37
|
+
Requires-Dist: scipy>=1.10; extra == "scoring"
|
|
31
38
|
Dynamic: license-file
|
|
32
39
|
|
|
33
40
|
# everestapi
|
|
@@ -89,8 +96,11 @@ api.submit_futures_predictions(model_id="my-fut-model", predictions={...})
|
|
|
89
96
|
Both Parquet and CSV are accepted. **Parquet is recommended** — float precision round-trips cleanly, files compress well, and it matches the format the SDK serves to you (`download_dataset` returns parquet).
|
|
90
97
|
|
|
91
98
|
```python
|
|
99
|
+
# A model slot must exist before you can submit — the platform never auto-creates one.
|
|
100
|
+
api.create_model(name="my-model")
|
|
101
|
+
|
|
92
102
|
api.submit_predictions_file(
|
|
93
|
-
|
|
103
|
+
model_id="my-model",
|
|
94
104
|
file_path="predictions.parquet", # or "predictions.csv"
|
|
95
105
|
tournament="equities",
|
|
96
106
|
)
|
|
@@ -168,16 +178,59 @@ api.get_stake_balance(model_id="my-model")
|
|
|
168
178
|
api.claim_payout(model_id="my-model", round_id="42")
|
|
169
179
|
```
|
|
170
180
|
|
|
171
|
-
###
|
|
181
|
+
### Score validation predictions offline
|
|
182
|
+
|
|
183
|
+
Reproduce the server's **exact** scoring — CORR20, AIMC20, FNC — *before* you
|
|
184
|
+
submit, so you stop guessing the sign of your signal ("submit raw and negated, let
|
|
185
|
+
the server decide"). The `everestapi.scoring` functions are a verbatim port of the
|
|
186
|
+
platform's scoring engine (verified equal to 1e-12), so your offline number **is**
|
|
187
|
+
the server's number.
|
|
188
|
+
|
|
189
|
+
Install the optional scoring extra (keeps the base SDK light — numpy/pandas/scipy
|
|
190
|
+
are only pulled in here):
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pip install "everestapi[scoring]"
|
|
194
|
+
```
|
|
172
195
|
|
|
173
196
|
```python
|
|
174
197
|
import pandas as pd
|
|
198
|
+
from everestapi import scoring
|
|
175
199
|
|
|
176
|
-
val = pd.read_parquet("
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
201
|
+
preds = my_model.predict(val.filter(like="feature_"))
|
|
202
|
+
|
|
203
|
+
# Score per exped (cross-section), then average — matches how the server scores.
|
|
204
|
+
per_exped = [
|
|
205
|
+
scoring.corr20(preds[val.exped == e], val.loc[val.exped == e, "target"])
|
|
206
|
+
for e in val.exped.unique()
|
|
207
|
+
]
|
|
208
|
+
print("mean CORR20:", sum(per_exped) / len(per_exped))
|
|
209
|
+
|
|
210
|
+
# Or every metric at once for one exped (ai_model = crowd consensus for that exped):
|
|
211
|
+
scoring.score(preds_e, target_e, ai_model=consensus_e, features=features_e)
|
|
212
|
+
# -> {"corr20", "aimc20", "payout", "fnc", "feature_exposure"}
|
|
179
213
|
```
|
|
180
214
|
|
|
215
|
+
**Sanity-check your pipeline against the example predictions.** The published
|
|
216
|
+
`eiq_validation_example_preds` are a benchmark-grade signal (the Minera ensemble)
|
|
217
|
+
and score a positive mean **CORR20 of ≈ 0.07**. Score that file and reproduce a
|
|
218
|
+
similar number — if you instead get ≈ −0.07, your sign is flipped; if you get ≈ 0,
|
|
219
|
+
your ids/alignment are off:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
ex = pd.read_parquet("eiq_validation_example_preds.parquet") # column: prediction
|
|
223
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
224
|
+
ref = [
|
|
225
|
+
scoring.corr20(ex.loc[val.exped == e, "prediction"], val.loc[val.exped == e, "target"])
|
|
226
|
+
for e in val.exped.unique()
|
|
227
|
+
]
|
|
228
|
+
print(sum(ref) / len(ref)) # ~0.07 -> pipeline + sign are correct
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
A quick convenience for a single overall correlation is also available:
|
|
232
|
+
`EverestAPI.evaluate(predictions, val, target="target_everest_20")`.
|
|
233
|
+
|
|
181
234
|
### CLI
|
|
182
235
|
|
|
183
236
|
```bash
|
|
@@ -207,6 +260,7 @@ with EverestAPI(api_key="...") as api:
|
|
|
207
260
|
|
|
208
261
|
- Python 3.10+
|
|
209
262
|
- httpx >= 0.27
|
|
263
|
+
- Optional `scoring` extra (`pip install "everestapi[scoring]"`): numpy, pandas, scipy — only needed for offline `everestapi.scoring`.
|
|
210
264
|
|
|
211
265
|
## Disclaimers
|
|
212
266
|
|
|
@@ -57,8 +57,11 @@ api.submit_futures_predictions(model_id="my-fut-model", predictions={...})
|
|
|
57
57
|
Both Parquet and CSV are accepted. **Parquet is recommended** — float precision round-trips cleanly, files compress well, and it matches the format the SDK serves to you (`download_dataset` returns parquet).
|
|
58
58
|
|
|
59
59
|
```python
|
|
60
|
+
# A model slot must exist before you can submit — the platform never auto-creates one.
|
|
61
|
+
api.create_model(name="my-model")
|
|
62
|
+
|
|
60
63
|
api.submit_predictions_file(
|
|
61
|
-
|
|
64
|
+
model_id="my-model",
|
|
62
65
|
file_path="predictions.parquet", # or "predictions.csv"
|
|
63
66
|
tournament="equities",
|
|
64
67
|
)
|
|
@@ -136,16 +139,59 @@ api.get_stake_balance(model_id="my-model")
|
|
|
136
139
|
api.claim_payout(model_id="my-model", round_id="42")
|
|
137
140
|
```
|
|
138
141
|
|
|
139
|
-
###
|
|
142
|
+
### Score validation predictions offline
|
|
143
|
+
|
|
144
|
+
Reproduce the server's **exact** scoring — CORR20, AIMC20, FNC — *before* you
|
|
145
|
+
submit, so you stop guessing the sign of your signal ("submit raw and negated, let
|
|
146
|
+
the server decide"). The `everestapi.scoring` functions are a verbatim port of the
|
|
147
|
+
platform's scoring engine (verified equal to 1e-12), so your offline number **is**
|
|
148
|
+
the server's number.
|
|
149
|
+
|
|
150
|
+
Install the optional scoring extra (keeps the base SDK light — numpy/pandas/scipy
|
|
151
|
+
are only pulled in here):
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install "everestapi[scoring]"
|
|
155
|
+
```
|
|
140
156
|
|
|
141
157
|
```python
|
|
142
158
|
import pandas as pd
|
|
159
|
+
from everestapi import scoring
|
|
143
160
|
|
|
144
|
-
val = pd.read_parquet("
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
162
|
+
preds = my_model.predict(val.filter(like="feature_"))
|
|
163
|
+
|
|
164
|
+
# Score per exped (cross-section), then average — matches how the server scores.
|
|
165
|
+
per_exped = [
|
|
166
|
+
scoring.corr20(preds[val.exped == e], val.loc[val.exped == e, "target"])
|
|
167
|
+
for e in val.exped.unique()
|
|
168
|
+
]
|
|
169
|
+
print("mean CORR20:", sum(per_exped) / len(per_exped))
|
|
170
|
+
|
|
171
|
+
# Or every metric at once for one exped (ai_model = crowd consensus for that exped):
|
|
172
|
+
scoring.score(preds_e, target_e, ai_model=consensus_e, features=features_e)
|
|
173
|
+
# -> {"corr20", "aimc20", "payout", "fnc", "feature_exposure"}
|
|
147
174
|
```
|
|
148
175
|
|
|
176
|
+
**Sanity-check your pipeline against the example predictions.** The published
|
|
177
|
+
`eiq_validation_example_preds` are a benchmark-grade signal (the Minera ensemble)
|
|
178
|
+
and score a positive mean **CORR20 of ≈ 0.07**. Score that file and reproduce a
|
|
179
|
+
similar number — if you instead get ≈ −0.07, your sign is flipped; if you get ≈ 0,
|
|
180
|
+
your ids/alignment are off:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
ex = pd.read_parquet("eiq_validation_example_preds.parquet") # column: prediction
|
|
184
|
+
val = pd.read_parquet("eiq_validation.parquet")
|
|
185
|
+
ref = [
|
|
186
|
+
scoring.corr20(ex.loc[val.exped == e, "prediction"], val.loc[val.exped == e, "target"])
|
|
187
|
+
for e in val.exped.unique()
|
|
188
|
+
]
|
|
189
|
+
print(sum(ref) / len(ref)) # ~0.07 -> pipeline + sign are correct
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A quick convenience for a single overall correlation is also available:
|
|
193
|
+
`EverestAPI.evaluate(predictions, val, target="target_everest_20")`.
|
|
194
|
+
|
|
149
195
|
### CLI
|
|
150
196
|
|
|
151
197
|
```bash
|
|
@@ -175,6 +221,7 @@ with EverestAPI(api_key="...") as api:
|
|
|
175
221
|
|
|
176
222
|
- Python 3.10+
|
|
177
223
|
- httpx >= 0.27
|
|
224
|
+
- Optional `scoring` extra (`pip install "everestapi[scoring]"`): numpy, pandas, scipy — only needed for offline `everestapi.scoring`.
|
|
178
225
|
|
|
179
226
|
## Disclaimers
|
|
180
227
|
|
|
@@ -27,13 +27,16 @@ dependencies = [
|
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
[project.optional-dependencies]
|
|
30
|
-
dev = ["pytest>=8.0", "pytest-httpx>=0.34.0"]
|
|
30
|
+
dev = ["pytest>=8.0", "pytest-httpx>=0.34.0", "numpy>=1.26", "pandas>=2.0", "scipy>=1.10"]
|
|
31
31
|
# MCP server (`python -m everestapi.mcp`). Optional — the server falls back to a
|
|
32
32
|
# built-in JSON-RPC stdio loop when the official `mcp` SDK isn't installed.
|
|
33
33
|
mcp = ["mcp>=1.0"]
|
|
34
34
|
# Plotting helpers (`everestapi.plots`). Heavy — pulls matplotlib/pandas/numpy/
|
|
35
35
|
# scipy/statsmodels/mizani. Optional: the SDK and MCP server never import it.
|
|
36
36
|
viz = ["plotnine>=0.13"]
|
|
37
|
+
# Local scoring (`everestapi.scoring`). Optional — keeps the base SDK light
|
|
38
|
+
# (httpx + click only); pull these in to score validation preds offline.
|
|
39
|
+
scoring = ["numpy>=1.26", "pandas>=2.0", "scipy>=1.10"]
|
|
37
40
|
|
|
38
41
|
[project.scripts]
|
|
39
42
|
everestapi = "everestapi.cli:cli"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""EverestAPI — Python SDK for the Everesteer prediction tournament platform."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.7"
|
|
4
4
|
|
|
5
5
|
from everestapi.client import EverestAPI, EverestError
|
|
6
6
|
from everestapi.types import (
|
|
@@ -29,4 +29,15 @@ __all__ = [
|
|
|
29
29
|
"StakeResponse",
|
|
30
30
|
"Submission",
|
|
31
31
|
"UniverseResponse",
|
|
32
|
+
"scoring",
|
|
32
33
|
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def __getattr__(name):
|
|
37
|
+
# Lazy import so `import everestapi` stays light (httpx + click only) — the
|
|
38
|
+
# numpy/pandas/scipy stack is only loaded if you actually use scoring.
|
|
39
|
+
if name == "scoring":
|
|
40
|
+
import everestapi.scoring as scoring
|
|
41
|
+
|
|
42
|
+
return scoring
|
|
43
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -108,7 +108,7 @@ def submit(model: str, file_path: str, tournament: str, api_key: str | None) ->
|
|
|
108
108
|
try:
|
|
109
109
|
if ext == "parquet":
|
|
110
110
|
result = api.submit_predictions_file(
|
|
111
|
-
|
|
111
|
+
model_id=model,
|
|
112
112
|
file_path=file_path,
|
|
113
113
|
tournament=tournament,
|
|
114
114
|
)
|
|
@@ -133,9 +133,7 @@ def _json_or_raise(resp: httpx.Response) -> dict:
|
|
|
133
133
|
except Exception:
|
|
134
134
|
body = (resp.text or "").strip()
|
|
135
135
|
looks_like_cf = (
|
|
136
|
-
"<html" in body[:200].lower()
|
|
137
|
-
or "cloudflare" in body.lower()
|
|
138
|
-
or "AccessDenied" in body
|
|
136
|
+
"<html" in body[:200].lower() or "cloudflare" in body.lower() or "AccessDenied" in body
|
|
139
137
|
)
|
|
140
138
|
hint = (
|
|
141
139
|
" — this looks like a Cloudflare Access challenge; set "
|
|
@@ -145,14 +143,11 @@ def _json_or_raise(resp: httpx.Response) -> dict:
|
|
|
145
143
|
)
|
|
146
144
|
raise EverestError(
|
|
147
145
|
resp.status_code,
|
|
148
|
-
f"expected a JSON response but received non-JSON content{hint}: "
|
|
149
|
-
f"{body[:200]}",
|
|
146
|
+
f"expected a JSON response but received non-JSON content{hint}: {body[:200]}",
|
|
150
147
|
)
|
|
151
148
|
|
|
152
149
|
|
|
153
|
-
def _check_prediction_range(
|
|
154
|
-
values: dict[str, float], *, lo: float = 0.0, hi: float = 1.0
|
|
155
|
-
) -> None:
|
|
150
|
+
def _check_prediction_range(values: dict[str, float], *, lo: float = 0.0, hi: float = 1.0) -> None:
|
|
156
151
|
"""Raise ``ValueError`` if any prediction is non-finite or outside ``[lo, hi]``.
|
|
157
152
|
|
|
158
153
|
Mirrors the server-side futures bound so a bad submission fails fast
|
|
@@ -173,11 +168,22 @@ def _check_prediction_range(
|
|
|
173
168
|
preview = ", ".join(bad[:5])
|
|
174
169
|
more = f" (+{len(bad) - 5} more)" if len(bad) > 5 else ""
|
|
175
170
|
raise ValueError(
|
|
176
|
-
f"{len(bad)} prediction(s) must be finite and within "
|
|
177
|
-
f"[{lo}, {hi}]: {preview}{more}"
|
|
171
|
+
f"{len(bad)} prediction(s) must be finite and within [{lo}, {hi}]: {preview}{more}"
|
|
178
172
|
)
|
|
179
173
|
|
|
180
174
|
|
|
175
|
+
def _resolve_default_base_url() -> str:
|
|
176
|
+
"""Resolve the default API base URL.
|
|
177
|
+
|
|
178
|
+
Honors ``EIQ_BASE_URL`` first (the var the MCP server + onboarding set), then
|
|
179
|
+
legacy ``EVEREST_API_URL``, then defaults to the API host
|
|
180
|
+
``https://app.everesteer.ai``. The apex ``everesteer.ai`` is a marketing site
|
|
181
|
+
(CloudFront→S3) that serves no ``/api`` — defaulting there returns an S3
|
|
182
|
+
``AccessDenied`` for every call, so it is deliberately not the fallback.
|
|
183
|
+
"""
|
|
184
|
+
return os.getenv("EIQ_BASE_URL") or os.getenv("EVEREST_API_URL") or "https://app.everesteer.ai"
|
|
185
|
+
|
|
186
|
+
|
|
181
187
|
class EverestAPI:
|
|
182
188
|
"""Synchronous client for the Everesteer tournament API.
|
|
183
189
|
|
|
@@ -186,8 +192,11 @@ class EverestAPI:
|
|
|
186
192
|
api_key : str, optional
|
|
187
193
|
Everesteer API key. Falls back to ``EIQ_API_KEY`` (or legacy ``EVEREST_API_KEY``) env var.
|
|
188
194
|
base_url : str, optional
|
|
189
|
-
Base URL for the API. Defaults to ``https://everesteer.ai
|
|
190
|
-
|
|
195
|
+
Base URL for the API. Defaults to the API host ``https://app.everesteer.ai``;
|
|
196
|
+
overridable via ``EIQ_BASE_URL`` (preferred — the var the MCP server and
|
|
197
|
+
onboarding set) or legacy ``EVEREST_API_URL``. The apex ``everesteer.ai``
|
|
198
|
+
is a marketing site (CloudFront→S3) that serves no ``/api``, so it is NOT
|
|
199
|
+
the default.
|
|
191
200
|
timeout : float
|
|
192
201
|
HTTP timeout in seconds.
|
|
193
202
|
tournament : str
|
|
@@ -200,7 +209,7 @@ class EverestAPI:
|
|
|
200
209
|
explicitly supplied.
|
|
201
210
|
"""
|
|
202
211
|
|
|
203
|
-
DEFAULT_BASE_URL =
|
|
212
|
+
DEFAULT_BASE_URL = _resolve_default_base_url()
|
|
204
213
|
|
|
205
214
|
def __init__(
|
|
206
215
|
self,
|
|
@@ -307,29 +316,42 @@ class EverestAPI:
|
|
|
307
316
|
|
|
308
317
|
def submit_predictions_file(
|
|
309
318
|
self,
|
|
310
|
-
|
|
311
|
-
file_path: str,
|
|
319
|
+
model_id: str | None = None,
|
|
320
|
+
file_path: str | None = None,
|
|
312
321
|
tournament: str | None = None,
|
|
322
|
+
*,
|
|
323
|
+
model_name: str | None = None,
|
|
313
324
|
) -> dict:
|
|
314
325
|
"""POST /api/v1/predictions/upload — submit predictions as a CSV or Parquet file.
|
|
315
326
|
|
|
316
327
|
Parameters
|
|
317
328
|
----------
|
|
318
|
-
|
|
319
|
-
Model identifier.
|
|
329
|
+
model_id : str
|
|
330
|
+
Model identifier. (``model_name`` is a deprecated alias.)
|
|
320
331
|
file_path : str
|
|
321
332
|
Path to a ``.csv`` or ``.parquet`` file with columns ``ticker`` (str)
|
|
322
333
|
and ``score`` (float in [-1, 1]).
|
|
323
334
|
tournament : str, optional
|
|
324
335
|
Tournament identifier. Defaults to the client's tournament.
|
|
325
336
|
"""
|
|
337
|
+
if model_id is None:
|
|
338
|
+
if model_name is None:
|
|
339
|
+
raise TypeError("submit_predictions_file() missing required argument: 'model_id'")
|
|
340
|
+
import warnings
|
|
341
|
+
|
|
342
|
+
warnings.warn(
|
|
343
|
+
"model_name= is deprecated; use model_id=", DeprecationWarning, stacklevel=2
|
|
344
|
+
)
|
|
345
|
+
model_id = model_name
|
|
346
|
+
if file_path is None:
|
|
347
|
+
raise TypeError("submit_predictions_file() missing required argument: 'file_path'")
|
|
326
348
|
tourn = tournament or self.tournament
|
|
327
349
|
with open(file_path, "rb") as f:
|
|
328
350
|
fname = file_path.rsplit("/", 1)[-1].rsplit("\\", 1)[-1]
|
|
329
351
|
resp = self._client.post(
|
|
330
352
|
"/api/v1/predictions/upload",
|
|
331
353
|
files={"file": (fname, f, "application/octet-stream")},
|
|
332
|
-
params={"model_id":
|
|
354
|
+
params={"model_id": model_id, "tournament": tourn},
|
|
333
355
|
)
|
|
334
356
|
return _json_or_raise(resp)
|
|
335
357
|
|
|
@@ -472,9 +494,7 @@ class EverestAPI:
|
|
|
472
494
|
)
|
|
473
495
|
return "\n".join(lines)
|
|
474
496
|
|
|
475
|
-
def get_validation_panel(
|
|
476
|
-
self, model_id: str, days: int = 365, source: str = "auto"
|
|
477
|
-
) -> dict:
|
|
497
|
+
def get_validation_panel(self, model_id: str, days: int = 365, source: str = "auto") -> dict:
|
|
478
498
|
"""GET /api/v1/models/{model_id}/diagnostics/validation — full validation metrics.
|
|
479
499
|
|
|
480
500
|
``source="auto"`` (default) returns the latest completed validation-diagnostics
|
|
@@ -532,9 +552,19 @@ class EverestAPI:
|
|
|
532
552
|
output_path,
|
|
533
553
|
)
|
|
534
554
|
|
|
535
|
-
def get_scores(self, model_id: str, days: int = 30) -> dict:
|
|
536
|
-
"""GET /api/v1/scores — scoring results for a model.
|
|
537
|
-
|
|
555
|
+
def get_scores(self, model_id: str, days: int = 30, response_format: str = "concise") -> dict:
|
|
556
|
+
"""GET /api/v1/scores — scoring results for a model.
|
|
557
|
+
|
|
558
|
+
``response_format="detailed"`` asks the platform to add a plain-language
|
|
559
|
+
``interpretation`` field that reads the model's own latest numbers and
|
|
560
|
+
state and explains how to improve. ``"concise"`` (default) leaves the
|
|
561
|
+
response unchanged.
|
|
562
|
+
"""
|
|
563
|
+
params = {
|
|
564
|
+
"model_id": model_id,
|
|
565
|
+
"days": str(days),
|
|
566
|
+
"response_format": response_format,
|
|
567
|
+
}
|
|
538
568
|
return self._request("GET", "/api/v1/scores", params=params)
|
|
539
569
|
|
|
540
570
|
def get_leaderboard(self, period: str = "30d") -> dict:
|
|
@@ -613,9 +643,15 @@ class EverestAPI:
|
|
|
613
643
|
params["date"] = date
|
|
614
644
|
return self._request("GET", f"/api/v1/diagnostics/{model_id}", params=params)
|
|
615
645
|
|
|
616
|
-
def get_round_diagnostics(self, model_id: str) -> dict:
|
|
617
|
-
"""GET /api/v1/diagnostics/{model_id}/rounds — per-round scoring breakdown.
|
|
618
|
-
|
|
646
|
+
def get_round_diagnostics(self, model_id: str, response_format: str = "concise") -> dict:
|
|
647
|
+
"""GET /api/v1/diagnostics/{model_id}/rounds — per-round scoring breakdown.
|
|
648
|
+
|
|
649
|
+
``response_format="detailed"`` asks the platform to add a plain-language
|
|
650
|
+
``interpretation`` field grounded in the model's own latest numbers and
|
|
651
|
+
state. ``"concise"`` (default) leaves the response unchanged.
|
|
652
|
+
"""
|
|
653
|
+
params = {"response_format": response_format}
|
|
654
|
+
return self._request("GET", f"/api/v1/diagnostics/{model_id}/rounds", params=params)
|
|
619
655
|
|
|
620
656
|
# -- futures tournament -----------------------------------------------
|
|
621
657
|
|
|
@@ -881,8 +917,8 @@ class EverestAPI:
|
|
|
881
917
|
|
|
882
918
|
# -- rounds -----------------------------------------------------------
|
|
883
919
|
|
|
884
|
-
def get_rounds(self, tournament: str = "
|
|
885
|
-
"""GET /api/v1/rounds — list tournament rounds."""
|
|
920
|
+
def get_rounds(self, tournament: str = "futures", limit: int = 25) -> dict:
|
|
921
|
+
"""GET /api/v1/rounds — list tournament rounds (defaults to 'futures'; equities is unlaunched)."""
|
|
886
922
|
return self._request(
|
|
887
923
|
"GET",
|
|
888
924
|
"/api/v1/rounds",
|
|
@@ -892,8 +928,13 @@ class EverestAPI:
|
|
|
892
928
|
},
|
|
893
929
|
)
|
|
894
930
|
|
|
895
|
-
def get_current_round(self, tournament: str = "
|
|
896
|
-
"""GET /api/v1/rounds/current — current active round.
|
|
931
|
+
def get_current_round(self, tournament: str = "futures") -> dict:
|
|
932
|
+
"""GET /api/v1/rounds/current — current active round (defaults to 'futures'; equities is unlaunched).
|
|
933
|
+
|
|
934
|
+
With a hackathon-scoped key there is no live round — the response is a
|
|
935
|
+
diagnostics-mode payload (mode='diagnostics_hackathon') directing you to
|
|
936
|
+
download_dataset + submit_validation_diagnostics.
|
|
937
|
+
"""
|
|
897
938
|
return self._request(
|
|
898
939
|
"GET",
|
|
899
940
|
"/api/v1/rounds/current",
|
|
@@ -906,6 +947,35 @@ class EverestAPI:
|
|
|
906
947
|
"""GET /api/v1/schedule — round schedule for both tournaments."""
|
|
907
948
|
return self._request("GET", "/api/v1/schedule")
|
|
908
949
|
|
|
950
|
+
def get_started(self) -> dict:
|
|
951
|
+
"""GET /api/v1/get_started — mode-aware orientation.
|
|
952
|
+
|
|
953
|
+
Returns what to do next given this key's scope: the display-only
|
|
954
|
+
diagnostics-hackathon loop, or the live futures tournament flow.
|
|
955
|
+
"""
|
|
956
|
+
return self._request("GET", "/api/v1/get_started")
|
|
957
|
+
|
|
958
|
+
def get_status(self) -> dict:
|
|
959
|
+
"""GET /api/v1/status — state-aware orientation ("where am I").
|
|
960
|
+
|
|
961
|
+
Your whole situational picture in one call: the models you own, each
|
|
962
|
+
one's latest submission and score state, the open round's clock, your
|
|
963
|
+
stake, and a ``next_actions`` list — instead of stitching together
|
|
964
|
+
get_models + get_scores + get_current_round. Display-only.
|
|
965
|
+
"""
|
|
966
|
+
return self._request("GET", "/api/v1/status")
|
|
967
|
+
|
|
968
|
+
def get_capabilities(self) -> dict:
|
|
969
|
+
"""GET / (site root) with ``Accept: application/json`` — capability index.
|
|
970
|
+
|
|
971
|
+
A small machine-readable map of where everything lives: pointers to
|
|
972
|
+
``llms.txt``, the OpenAPI spec, the MCP endpoint, the API base, the SDK,
|
|
973
|
+
and related discovery URLs. Hit before anything else to learn the
|
|
974
|
+
platform's shape without hardcoding paths. The index sits at the host
|
|
975
|
+
root; a leading-slash path resolves there against the API host base URL.
|
|
976
|
+
"""
|
|
977
|
+
return self._request("GET", "/", headers={"Accept": "application/json"})
|
|
978
|
+
|
|
909
979
|
# -- model uploads ----------------------------------------------------
|
|
910
980
|
|
|
911
981
|
def upload_model(self, model_id: str, file_path: str) -> dict:
|