mainsequence 4.1.4__tar.gz → 4.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 (124) hide show
  1. {mainsequence-4.1.4 → mainsequence-4.1.6}/PKG-INFO +1 -1
  2. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +18 -37
  3. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/ms-markets/SKILL.md +2 -2
  4. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/api.py +4 -4
  5. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/cli.py +12 -11
  6. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/base.py +0 -1
  7. {mainsequence-4.1.4/mainsequence → mainsequence-4.1.6/mainsequence/client}/compute_validation.py +6 -2
  8. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/data_sources_interfaces/duckdb.py +2 -3
  9. mainsequence-4.1.6/mainsequence/client/data_sources_interfaces/local_paths.py +14 -0
  10. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/data_sources_interfaces/sqlite.py +2 -3
  11. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/models_foundry.py +5 -4
  12. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/models_helpers.py +2 -2
  13. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/logconf.py +23 -39
  14. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/__init__.py +0 -9
  15. mainsequence-4.1.6/mainsequence/meta_tables/compiled_sql/__init__.py +5 -0
  16. mainsequence-4.1.4/mainsequence/meta_tables/compiled_sql.py → mainsequence-4.1.6/mainsequence/meta_tables/compiled_sql/v1.py +3 -3
  17. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/build_operations.py +1 -1
  18. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/data_nodes.py +2 -2
  19. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/persist_managers.py +2 -2
  20. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/runtime_flags.py +2 -2
  21. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/PKG-INFO +1 -1
  22. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/SOURCES.txt +4 -5
  23. {mainsequence-4.1.4 → mainsequence-4.1.6}/pyproject.toml +1 -1
  24. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_auth_precedence.py +5 -9
  25. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_build_operations_hashing.py +5 -5
  26. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_cli.py +30 -18
  27. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_data_access_mixin_dimension_audit.py +0 -55
  28. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_data_node_storage_dimension_queries.py +0 -20
  29. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_data_node_update_flow.py +0 -2
  30. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_duckdb_interface_dimensions.py +0 -2
  31. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_filter_normalization.py +0 -26
  32. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_logconf.py +21 -21
  33. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_meta_tables_client_models.py +4 -7
  34. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -1
  35. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_run_configuration.py +5 -9
  36. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_source_table_configuration.py +1 -71
  37. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_sqlite_interface_dimensions.py +0 -2
  38. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_update_statistics.py +1 -4
  39. mainsequence-4.1.4/mainsequence/meta_tables/config.py +0 -128
  40. mainsequence-4.1.4/mainsequence/meta_tables/configuration_models.py +0 -5
  41. mainsequence-4.1.4/mainsequence/meta_tables/utils.py +0 -45
  42. {mainsequence-4.1.4 → mainsequence-4.1.6}/LICENSE +0 -0
  43. {mainsequence-4.1.4 → mainsequence-4.1.6}/README.md +0 -0
  44. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/AGENTS.md +0 -0
  45. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  46. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  47. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  48. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  49. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  50. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  51. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  52. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  53. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  54. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  55. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  56. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  57. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  58. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  59. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  60. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  61. {mainsequence-4.1.4 → mainsequence-4.1.6}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  62. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/__init__.py +0 -0
  63. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/__main__.py +0 -0
  64. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/bootstrap.py +0 -0
  65. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/__init__.py +0 -0
  66. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/browser_auth.py +0 -0
  67. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/config.py +0 -0
  68. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/docker_utils.py +0 -0
  69. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/doctor.py +0 -0
  70. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/local_ops.py +0 -0
  71. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/model_filters.py +0 -0
  72. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/project_status.py +0 -0
  73. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/pydantic_cli.py +0 -0
  74. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/sdk_utils.py +0 -0
  75. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/ssh_utils.py +0 -0
  76. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/cli/ui.py +0 -0
  77. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/__init__.py +0 -0
  78. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/agent_runtime_models.py +0 -0
  79. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/client.py +0 -0
  80. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/__init__.py +0 -0
  81. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/app_component.py +0 -0
  82. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/connections.py +0 -0
  83. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/data_models.py +0 -0
  84. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/workspace.py +0 -0
  85. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  86. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  87. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/dtype_codec.py +0 -0
  88. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/exceptions.py +0 -0
  89. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/fastapi/__init__.py +0 -0
  90. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/fastapi/auth.py +0 -0
  91. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/models_metatables.py +0 -0
  92. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/models_user.py +0 -0
  93. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/client/utils.py +0 -0
  94. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/defaults.py +0 -0
  95. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/instrumentation/__init__.py +0 -0
  96. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/instrumentation/utils.py +0 -0
  97. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/__main__.py +0 -0
  98. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  99. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  100. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  101. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  102. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  103. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/future_registry.py +0 -0
  104. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/hashing.py +0 -0
  105. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  106. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  107. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/dependency_links.txt +0 -0
  108. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/entry_points.txt +0 -0
  109. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/requires.txt +0 -0
  110. {mainsequence-4.1.4 → mainsequence-4.1.6}/mainsequence.egg-info/top_level.txt +0 -0
  111. {mainsequence-4.1.4 → mainsequence-4.1.6}/setup.cfg +0 -0
  112. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_cli_browser_auth.py +0 -0
  113. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_client.py +0 -0
  114. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_command_center_app_component_models.py +0 -0
  115. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_command_center_data_models.py +0 -0
  116. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_command_center_models.py +0 -0
  117. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_dependency_extras.py +0 -0
  118. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_models_user_request_bound_auth.py +0 -0
  119. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_pod_project_resolution.py +0 -0
  120. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_project_batch_jobs_from_file.py +0 -0
  121. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_secret_client_model.py +0 -0
  122. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_update_runner_uid_runtime.py +0 -0
  123. {mainsequence-4.1.4 → mainsequence-4.1.6}/tests/test_update_uid_guards.py +0 -0
  124. {mainsequence-4.1.4 → mainsequence-4.1.6}/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.4
