mainsequence 4.1.3__tar.gz → 4.1.5__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 (125) hide show
  1. {mainsequence-4.1.3 → mainsequence-4.1.5}/PKG-INFO +1 -1
  2. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +7 -26
  3. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/ms-markets/SKILL.md +2 -2
  4. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/cli.py +1 -1
  5. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/base.py +10 -32
  6. {mainsequence-4.1.3/mainsequence → mainsequence-4.1.5/mainsequence/client}/compute_validation.py +6 -2
  7. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/duckdb.py +4 -86
  8. mainsequence-4.1.5/mainsequence/client/data_sources_interfaces/local_paths.py +14 -0
  9. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/sqlite.py +3 -73
  10. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/models_foundry.py +5 -4
  11. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/models_helpers.py +2 -2
  12. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/models_metatables.py +8 -279
  13. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/utils.py +0 -3
  14. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/logconf.py +22 -36
  15. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/__init__.py +0 -11
  16. mainsequence-4.1.5/mainsequence/meta_tables/compiled_sql/__init__.py +5 -0
  17. mainsequence-4.1.3/mainsequence/meta_tables/compiled_sql.py → mainsequence-4.1.5/mainsequence/meta_tables/compiled_sql/v1.py +3 -3
  18. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/__init__.py +0 -2
  19. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/data_nodes.py +8 -29
  20. mainsequence-4.1.5/mainsequence/meta_tables/data_nodes/models.py +33 -0
  21. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/namespacing.py +2 -2
  22. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/sqlalchemy_contracts.py +3 -3
  23. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/runtime_flags.py +2 -2
  24. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/PKG-INFO +1 -1
  25. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/SOURCES.txt +4 -5
  26. {mainsequence-4.1.3 → mainsequence-4.1.5}/pyproject.toml +1 -1
  27. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_auth_precedence.py +5 -9
  28. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_build_operations_hashing.py +13 -14
  29. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_cli.py +1 -1
  30. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_data_access_mixin_dimension_audit.py +0 -55
  31. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_data_node_storage_dimension_queries.py +2 -60
  32. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_data_node_update_flow.py +13 -42
  33. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_duckdb_interface_dimensions.py +0 -18
  34. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_filter_normalization.py +8 -24
  35. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_logconf.py +26 -11
  36. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_meta_tables_client_models.py +4 -7
  37. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_meta_tables_sqlalchemy_contracts.py +1 -2
  38. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_run_configuration.py +19 -13
  39. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_source_table_configuration.py +1 -71
  40. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_sqlite_interface_dimensions.py +0 -18
  41. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_update_statistics.py +1 -4
  42. mainsequence-4.1.3/mainsequence/meta_tables/config.py +0 -128
  43. mainsequence-4.1.3/mainsequence/meta_tables/configuration_models.py +0 -5
  44. mainsequence-4.1.3/mainsequence/meta_tables/data_nodes/models.py +0 -106
  45. mainsequence-4.1.3/mainsequence/meta_tables/utils.py +0 -45
  46. {mainsequence-4.1.3 → mainsequence-4.1.5}/LICENSE +0 -0
  47. {mainsequence-4.1.3 → mainsequence-4.1.5}/README.md +0 -0
  48. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/AGENTS.md +0 -0
  49. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  50. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  51. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  52. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  53. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  54. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  55. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  56. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  57. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  58. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  59. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  60. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  61. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  62. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  63. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  64. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  65. {mainsequence-4.1.3 → mainsequence-4.1.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  66. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/__init__.py +0 -0
  67. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/__main__.py +0 -0
  68. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/bootstrap.py +0 -0
  69. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/__init__.py +0 -0
  70. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/api.py +0 -0
  71. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/browser_auth.py +0 -0
  72. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/config.py +0 -0
  73. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/docker_utils.py +0 -0
  74. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/doctor.py +0 -0
  75. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/local_ops.py +0 -0
  76. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/model_filters.py +0 -0
  77. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/project_status.py +0 -0
  78. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/pydantic_cli.py +0 -0
  79. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/sdk_utils.py +0 -0
  80. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/ssh_utils.py +0 -0
  81. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/cli/ui.py +0 -0
  82. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/__init__.py +0 -0
  83. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/agent_runtime_models.py +0 -0
  84. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/client.py +0 -0
  85. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/__init__.py +0 -0
  86. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/app_component.py +0 -0
  87. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/connections.py +0 -0
  88. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/data_models.py +0 -0
  89. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/workspace.py +0 -0
  90. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  91. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  92. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/dtype_codec.py +0 -0
  93. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/exceptions.py +0 -0
  94. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/fastapi/__init__.py +0 -0
  95. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/fastapi/auth.py +0 -0
  96. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/client/models_user.py +0 -0
  97. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/defaults.py +0 -0
  98. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/instrumentation/__init__.py +0 -0
  99. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/instrumentation/utils.py +0 -0
  100. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/__main__.py +0 -0
  101. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  102. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  103. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  104. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  105. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/future_registry.py +0 -0
  106. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/hashing.py +0 -0
  107. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  108. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/dependency_links.txt +0 -0
  109. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/entry_points.txt +0 -0
  110. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/requires.txt +0 -0
  111. {mainsequence-4.1.3 → mainsequence-4.1.5}/mainsequence.egg-info/top_level.txt +0 -0
  112. {mainsequence-4.1.3 → mainsequence-4.1.5}/setup.cfg +0 -0
  113. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_cli_browser_auth.py +0 -0
  114. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_client.py +0 -0
  115. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_command_center_app_component_models.py +0 -0
  116. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_command_center_data_models.py +0 -0
  117. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_command_center_models.py +0 -0
  118. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_dependency_extras.py +0 -0
  119. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_models_user_request_bound_auth.py +0 -0
  120. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_pod_project_resolution.py +0 -0
  121. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_project_batch_jobs_from_file.py +0 -0
  122. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_secret_client_model.py +0 -0
  123. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_update_runner_uid_runtime.py +0 -0
  124. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_update_uid_guards.py +0 -0
  125. {mainsequence-4.1.3 → mainsequence-4.1.5}/tests/test_workspace_snapshot.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.1.3
3
+ Version: 4.1.5
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -30,8 +30,7 @@ Canonical workflow:
30
30
  - `dependencies()`
31
31
  - `update()`
32
32
  - `prepare_update_statistics()`
33
- - `get_asset_list()` when the update is asset scoped
34
- - design single-index or `(time_index, unique_identifier)` DataFrame outputs
33
+ - design single-index or multidimensional time-first DataFrame outputs
35
34
  - validate output shape against a `PlatformTimeIndexMetaData` storage contract
36
35
  - define explicit `hash_namespace(...)` validation strategy
37
36
  - write or review DataNode smoke tests
@@ -80,7 +79,6 @@ Before changing code, collect or infer:
80
79
  - expected time index and identity index shape
81
80
  - expected columns and dtypes from the storage class
82
81
  - upstream dependencies
83
- - whether the update is asset scoped
84
82
  - first-run or backfill bounds
85
83
  - whether the change must preserve the existing table contract
86
84
 
@@ -302,30 +300,19 @@ changing it changes the dependency graph and update identity.
302
300
 
303
301
  Do not construct dependency graphs dynamically inside `update()`.
304
302
 
305
- ### 8. Asset-Scoped Updates Must Be Explicit
306
-
307
- If the node emits `(time_index, unique_identifier)`:
308
-
309
- - `unique_identifier` should represent an Asset identity
310
- - asset scope should live in update configuration when it affects the updater
311
- - `get_asset_list()` must reflect the effective updater asset scope when the
312
- workflow uses that hook
313
- - missing assets should be resolved or registered by the relevant workflow
314
-
315
- ### 9. Foreign Keys Belong To The Storage Contract
303
+ ### 8. Foreign Keys Belong To The Storage Contract
316
304
 
317
305
  For new code, model foreign keys on the `PlatformTimeIndexMetaData` storage
318
306
  class or route the storage-contract work to the MetaTable skill.
319
307
 
320
308
  Do not add DataNode configuration fields just to mutate storage metadata.
321
309
 
322
- ### 10. Metadata Belongs To Storage
310
+ ### 9. Metadata Belongs To Storage
323
311
 
324
312
  Production-quality table identifiers, descriptions, labels, column docs, and
325
313
  foreign-key metadata belong to the storage class/MetaTable registration path.
326
314
 
327
- Do not use `DataNodeMetaData` or `RecordDefinition` as the canonical schema
328
- surface for new DataNode work.
315
+ Do not put schema or published table metadata on the DataNode configuration.
329
316
 
330
317
  ## Review Rules
331
318
 
@@ -333,7 +320,7 @@ When reviewing an existing DataNode, look for:
333
320
 
334
321
  - output storage contract hidden in `DataNodeConfiguration`
335
322
  - dependency storage table passed as an ad hoc constructor argument
336
- - old `RecordDefinition` or `DataNodeMetaData` schema patterns
323
+ - schema or published table metadata hidden in DataNode configuration
337
324
  - `update_only`, `runtime_only`, or `ignore_from_storage_hash`
338
325
  - `test_node=True`
339
326
  - missing explicit `storage_table`
@@ -342,7 +329,7 @@ When reviewing an existing DataNode, look for:
342
329
  - misuse of `hash_namespace`
343
330
  - non-incremental `update()` behavior
344
331
  - hidden dependency creation inside `update()`
345
- - invalid asset-indexed output shape
332
+ - invalid identity-indexed output shape
346
333
  - `time_index` dtype that is not exactly `datetime64[ns, UTC]`
347
334
  - DataFrame columns that do not match the `PlatformTimeIndexMetaData` class
348
335
 
@@ -363,17 +350,11 @@ Do not claim success until you have checked:
363
350
  - non-empty outputs have first index level `time_index` with dtype `datetime64[ns, UTC]`
364
351
  - the first validation run uses explicit `hash_namespace(...)` when it touches a shared backend
365
352
 
366
- For asset-scoped updates, also check:
367
-
368
- - `get_asset_list()` is correct when used
369
- - no duplicate `(time_index, unique_identifier)` rows are emitted
370
- - assets exist or are registered idempotently when needed
371
-
372
353
  ## This Skill Must Stop And Escalate When
373
354
 
374
355
  - the change may break an existing published table contract and the versioning decision is unclear
375
356
  - the intended storage class or MetaTable registration path is unclear
376
- - the node needs asset identities but the asset-resolution strategy is unclear
357
+ - the node needs identity dimensions but the coordinate strategy is unclear
377
358
  - the task is actually an API, MetaTable, orchestration, or sharing problem
378
359
  - docs, skill instructions, and code disagree on hashing or runtime behavior
379
360
 
@@ -6,8 +6,8 @@ description: Use this skill when a Main Sequence project needs financial markets
6
6
  # Main Sequence ms-markets
7
7
 
8
8
  `ms-markets` is the Main Sequence supported library for financial markets workflows.
9
- Use it for market assets, market MetaTables, asset-indexed DataNodes, portfolios,
10
- orders, trades, and related market-domain APIs.
9
+ Use it for market assets, market MetaTables, portfolios, orders, trades, and
10
+ related market-domain APIs.
11
11
 
12
12
  This SDK scaffold skill is intentionally tiny. It does not own market-domain
13
13
  implementation rules. Install `ms-markets` and copy its packaged skills into the
@@ -45,7 +45,7 @@ import click
45
45
  import typer
46
46
  import yaml
47
47
 
48
- from ..compute_validation import decimal_to_storage, parse_cpu_request, parse_memory_request
48
+ from ..client.compute_validation import decimal_to_storage, parse_cpu_request, parse_memory_request
49
49
  from . import config as cfg
50
50
  from .api import (
51
51
  ApiError,
@@ -1,6 +1,5 @@
1
1
  import inspect
2
2
  import os
3
- import warnings
4
3
  from collections.abc import Callable, Iterable
5
4
  from datetime import datetime
6
5
  from typing import Any, ClassVar
@@ -85,7 +84,6 @@ class BaseObjectOrm:
85
84
  "MultiIndexMetadata": "orm/multi_index_metadata",
86
85
  "ContinuousAggMultiIndex": "ts_manager/cont_agg_multi_ind",
87
86
  "TimeIndexMetaData": "ts_manager/dynamic_table",
88
- # "LocalTimeSerieNodesMethods": "ogm/local_time_serie",
89
87
  "LocalTimeSerieNodesMethods": "ts_manager/local_time_serie",
90
88
  "DataNodeUpdate": "ts_manager/local_time_serie",
91
89
  "DataNodeUpdateDetails": "ts_manager/local_time_serie_update_details",
@@ -98,13 +96,12 @@ class BaseObjectOrm:
98
96
  "JobRun": "pods/job-run",
99
97
  "Constant": "pods/constant",
100
98
  "Secret": "pods/secret",
101
- "ProjectBaseImage":"pods/project-base-image",
102
- "ProjectImage":"pods/project-image",
99
+ "ProjectBaseImage": "pods/project-base-image",
100
+ "ProjectImage": "pods/project-image",
103
101
  "GithubOrganization": "pods/github-organization",
104
- "ProjectResource":"pods/project-resource",
105
- "ResourceRelease":"pods/resource-release",
106
- "Bucket":"pods/bucket",
107
-
102
+ "ProjectResource": "pods/project-resource",
103
+ "ResourceRelease": "pods/resource-release",
104
+ "Bucket": "pods/bucket",
108
105
  }
109
106
  ROOT_URL = API_ENDPOINT
110
107
  LOADERS = loaders
@@ -527,7 +524,6 @@ class BaseObjectOrm:
527
524
  )
