mainsequence 4.3.5__tar.gz → 4.3.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.
- {mainsequence-4.3.5/mainsequence.egg-info → mainsequence-4.3.6}/PKG-INFO +1 -1
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +7 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +5 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/metatables/core.py +52 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/migrations.py +3 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/sqlalchemy_contracts.py +37 -1
- {mainsequence-4.3.5 → mainsequence-4.3.6/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-4.3.5 → mainsequence-4.3.6}/pyproject.toml +1 -1
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_meta_tables_sqlalchemy_contracts.py +75 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/LICENSE +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/README.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/__main__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/base.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/client.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/defaults.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/logconf.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/setup.cfg +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_cli.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_cli_migrations.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_client.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_instrumentation.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_logconf.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_schema_names.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.3.5 → mainsequence-4.3.6}/tests/test_workspace_snapshot.py +0 -0
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
@@ -159,6 +159,7 @@ class PricesTable(PlatformTimeIndexMetaTable, Base):
|
|
|
159
159
|
"risk analytics."
|
|
160
160
|
)
|
|
161
161
|
__time_index_name__ = "time_index"
|
|
162
|
+
__cadence__ = "1d"
|
|
162
163
|
__index_names__ = ["time_index", "unique_identifier"]
|
|
163
164
|
|
|
164
165
|
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
@@ -197,6 +198,12 @@ autogenerate runs. Do not manually repeat the full grain unique index in
|
|
|
197
198
|
`__table_args__`; add ordinary SQLAlchemy `Index(...)` entries only for
|
|
198
199
|
additional lookup/performance paths.
|
|
199
200
|
|
|
201
|
+
When the dataset has a known stable observation interval, declare `__cadence__`
|
|
202
|
+
on the `PlatformTimeIndexMetaTable` model, for example `1m`, `5m`, `1h`, `1d`,
|
|
203
|
+
`1w`, `1mo`, `1q`, or `1y`. Cadence is table metadata and should be included
|
|
204
|
+
whenever possible; do not make it a DataNode runtime configuration field unless
|
|
205
|
+
changing it actually changes the produced dataset identity.
|
|
206
|
+
|
|
200
207
|
`PlatformTimeIndexMetaTable.register()` remains SDK plumbing for the migration
|
|
201
208
|
workflow. Do not manually attach an existing UID, reconstruct a generic
|
|
202
209
|
`MetaTable`, or use manual bind helpers as an authoring step.
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md
RENAMED
|
@@ -137,6 +137,11 @@ first version, use Alembic. Keep the SDK model as a normal
|
|
|
137
137
|
`PlatformManagedMetaTable` or `PlatformTimeIndexMetaTable` catalog contract, and
|
|
138
138
|
apply physical schema changes through the Alembic migration workflow.
|
|
139
139
|
|
|
140
|
+
For `PlatformTimeIndexMetaTable`, declare `__cadence__` whenever the table has a
|
|
141
|
+
known stable observation interval, for example `1m`, `5m`, `1h`, `1d`, `1w`,
|
|
142
|
+
`1mo`, `1q`, or `1y`. Cadence is table metadata and belongs on the storage
|
|
143
|
+
model when possible.
|
|
144
|
+
|
|
140
145
|
Default-schema tables must leave SQLAlchemy `Table.schema` unset; do not write
|
|
141
146
|
`__table_args__ = {"schema": "public"}` for the default PostgreSQL schema. Set
|
|
142
147
|
schema metadata only for non-default schemas, using `__table_args__ = {"schema":
|
|
@@ -8,6 +8,7 @@ import gzip
|
|
|
8
8
|
import json
|
|
9
9
|
import math
|
|
10
10
|
import os
|
|
11
|
+
import re
|
|
11
12
|
import time
|
|
12
13
|
from collections.abc import Callable, Mapping, Sequence
|
|
13
14
|
from dataclasses import dataclass
|
|
@@ -58,6 +59,24 @@ from ..utils import (
|
|
|
58
59
|
DUCK_DB = "duck_db"
|
|
59
60
|
SQLITE = "sqlite"
|
|
60
61
|
LOCAL_DATA_SOURCE_CLASS_TYPES = {DUCK_DB, SQLITE}
|
|
62
|
+
_TIME_INDEXED_CADENCE_RE = re.compile(
|
|
63
|
+
r"^[1-9][0-9]*(mo|s|m|h|d|w|q|y)$",
|
|
64
|
+
re.IGNORECASE,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _normalize_time_indexed_cadence(value: Any) -> str | None:
|
|
69
|
+
if value is None:
|
|
70
|
+
return None
|
|
71
|
+
normalized = str(value).strip().lower()
|
|
72
|
+
if not normalized:
|
|
73
|
+
return None
|
|
74
|
+
if not _TIME_INDEXED_CADENCE_RE.fullmatch(normalized):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
"cadence must be an interval token such as 1m, 5m, 1h, 1d, 1w, "
|
|
77
|
+
"1mo, 1q, or 1y."
|
|
78
|
+
)
|
|
79
|
+
return normalized
|
|
61
80
|
|
|
62
81
|
|
|
63
82
|
def _duckdb_interface():
|
|
@@ -667,6 +686,11 @@ class ManagedMetaTableFinalizeTableResult(BasePydanticModel):
|
|
|
667
686
|
)
|
|
668
687
|
table_kind: str = Field(..., description="Backend table kind after reconciliation.")
|
|
669
688
|
time_indexed: bool = Field(..., description="Whether the MetaTable is time-indexed.")
|
|
689
|
+
cadence: str | None = Field(
|
|
690
|
+
None,
|
|
691
|
+
max_length=32,
|
|
692
|
+
description="Optional declared cadence for time-indexed MetaTables.",
|
|
693
|
+
)
|
|
670
694
|
finalized: bool = Field(
|
|
671
695
|
...,
|
|
672
696
|
description="True when this row was reconciled and activated by this call.",
|
|
@@ -695,6 +719,11 @@ class ManagedMetaTableFinalizeTableResult(BasePydanticModel):
|
|
|
695
719
|
description="Per-table structured error when finalization did not activate this row.",
|
|
696
720
|
)
|
|
697
721
|
|
|
722
|
+
@field_validator("cadence")
|
|
723
|
+
@classmethod
|
|
724
|
+
def _normalize_cadence(cls, value: str | None) -> str | None:
|
|
725
|
+
return _normalize_time_indexed_cadence(value)
|
|
726
|
+
|
|
698
727
|
model_config = ConfigDict(extra="ignore")
|
|
699
728
|
|
|
700
729
|
|
|
@@ -1923,6 +1952,14 @@ class TimeIndexMetaTableRegistrationRequest(BasePydanticModel):
|
|
|
1923
1952
|
default_factory=lambda: {"create_table": True, "if_not_exists": True}
|
|
1924
1953
|
)
|
|
1925
1954
|
time_index_name: str = Field(..., description="Canonical timestamp column name")
|
|
1955
|
+
cadence: str | None = Field(
|
|
1956
|
+
None,
|
|
1957
|
+
max_length=32,
|
|
1958
|
+
description=(
|
|
1959
|
+
"Optional time-indexed cadence token such as 1m, 5m, 1h, 1d, "
|
|
1960
|
+
"1w, 1mo, 1q, or 1y."
|
|
1961
|
+
),
|
|
1962
|
+
)
|
|
1926
1963
|
partition_strategy: str = Field(
|
|
1927
1964
|
default="backend_default",
|
|
1928
1965
|
description="Time-indexed MetaTable physical partitioning strategy",
|
|
@@ -1973,6 +2010,11 @@ class TimeIndexMetaTableRegistrationRequest(BasePydanticModel):
|
|
|
1973
2010
|
data["table_contract"] = normalized_contract
|
|
1974
2011
|
return data
|
|
1975
2012
|
|
|
2013
|
+
@field_validator("cadence")
|
|
2014
|
+
@classmethod
|
|
2015
|
+
def _normalize_cadence(cls, value: str | None) -> str | None:
|
|
2016
|
+
return _normalize_time_indexed_cadence(value)
|
|
2017
|
+
|
|
1976
2018
|
|
|
1977
2019
|
def _payload_get(obj: Any, key: str, default: Any = None) -> Any:
|
|
1978
2020
|
if isinstance(obj, Mapping):
|
|
@@ -2113,6 +2155,11 @@ class TimeIndexedProfile(TimeIndexedProfileBase, BasePydanticModel):
|
|
|
2113
2155
|
None, description="Public uid of the related TimeIndexMetaTable"
|
|
2114
2156
|
)
|
|
2115
2157
|
time_index_name: str = Field(..., max_length=100, description="Time index name")
|
|
2158
|
+
cadence: str | None = Field(
|
|
2159
|
+
None,
|
|
2160
|
+
max_length=32,
|
|
2161
|
+
description="Optional declared cadence for the time-indexed table.",
|
|
2162
|
+
)
|
|
2116
2163
|
partition_strategy: str | None = None
|
|
2117
2164
|
last_time_index_value: datetime.datetime | None = Field(
|
|
2118
2165
|
None, description="Last time index value"
|
|
@@ -2124,6 +2171,11 @@ class TimeIndexedProfile(TimeIndexedProfileBase, BasePydanticModel):
|
|
|
2124
2171
|
physical_index_plan: dict[str, Any] | None = Field(
|
|
2125
2172
|
None, description="Server-rendered physical index plan"
|
|
2126
2173
|
)
|
|
2174
|
+
|
|
2175
|
+
@field_validator("cadence")
|
|
2176
|
+
@classmethod
|
|
2177
|
+
def _normalize_cadence(cls, value: str | None) -> str | None:
|
|
2178
|
+
return _normalize_time_indexed_cadence(value)
|
|
2127
2179
|
multi_index_stats: dict[str, Any] | None = Field(
|
|
2128
2180
|
None, description="Canonical multi-index progress statistics"
|
|
2129
2181
|
)
|
|
@@ -935,6 +935,9 @@ def _collection_create_row_from_registration_request(
|
|
|
935
935
|
time_index_name = getattr(request, "time_index_name", None)
|
|
936
936
|
if time_index_name not in (None, ""):
|
|
937
937
|
row["time_index_name"] = str(time_index_name)
|
|
938
|
+
cadence = getattr(request, "cadence", None)
|
|
939
|
+
if cadence not in (None, ""):
|
|
940
|
+
row["cadence"] = str(cadence)
|
|
938
941
|
partition_strategy = getattr(request, "partition_strategy", None)
|
|
939
942
|
if partition_strategy not in (None, ""):
|
|
940
943
|
row["partition_strategy"] = str(partition_strategy)
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import contextvars
|
|
5
5
|
import json
|
|
6
|
+
import re
|
|
6
7
|
from collections.abc import Mapping, Sequence
|
|
7
8
|
from dataclasses import dataclass
|
|
8
9
|
from typing import Any, ClassVar
|
|
@@ -37,6 +38,10 @@ DEFAULT_PLATFORM_MANAGED_PROVISIONING = {
|
|
|
37
38
|
}
|
|
38
39
|
SERVER_GENERATED_UUID_DEFAULT = "gen_random_uuid()"
|
|
39
40
|
DEFAULT_POSTGRES_SCHEMA = "public"
|
|
41
|
+
_TIME_INDEXED_CADENCE_RE = re.compile(
|
|
42
|
+
r"^[1-9][0-9]*(mo|s|m|h|d|w|q|y)$",
|
|
43
|
+
re.IGNORECASE,
|
|
44
|
+
)
|
|
40
45
|
_PLATFORM_MANAGED_MIGRATION_REGISTRATION_CONTEXT: contextvars.ContextVar[bool] = (
|
|
41
46
|
contextvars.ContextVar(
|
|
42
47
|
"mainsequence_platform_managed_migration_registration_context",
|
|
@@ -292,6 +297,7 @@ class PlatformTimeIndexMetaTable(PlatformManagedMetaTable):
|
|
|
292
297
|
"""
|
|
293
298
|
|
|
294
299
|
__time_index_meta_table__: ClassVar[TimeIndexMetaTable | None] = None
|
|
300
|
+
__cadence__: ClassVar[str | None] = None
|
|
295
301
|
|
|
296
302
|
__mapper_args__ = _sqlalchemy_declared_attr.directive(_time_index_mapper_args)
|
|
297
303
|
|
|
@@ -505,6 +511,7 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
505
511
|
model_or_table,
|
|
506
512
|
storage_layout=storage_layout,
|
|
507
513
|
)
|
|
514
|
+
resolved_cadence = _resolve_time_index_cadence(model_or_table)
|
|
508
515
|
columns = _iter_columns(table)
|
|
509
516
|
_validate_time_index_contract(
|
|
510
517
|
columns=columns,
|
|
@@ -520,6 +527,7 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
520
527
|
time_index_name=resolved_time_index_name,
|
|
521
528
|
index_names=resolved_index_names,
|
|
522
529
|
storage_layout=resolved_storage_layout,
|
|
530
|
+
cadence=resolved_cadence,
|
|
523
531
|
),
|
|
524
532
|
hash_namespace=resolved_hash_namespace,
|
|
525
533
|
extra_hash_components=resolved_extra_hash_components,
|
|
@@ -559,6 +567,7 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
559
567
|
labels=_resolve_labels(model_or_table, labels=labels),
|
|
560
568
|
provisioning=_resolve_provisioning(model_or_table, provisioning=provisioning),
|
|
561
569
|
time_index_name=resolved_time_index_name,
|
|
570
|
+
cadence=resolved_cadence,
|
|
562
571
|
table_contract={
|
|
563
572
|
"version": "relational-table.v1",
|
|
564
573
|
"table_kind": "time_indexed",
|
|
@@ -571,6 +580,7 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
571
580
|
"time_indexed": {
|
|
572
581
|
"time_index_name": resolved_time_index_name,
|
|
573
582
|
"index_names": resolved_index_names,
|
|
583
|
+
**({"cadence": resolved_cadence} if resolved_cadence else {}),
|
|
574
584
|
**(
|
|
575
585
|
{"storage_layout": dict(resolved_storage_layout)}
|
|
576
586
|
if resolved_storage_layout
|
|
@@ -1246,6 +1256,7 @@ def _configured_table_storage_identity(model_or_table: Any, *, table: Any) -> di
|
|
|
1246
1256
|
time_index_name=time_index_name,
|
|
1247
1257
|
index_names=index_names,
|
|
1248
1258
|
storage_layout=_resolve_time_index_storage_layout(model_or_table),
|
|
1259
|
+
cadence=_resolve_time_index_cadence(model_or_table),
|
|
1249
1260
|
)
|
|
1250
1261
|
|
|
1251
1262
|
|
|
@@ -1260,12 +1271,14 @@ def _time_index_table_storage_identity(
|
|
|
1260
1271
|
time_index_name: str,
|
|
1261
1272
|
index_names: Sequence[str],
|
|
1262
1273
|
storage_layout: Mapping[str, Any] | None,
|
|
1274
|
+
cadence: str | None,
|
|
1263
1275
|
) -> dict[str, Any]:
|
|
1264
1276
|
return _time_index_storage_identity(
|
|
1265
1277
|
table_storage_identity=_table_storage_identity(table),
|
|
1266
1278
|
time_index_name=time_index_name,
|
|
1267
1279
|
index_names=index_names,
|
|
1268
1280
|
storage_layout=storage_layout,
|
|
1281
|
+
cadence=cadence,
|
|
1269
1282
|
)
|
|
1270
1283
|
|
|
1271
1284
|
|
|
@@ -1275,12 +1288,14 @@ def _time_index_table_items_storage_identity(
|
|
|
1275
1288
|
time_index_name: str,
|
|
1276
1289
|
index_names: Sequence[str],
|
|
1277
1290
|
storage_layout: Mapping[str, Any] | None,
|
|
1291
|
+
cadence: str | None,
|
|
1278
1292
|
) -> dict[str, Any]:
|
|
1279
1293
|
return _time_index_storage_identity(
|
|
1280
1294
|
table_storage_identity=_table_items_storage_identity(table_items),
|
|
1281
1295
|
time_index_name=time_index_name,
|
|
1282
1296
|
index_names=index_names,
|
|
1283
1297
|
storage_layout=storage_layout,
|
|
1298
|
+
cadence=cadence,
|
|
1284
1299
|
)
|
|
1285
1300
|
|
|
1286
1301
|
|
|
@@ -1290,12 +1305,15 @@ def _time_index_storage_identity(
|
|
|
1290
1305
|
time_index_name: str,
|
|
1291
1306
|
index_names: Sequence[str],
|
|
1292
1307
|
storage_layout: Mapping[str, Any] | None,
|
|
1308
|
+
cadence: str | None,
|
|
1293
1309
|
) -> dict[str, Any]:
|
|
1294
1310
|
profile: dict[str, Any] = {
|
|
1295
1311
|
"kind": "time_indexed",
|
|
1296
1312
|
"time_index_name": str(time_index_name),
|
|
1297
1313
|
"index_names": [str(name) for name in index_names],
|
|
1298
1314
|
}
|
|
1315
|
+
if cadence:
|
|
1316
|
+
profile["cadence"] = cadence
|
|
1299
1317
|
if storage_layout:
|
|
1300
1318
|
profile["storage_layout"] = dict(storage_layout)
|
|
1301
1319
|
return {
|
|
@@ -1399,10 +1417,28 @@ def _resolve_time_index_storage_layout(
|
|
|
1399
1417
|
if not isinstance(resolved, Mapping):
|
|
1400
1418
|
raise ValueError(
|
|
1401
1419
|
"PlatformTimeIndexMetaTable storage_layout must be a mapping when provided."
|
|
1402
|
-
|
|
1420
|
+
)
|
|
1403
1421
|
return resolved
|
|
1404
1422
|
|
|
1405
1423
|
|
|
1424
|
+
def _resolve_time_index_cadence(model_or_table: Any) -> str | None:
|
|
1425
|
+
resolved = (
|
|
1426
|
+
getattr(model_or_table, "__cadence__", None)
|
|
1427
|
+
or getattr(model_or_table, "__dynamic_table_cadence__", None)
|
|
1428
|
+
)
|
|
1429
|
+
if resolved in (None, ""):
|
|
1430
|
+
return None
|
|
1431
|
+
normalized = str(resolved).strip().lower()
|
|
1432
|
+
if not normalized:
|
|
1433
|
+
return None
|
|
1434
|
+
if not _TIME_INDEXED_CADENCE_RE.fullmatch(normalized):
|
|
1435
|
+
raise ValueError(
|
|
1436
|
+
"PlatformTimeIndexMetaTable cadence must be an interval token such as "
|
|
1437
|
+
"1m, 5m, 1h, 1d, 1w, 1mo, 1q, or 1y."
|
|
1438
|
+
)
|
|
1439
|
+
return normalized
|
|
1440
|
+
|
|
1441
|
+
|
|
1406
1442
|
def _dynamic_table_info_value(model_or_table: Any, key: str) -> Any:
|
|
1407
1443
|
dynamic_table = _table_info_value(model_or_table, "dynamic_table")
|
|
1408
1444
|
if isinstance(dynamic_table, Mapping):
|
|
@@ -180,6 +180,7 @@ def _time_index_model_class(
|
|
|
180
180
|
identifier=None,
|
|
181
181
|
time_index_name="time_index",
|
|
182
182
|
index_names=None,
|
|
183
|
+
cadence=None,
|
|
183
184
|
):
|
|
184
185
|
attrs = _model_attrs(
|
|
185
186
|
name,
|
|
@@ -193,6 +194,8 @@ def _time_index_model_class(
|
|
|
193
194
|
"__index_names__": list(index_names or [time_index_name]),
|
|
194
195
|
}
|
|
195
196
|
)
|
|
197
|
+
if cadence is not None:
|
|
198
|
+
attrs["__cadence__"] = cadence
|
|
196
199
|
return type(
|
|
197
200
|
name,
|
|
198
201
|
(PlatformTimeIndexMetaTable,),
|
|
@@ -1247,6 +1250,78 @@ def test_time_index_meta_table_registration_request_uses_dynamic_contract():
|
|
|
1247
1250
|
assert "physical_index_plan" not in payload
|
|
1248
1251
|
|
|
1249
1252
|
|
|
1253
|
+
def test_time_index_meta_table_registration_request_uses_class_cadence():
|
|
1254
|
+
table = FakeTable(
|
|
1255
|
+
"example_assets__prices",
|
|
1256
|
+
columns=[
|
|
1257
|
+
FakeColumn("time_index", DateTime(timezone=True), nullable=False),
|
|
1258
|
+
FakeColumn("asset_uid", Uuid(), nullable=False),
|
|
1259
|
+
FakeColumn("price", String(64), nullable=False),
|
|
1260
|
+
],
|
|
1261
|
+
)
|
|
1262
|
+
DailyPrices = _time_index_model_class(
|
|
1263
|
+
"DailyPrices",
|
|
1264
|
+
table,
|
|
1265
|
+
index_names=["time_index", "asset_uid"],
|
|
1266
|
+
cadence="1D",
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
request = DailyPrices.build_registration_request(
|
|
1270
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
1271
|
+
)
|
|
1272
|
+
|
|
1273
|
+
assert request.cadence == "1d"
|
|
1274
|
+
assert request.table_contract["authoring"]["time_indexed"]["cadence"] == "1d"
|
|
1275
|
+
payload = request.model_dump(mode="json", exclude_none=True)
|
|
1276
|
+
assert payload["cadence"] == "1d"
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
def test_time_index_meta_table_cadence_changes_configured_storage_hash():
|
|
1280
|
+
table = FakeTable(
|
|
1281
|
+
"example_assets__prices",
|
|
1282
|
+
columns=[
|
|
1283
|
+
FakeColumn("time_index", DateTime(timezone=True), nullable=False),
|
|
1284
|
+
FakeColumn("asset_uid", Uuid(), nullable=False),
|
|
1285
|
+
FakeColumn("price", String(64), nullable=False),
|
|
1286
|
+
],
|
|
1287
|
+
)
|
|
1288
|
+
DailyPrices = _time_index_model_class(
|
|
1289
|
+
"DailyPrices",
|
|
1290
|
+
table,
|
|
1291
|
+
index_names=["time_index", "asset_uid"],
|
|
1292
|
+
cadence="1d",
|
|
1293
|
+
)
|
|
1294
|
+
IntradayPrices = _time_index_model_class(
|
|
1295
|
+
"IntradayPrices",
|
|
1296
|
+
table,
|
|
1297
|
+
index_names=["time_index", "asset_uid"],
|
|
1298
|
+
cadence="5m",
|
|
1299
|
+
)
|
|
1300
|
+
|
|
1301
|
+
assert _configured_storage_hash(DailyPrices) != _configured_storage_hash(IntradayPrices)
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
def test_time_index_meta_table_rejects_invalid_class_cadence():
|
|
1305
|
+
table = FakeTable(
|
|
1306
|
+
"example_assets__prices",
|
|
1307
|
+
columns=[
|
|
1308
|
+
FakeColumn("time_index", DateTime(timezone=True), nullable=False),
|
|
1309
|
+
FakeColumn("asset_uid", Uuid(), nullable=False),
|
|
1310
|
+
],
|
|
1311
|
+
)
|
|
1312
|
+
Prices = _time_index_model_class(
|
|
1313
|
+
"Prices",
|
|
1314
|
+
table,
|
|
1315
|
+
index_names=["time_index", "asset_uid"],
|
|
1316
|
+
cadence="daily",
|
|
1317
|
+
)
|
|
1318
|
+
|
|
1319
|
+
with pytest.raises(ValueError, match="cadence"):
|
|
1320
|
+
Prices.build_registration_request(
|
|
1321
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
1322
|
+
)
|
|
1323
|
+
|
|
1324
|
+
|
|
1250
1325
|
def test_time_index_meta_table_registration_request_uses_class_metatable_description():
|
|
1251
1326
|
table = FakeTable(
|
|
1252
1327
|
"placeholder",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/command_center/connections/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/dashboards/streamlit/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/app_component.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/command_center/workspace_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/local_paths.py
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/client/data_sources_interfaces/sqlite.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
File without changes
|
{mainsequence-4.3.5 → mainsequence-4.3.6}/mainsequence/meta_tables/data_nodes/run_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|