subspacecomputing 0.1.5__tar.gz → 0.1.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 (17) hide show
  1. {subspacecomputing-0.1.5/subspacecomputing.egg-info → subspacecomputing-0.1.6}/PKG-INFO +41 -4
  2. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/README.md +40 -3
  3. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/pyproject.toml +1 -1
  4. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/setup.py +1 -1
  5. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing/__init__.py +1 -1
  6. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing/client.py +91 -1
  7. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6/subspacecomputing.egg-info}/PKG-INFO +41 -4
  8. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/LICENSE +0 -0
  9. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/MANIFEST.in +0 -0
  10. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/setup.cfg +0 -0
  11. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing/errors.py +0 -0
  12. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing/utils/__init__.py +0 -0
  13. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing/utils/pandas_integration.py +0 -0
  14. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing.egg-info/SOURCES.txt +0 -0
  15. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing.egg-info/dependency_links.txt +0 -0
  16. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing.egg-info/requires.txt +0 -0
  17. {subspacecomputing-0.1.5 → subspacecomputing-0.1.6}/subspacecomputing.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: subspacecomputing
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Python SDK for ASTRIA — Subspace Computing Engine
5
5
  Home-page: https://www.subspacecomputing.com/developer
6
6
  Author: Subspace Computing Inc.
@@ -45,9 +45,6 @@ from subspacecomputing import ASTRIA
45
45
 
46
46
  # Initialize the client (defaults to production URL)
47
47
  client = ASTRIA(api_key='your-api-key-here')
48
-
49
- # For local testing or custom environments (optional)
50
- # client = ASTRIA(api_key='your-api-key-here', base_url='http://localhost:8000')
51
48
  ```
52
49
 
53
50
  ### Teams (`X-Team-Id`)
@@ -176,6 +173,46 @@ print(f"Total capital: {result['aggregations']['capital_total']}")
176
173
  print(f"Average: {result['aggregations']['moyenne_capital']}")
177
174
  ```
178
175
 
176
+ ### Run a stored model (`projection_id` + inputs)
177
+
178
+ Execute a projection already saved in Subspace. Load `run_inputs` once (cache in your app), build UI/batch from that descriptor, then call `run_model` many times with only the overrides. Astria runs the calculation; model structure (`formula`, dist type, `per`) is never overridable.
179
+
180
+ ```python
181
+ proj = client.load_model(projection_id) # get_projection + in-memory cache on this client
182
+ # Or: proj = client.get_projection(projection_id)
183
+
184
+ result = client.run_model(projection_id, inputs={"capital": 25000})
185
+ print(result["final_values"]["capital"])
186
+ ```
187
+
188
+ Input shapes by `run_inputs.kind`:
189
+
190
+ | `kind` | Example value in `inputs` |
191
+ |--------|---------------------------|
192
+ | `init` | `{"capital": 25000}` |
193
+ | `params` | `{"taux": {"min": 0.02, "max": 0.05}}` |
194
+ | `values` | `{"inflation": [0.01, 0.02, 0.03]}` |
195
+ | `table_ref` / `table` | `{"mortality": "<uuid>"}` or `{"grid": {...}}` |
196
+ | `run` | `{"scenarios": 100, "steps": 12}` |
197
+
198
+ Unknown input keys → API **400** `unknown_run_input`. `rerun_run(run_id)` re-executes a past **run snapshot**, not the live model.
199
+
200
+ Pattern for a dynamic form / batch loop:
201
+
202
+ ```python
203
+ proj = client.load_model(projection_id)
204
+ inputs = {}
205
+ for desc in proj.get("run_inputs") or []:
206
+ kind = desc.get("kind")
207
+ name = desc["name"]
208
+ if kind == "init":
209
+ inputs[name] = desc.get("default") # or from UI
210
+ elif kind == "params":
211
+ inputs[name] = dict(desc.get("default") or {})
212
+ # elif kind == "values": ...
213
+ result = client.run_model(projection_id, inputs=inputs)
214
+ ```
215
+
179
216
  ### Runs: read, artifact, rerun, replay