3
+ Version: 4.1.6
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
@@ -15,7 +15,7 @@ is defined by a registered `PlatformTimeIndexMetaData` SQLAlchemy model.
15
15
  Canonical workflow:
16
16
 
17
17
  1. Define a `PlatformTimeIndexMetaData` storage class.
18
- 2. Register or bind that storage class before constructing the DataNode.
18
+ 2. Register that storage class before constructing the DataNode.
19
19
  3. Construct the DataNode with `config=...` and `storage_table=StorageClass`.
20
20
  4. Return a DataFrame from `update()` that matches the storage class contract.
21
21
 
@@ -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
 
@@ -93,7 +91,7 @@ For every non-trivial DataNode task, make these decisions explicitly:
93
91
 
94
92
  1. Is this a new dataset or the same dataset?
95
93
  2. Is this change storage-contract work or update-process work?
96
- 3. Is the storage class already registered or should the MetaTable skill handle it?
94
+ 3. Is the storage class registered through its MetaTable path, or should the MetaTable skill handle it?
97
95
  4. Is the node single-index or MultiIndex?
98
96
  5. Does the first validation run happen under an explicit `hash_namespace(...)`?
99
97
 
@@ -141,17 +139,17 @@ class PricesTable(PlatformTimeIndexMetaData, Base):
141
139
  close: Mapped[float] = mapped_column(Float, nullable=False)
142
140
  ```
143
141
 
144
- Register or bind storage before constructing the DataNode:
142
+ Register storage before constructing the DataNode:
145
143
 
146
144
  ```python
147
145
  PricesTable.register(data_source_uid=data_source_uid)
