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.
Files changed (26) hide show
  1. {everestapi-0.2.4/src/everestapi.egg-info → everestapi-0.2.6}/PKG-INFO +1 -1
  2. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/__init__.py +1 -1
  3. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/client.py +30 -3
  4. {everestapi-0.2.4 → everestapi-0.2.6/src/everestapi.egg-info}/PKG-INFO +1 -1
  5. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_client.py +46 -0
  6. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_mcp_and_models.py +8 -6
  7. {everestapi-0.2.4 → everestapi-0.2.6}/LICENSE +0 -0
  8. {everestapi-0.2.4 → everestapi-0.2.6}/README.md +0 -0
  9. {everestapi-0.2.4 → everestapi-0.2.6}/pyproject.toml +0 -0
  10. {everestapi-0.2.4 → everestapi-0.2.6}/setup.cfg +0 -0
  11. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/__main__.py +0 -0
  12. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/cli.py +0 -0
  13. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/__init__.py +0 -0
  14. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/__main__.py +0 -0
  15. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/mcp/server.py +0 -0
  16. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/plots.py +0 -0
  17. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi/types.py +0 -0
  18. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/SOURCES.txt +0 -0
  19. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/dependency_links.txt +0 -0
  20. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/entry_points.txt +0 -0
  21. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/requires.txt +0 -0
  22. {everestapi-0.2.4 → everestapi-0.2.6}/src/everestapi.egg-info/top_level.txt +0 -0
  23. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_cli.py +0 -0
  24. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_diagnostics.py +0 -0
  25. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_json_or_raise.py +0 -0
  26. {everestapi-0.2.4 → everestapi-0.2.6}/tests/test_prediction_range.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.2.4
3
+ Version: 0.2.6
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.2.4"
3
+ __version__ = "0.2.6"
4
4
 
5
5
  from everestapi.client import EverestAPI, EverestError
6
6
  from everestapi.types import (
@@ -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`` (apex
190
- domain), overridable via ``EVEREST_API_URL`` env var.
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 = os.getenv("EVEREST_API_URL", "https://everesteer.ai")
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}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: everestapi
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: Python SDK for the Everesteer prediction tournament platform
5
5
  Author-email: Everesteer <support@everesteer.ai>
6
6
  License-Expression: MIT
@@ -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/futures/train",
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="futures",
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/futures/train"
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": ["futures"]}]},
99
+ json={"versions": [{"version": "bregen", "universes": ["equities"]}]},
98
100
  )
99
101
  httpx_mock.add_response(
100
- url="http://test/api/v1/data/download/bregen/futures/live",
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="futures",
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