180
217
 
181
218
  Four distinct operations on persisted runs:
@@ -17,9 +17,6 @@ from subspacecomputing import ASTRIA
17
17
 
18
18
  # Initialize the client (defaults to production URL)
19
19
  client = ASTRIA(api_key='your-api-key-here')
20
-
21
- # For local testing or custom environments (optional)
22
- # client = ASTRIA(api_key='your-api-key-here', base_url='http://localhost:8000')
23
20
  ```
24
21
 
25
22
  ### Teams (`X-Team-Id`)
@@ -148,6 +145,46 @@ print(f"Total capital: {result['aggregations']['capital_total']}")
148
145
  print(f"Average: {result['aggregations']['moyenne_capital']}")
149
146
  ```
150
147
 
148
+ ### Run a stored model (`projection_id` + inputs)
149
+
150
+ Execute a projection already saved in Subspace. Load `run_inputs` once (cache in your app), build UI/batch from that descriptor, then call `run_model` many times with only the overrides. Astria runs the calculation; model structure (`formula`, dist type, `per`) is never overridable.
151
+
152
+ ```python
153
+ proj = client.load_model(projection_id) # get_projection + in-memory cache on this client
154
+ # Or: proj = client.get_projection(projection_id)
155
+
156
+ result = client.run_model(projection_id, inputs={"capital": 25000})
157
+ print(result["final_values"]["capital"])
158
+ ```
159
+
160
+ Input shapes by `run_inputs.kind`:
161
+
162
+ | `kind` | Example value in `inputs` |
163
+ |--------|---------------------------|
164
+ | `init` | `{"capital": 25000}` |
165
+ | `params` | `{"taux": {"min": 0.02, "max": 0.05}}` |
166
+ | `values` | `{"inflation": [0.01, 0.02, 0.03]}` |
167
+ | `table_ref` / `table` | `{"mortality": "<uuid>"}` or `{"grid": {...}}` |
168
+ | `run` | `{"scenarios": 100, "steps": 12}` |
169
+
170
+ Unknown input keys → API **400** `unknown_run_input`. `rerun_run(run_id)` re-executes a past **run snapshot**, not the live model.
171
+
172
+ Pattern for a dynamic form / batch loop:
173
+
174
+ ```python
175
+ proj = client.load_model(projection_id)
176
+ inputs = {}
177
+ for desc in proj.get("run_inputs") or []:
178
+ kind = desc.get("kind")
179
+ name = desc["name"]
180
+ if kind == "init":
181
+ inputs[name] = desc.get("default") # or from UI
182
+ elif kind == "params":
183
+ inputs[name] = dict(desc.get("default") or {})
184
+ # elif kind == "values": ...
185
+ result = client.run_model(projection_id, inputs=inputs)
186
+ ```
187
+
151
188
  ### Runs: read, artifact, rerun, replay
152
189
 
153
190
  Four distinct operations on persisted runs:
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "subspacecomputing"
9
- version = "0.1.5"
9
+ version = "0.1.6"
10
10
  description = "Python SDK for ASTRIA — Subspace Computing Engine"
11
11
  authors = [{name = "Subspace Computing Inc.", email = "contact@subspacecomputing.com"}]
12
12
  readme = "README.md"
@@ -13,7 +13,7 @@ except FileNotFoundError:
13
13
 