528
525
  raise_for_response(r)
529
526
 
530
-
531
527
  data = r.json()
532
528
  data["orm_class"] = cls.__name__
533
529
  return cls(**data)
@@ -540,7 +536,6 @@ class BaseObjectOrm:
540
536
  if len(candidates) > 1:
541
537
  raise ApiError(f"Multiple objects returned for {cls.__name__} with filters={filters}")
542
538
 
543
-
544
539
  return candidates[0]
545
540
 
546
541
  @classmethod
@@ -556,8 +551,6 @@ class BaseObjectOrm:
556
551
 
557
552
  @staticmethod
558
553
  def serialize_for_json(kwargs):
559
-
560
-
561
554
  return serialize_to_json(kwargs)
562
555
 
563
556
  @classmethod
@@ -598,15 +591,6 @@ class BaseObjectOrm:
598
591
 
599
592
  return cls(**r.json())
600
593
 
601
- @classmethod
602
- def _warn_internal_id_compatibility(cls, method_name: str, replacement_name: str) -> None:
603
- warnings.warn(
604
- f"{cls.__name__}.{method_name}() is an internal-only compatibility shim; "
605
- f"use {cls.__name__}.{replacement_name}() with uid instead.",
606
- DeprecationWarning,
607
- stacklevel=3,
608
- )
609
-
610
594
  @classmethod
