mainsequence 4.1.4__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 (124) hide show
  1. {mainsequence-4.1.4 → mainsequence-4.1.5}/PKG-INFO +1 -1
  2. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +7 -26
  3. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/ms-markets/SKILL.md +2 -2
  4. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/cli.py +1 -1
  5. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/base.py +0 -1
  6. {mainsequence-4.1.4/mainsequence → mainsequence-4.1.5/mainsequence/client}/compute_validation.py +6 -2
  7. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/duckdb.py +2 -3
  8. mainsequence-4.1.5/mainsequence/client/data_sources_interfaces/local_paths.py +14 -0
  9. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/sqlite.py +2 -3
  10. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/models_foundry.py +5 -4
  11. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/models_helpers.py +2 -2
  12. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/logconf.py +22 -36
  13. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/__init__.py +0 -9
  14. mainsequence-4.1.5/mainsequence/meta_tables/compiled_sql/__init__.py +5 -0
  15. mainsequence-4.1.4/mainsequence/meta_tables/compiled_sql.py → mainsequence-4.1.5/mainsequence/meta_tables/compiled_sql/v1.py +3 -3
  16. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/runtime_flags.py +2 -2
  17. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/PKG-INFO +1 -1
  18. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/SOURCES.txt +4 -5
  19. {mainsequence-4.1.4 → mainsequence-4.1.5}/pyproject.toml +1 -1
  20. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_auth_precedence.py +5 -9
  21. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_build_operations_hashing.py +5 -5
  22. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_cli.py +1 -1
  23. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_data_access_mixin_dimension_audit.py +0 -55
  24. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_data_node_storage_dimension_queries.py +0 -20
  25. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_data_node_update_flow.py +0 -2
  26. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_duckdb_interface_dimensions.py +0 -2
  27. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_filter_normalization.py +0 -26
  28. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_logconf.py +26 -11
  29. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_meta_tables_client_models.py +4 -7
  30. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -1
  31. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_run_configuration.py +3 -7
  32. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_source_table_configuration.py +1 -71
  33. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_sqlite_interface_dimensions.py +0 -2
  34. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_update_statistics.py +1 -4
  35. mainsequence-4.1.4/mainsequence/meta_tables/config.py +0 -128
  36. mainsequence-4.1.4/mainsequence/meta_tables/configuration_models.py +0 -5
  37. mainsequence-4.1.4/mainsequence/meta_tables/utils.py +0 -45
  38. {mainsequence-4.1.4 → mainsequence-4.1.5}/LICENSE +0 -0
  39. {mainsequence-4.1.4 → mainsequence-4.1.5}/README.md +0 -0
  40. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/AGENTS.md +0 -0
  41. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
  42. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  43. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
  44. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  45. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  46. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
  47. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  48. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  49. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
  50. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  51. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  52. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
  53. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  54. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  55. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  56. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  57. {mainsequence-4.1.4 → mainsequence-4.1.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
  58. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/__init__.py +0 -0
  59. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/__main__.py +0 -0
  60. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/bootstrap.py +0 -0
  61. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/__init__.py +0 -0
  62. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/api.py +0 -0
  63. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/browser_auth.py +0 -0
  64. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/config.py +0 -0
  65. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/docker_utils.py +0 -0
  66. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/doctor.py +0 -0
  67. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/local_ops.py +0 -0
  68. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/model_filters.py +0 -0
  69. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/project_status.py +0 -0
  70. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/pydantic_cli.py +0 -0
  71. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/sdk_utils.py +0 -0
  72. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/ssh_utils.py +0 -0
  73. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/cli/ui.py +0 -0
  74. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/__init__.py +0 -0
  75. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/agent_runtime_models.py +0 -0
  76. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/client.py +0 -0
  77. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/__init__.py +0 -0
  78. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/app_component.py +0 -0
  79. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/connections.py +0 -0
  80. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/data_models.py +0 -0
  81. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/workspace.py +0 -0
  82. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  83. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  84. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/dtype_codec.py +0 -0
  85. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/exceptions.py +0 -0
  86. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/fastapi/__init__.py +0 -0
  87. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/fastapi/auth.py +0 -0
  88. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/models_metatables.py +0 -0
  89. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/models_user.py +0 -0
  90. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/client/utils.py +0 -0
  91. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/defaults.py +0 -0
  92. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/instrumentation/__init__.py +0 -0
  93. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/instrumentation/utils.py +0 -0
  94. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/__main__.py +0 -0
  95. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
  96. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
  97. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
  98. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/models.py +0 -0
  99. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
  100. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
  101. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
  102. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
  103. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/future_registry.py +0 -0
  104. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/hashing.py +0 -0
  105. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
  106. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
  107. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/dependency_links.txt +0 -0
  108. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/entry_points.txt +0 -0
  109. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/requires.txt +0 -0
  110. {mainsequence-4.1.4 → mainsequence-4.1.5}/mainsequence.egg-info/top_level.txt +0 -0
  111. {mainsequence-4.1.4 → mainsequence-4.1.5}/setup.cfg +0 -0
  112. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_cli_browser_auth.py +0 -0
  113. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_client.py +0 -0
  114. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_command_center_app_component_models.py +0 -0
  115. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_command_center_data_models.py +0 -0
  116. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_command_center_models.py +0 -0
  117. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_dependency_extras.py +0 -0
  118. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_models_user_request_bound_auth.py +0 -0
  119. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_pod_project_resolution.py +0 -0
  120. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_project_batch_jobs_from_file.py +0 -0
  121. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_secret_client_model.py +0 -0
  122. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_update_runner_uid_runtime.py +0 -0
  123. {mainsequence-4.1.4 → mainsequence-4.1.5}/tests/test_update_uid_guards.py +0 -0
  124. {mainsequence-4.1.4 → 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.4
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,
@@ -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
@@ -243,10 +237,8 @@ def _build_backend_bindings(
243
237
  bindings["project_id"] = project_id
244
238
  bindings["data_source_id"] = startup_state.get("data_source_id")
245
239
  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
240
+ bindings["job_run_uid"] = startup_state.get("job_run_uid")
241
+ bindings["command_id"] = startup_state.get("command_id")
250
242
  else:
251
243
  # your existing behavior: bind job_run_id to user_id in local-ish mode
252
244
  if "user_id" in startup_state:
@@ -276,14 +268,12 @@ def _bind_runtime(logger_: BoundLogger, **bindings: Any) -> BoundLogger:
276
268
  def apply_startup_state_bindings(
277
269
  logger_: BoundLogger,
278
270
  startup_state: Mapping[str, Any],
279
- *,
280
- command_id: str | None = None,
281
271
  ) -> BoundLogger:
282
272
  """
283
273
  Apply env + bindings derived from startup_state to the given logger.
284
274
  """
285
275
  _apply_additional_environment(startup_state)
286
- binds = _build_backend_bindings(startup_state, command_id=command_id)
276
+ binds = _build_backend_bindings(startup_state)
287
277
  return _bind_runtime(logger_, **binds)
288
278
 
289
279
 
@@ -295,7 +285,6 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
295
285
  """
296
286
 
297
287
  # do initial request when on logger initialization
298
- command_id = os.getenv("COMMAND_ID")
299
288
  json_response = _request_job_startup_state()
300
289
 
301
290
  # set additional args from backend
@@ -341,22 +330,22 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
341
330
  }
342
331
  else:
343
332
  handlers["console"] = {
344
- "class": "logging.StreamHandler",
345
- "formatter": "colored",
346
- "level": os.getenv("LOG_LEVEL", "DEBUG"),
333
+ "class": "logging.StreamHandler",
334
+ "formatter": "colored",
335
+ "level": os.getenv("LOG_LEVEL", "DEBUG"),
347
336
  }
348
337
 
349
338
  if logger_file is not None:
350
339
  ensure_dir(logger_file) # Ensure the directory for the log file exists
351
340
  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
341
+ "class": "concurrent_log_handler.ConcurrentRotatingFileHandler",
342
+ "formatter": "plain",
343
+ "level": os.getenv("LOG_LEVEL_FILE", "DEBUG"),
344
+ "filename": logger_file,
345
+ "mode": "a",
346
+ "delay": True,
347
+ "maxBytes": 5 * 1024 * 1024, # Rotate after 5 MB
348
+ "backupCount": 5, # Keep up to 5 backup files
360
349
  }
361
350
 
362
351
  logging_config = {
@@ -421,20 +410,17 @@ def build_application_logger(application_name: str = "ms-sdk", **metadata):
421
410
  )
422
411
 
423
412
  try:
424
-
425
- backend_binds = _build_backend_bindings(json_response, command_id=command_id)
413
+ backend_binds = _build_backend_bindings(json_response)
426
414
  logger = _bind_runtime(logger, **backend_binds)
427
415
 
428
-
429
-
430
416
  except Exception:
431
-
432
417
  logger.exception("Logger could not be binded running in local mode")
433
418
  logger = logger.bind(local_mode="no_app", **metadata)
434
419
 
435
420
  logger = logger.bind()
436
421
  return logger
437
422
 
423
+
438
424
  def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogger:
439
425
  """
440
426
  Importable helper: call this AFTER the user authenticates / token exists.
@@ -442,13 +428,12 @@ def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogg
442
428
  """
443
429
  global logger
444
430
 
445
- command_id = os.getenv("COMMAND_ID")
446
431
  json_response = _request_job_startup_state(timeout_s=timeout_s)
447
432
 
448
433
  # set additional args from backend
449
434
  _apply_additional_environment(json_response)
450
435
 
451
- backend_binds = _build_backend_bindings(json_response, command_id=command_id)
436
+ backend_binds = _build_backend_bindings(json_response)
452
437
 
453
438
  # if somebody calls this super early
454
439
  if logger is None:
@@ -458,6 +443,7 @@ def refresh_application_logger_bindings(*, timeout_s: float = 10.0) -> BoundLogg
458
443
  logger = _bind_runtime(logger, **backend_binds)
459
444
  return logger
460
445
 
446
+
461
447
  def dump_structlog_bound_logger(logger: BoundLogger) -> dict[str, Any]:
462
448
  """
463
449
  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
  ]
@@ -7,8 +7,8 @@ def is_running_in_pod() -> bool:
7
7
  """
8
8
  Return whether the current process is running inside a MainSequence pod.
9
9
 
10
- Pod runtime is identified by execution markers injected by the platform.
10
+ Pod runtime is identified by the job-run UID injected by the platform.
11
11
  Local workspaces may also set `MAIN_SEQUENCE_PROJECT_ID`, so that variable
12
12
  must not be used as the pod-runtime discriminator.
13
13
  """
14
- return bool((os.getenv("COMMAND_ID") or "").strip() or (os.getenv("JOB_RUN_ID") or "").strip())
14
+ return bool((os.getenv("JOB_RUN_UID") or "").strip())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 4.1.4
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
@@ -24,7 +24,6 @@ agent_scaffold/skills/project_to_agent/SKILL.md
24
24
  mainsequence/__init__.py
25
25
  mainsequence/__main__.py
26
26
  mainsequence/bootstrap.py
27
- mainsequence/compute_validation.py
28
27
  mainsequence/defaults.py
29
28
  mainsequence/logconf.py
30
29
  mainsequence/runtime_flags.py
@@ -52,6 +51,7 @@ mainsequence/client/__init__.py
52
51
  mainsequence/client/agent_runtime_models.py
53
52
  mainsequence/client/base.py
54
53
  mainsequence/client/client.py
54
+ mainsequence/client/compute_validation.py
55
55
  mainsequence/client/dtype_codec.py
56
56
  mainsequence/client/exceptions.py
57
57
  mainsequence/client/models_foundry.py
@@ -67,6 +67,7 @@ mainsequence/client/command_center/workspace.py
67
67
  mainsequence/client/command_center/workspace_snapshot.py
68
68
  mainsequence/client/data_sources_interfaces/__init__.py
69
69
  mainsequence/client/data_sources_interfaces/duckdb.py
70
+ mainsequence/client/data_sources_interfaces/local_paths.py
70
71
  mainsequence/client/data_sources_interfaces/sqlite.py
71
72
  mainsequence/client/fastapi/__init__.py
72
73
  mainsequence/client/fastapi/auth.py
@@ -74,14 +75,12 @@ mainsequence/instrumentation/__init__.py
74
75
  mainsequence/instrumentation/utils.py
75
76
  mainsequence/meta_tables/__init__.py
76
77
  mainsequence/meta_tables/__main__.py
77
- mainsequence/meta_tables/compiled_sql.py
78
- mainsequence/meta_tables/config.py
79
- mainsequence/meta_tables/configuration_models.py
80
78
  mainsequence/meta_tables/future_registry.py
81
79
  mainsequence/meta_tables/hashing.py
82
80
  mainsequence/meta_tables/pydantic_metadata.py
83
81
  mainsequence/meta_tables/sqlalchemy_contracts.py
84
- mainsequence/meta_tables/utils.py
82
+ mainsequence/meta_tables/compiled_sql/__init__.py
83
+ mainsequence/meta_tables/compiled_sql/v1.py
85
84
  mainsequence/meta_tables/data_nodes/__init__.py
86
85
  mainsequence/meta_tables/data_nodes/build_operations.py
87
86
  mainsequence/meta_tables/data_nodes/data_nodes.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "4.1.4"
7
+ version = "4.1.5"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -88,8 +88,7 @@ def test_auth_loaders_build_headers_from_env_jwt(monkeypatch):
88
88
  def test_logconf_refreshes_jwt_before_startup_state_request(monkeypatch):
89
89
  monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
90
90
  monkeypatch.setenv("MAINSEQUENCE_REFRESH_TOKEN", "jwt-refresh")
91
- monkeypatch.setenv("COMMAND_ID", "77")
92
- monkeypatch.setenv("JOB_RUN_ID", "34")
91
+ monkeypatch.setenv("JOB_RUN_UID", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
93
92
  monkeypatch.setenv("MAINSEQUENCE_ENDPOINT", "https://backend.example")
94
93
 
95
94
  get_calls: list[dict] = []
@@ -119,8 +118,7 @@ def test_logconf_runtime_credential_exchanges_before_startup_state_request(monke
119
118
  monkeypatch.setenv("MAINSEQUENCE_AUTH_MODE", "runtime_credential")
120
119
  monkeypatch.setenv("MAINSEQUENCE_RUNTIME_CREDENTIAL_ID", "cred-id")
121
120
  monkeypatch.setenv("MAINSEQUENCE_RUNTIME_CREDENTIAL_SECRET", "cred-secret")
122
- monkeypatch.setenv("COMMAND_ID", "77")
123
- monkeypatch.setenv("JOB_RUN_ID", "34")
121
+ monkeypatch.setenv("JOB_RUN_UID", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
124
122
  monkeypatch.setenv("MAINSEQUENCE_ENDPOINT", "https://backend.example")
125
123
 
126
124
  get_calls: list[dict] = []
@@ -161,8 +159,7 @@ def test_logconf_runtime_credential_retries_after_auth_failure(monkeypatch):
161
159
  monkeypatch.setenv("MAINSEQUENCE_AUTH_MODE", "runtime_credential")
162
160
  monkeypatch.setenv("MAINSEQUENCE_RUNTIME_CREDENTIAL_ID", "cred-id")
163
161
  monkeypatch.setenv("MAINSEQUENCE_RUNTIME_CREDENTIAL_SECRET", "cred-secret")
164
- monkeypatch.setenv("COMMAND_ID", "77")
165
- monkeypatch.setenv("JOB_RUN_ID", "34")
162
+ monkeypatch.setenv("JOB_RUN_UID", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
166
163
  monkeypatch.setenv("MAINSEQUENCE_ENDPOINT", "https://backend.example")
167
164
 
168
165
  get_calls: list[dict] = []
@@ -499,8 +496,7 @@ def test_logconf_session_jwt_does_not_refresh(monkeypatch):
499
496
  monkeypatch.setenv("MAINSEQUENCE_ACCESS_TOKEN", "runtime-access")
500
497
  monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
501
498
  monkeypatch.setenv("MAINSEQUENCE_AUTH_MODE", "session_jwt")
502
- monkeypatch.setenv("JOB_RUN_ID", "34")
503
- monkeypatch.setenv("COMMAND_ID", "12")
499
+ monkeypatch.setenv("JOB_RUN_UID", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
504
500
  monkeypatch.setenv("MAINSEQUENCE_ENDPOINT", "https://backend.example")
505
501
 
506
502
  get_calls: list[dict] = []
@@ -528,7 +524,7 @@ def test_logconf_session_jwt_rejects_refresh_token(monkeypatch):
528
524
  monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
529
525
  monkeypatch.setenv("MAINSEQUENCE_REFRESH_TOKEN", "bad-refresh")
530
526
  monkeypatch.setenv("MAINSEQUENCE_AUTH_MODE", "session_jwt")
531
- monkeypatch.setenv("JOB_RUN_ID", "34")
527
+ monkeypatch.setenv("JOB_RUN_UID", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")
532
528
 
533
529
  try:
534
530
  _load_mainsequence_submodule("mainsequence.logconf")
@@ -264,18 +264,18 @@ def test_plain_dict_with_pydantic_model_import_path_key_is_not_treated_as_wrappe
264
264
  assert build_operations.hash_signature(payload_a) != build_operations.hash_signature(payload_b)
265
265
 
266
266
 
267
- def test_legacy_ignore_from_storage_hash_metadata_is_rejected():
268
- class LegacyConfig(BaseModel):
267
+ def test_removed_storage_hash_metadata_is_rejected():
268
+ class RemovedStorageHashConfig(BaseModel):
269
269
  shard_id: str = Field(..., json_schema_extra={"ignore_from_storage_hash": True})
270
270
 
271
271
  with pytest.raises(ValueError, match="ignore_from_storage_hash"):
272
- build_operations.serialize_argument(LegacyConfig(shard_id="desk_a"))
272
+ build_operations.serialize_argument(RemovedStorageHashConfig(shard_id="desk_a"))
273
273
 
274
274
 
275
- def test_legacy_args_ignore_in_storage_hash_class_attribute_is_rejected():
275
+ def test_removed_storage_hash_class_attribute_is_rejected():
276
276
  with pytest.raises(TypeError, match="_ARGS_IGNORE_IN_STORAGE_HASH"):
277
277
 
278
- class LegacyDataNode(DataNode):
278
+ class RemovedStorageHashDataNode(DataNode):
279
279
  _ARGS_IGNORE_IN_STORAGE_HASH = ["asset_list"]
280
280
 
281
281
  def __init__(self, *args, **kwargs):
@@ -1820,7 +1820,7 @@ def test_model_filter_parser_uses_filterset_metadata():
1820
1820
 
1821
1821
 
1822
1822
  def test_shared_compute_validation_supports_k8s_quantities(cli_mod):
1823
- compute_mod = importlib.import_module("mainsequence.compute_validation")
1823
+ compute_mod = importlib.import_module("mainsequence.client.compute_validation")
1824
1824
 
1825
1825
  decimal_out = compute_mod.validate_and_normalize_compute_fields(
1826
1826
  cpu_request="500m",