14
14
  setup(
15
15
  name="subspacecomputing",
16
- version="0.1.5",
16
+ version="0.1.6",
17
17
  description="Python SDK for ASTRIA — Subspace Computing Engine",
18
18
  long_description=long_description,
19
19
  long_description_content_type="text/markdown",
@@ -14,7 +14,7 @@ from .errors import (
14
14
  ValidationError,
15
15
  )
16
16
 
17
- __version__ = "0.1.5"
17
+ __version__ = "0.1.6"
18
18
  __all__ = [
19
19
  "ASTRIA",
20
20
  "SubspaceError",
@@ -61,6 +61,7 @@ class ASTRIA:
61
61
  headers["X-Team-Id"] = self._team_id
62
62
  self.session.headers.update(headers)
63
63
  self.last_response = None # Store last response for header access
64
+ self._model_cache: Dict[str, Dict[str, Any]] = {}
64
65
 
65
66
  def set_team_id(self, team_id: Optional[str] = None) -> None:
66
67
  """Set or clear ``X-Team-Id`` for subsequent requests."""
@@ -379,7 +380,7 @@ class ASTRIA:
379
380
  projection_id: Projection ID
380
381
 
381
382
  Returns:
382
- Response with projection details
383
+ Response with projection details (includes ``run_inputs`` when present)
383
384
  """
384
385
  response = self.session.get(
385
386
  f"{self.base_url}/projections/{projection_id}",
@@ -389,6 +390,60 @@ class ASTRIA:
389
390
  self._handle_response(response)
390
391
  return response.json()
391
392
 
393
+ def load_model(self, projection_id: str) -> Dict[str, Any]:
394
+ """
395
+ Load a projection (schema + ``run_inputs``) with a simple in-memory cache
396
+ on this client instance. Prefer this once per session; still avoid calling
397
+ on every ``run_model``.
398
+ """
399
+ if not projection_id or not str(projection_id).strip():
400
+ raise ValueError("projection_id is required.")
401
+ pid = str(projection_id).strip()
402
+ cached = self._model_cache.get(pid)
403
+ if cached is not None:
404
+ return cached
405
+ proj = self.get_projection(pid)
406
+ self._model_cache[pid] = proj
407
+ return proj
408
+
409
+ def clear_model_cache(self, projection_id: Optional[str] = None) -> None:
410
+ """Clear in-memory model cache from :meth:`load_model`."""
411
+ if projection_id and str(projection_id).strip():
412
+ self._model_cache.pop(str(projection_id).strip(), None)
413
+ else:
414
+ self._model_cache.clear()
415
+
416
+ def run_model(
417
+ self,
418
+ projection_id: str,
419
+ inputs: Optional[Dict[str, Any]] = None,
420
+ ) -> Dict[str, Any]:
421
+ """
422
+ Run a stored projection by ID with optional runtime input overrides.
423
+
424
+ Applies ``run_inputs`` by kind (init, params, values, tables, run knobs).
425
+ Does not change formula / dist type / per. Distinct from :meth:`rerun_run`.
426
+
427
+ Args:
428
+ projection_id: Stored projection UUID
429
+ inputs: Optional overrides keyed by run_input name
430
+ (e.g. ``{"capital": 25000}``, ``{"taux": {"min": 0.02, "max": 0.05}}``)
431
+
432
+ Returns:
433
+ Same shape as ``project()`` or ``simulate()`` depending on scenarios
434
+ """
435
+ if not projection_id or not str(projection_id).strip():
436
+ raise ValueError("projection_id is required.")
437
+ safe_id = quote(str(projection_id).strip(), safe="")
438
+ response = self.session.post(
439
+ f"{self.base_url}/projections/{safe_id}/run",
440
+ json={"inputs": inputs or {}},
441
+ timeout=self.timeout,
442
+ )
443
+ self.last_response = response
444
+ self._handle_response(response)
445
+ return response.json()
446
+
392
447
  def delete_projection(self, projection_id: str) -> None:
393
448
  """
394
449
  Delete a projection.
@@ -465,6 +520,41 @@ class ASTRIA:
465
520
  self._handle_response(response)
466
521
  return response.json()
467
522
 
523
+ def get_run_proof(self, run_id: str, *, include_spec: bool = False) -> Dict[str, Any]:
524
+ """
525
+ Fetch Astria run proof pack for a projection run.
526
+
527
+ Args:
528
+ run_id: Projection run ID
529
+ include_spec: If True, include resolved SP snapshot in the proof
530
+
531
+ Returns:
532
+ Proof pack (schema_version, hashes, seed, …)
533
+ """
534
+ params = {"include_spec": "true"} if include_spec else None
535
+ response = self.session.get(
536
+ f"{self.base_url}/projection-runs/{run_id}/proof",
537
+ params=params,
538
+ timeout=self.timeout,
539
+ )
540
+ self.last_response = response
541
+ self._handle_response(response)
542
+ return response.json()
543
+
544
+ def get_execution_proof(self, execution_id: str) -> Dict[str, Any]:
545
+ """
546
+ Fetch Astria run proof pack from a Registry execution (lineage.astria).
547
+
548
+ Requires Console JWT auth on the session (same as other /executions routes).
549
+ """
550
+ response = self.session.get(
551
+ f"{self.base_url}/executions/{execution_id}/proof",
552
+ timeout=self.timeout,
553
+ )
554
+ self.last_response = response
555
+ self._handle_response(response)
556
+ return response.json()
557
+
468
558
  def replay_run(self, run_id: str, scenario_id: int) -> Dict[str, Any]:
469
559
  """
470
560
  Replay a scenario from a stored run (requires persisted seeds).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: subspacecomputing
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: Python SDK for ASTRIA — Subspace Computing Engine
5
5
  Home-page: https://www.subspacecomputing.com/developer
6
6
  Author: Subspace Computing Inc.
@@ -45,9 +45,6 @@ from subspacecomputing import ASTRIA
45
45
 
46
46
  # Initialize the client (defaults to production URL)
47
47
  client = ASTRIA(api_key='your-api-key-here')
48
-
49
- # For local testing or custom environments (optional)
50
- # client = ASTRIA(api_key='your-api-key-here', base_url='http://localhost:8000')
51
48
  ```
52
49
 
53
50
  ### Teams (`X-Team-Id`)
@@ -176,6 +173,46 @@ print(f"Total capital: {result['aggregations']['capital_total']}")
176
173
  print(f"Average: {result['aggregations']['moyenne_capital']}")
177
174
  ```
178
175
 
176
+ ### Run a stored model (`projection_id` + inputs)
177
+
178
+ Execute a projection already saved in Subspace. Load `run_inputs` once (cache in your app), build UI/batch from that descriptor, then call `run_model` many times with only the overrides. Astria runs the calculation; model structure (`formula`, dist type, `per`) is never overridable.
179
+
180
+ ```python
181
+ proj = client.load_model(projection_id) # get_projection + in-memory cache on this client
182
+ # Or: proj = client.get_projection(projection_id)
183
+
184
+ result = client.run_model(projection_id, inputs={"capital": 25000})
185
+ print(result["final_values"]["capital"])
186
+ ```
187
+
188
+ Input shapes by `run_inputs.kind`:
189
+
190
+ | `kind` | Example value in `inputs` |
191
+ |--------|---------------------------|
192
+ | `init` | `{"capital": 25000}` |
193
+ | `params` | `{"taux": {"min": 0.02, "max": 0.05}}` |
194
+ | `values` | `{"inflation": [0.01, 0.02, 0.03]}` |
195
+ | `table_ref` / `table` | `{"mortality": "<uuid>"}` or `{"grid": {...}}` |
196
+ | `run` | `{"scenarios": 100, "steps": 12}` |
197
+
198
+ Unknown input keys → API **400** `unknown_run_input`. `rerun_run(run_id)` re-executes a past **run snapshot**, not the live model.
199
+
200
+ Pattern for a dynamic form / batch loop:
201
+
202
+ ```python
203
+ proj = client.load_model(projection_id)
204
+ inputs = {}
205
+ for desc in proj.get("run_inputs") or []:
206
+ kind = desc.get("kind")
207
+ name = desc["name"]
208
+ if kind == "init":
209
+ inputs[name] = desc.get("default") # or from UI
210
+ elif kind == "params":
211
+ inputs[name] = dict(desc.get("default") or {})
212
+ # elif kind == "values": ...
213
+ result = client.run_model(projection_id, inputs=inputs)
214
+ ```
215
+
179
216
  ### Runs: read, artifact, rerun, replay
180
217
 
181
218
  Four distinct operations on persisted runs: