everestapi 0.2.4__tar.gz → 0.2.6__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.4/src/everestapi.egg-info → everestapi-0.2.6}/PKG-INFO +1 -1
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/__init__.py +1 -1
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/client.py +30 -3
- {everestapi-0.2.4 → everestapi-0.2.6/src/everestapi.egg-info}/PKG-INFO +1 -1
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_client.py +46 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_mcp_and_models.py +8 -6
- {everestapi-0.2.4 → everestapi-0.2.6}/LICENSE +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/README.md +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/pyproject.toml +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/setup.cfg +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/__main__.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/cli.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/__init__.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/__main__.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/server.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/plots.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/types.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/SOURCES.txt +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/dependency_links.txt +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/entry_points.txt +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/requires.txt +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/top_level.txt +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_cli.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_diagnostics.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_json_or_raise.py +0 -0
- {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_prediction_range.py +0 -0
|
@@ -178,6 +178,22 @@ def _check_prediction_range(
|
|
|
178
178
|
)
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
def _resolve_default_base_url() -> str:
|
|
182
|
+
"""Resolve the default API base URL.
|
|
183
|
+
|
|
184
|
+
Honors ``EIQ_BASE_URL`` first (the var the MCP server + onboarding set), then
|
|
185
|
+
legacy ``EVEREST_API_URL``, then defaults to the API host
|
|
186
|
+
``https://app.everesteer.ai``. The apex ``everesteer.ai`` is a marketing site
|
|
187
|
+
(CloudFront→S3) that serves no ``/api`` — defaulting there returns an S3
|
|
188
|
+
``AccessDenied`` for every call, so it is deliberately not the fallback.
|
|
189
|
+
"""
|
|
190
|
+
return (
|
|
191
|
+
os.getenv("EIQ_BASE_URL")
|
|
192
|
+
or os.getenv("EVEREST_API_URL")
|
|
193
|
+
or "https://app.everesteer.ai"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
181
197
|
class EverestAPI:
|
|
182
198
|
"""Synchronous client for the Everesteer tournament API.
|
|
183
199
|
|
|
@@ -186,8 +202,11 @@ class EverestAPI:
|
|
|
186
202
|
api_key : str, optional
|
|
187
203
|
Everesteer API key. Falls back to ``EIQ_API_KEY`` (or legacy ``EVEREST_API_KEY``) env var.
|
|
188
204
|
base_url : str, optional
|
|
189
|
-
Base URL for the API. Defaults to ``https://everesteer.ai
|
|
190
|
-
|
|
205
|
+
Base URL for the API. Defaults to the API host ``https://app.everesteer.ai``;
|
|
206
|
+
overridable via ``EIQ_BASE_URL`` (preferred — the var the MCP server and
|
|
207
|
+
onboarding set) or legacy ``EVEREST_API_URL``. The apex ``everesteer.ai``
|
|
208
|
+
is a marketing site (CloudFront→S3) that serves no ``/api``, so it is NOT
|
|
209
|
+
the default.
|
|
191
210
|
timeout : float
|
|
192
211
|
HTTP timeout in seconds.
|
|
193
212
|
tournament : str
|
|
@@ -200,7 +219,7 @@ class EverestAPI:
|
|
|
200
219
|
explicitly supplied.
|
|
201
220
|
"""
|
|
202
221
|
|
|
203
|
-
DEFAULT_BASE_URL =
|
|
222
|
+
DEFAULT_BASE_URL = _resolve_default_base_url()
|
|
204
223
|
|
|
205
224
|
def __init__(
|
|
206
225
|
self,
|
|
@@ -561,9 +580,17 @@ class EverestAPI:
|
|
|
561
580
|
|
|
562
581
|
Splits: ``train`` / ``validation`` (features + targets), ``live``
|
|
563
582
|
(current round features only).
|
|
583
|
+
|
|
584
|
+
Futures is served by the futures endpoint (``version`` is a no-op):
|
|
585
|
+
it returns the real bregen tree to full-scope keys and the
|
|
586
|
+
target-stripped hackathon tree to hackathon-scoped keys (EVE-931).
|
|
564
587
|
"""
|
|
565
588
|
if output_path is None:
|
|
566
589
|
output_path = f"{universe}_{split}.parquet"
|
|
590
|
+
# Futures uses the futures endpoint (see docstring); the version route
|
|
591
|
+
# below is full-scope-only and 403s hackathon-scoped keys.
|
|
592
|
+
if universe == "futures":
|
|
593
|
+
return self.download_futures_data(split=split, output_path=output_path)
|
|
567
594
|
if version in (None, "latest", "current"):
|
|
568
595
|
version = self._current_dataset_version(universe) or "v0"
|
|
569
596
|
path = f"/api/v1/data/download/{version}/{universe}/{split}"
|
|
@@ -49,6 +49,35 @@ def test_get_universe(httpx_mock, api):
|
|
|
49
49
|
assert result["count"] == 0
|
|
50
50
|
|
|
51
51
|
|
|
52
|
+
# --------------------------------------------------------------------------- #
|
|
53
|
+
# Default base URL resolution: EIQ_BASE_URL (ecosystem var) > EVEREST_API_URL >
|
|
54
|
+
# the API host app.everesteer.ai. The apex everesteer.ai is a marketing site
|
|
55
|
+
# that serves no /api, so it must never be the default — that was the CLI 403.
|
|
56
|
+
# --------------------------------------------------------------------------- #
|
|
57
|
+
def test_default_base_url_prefers_eiq_base_url(monkeypatch):
|
|
58
|
+
from everestapi.client import _resolve_default_base_url
|
|
59
|
+
|
|
60
|
+
monkeypatch.setenv("EIQ_BASE_URL", "https://app.everesteer.ai")
|
|
61
|
+
monkeypatch.setenv("EVEREST_API_URL", "https://legacy.example")
|
|
62
|
+
assert _resolve_default_base_url() == "https://app.everesteer.ai"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_default_base_url_falls_back_to_legacy_var(monkeypatch):
|
|
66
|
+
from everestapi.client import _resolve_default_base_url
|
|
67
|
+
|
|
68
|
+
monkeypatch.delenv("EIQ_BASE_URL", raising=False)
|
|
69
|
+
monkeypatch.setenv("EVEREST_API_URL", "https://legacy.example")
|
|
70
|
+
assert _resolve_default_base_url() == "https://legacy.example"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_default_base_url_defaults_to_app_host(monkeypatch):
|
|
74
|
+
from everestapi.client import _resolve_default_base_url
|
|
75
|
+
|
|
76
|
+
monkeypatch.delenv("EIQ_BASE_URL", raising=False)
|
|
77
|
+
monkeypatch.delenv("EVEREST_API_URL", raising=False)
|
|
78
|
+
assert _resolve_default_base_url() == "https://app.everesteer.ai"
|
|
79
|
+
|
|
80
|
+
|
|
52
81
|
def test_submit_predictions_posts_body(httpx_mock, api):
|
|
53
82
|
httpx_mock.add_response(
|
|
54
83
|
url="http://test/api/v1/predictions",
|
|
@@ -92,6 +121,23 @@ def test_safe_output_path_rejects_traversal(api, tmp_path):
|
|
|
92
121
|
assert _safe_output_path(str(tmp_path / "ok.bin")) == str(tmp_path / "ok.bin")
|
|
93
122
|
|
|
94
123
|
|
|
124
|
+
def test_download_dataset_futures_uses_futures_endpoint(httpx_mock, api, tmp_path):
|
|
125
|
+
# EVE-931: futures must route through the futures endpoint (which serves
|
|
126
|
+
# hackathon-scoped keys), NOT the full-scope-only version route that 403s
|
|
127
|
+
# them. version is a no-op for futures.
|
|
128
|
+
httpx_mock.add_response(
|
|
129
|
+
url="http://test/api/v1/futures/data/validation", content=b"PARQUET"
|
|
130
|
+
)
|
|
131
|
+
out = api.download_dataset(
|
|
132
|
+
universe="futures", split="validation",
|
|
133
|
+
output_path=str(tmp_path / "f.parquet"), version="bregen",
|
|
134
|
+
)
|
|
135
|
+
assert out == str(tmp_path / "f.parquet")
|
|
136
|
+
paths = [r.url.path for r in httpx_mock.get_requests()]
|
|
137
|
+
assert paths == ["/api/v1/futures/data/validation"]
|
|
138
|
+
assert not any("/data/download" in p or "/data/versions" in p for p in paths)
|
|
139
|
+
|
|
140
|
+
|
|
95
141
|
# --- Cloudflare Access service token ---------------------------------------
|
|
96
142
|
# Staging sits behind Cloudflare Access; a service token clears the gate
|
|
97
143
|
# programmatically. Headers ride alongside X-API-Key and stay absent by default.
|
|
@@ -75,18 +75,20 @@ def test_get_models(httpx_mock, api):
|
|
|
75
75
|
# -- client: versioned dataset download (the #558 fix) -------------------
|
|
76
76
|
|
|
77
77
|
|
|
78
|
+
# Non-futures universes still use the version-scoped route (futures bypasses it
|
|
79
|
+
# via the futures endpoint, EVE-931 — see test_client.py).
|
|
78
80
|
def test_download_dataset_uses_versioned_route(httpx_mock, api, tmp_path):
|
|
79
81
|
httpx_mock.add_response(
|
|
80
|
-
url="http://test/api/v1/data/download/bregen/
|
|
82
|
+
url="http://test/api/v1/data/download/bregen/equities/train",
|
|
81
83
|
content=b"PAR1data",
|
|
82
84
|
)
|
|
83
85
|
out = api.download_dataset(
|
|
84
|
-
universe="
|
|
86
|
+
universe="equities",
|
|
85
87
|
split="train",
|
|
86
88
|
version="bregen",
|
|
87
89
|
output_path=str(tmp_path / "t.parquet"),
|
|
88
90
|
)
|
|
89
|
-
assert httpx_mock.get_request().url.path == "/api/v1/data/download/bregen/
|
|
91
|
+
assert httpx_mock.get_request().url.path == "/api/v1/data/download/bregen/equities/train"
|
|
90
92
|
with open(out, "rb") as f:
|
|
91
93
|
assert f.read() == b"PAR1data"
|
|
92
94
|
|
|
@@ -94,14 +96,14 @@ def test_download_dataset_uses_versioned_route(httpx_mock, api, tmp_path):
|
|
|
94
96
|
def test_download_dataset_latest_resolves_version(httpx_mock, api, tmp_path):
|
|
95
97
|
httpx_mock.add_response(
|
|
96
98
|
url="http://test/api/v1/data/versions",
|
|
97
|
-
json={"versions": [{"version": "bregen", "universes": ["
|
|
99
|
+
json={"versions": [{"version": "bregen", "universes": ["equities"]}]},
|
|
98
100
|
)
|
|
99
101
|
httpx_mock.add_response(
|
|
100
|
-
url="http://test/api/v1/data/download/bregen/
|
|
102
|
+
url="http://test/api/v1/data/download/bregen/equities/live",
|
|
101
103
|
content=b"PAR1live",
|
|
102
104
|
)
|
|
103
105
|
out = api.download_dataset(
|
|
104
|
-
universe="
|
|
106
|
+
universe="equities",
|
|
105
107
|
split="live",
|
|
106
108
|
version="latest",
|
|
107
109
|
output_path=str(tmp_path / "l.parquet"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|