148
146
  ```
149
147
 
150
- or:
151
-
152
- ```python
153
- PricesTable.bind_meta_table(existing_meta_table)
154
- ```
148
+ `PlatformTimeIndexMetaData.register(...)` is the storage lifecycle path. Treat it
149
+ as an idempotent get-or-create operation: the platform returns the registered
150
+ metadata and UID, and the SDK records that metadata on the class. Do not manually
151
+ attach an existing UID, reconstruct a generic `MetaTable`, or use manual bind
152
+ helpers as an authoring step.
155
153
 
156
154
  ### 2. Keep DataNode As Update Logic
157
155
 
@@ -169,7 +167,7 @@ dependency, put that dependency storage reference in the config as
169
167
  `type[PlatformTimeIndexMetaData]`. Do not add an extra constructor argument for
170
168
  dependency storage tables. Config values of this type are hashed by the bound
171
169
  `TimeIndexMetaData.uid` from `StorageClass.__time_index_metadata__`, so the class
172
- must be registered or bound before DataNode construction.
170
+ must be registered before DataNode construction.
173
171
 
174
172
  Do not accept `test_node`. It has been removed. Use explicit
175
173
  `hash_namespace(...)` or `hash_namespace="..."`.
@@ -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
 
@@ -351,9 +338,9 @@ When reviewing an existing DataNode, look for:
351
338
  Do not claim success until you have checked:
352
339
 
353
340
  - the relevant docs were read first
354
- - storage is a registered or bound `PlatformTimeIndexMetaData` class
341
+ - storage is a registered `PlatformTimeIndexMetaData` class
355
342
  - the DataNode constructor requires `storage_table`
356
- - dependency storage-table references live in config and are registered or bound
343
+ - dependency storage-table references live in config and are registered
357
344
  - config fields are updater-scoped by default
358
345
  - no removed hash metadata markers remain
359
346
  - no `test_node` usage remains
@@ -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
@@ -5136,7 +5136,7 @@ def list_project_job_runs(
5136
5136
 
5137
5137
 
5138
5138
  def get_project_job_run_logs(
5139
- job_run_id: int | str,
5139
+ job_run_uid: str,
5140
5140
  *,
5141
5141
  timeout: int | None = None,
5142
5142
  ) -> dict[str, Any]:
@@ -5191,20 +5191,20 @@ def get_project_job_run_logs(
5191
5191
  BaseObjectOrm.ROOT_URL = root_url
5192
5192
  ClientJobRun.ROOT_URL = root_url
5193
5193
 
5194
- job_run = ClientJobRun.get(pk=int(job_run_id), timeout=timeout)
5194
+ job_run = ClientJobRun.get(pk=job_run_uid, timeout=timeout)
5195
5195
  payload = job_run.get_logs(timeout=timeout)
5196
5196
  if isinstance(payload, dict):
5197
5197
  return payload
5198
5198
  if hasattr(payload, "model_dump"):
5199
5199
  return payload.model_dump()
5200
- return {"job_run_id": int(job_run_id), "rows": []}
5200
+ return {"job_run_uid": job_run_uid, "rows": []}
5201
5201
 
5202
5202
  except Exception as e:
5203
5203
  err_name = type(e).__name__
5204
5204
  if err_name in {"AuthenticationError", "PermissionDeniedError"}:
5205
5205
  raise NotLoggedIn(str(e) or "Not logged in.") from e
5206
5206
  if err_name == "NotFoundError":
5207
- raise ApiError(f"Job run not found: {job_run_id}") from e
5207
+ raise ApiError(f"Job run not found: {job_run_uid}") from e
5208
5208
  raise ApiError(f"Project job run logs fetch failed: {e}") from e
5209
5209
  finally:
5210
5210
  if client_utils is not None:
@@ -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,
@@ -10196,7 +10196,7 @@ def project_jobs_run_cmd(
10196
10196
  if payload:
10197
10197
  preferred_keys = [
10198
10198
  ("Job ID", str(payload.get("job") or payload.get("job_id") or job_id)),
10199
- ("Job Run ID", str(payload.get("id") or payload.get("job_run_id") or "-")),
10199
+ ("Job Run UID", str(payload.get("uid") or payload.get("job_run_uid") or "-")),
10200
10200
  ("Name", str(payload.get("name") or payload.get("job_name") or "-")),
10201
10201
  ("Unique Identifier", str(payload.get("unique_identifier") or "-")),
10202
10202
  ("Status", str(payload.get("status") or "-")),
@@ -10208,7 +10208,8 @@ def project_jobs_run_cmd(
10208
10208
  "job",
10209
10209
  "job_id",
10210
10210
  "id",
10211
- "job_run_id",
10211
+ "uid",
10212
+ "job_run_uid",
10212
10213
  "name",
10213
10214
  "job_name",
10214
10215
  "unique_identifier",
@@ -10254,8 +10255,8 @@ def project_job_runs_list_cmd(
10254
10255
 
10255
10256
  @project_job_runs_group.command("logs")
10256
10257
  def project_job_runs_logs_cmd(
10257
- job_run_id: int = pydantic_argument(
10258
- JOB_RUN_MODEL_REF, "id", ..., help="Job run ID whose logs will be shown."
10258
+ job_run_uid: str = pydantic_argument(
10259
+ JOB_RUN_MODEL_REF, "uid", ..., help="Job run UID whose logs will be shown."
10259
10260
  ),
10260
10261
  poll_interval: int = typer.Option(
10261
10262
  30,
@@ -10279,10 +10280,10 @@ def project_job_runs_logs_cmd(
10279
10280
  Examples
10280
10281
  --------
10281
10282
  ```bash