611
595
  def _destroy_by_reference(cls, public_reference, *args, timeout=None, **kwargs):
612
596
  base_url = cls.get_object_url()
@@ -632,11 +616,6 @@ class BaseObjectOrm:
632
616
  if r.status_code != 204:
633
617
  raise_for_response(r)
634
618
 
635
- @classmethod
636
- def _destroy_by_id_compat(cls, instance_id, *args, timeout=None, **kwargs):
637
- cls._warn_internal_id_compatibility("_destroy_by_id_compat", "destroy_by_uid")
638
- return cls._destroy_by_reference(instance_id, *args, timeout=timeout, **kwargs)
639
-
640
619
  @classmethod
641
620
  def destroy_by_uid(cls, uid: str, *args, timeout=None, **kwargs):
642
621
  return cls._destroy_by_reference(uid, *args, timeout=timeout, **kwargs)
@@ -679,7 +658,11 @@ class BaseObjectOrm:
679
658
  aliases.add(choice)
680
659
  else:
681
660
  path = getattr(validation_alias, "path", None)
682
- if isinstance(path, (list, tuple)) and len(path) == 1 and isinstance(path[0], str):
661
+ if (
662
+ isinstance(path, (list, tuple))
663
+ and len(path) == 1
664
+ and isinstance(path[0], str)
665
+ ):
683
666
  aliases.add(path[0])
684
667
 
685
668
  return aliases
@@ -734,11 +717,6 @@ class BaseObjectOrm:
734
717
  # Otherwise return a new instance
735
718
  return cls(**body)
736
719
 
737
- @classmethod
738
- def _patch_by_id_compat(cls, instance_id, *args, _into=None, **kwargs):
739
- cls._warn_internal_id_compatibility("_patch_by_id_compat", "patch_by_uid")
740
- return cls._patch_by_reference(instance_id, *args, _into=_into, **kwargs)
741
-
742
720
  @classmethod
743
721
  def patch_by_uid(cls, uid: str, *args, _into=None, **kwargs):
744
722
  return cls._patch_by_reference(uid, *args, _into=_into, **kwargs)
@@ -114,7 +114,9 @@ def parse_gpu_request(value: Any, *, field_name: str = "gpu_request") -> int | N
114
114
  raise ValueError(f"{field_name} must be a valid integer.") from exc
115
115
 
116
116
 
117
- def format_cpu_request(value: Decimal | None, *, output_format: Literal["decimal", "k8s"] = "decimal") -> str | None:
117
+ def format_cpu_request(
118
+ value: Decimal | None, *, output_format: Literal["decimal", "k8s"] = "decimal"
119
+ ) -> str | None:
118
120
  if value is None:
119
121
  return None
120
122
  if output_format == "decimal":
@@ -129,7 +131,9 @@ def format_cpu_request(value: Decimal | None, *, output_format: Literal["decimal
129
131
  return decimal_to_storage(value)
130
132
 
131
133
 
132
- def format_memory_request(value: Decimal | None, *, output_format: Literal["decimal", "k8s"] = "decimal") -> str | None:
134
+ def format_memory_request(
135
+ value: Decimal | None, *, output_format: Literal["decimal", "k8s"] = "decimal"
136
+ ) -> str | None:
133
137
  if value is None:
134
138
  return None
135
139
  if output_format == "decimal":
@@ -22,7 +22,8 @@ from ..dtype_codec import (
22
22
  token_to_pandas_dtype,
23
23
  token_to_pandas_series,
24
24
  )
25
- from ..utils import DataFrequency, UniqueIdentifierRangeMap
25
+ from ..utils import DataFrequency
26
+ from .local_paths import local_data_path
26
27
 
27
28
 
28
29
  def get_logger():
@@ -53,12 +54,10 @@ class DuckDBInterface:
53
54
  environment variable or 'analytics.duckdb'
54
55
  in the current directory if the variable is not set.
55
56
  """
56
- from mainsequence.meta_tables.config import META_TABLES_DATA_PATH
57
-
58
57
  # ── choose default & normalise to string ───────────────────────────
59
58
  default_path = os.getenv(
60
59
  "DUCKDB_PATH",
61
- os.path.join(f"{META_TABLES_DATA_PATH}", "duck_db"),
60
+ os.fspath(local_data_path() / "duck_db"),
62
61
  )
63
62
  db_uri = str(db_path or default_path).rstrip("/")
64
63
 
@@ -711,8 +710,6 @@ class DuckDBInterface:
711
710
  dimension_filters: dict[str, list[Any]] | None = None,
712
711
  index_coordinates: list[dict[str, Any]] | None = None,
713
712
  dimension_range_map: list[dict[str, Any]] | None = None,
714
- ids: list[str] | None = None,
715
- unique_identifier_range_map: dict[str, dict[str, Any]] | None = None,
716
713
  max_rows: int | None = None,
717
714
  now: datetime.datetime | None = None,
718
715
  ) -> tuple[
@@ -971,45 +968,6 @@ class DuckDBInterface:
971
968
  f"time_index_name {time_index_name!r} must be present in index_names {index_names!r}"
972
969
  )
973
970
 
974
- identity_dimensions = [name for name in index_names if name != time_index_name]
975
- uses_legacy_unique_identifier = identity_dimensions == ["unique_identifier"]
976
-
977
- if ids is not None and unique_identifier_range_map is not None:
978
- raise ValueError("Cannot provide both 'ids' and 'unique_identifier_range_map'.")
979
- if ids is not None and not uses_legacy_unique_identifier:
980
- raise ValueError("Legacy 'ids' reads are valid only for unique_identifier tables.")
981
- if unique_identifier_range_map is not None and not uses_legacy_unique_identifier:
982
- raise ValueError(
983
- "Legacy 'unique_identifier_range_map' reads are valid only for "
984
- "unique_identifier tables."
985
- )
986
- if unique_identifier_range_map is not None and normalized_dimension_range_map is not None:
987
- raise ValueError(
988
- "Cannot provide both 'unique_identifier_range_map' and 'dimension_range_map'."
989
- )
990
-
991
- # If ids are given without a range map, create a simple one from start/end
992
- if ids and (normalized_dimension_range_map is None):
993
- normalized_dimension_range_map = [
994
- {
995
- "coordinate": {"unique_identifier": uid},
996
- "start_date": start_ts.to_pydatetime() if start_ts is not None else None,
997
- "start_date_operand": ">=",
998
- "end_date": (
999
- (end_ts or _to_utc_ts(now)).to_pydatetime()
1000
- if (end_ts or now) is not None
1001
- else None
1002
- ),
1003
- "end_date_operand": "<=",
1004
- }
1005
- for uid in ids
1006
- ]
1007
- if unique_identifier_range_map is not None:
1008
- normalized_dimension_range_map = [
1009
- {"coordinate": {"unique_identifier": uid}, **dict(info)}
1010
- for uid, info in unique_identifier_range_map.items()
1011
- ]
1012
-
1013
971
  # Compute global window from inputs
1014
972
  if normalized_dimension_range_map:
1015
973
  eff_start = _effective_start_from_range_map(normalized_dimension_range_map)
@@ -1192,10 +1150,7 @@ class DuckDBInterface:
1192
1150
  dimension_filters: dict[str, list[Any]] | None = None,
1193
1151
  index_coordinates: list[dict[str, Any]] | None = None,
1194
1152
  dimension_range_map: list[dict[str, Any]] | None = None,
1195
- ids: list[str] | None = None,
1196
1153
  columns: list[str] | None = None,
1197
- unique_identifier_range_map: UniqueIdentifierRangeMap | None = None,
1198
- column_range_descriptor: dict[str, UniqueIdentifierRangeMap] | None = None,
1199
1154
  ) -> pd.DataFrame:
1200
1155
  """
1201
1156
  Reads data from the specified table, with optional filtering.
@@ -1207,18 +1162,10 @@ class DuckDBInterface:
1207
1162
  end (Optional[datetime.datetime]): Maximum temporal-column filter.
1208
1163
  great_or_equal (bool): If True, use >= for start date comparison. Defaults to True.
1209
1164
  less_or_equal (bool): If True, use <= for end date comparison. Defaults to True.
1210
- ids (Optional[List[str]]): List of specific unique_identifiers to include.
1211
1165
  columns (Optional[List[str]]): Specific columns to select. Reads all if None.
1212
- unique_identifier_range_map (Optional[UniqueIdentifierRangeMap]):
1213
- A map where keys are unique_identifiers and values are dicts specifying
1214
- date ranges (start_date, end_date, start_date_operand, end_date_operand)
1215
- for that identifier. Mutually exclusive with 'ids'.
1216
1166
 
1217
1167
  Returns:
1218
1168
  pd.DataFrame: The queried data, or an empty DataFrame if the table doesn't exist.
1219
-
1220
- Raises:
1221
- ValueError: If both `ids` and `unique_identifier_range_map` are provided.
1222
1169
  """
1223
1170
 
1224
1171
  def qident(name: str) -> str:
@@ -1248,39 +1195,10 @@ class DuckDBInterface:
1248
1195
  f"time_index_name {time_index_name!r} must be present in index_names {index_names!r}"
1249
1196
  )
1250
1197
  identity_dimensions = [name for name in index_names if name != time_index_name]
1251
- uses_legacy_unique_identifier = identity_dimensions == ["unique_identifier"]
1252
-
1253
- if ids is not None and unique_identifier_range_map is not None:
1254
- raise ValueError("Cannot provide both 'ids' and 'unique_identifier_range_map'.")
1255
- if ids is not None and not uses_legacy_unique_identifier:
1256
- raise ValueError("Legacy 'ids' reads are valid only for unique_identifier tables.")
1257
- if unique_identifier_range_map is not None and not uses_legacy_unique_identifier:
1258
- raise ValueError(
1259
- "Legacy 'unique_identifier_range_map' reads are valid only for "
1260
- "unique_identifier tables."
1261
- )
1262
- if unique_identifier_range_map is not None and dimension_range_map is not None:
1263
- raise ValueError(
1264
- "Cannot provide both 'unique_identifier_range_map' and 'dimension_range_map'."
1265
- )
1266
- if column_range_descriptor is not None:
1267
- raise NotImplementedError("DuckDB column_range_descriptor reads are not supported.")
1268
-
1269
- if ids:
1270
- dimension_filters = dict(dimension_filters or {})
1271
- if "unique_identifier" in dimension_filters:
1272
- raise ValueError("Cannot provide both 'ids' and a unique_identifier filter.")
1273
- dimension_filters["unique_identifier"] = list(ids)
1274
-
1275
- if unique_identifier_range_map is not None:
1276
- dimension_range_map = [
1277
- {"coordinate": {"unique_identifier": uid}, **dict(info)}
1278
- for uid, info in unique_identifier_range_map.items()
1279
- ]
1280
1198
 
1281
1199
  logger.debug(
1282
1200
  f"Duck DB: Reading from table '{table}' with filters: start={start}, end={end}, "
1283
- f"ids={ids is not None}, columns={columns}, range_map={dimension_range_map is not None}"
1201
+ f"columns={columns}, range_map={dimension_range_map is not None}"
1284
1202
  )
1285
1203
 
1286
1204
  if not self.table_exists(table):
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+
7
+ def local_data_path() -> Path:
8
+ configured = (os.getenv("MAINSEQUENCE_LOCAL_DATA_PATH") or "").strip()
9
+ if configured:
10
+ return Path(configured).expanduser()
11
+ return Path.home() / "meta_tables" / "data"
12
+
13
+
14
+ __all__ = ["local_data_path"]
@@ -16,7 +16,7 @@ from ..dtype_codec import (
16
16
  token_to_backend_type,
17
17
  token_to_pandas_series,
18
18
  )
19
- from ..utils import UniqueIdentifierRangeMap
19
+ from .local_paths import local_data_path
20
20
 
21
21
 
22
22
  def get_logger():
@@ -33,11 +33,9 @@ class SQLiteInterface:
33
33
  """
34
34
 
35
35
  def __init__(self, db_path: str | Path | None = None):
36
- from mainsequence.meta_tables.config import META_TABLES_DATA_PATH
37
-
38
36
  default_path = os.getenv(
39
37
  "SQLITE_PATH",
40
- os.path.join(f"{META_TABLES_DATA_PATH}", "sqlite"),
38
+ os.fspath(local_data_path() / "sqlite"),
41
39
  )
42
40
  raw_path = Path(str(db_path or default_path)).expanduser()
43
41
  if raw_path.suffix in {".db", ".sqlite", ".sqlite3"}:
@@ -192,7 +190,6 @@ class SQLiteInterface:
192
190
  *,
193
191
  index_names: list[str],
194
192
  time_index_name: str,
195
- **_: Any,
196
193
  ) -> None:
197
194
  self._validate_index(index_names, time_index_name)
198
195
  if df.empty:
@@ -254,8 +251,6 @@ class SQLiteInterface:
254
251
  dimension_filters: dict[str, list[Any]] | None = None,
255
252
  index_coordinates: list[dict[str, Any]] | None = None,
256
253
  dimension_range_map: list[dict[str, Any]] | None = None,
257
- ids: list[str] | None = None,
258
- unique_identifier_range_map: dict[str, dict[str, Any]] | None = None,
259
254
  max_rows: int | None = None,
260
255
  now: datetime.datetime | None = None,
261
256
  ) -> tuple[
@@ -265,42 +260,9 @@ class SQLiteInterface:
265
260
  dict[str, Any],
266
261
  ]:
267
262
  self._validate_index(index_names, time_index_name)
268
- identity_dimensions = [name for name in index_names if name != time_index_name]
269
- uses_legacy_unique_identifier = identity_dimensions == ["unique_identifier"]
270
-
271
- if ids is not None and unique_identifier_range_map is not None:
272
- raise ValueError("Cannot provide both 'ids' and 'unique_identifier_range_map'.")
273
- if ids is not None and not uses_legacy_unique_identifier:
274
- raise ValueError("Legacy 'ids' reads are valid only for unique_identifier tables.")
275
- if unique_identifier_range_map is not None and not uses_legacy_unique_identifier:
276
- raise ValueError(
277
- "Legacy 'unique_identifier_range_map' reads are valid only for "
278
- "unique_identifier tables."
279
- )
280
- if unique_identifier_range_map is not None and dimension_range_map is not None:
281
- raise ValueError(
282
- "Cannot provide both 'unique_identifier_range_map' and 'dimension_range_map'."
283
- )
284
-
285
263
  normalized_dimension_range_map = (
286
264
  None if dimension_range_map is None else [dict(item) for item in dimension_range_map]
287
265
  )
288
- if ids and normalized_dimension_range_map is None:
289
- normalized_dimension_range_map = [
290
- {
291
- "coordinate": {"unique_identifier": uid},
292
- "start_date": start,
293
- "start_date_operand": ">=",
294
- "end_date": end or now,
295
- "end_date_operand": "<=",
296
- }
297
- for uid in ids
298
- ]
299
- if unique_identifier_range_map is not None:
300
- normalized_dimension_range_map = [
301
- {"coordinate": {"unique_identifier": uid}, **dict(info)}
302
- for uid, info in unique_identifier_range_map.items()
303
- ]
304
266
 
305
267
  diagnostics = {
306
268
  "limited": False,
@@ -324,42 +286,9 @@ class SQLiteInterface:
324
286
  dimension_filters: dict[str, list[Any]] | None = None,
325
287
  index_coordinates: list[dict[str, Any]] | None = None,
326
288
  dimension_range_map: list[dict[str, Any]] | None = None,
327
- ids: list[str] | None = None,
328
289
  columns: list[str] | None = None,
329
- unique_identifier_range_map: UniqueIdentifierRangeMap | None = None,
330
- column_range_descriptor: dict[str, UniqueIdentifierRangeMap] | None = None,
331
- **_: Any,
332
290
  ) -> pd.DataFrame:
333
291
  self._validate_index(index_names, time_index_name)
334
- identity_dimensions = [name for name in index_names if name != time_index_name]
335
- uses_legacy_unique_identifier = identity_dimensions == ["unique_identifier"]
336
-
337
- if ids is not None and unique_identifier_range_map is not None:
338
- raise ValueError("Cannot provide both 'ids' and 'unique_identifier_range_map'.")
339
- if ids is not None and not uses_legacy_unique_identifier:
340
- raise ValueError("Legacy 'ids' reads are valid only for unique_identifier tables.")
341
- if unique_identifier_range_map is not None and not uses_legacy_unique_identifier:
342
- raise ValueError(
343
- "Legacy 'unique_identifier_range_map' reads are valid only for "
344
- "unique_identifier tables."
345
- )
346
- if unique_identifier_range_map is not None and dimension_range_map is not None:
347
- raise ValueError(
348
- "Cannot provide both 'unique_identifier_range_map' and 'dimension_range_map'."
349
- )
350
- if column_range_descriptor is not None:
351
- raise NotImplementedError("SQLite column_range_descriptor reads are not supported.")
352
-
353
- if ids:
354
- dimension_filters = dict(dimension_filters or {})
355
- if "unique_identifier" in dimension_filters:
356
- raise ValueError("Cannot provide both 'ids' and a unique_identifier filter.")
357
- dimension_filters["unique_identifier"] = list(ids)
358
- if unique_identifier_range_map is not None:
359
- dimension_range_map = [
360
- {"coordinate": {"unique_identifier": uid}, **dict(info)}
361
- for uid, info in unique_identifier_range_map.items()
362
- ]
363
292
 
364
293
  if not self.table_exists(table):
365
294
  logger.warning(f"Table '{table}' does not exist in {self.db_path}.")
@@ -382,6 +311,7 @@ class SQLiteInterface:
382
311
  sql_parts = [f"SELECT {select_sql} FROM {self._qident(table)}"]
383
312
  where_clauses: list[str] = []
384
313
  params: list[Any] = []
314
+ identity_dimensions = [name for name in index_names if name != time_index_name]
385
315
 
386
316
  def validate_dimension_name(name: str) -> None:
387
317
  if name not in identity_dimensions:
@@ -760,10 +760,11 @@ def add_created_object_to_jobrun(
760
760
  Returns:
761
761
  A dictionary representing the created record.
762
762
  """
763
- url = (
764
- MAINSEQUENCE_ENDPOINT
765
- + f"/orm/api/pods/job-run/{os.getenv('JOB_RUN_ID')}/add_created_object/"
766
- )
763
+ job_run_uid = (os.getenv("JOB_RUN_UID") or "").strip()
764
+ if not job_run_uid:
765
+ raise RuntimeError("JOB_RUN_UID is required to attach created objects to a job run.")
766
+
767
+ url = MAINSEQUENCE_ENDPOINT + f"/orm/api/pods/job-run/{job_run_uid}/add_created_object/"
767
768
  payload = {"json": {"app_label": app_label, "model_name": model_name, "object_id": object_id}}
768
769
 
769
770
  r = make_request(
@@ -13,12 +13,12 @@ from uuid import UUID
13
13
  import yaml
14
14
  from pydantic import BaseModel, Field, PositiveInt
15
15
 
16
- from ..compute_validation import (
16
+ from .base import BaseObjectOrm, BasePydanticModel, ShareableObjectMixin
17
+ from .compute_validation import (
17
18
  decimal_to_storage,
18
19
  normalize_string,
19
20
  validate_and_normalize_compute_fields,
20
21
  )
21
- from .base import BaseObjectOrm, BasePydanticModel, ShareableObjectMixin
22
22
  from .exceptions import raise_for_response
23
23
  from .models_foundry import (
24
24
  Project,