10282
- mainsequence project jobs runs logs 501
10283
- mainsequence project jobs runs logs 501 --poll-interval 10
10284
- mainsequence project jobs runs logs 501 --max-wait-seconds 900
10285
- mainsequence project jobs runs logs 501 --poll-interval 0
10283
+ mainsequence project jobs runs logs 4c1d77c8-8a42-42b8-a9c1-06be9a336e5d
10284
+ mainsequence project jobs runs logs 4c1d77c8-8a42-42b8-a9c1-06be9a336e5d --poll-interval 10
10285
+ mainsequence project jobs runs logs 4c1d77c8-8a42-42b8-a9c1-06be9a336e5d --max-wait-seconds 900
10286
+ mainsequence project jobs runs logs 4c1d77c8-8a42-42b8-a9c1-06be9a336e5d --poll-interval 0
10286
10287
  ```
10287
10288
  """
10288
10289
  _require_login()
@@ -10296,7 +10297,7 @@ def project_job_runs_logs_cmd(
10296
10297
 
10297
10298
  while True:
10298
10299
  try:
10299
- payload = get_project_job_run_logs(job_run_id=job_run_id, timeout=timeout)
10300
+ payload = get_project_job_run_logs(job_run_uid=job_run_uid, timeout=timeout)
10300
10301
  except ApiError as e:
10301
10302
  error(f"Project job run logs fetch failed: {e}")
10302
10303
  raise typer.Exit(1) from e
@@ -10313,7 +10314,7 @@ def project_job_runs_logs_cmd(
10313
10314
  print_kv(
10314
10315
  "Job Run Logs",
10315
10316
  [
10316
- ("Job Run ID", str(payload.get("job_run_id") or job_run_id)),
10317
+ ("Job Run UID", str(payload.get("job_run_uid") or payload.get("uid") or job_run_uid)),
10317
10318
  ("Status", status_value),
10318
10319
  ],
10319
10320
  )
@@ -84,7 +84,6 @@ class BaseObjectOrm:
84
84
  "MultiIndexMetadata": "orm/multi_index_metadata",
85
85
  "ContinuousAggMultiIndex": "ts_manager/cont_agg_multi_ind",
86
86
  "TimeIndexMetaData": "ts_manager/dynamic_table",
87
- # "LocalTimeSerieNodesMethods": "ogm/local_time_serie",
88
87
  "LocalTimeSerieNodesMethods": "ts_manager/local_time_serie",
89
88
  "DataNodeUpdate": "ts_manager/local_time_serie",
90
89
  "DataNodeUpdateDetails": "ts_manager/local_time_serie_update_details",
@@ -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":
@@ -23,6 +23,7 @@ from ..dtype_codec import (
23
23
  token_to_pandas_series,
24
24
  )
25
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
 
@@ -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,6 +16,7 @@ from ..dtype_codec import (
16
16
  token_to_backend_type,
17
17
  token_to_pandas_series,
18
18
  )
19
+ from .local_paths import local_data_path
19
20
 
20
21
 
21
22
  def get_logger():
@@ -32,11 +33,9 @@ class SQLiteInterface:
32
33
  """
33
34
 
34
35
  def __init__(self, db_path: str | Path | None = None):
35
- from mainsequence.meta_tables.config import META_TABLES_DATA_PATH
36
-
37
36
  default_path = os.getenv(
38
37
  "SQLITE_PATH",
39
- os.path.join(f"{META_TABLES_DATA_PATH}", "sqlite"),
38
+ os.fspath(local_data_path() / "sqlite"),
40
39
  )
41
40
  raw_path = Path(str(db_path or default_path)).expanduser()
42
41
  if raw_path.suffix in {".db", ".sqlite", ".sqlite3"}:
@@ -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,
@@ -71,7 +71,7 @@ class CustomConsoleRenderer(ConsoleRenderer):
71
71
 
72
72
  def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
73
73
  """
74
- Fetch startup state from backend using current env vars (JWT auth, endpoint, job_run_id, command_id).
74
+ Fetch startup state from backend using current env vars (JWT auth, endpoint, job_run_uid).
75
75
  Safe to call later after auth when access/refresh tokens become available.
76
76
  """
77
77
  if not is_running_in_pod():
@@ -183,16 +183,12 @@ def _request_job_startup_state(*, timeout_s: float = 10.0) -> dict[str, Any]:
183
183
 
184
184
  headers, using_jwt = _auth_headers()
185
185
 
186
- job_run_id = (os.getenv("JOB_RUN_ID") or "").strip()
187
- if not job_run_id:
186
+ job_run_uid = (os.getenv("JOB_RUN_UID") or "").strip()
187
+ if not job_run_uid:
188
188
  return {}
189
189
 
190
- endpoint = f"{_backend_base_url()}/orm/api/pods/job-run/{job_run_id}/startup-state/"
191
-
192
- command_id = os.getenv("COMMAND_ID")
190
+ endpoint = f"{_backend_base_url()}/orm/api/pods/job-run/{job_run_uid}/startup-state/"
193
191
  params: dict[str, Any] = {}
194
- if command_id:
195
- params["command_id"] = command_id
196
192
 
197
193
  resp = requests.get(endpoint, headers=headers, params=params, timeout=timeout_s)
198
194
  if resp.status_code in {401, 403} and using_jwt and _refresh_access_token():
@@ -225,8 +221,6 @@ def _apply_additional_environment(startup_state: Mapping[str, Any]) -> None:
225
221
 
226
222
  def _build_backend_bindings(
227
223
  startup_state: Mapping[str, Any],
228
- *,
229
- command_id: str | None = None,
230
224
  ) -> dict[str, Any]:
231
225
  """
232
226
  Pure function: compute the keys you want to bind on the logger
@@ -242,15 +236,11 @@ def _build_backend_bindings(
242
236
  if project_id is not None:
243
237
  bindings["project_id"] = project_id
244
238
  bindings["data_source_id"] = startup_state.get("data_source_id")
245
- bindings["job_run_id"] = startup_state.get("job_run_id")
246
-
247
- if command_id is None:
248
- command_id = os.getenv("COMMAND_ID")
249
- bindings["command_id"] = int(command_id) if command_id else None
239
+ bindings["job_run_uid"] = startup_state.get("job_run_uid")
240
+ bindings["command_id"] = startup_state.get("command_id")
250
241
  else:
251
- # your existing behavior: bind job_run_id to user_id in local-ish mode
252
242
  if "user_id" in startup_state:
253
- bindings["job_run_id"] = startup_state.get("user_id")
243
+ bindings["user_id"] = startup_state.get("user_id")
254
244
  else:
255
245
  bindings["local_mode"] = "no_app"
256
246
 
@@ -276,14 +266,12 @@ def _bind_runtime(logger_: BoundLogger, **bindings: Any) -> BoundLogger:
276
266
  def apply_startup_state_bindings(
277
267
  logger_: BoundLogger,
278
268
  startup_state: Mapping[str, Any],
279
- *,
280
- command_id: str | None = None,
281
269
  ) -> BoundLogger:
282
270
  """
283
271
  Apply env + bindings derived from startup_state to the given logger.
284
272
  """
285
273
  _apply_additional_environment(startup_state)
286
- binds = _build_backend_bindings(startup_state, command_id=command_id)
274
+ binds = _build_backend_bindings(startup_state)
287
275
  return _bind_runtime(logger_, **binds)
288
276
 
289
277
 
@@ -295,7 +283,6 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
295
283
  """
296
284
 
297
285
  # do initial request when on logger initialization
298
- command_id = os.getenv("COMMAND_ID")
299
286
  json_response = _request_job_startup_state()
300
287
 
301
288
  # set additional args from backend
@@ -341,22 +328,22 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
341
328
  }
342
329
  else:
343
330
  handlers["console"] = {
344
- "class": "logging.StreamHandler",
345
- "formatter": "colored",
346
- "level": os.getenv("LOG_LEVEL", "DEBUG"),
331
+ "class": "logging.StreamHandler",
332
+ "formatter": "colored",
333
+ "level": os.getenv("LOG_LEVEL", "DEBUG"),
347
334
  }
348
335
 
349
336
  if logger_file is not None:
350
337
  ensure_dir(logger_file) # Ensure the directory for the log file exists
351
338
  handlers["file"] = {
352
- "class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
353
- "formatter": "plain",
354
- "level": os.getenv("LOG_LEVEL_FILE", "DEBUG"),
355
- "filename": logger_file,
356
- "mode": "a",
357
- "delay": True,
358
- "maxBytes": 5 * 1024 * 1024, # Rotate after 5 MB
359
- "backupCount": 5, # Keep up to 5 backup files
339
+ "class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
340
+ "formatter": "plain",
341
+ "level": os.getenv("LOG_LEVEL_FILE", "DEBUG"),
342
+ "filename": logger_file,
343
+ "mode": "a",
344
+ "delay": True,
345
+ "maxBytes": 5 * 1024 * 1024, # Rotate after 5 MB
346
+ "backupCount": 5, # Keep up to 5 backup files
360
347
  }
361
348
 
362
349
  logging_config = {
@@ -421,20 +408,17 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
421
408
  )
422
409
 
423
410
  try:
424
-
425
- backend_binds = _build_backend_bindings(json_response, command_id=command_id)
411
+ backend_binds = _build_backend_bindings(json_response)
426
412
  logger = _bind_runtime(logger, **backend_binds)
427
413
 
428
-
429
-
430
414
  except Exception:
431
-
432
415
  logger.exception("Logger could not be binded running in local mode")
433
416
  logger = logger.bind(local_mode="no_app", **metadata)
434
417
 
435
418
  logger = logger.bind()
436
419
  return logger
437
420
 
421
+
438
422
  def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogger:
439
423
  """
440
424
  Importable helper: call this AFTER the user authenticates / token exists.
@@ -442,13 +426,12 @@ def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogg
442
426
  """
443
427
  global logger
444
428
 
445
- command_id = os.getenv("COMMAND_ID")
446
429
  json_response = _request_job_startup_state(timeout_s=timeout_s)
447
430
 
448
431
  # set additional args from backend
449
432
  _apply_additional_environment(json_response)
450
433
 
451
- backend_binds = _build_backend_bindings(json_response, command_id=command_id)
434
+ backend_binds = _build_backend_bindings(json_response)
452
435
 
453
436
  # if somebody calls this super early
454
437
  if logger is None:
@@ -458,6 +441,7 @@ def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogg
458
441
  logger = _bind_runtime(logger, **backend_binds)
459
442
  return logger
460
443
 
444
+
461
445
  def dump_structlog_bound_logger(logger: BoundLogger) -> dict[str, Any]:
462
446
  """
463
447
  Serialize a fully‑initialized structlog BoundLogger into a dict:
@@ -5,10 +5,6 @@ from importlib import import_module
5
5
  from mainsequence.instrumentation import TracerInstrumentator
6
6
 
7
7
  _LAZY_IMPORTS = {
8
- "TIME_SERIES_SOURCE_TIMESCALE": (".config", "TIME_SERIES_SOURCE_TIMESCALE"),
9
- "RunningMode": (".config", "RunningMode"),
10
- "configuration": (".config", "configuration"),
11
- "ogm": (".config", "ogm"),
12
8
  "DEFAULT_PLATFORM_MANAGED_PROVISIONING": (
13
9
  ".sqlalchemy_contracts",
14
10
  "DEFAULT_PLATFORM_MANAGED_PROVISIONING",
@@ -20,16 +16,11 @@ _LAZY_IMPORTS = {
20
16
  "APIDataNode": (".data_nodes", "APIDataNode"),
21
17
  "DataNode": (".data_nodes", "DataNode"),
22
18
  "DataNodeConfiguration": (".data_nodes", "DataNodeConfiguration"),
23
- "build_compiled_sql_v1_operation": (
24
- ".compiled_sql",
25
- "build_compiled_sql_v1_operation",
26
- ),
27
19
  "build_meta_table_configured_storage_hash": (
28
20
  ".hashing",
29
21
  "build_meta_table_configured_storage_hash",
30
22
  ),
31
23
  "build_meta_table_storage_hash": (".hashing", "build_meta_table_storage_hash"),
32
- "compile_sqlalchemy_statement": (".compiled_sql", "compile_sqlalchemy_statement"),
33
24
  "external_registered_registration_request_from_sqlalchemy_model": (
34
25
  ".sqlalchemy_contracts",
35
26
  "external_registered_registration_request_from_sqlalchemy_model",
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from . import v1
4
+
5
+ __all__ = ["v1"]
@@ -17,7 +17,7 @@ from mainsequence.client.models_metatables import (
17
17
  )
18
18
 
19
19
 
20
- def build_compiled_sql_v1_operation(
20
+ def build_operation(
21
21
  *,
22
22
  operation: MetaTableOperation,
23
23
  sql: str,
@@ -115,7 +115,7 @@ def compile_sqlalchemy_statement(
115
115
  for table in scope_tables
116
116
  ]
117
117
  )
118
- return build_compiled_sql_v1_operation(
118
+ return build_operation(
119
119
  operation=operation,
120
120
  sql=str(compiled),
121
121
  parameters=dict(compiled.params),
@@ -143,6 +143,6 @@ def _compiled_sqlalchemy_parameter_types(compiled: Any) -> dict[str, str]:
143
143
 
144
144
 
145
145
  __all__ = [
146
- "build_compiled_sql_v1_operation",
146
+ "build_operation",
147
147
  "compile_sqlalchemy_statement",
148
148
  ]
@@ -96,7 +96,7 @@ def _(value: type[Any]) -> Any:
96
96
  uid = getattr(time_index_metadata, "uid", None)
97
97
  if uid in (None, ""):
98
98
  raise ValueError(
99
- "PlatformTimeIndexMetaData config values must be registered or bound "
99
+ "PlatformTimeIndexMetaData config values must be registered "
100
100
  "before they can be hashed."
101
101
  )
102
102
  return {
@@ -647,7 +647,7 @@ class DataNode(DataAccessMixin, ABC):
647
647
  )
648
648
  if value.get_time_index_metadata() is None:
649
649
  raise ValueError(
650
- "DataNode storage_table must be registered or bound before construction."
650
+ "DataNode storage_table must be registered before construction."
651
651
  )
652
652
  if value.get_meta_table_uid() in (None, ""):
653
653
  raise ValueError("DataNode storage_table must provide a MetaTable UID.")
@@ -659,7 +659,7 @@ class DataNode(DataAccessMixin, ABC):
659
659
  def storage_metadata(self) -> Any:
660
660
  storage_metadata = self.storage_table.get_time_index_metadata()
661
661
  if storage_metadata is None:
662
- raise ValueError("DataNode storage_table must be registered or bound before use.")
662
+ raise ValueError("DataNode storage_table must be registered before use.")
663
663
  return storage_metadata
664
664
 
665
665
  def _initialize_configuration(self, init_kwargs: dict) -> None: