mainsequence 8.1.20.dev4__tar.gz → 8.1.20.dev8__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-8.1.20.dev4/mainsequence.egg-info → mainsequence-8.1.20.dev8}/PKG-INFO +1 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/cli.py +6 -2
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/base.py +14 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/metatables/core.py +61 -95
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/models_foundry.py +0 -29
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence.egg-info/SOURCES.txt +1 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/pyproject.toml +1 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_canonical_api_route_contract.py +18 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_cli.py +34 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_branch_contract.py +6 -2
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_runtime_context.py +7 -3
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_environment_response_contracts.py +5 -5
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_filter_normalization.py +66 -35
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_observability_client.py +6 -5
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_source_table_configuration.py +14 -8
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_update_configuration_client.py +6 -3
- mainsequence-8.1.20.dev8/tests/test_tolerant_response_reading.py +137 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_update_uid_guards.py +0 -1
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/LICENSE +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/MANIFEST.in +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/README.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/data_publishing/time_index_table_updates/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/__main__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/bootstrap.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/api.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/code_repository_status.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/config.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/cli/ui.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/client.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/github_issues.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/models_user.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/observability.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/code_repository_context.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/code_repository_skills.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/defaults.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/logconf.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_refs.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/__init__.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/configuration.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/dependencies.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/managers.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/namespacing.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/runner.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/updaters.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/meta_tables/time_index_table_updates/utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/repository_identity_security.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/setup.cfg +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/fixtures/time_index_table_update_hash_v2_golden.json +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_auth_precedence.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_cli_migrations.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_client.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_batch_jobs_from_file.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_documentation_contract.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_skills.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_dependency_extras.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_dev_release_version.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_environment_resource_context.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_github_issues_client.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_instrumentation.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_local_ops.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_logconf.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_schema_names.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_secret_client_model.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_ssh_utils.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_access_dimension_audit.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_dimension_queries.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_update_configuration.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_update_flow.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_time_index_table_update_hard_cut.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_update_statistics.py +0 -0
|
@@ -5912,7 +5912,9 @@ def _meta_table_run_query_impl(
|
|
|
5912
5912
|
try:
|
|
5913
5913
|
payload = run_meta_table_query(meta_table_uid, sql, timeout=timeout)
|
|
5914
5914
|
except ApiError as e:
|
|
5915
|
-
|
|
5915
|
+
# The api layer already names the operation in the message; prefixing again
|
|
5916
|
+
# produces "MetaTable query failed: MetaTable query failed: ...".
|
|
5917
|
+
error(str(e))
|
|
5916
5918
|
raise typer.Exit(1) from e
|
|
5917
5919
|
|
|
5918
5920
|
ok = bool(payload.get("ok"))
|
|
@@ -5941,7 +5943,9 @@ def _time_index_table_run_query_impl(
|
|
|
5941
5943
|
try:
|
|
5942
5944
|
payload = run_time_index_table_query(table_uid, sql, timeout=timeout)
|
|
5943
5945
|
except ApiError as e:
|
|
5944
|
-
|
|
5946
|
+
# The api layer already names the operation in the message; prefixing again
|
|
5947
|
+
# produces "Time-index table query failed: Time-index table query failed: ...".
|
|
5948
|
+
error(str(e))
|
|
5945
5949
|
raise typer.Exit(1) from e
|
|
5946
5950
|
|
|
5947
5951
|
ok = bool(payload.get("ok"))
|
|
@@ -58,7 +58,9 @@ class HtmlSaveException(Exception):
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
class BasePydanticModel(BaseModel):
|
|
61
|
-
|
|
61
|
+
# Tolerant reading (ADR 0032): a backend response that carries a field this
|
|
62
|
+
# SDK release does not declare parses, and the undeclared field is dropped.
|
|
63
|
+
model_config = ConfigDict(extra="ignore")
|
|
62
64
|
orm_class: str = None # This will be set to the class that inherits
|
|
63
65
|
|
|
64
66
|
@classmethod
|
|
@@ -780,10 +782,21 @@ class BaseObjectOrm:
|
|
|
780
782
|
|
|
781
783
|
return raw_key
|
|
782
784
|
|
|
785
|
+
def _declares_field(obj, field_name: str) -> bool:
|
|
786
|
+
if not isinstance(obj, BaseModel):
|
|
787
|
+
return True
|
|
788
|
+
if field_name in type(obj).model_fields:
|
|
789
|
+
return True
|
|
790
|
+
# Tolerant reading (ADR 0032): a model that keeps undeclared keys can
|
|
791
|
+
# take them; every other model drops them, as parsing does.
|
|
792
|
+
return type(obj).model_config.get("extra") == "allow"
|
|
793
|
+
|
|
783
794
|
def recursive_update(obj, update_dict, path=()):
|
|
784
795
|
for k, v in update_dict.items():
|
|
785
796
|
current_path = (*path, k)
|
|
786
797
|
field_name = _resolve_model_field_name(obj, k)
|
|
798
|
+
if not _declares_field(obj, field_name):
|
|
799
|
+
continue
|
|
787
800
|
# Get the existing nested object, defaulting to None if it doesn't exist
|
|
788
801
|
nested_obj = getattr(obj, field_name, None)
|
|
789
802
|
|
{mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/mainsequence/client/metatables/core.py
RENAMED
|
@@ -1904,35 +1904,18 @@ class MetaTable(
|
|
|
1904
1904
|
|
|
1905
1905
|
cls = type(self)
|
|
1906
1906
|
url = f"{cls.get_object_url().rstrip('/')}/{self._public_uid()}/run-query/"
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
)
|
|
1920
|
-
finally:
|
|
1921
|
-
if old_content_type is None:
|
|
1922
|
-
session.headers.pop("Content-Type", None)
|
|
1923
|
-
else:
|
|
1924
|
-
session.headers["Content-Type"] = old_content_type
|
|
1925
|
-
error_payload = {"data": sql}
|
|
1926
|
-
else:
|
|
1927
|
-
response = make_request(
|
|
1928
|
-
s=cls.build_session(),
|
|
1929
|
-
loaders=cls.LOADERS,
|
|
1930
|
-
r_type="POST",
|
|
1931
|
-
url=url,
|
|
1932
|
-
payload={"json": sql},
|
|
1933
|
-
time_out=timeout,
|
|
1934
|
-
)
|
|
1935
|
-
error_payload = {"json": sql}
|
|
1907
|
+
# The backend reads the request body as the raw SQL string and parses it with
|
|
1908
|
+
# DRF's JSON parser, for every MetaTable endpoint. Sending a JSON string is the
|
|
1909
|
+
# only transport it accepts; a text/plain body is rejected with HTTP 415.
|
|
1910
|
+
payload = {"json": sql}
|
|
1911
|
+
response = make_request(
|
|
1912
|
+
s=cls.build_session(),
|
|
1913
|
+
loaders=cls.LOADERS,
|
|
1914
|
+
r_type="POST",
|
|
1915
|
+
url=url,
|
|
1916
|
+
payload=payload,
|
|
1917
|
+
time_out=timeout,
|
|
1918
|
+
)
|
|
1936
1919
|
|
|
1937
1920
|
try:
|
|
1938
1921
|
data = response.json()
|
|
@@ -1942,57 +1925,9 @@ class MetaTable(
|
|
|
1942
1925
|
if isinstance(data, dict) and "ok" in data:
|
|
1943
1926
|
return data
|
|
1944
1927
|
|
|
1945
|
-
raise_for_response(response, payload=
|
|
1928
|
+
raise_for_response(response, payload=payload)
|
|
1946
1929
|
return response.json()
|
|
1947
1930
|
|
|
1948
|
-
@classmethod
|
|
1949
|
-
def description_search(
|
|
1950
|
-
cls,
|
|
1951
|
-
q: str,
|
|
1952
|
-
*,
|
|
1953
|
-
q_embedding: Sequence[float] | None = None,
|
|
1954
|
-
trigram_k: int = 200,
|
|
1955
|
-
embed_k: int = 200,
|
|
1956
|
-
w_trgm: float = 0.65,
|
|
1957
|
-
w_emb: float = 0.35,
|
|
1958
|
-
embedding_model: str = "default",
|
|
1959
|
-
**filters,
|
|
1960
|
-
):
|
|
1961
|
-
q = (q or "").strip()
|
|
1962
|
-
if not q:
|
|
1963
|
-
raise ValueError("q is required")
|
|
1964
|
-
operation = f"{cls.__name__}.description_search"
|
|
1965
|
-
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
1966
|
-
|
|
1967
|
-
body = {
|
|
1968
|
-
"q": q,
|
|
1969
|
-
"trigram_k": trigram_k,
|
|
1970
|
-
"embed_k": embed_k,
|
|
1971
|
-
"w_trgm": w_trgm,
|
|
1972
|
-
"w_emb": w_emb,
|
|
1973
|
-
"embedding_model": embedding_model,
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
if q_embedding is not None:
|
|
1977
|
-
body["q_embedding"] = [float(x) for x in q_embedding]
|
|
1978
|
-
|
|
1979
|
-
if filters:
|
|
1980
|
-
body.update(filters)
|
|
1981
|
-
body.update(cls._sdk_owned_query_context(operation))
|
|
1982
|
-
|
|
1983
|
-
payload = {"params": serialize_to_json(body)}
|
|
1984
|
-
response = make_request(
|
|
1985
|
-
s=cls.build_session(),
|
|
1986
|
-
loaders=cls.LOADERS,
|
|
1987
|
-
r_type="GET",
|
|
1988
|
-
url=f"{cls.get_object_url().rstrip('/')}/description-search/",
|
|
1989
|
-
payload=payload,
|
|
1990
|
-
)
|
|
1991
|
-
if response.status_code != 200:
|
|
1992
|
-
raise_for_response(response, payload=payload)
|
|
1993
|
-
|
|
1994
|
-
return cls._deserialize_search_response(response.json())
|
|
1995
|
-
|
|
1996
1931
|
@classmethod
|
|
1997
1932
|
def column_search(cls, q: str, **filters):
|
|
1998
1933
|
q = (q or "").strip()
|
|
@@ -2768,23 +2703,6 @@ class TimeIndexTableUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
2768
2703
|
return None
|
|
2769
2704
|
return r.json()
|
|
2770
2705
|
|
|
2771
|
-
def verify_if_direct_dependencies_are_updated(self) -> dict:
|
|
2772
|
-
"""
|
|
2773
|
-
Response({
|
|
2774
|
-
"error_on_update_dependencies": False,
|
|
2775
|
-
"updated": all_success,
|
|
2776
|
-
})
|
|
2777
|
-
"""
|
|
2778
|
-
s = self.build_session()
|
|
2779
|
-
url = (
|
|
2780
|
-
self.get_object_url()
|
|
2781
|
-
+ f"/{self._public_uid()}/verify-if-direct-dependencies-are-updated/"
|
|
2782
|
-
)
|
|
2783
|
-
r = make_request(s=s, loaders=None, r_type="GET", url=url)
|
|
2784
|
-
if r.status_code != 200:
|
|
2785
|
-
raise Exception(f"Error in request: {r.text}")
|
|
2786
|
-
return r.json()
|
|
2787
|
-
|
|
2788
2706
|
def get_data_between_dates_from_api(self, *args, **kwargs):
|
|
2789
2707
|
return self.output_table.get_data_between_dates_from_api(*args, **kwargs)
|
|
2790
2708
|
|
|
@@ -3259,6 +3177,54 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
3259
3177
|
)
|
|
3260
3178
|
return self
|
|
3261
3179
|
|
|
3180
|
+
@classmethod
|
|
3181
|
+
def description_search(
|
|
3182
|
+
cls,
|
|
3183
|
+
q: str,
|
|
3184
|
+
*,
|
|
3185
|
+
q_embedding: Sequence[float] | None = None,
|
|
3186
|
+
trigram_k: int = 200,
|
|
3187
|
+
embed_k: int = 200,
|
|
3188
|
+
w_trgm: float = 0.65,
|
|
3189
|
+
w_emb: float = 0.35,
|
|
3190
|
+
embedding_model: str = "default",
|
|
3191
|
+
**filters,
|
|
3192
|
+
):
|
|
3193
|
+
q = (q or "").strip()
|
|
3194
|
+
if not q:
|
|
3195
|
+
raise ValueError("q is required")
|
|
3196
|
+
operation = f"{cls.__name__}.description_search"
|
|
3197
|
+
cls._reject_sdk_context_overrides(filters, operation=operation)
|
|
3198
|
+
|
|
3199
|
+
body = {
|
|
3200
|
+
"q": q,
|
|
3201
|
+
"trigram_k": trigram_k,
|
|
3202
|
+
"embed_k": embed_k,
|
|
3203
|
+
"w_trgm": w_trgm,
|
|
3204
|
+
"w_emb": w_emb,
|
|
3205
|
+
"embedding_model": embedding_model,
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
if q_embedding is not None:
|
|
3209
|
+
body["q_embedding"] = [float(x) for x in q_embedding]
|
|
3210
|
+
|
|
3211
|
+
if filters:
|
|
3212
|
+
body.update(filters)
|
|
3213
|
+
body.update(cls._sdk_owned_query_context(operation))
|
|
3214
|
+
|
|
3215
|
+
payload = {"params": serialize_to_json(body)}
|
|
3216
|
+
response = make_request(
|
|
3217
|
+
s=cls.build_session(),
|
|
3218
|
+
loaders=cls.LOADERS,
|
|
3219
|
+
r_type="GET",
|
|
3220
|
+
url=f"{cls.get_object_url().rstrip('/')}/description-search/",
|
|
3221
|
+
payload=payload,
|
|
3222
|
+
)
|
|
3223
|
+
if response.status_code != 200:
|
|
3224
|
+
raise_for_response(response, payload=payload)
|
|
3225
|
+
|
|
3226
|
+
return cls._deserialize_search_response(response.json())
|
|
3227
|
+
|
|
3262
3228
|
def _time_indexed_storage_layout(self) -> dict[str, Any]:
|
|
3263
3229
|
profile = self.time_indexed_profile
|
|
3264
3230
|
if profile is not None and isinstance(profile.storage_layout, Mapping):
|
|
@@ -697,13 +697,6 @@ class GitHubRepositoryBranchList(BasePydanticModel):
|
|
|
697
697
|
results: list[GitHubRepositoryBranch]
|
|
698
698
|
|
|
699
699
|
|
|
700
|
-
class GitHubRepositoryBranchImportResult(BasePydanticModel):
|
|
701
|
-
repository_uid: str
|
|
702
|
-
code_repository_branch_uid: str
|
|
703
|
-
repository_branch: str
|
|
704
|
-
is_initialized: bool
|
|
705
|
-
|
|
706
|
-
|
|
707
700
|
class GitHubRepositoryBinding(BasePydanticModel, BaseObjectOrm):
|
|
708
701
|
FILTERSET_FIELDS: ClassVar[dict[str, list[str]]] = {
|
|
709
702
|
"uid": ["in", "exact"],
|
|
@@ -731,28 +724,6 @@ class GitHubRepositoryBinding(BasePydanticModel, BaseObjectOrm):
|
|
|
731
724
|
raise_for_response(r)
|
|
732
725
|
return GitHubRepositoryBranchList.model_validate(r.json())
|
|
733
726
|
|
|
734
|
-
def import_branch(
|
|
735
|
-
self,
|
|
736
|
-
*,
|
|
737
|
-
repository_branch: str,
|
|
738
|
-
metatables_data_source_uid: str | None = None,
|
|
739
|
-
timeout=None,
|
|
740
|
-
) -> GitHubRepositoryBranchImportResult:
|
|
741
|
-
body: dict[str, Any] = {"repository_branch": repository_branch}
|
|
742
|
-
if metatables_data_source_uid is not None:
|
|
743
|
-
body["metatables_data_source_uid"] = str(metatables_data_source_uid)
|
|
744
|
-
url = f"{type(self).get_object_url()}/{self._public_detail_reference()}/import-branch/"
|
|
745
|
-
r = make_request(
|
|
746
|
-
s=type(self).build_session(),
|
|
747
|
-
loaders=type(self).LOADERS,
|
|
748
|
-
r_type="POST",
|
|
749
|
-
url=url,
|
|
750
|
-
payload={"json": body},
|
|
751
|
-
time_out=timeout,
|
|
752
|
-
)
|
|
753
|
-
raise_for_response(r)
|
|
754
|
-
return GitHubRepositoryBranchImportResult.model_validate(r.json())
|
|
755
|
-
|
|
756
727
|
|
|
757
728
|
class CodeRepositoryImageSourceProvenance(BasePydanticModel):
|
|
758
729
|
verification_state: Literal["verified", "unverified", "building", "failed"]
|
|
@@ -133,6 +133,7 @@ tests/test_time_index_table_update_configuration.py
|
|
|
133
133
|
tests/test_time_index_table_update_configuration_client.py
|
|
134
134
|
tests/test_time_index_table_update_flow.py
|
|
135
135
|
tests/test_time_index_table_update_hard_cut.py
|
|
136
|
+
tests/test_tolerant_response_reading.py
|
|
136
137
|
tests/test_update_runner_uid_runtime.py
|
|
137
138
|
tests/test_update_statistics.py
|
|
138
139
|
tests/test_update_uid_guards.py
|
{mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_canonical_api_route_contract.py
RENAMED
|
@@ -129,11 +129,18 @@ def test_client_auth_provider_routes_match_backend_contract(monkeypatch):
|
|
|
129
129
|
|
|
130
130
|
def test_removed_backend_actions_are_not_exposed_by_client_models():
|
|
131
131
|
from mainsequence.client.metatables import DataSource, TimeIndexTableUpdate
|
|
132
|
+
from mainsequence.client.models_foundry import GitHubRepositoryBinding
|
|
132
133
|
from mainsequence.client.models_helpers import Job
|
|
133
134
|
|
|
134
135
|
removed_actions = {
|
|
135
136
|
DataSource: ("get_or_create_sqlite", "create_sqlite"),
|
|
136
|
-
TimeIndexTableUpdate: (
|
|
137
|
+
TimeIndexTableUpdate: (
|
|
138
|
+
"add_tags",
|
|
139
|
+
"filter_by_hash_id",
|
|
140
|
+
"get_upstream_nodes",
|
|
141
|
+
"verify_if_direct_dependencies_are_updated",
|
|
142
|
+
),
|
|
143
|
+
GitHubRepositoryBinding: ("import_branch",),
|
|
137
144
|
Job: ("bulk_get_or_create", "create_from_configuration", "sync_jobs"),
|
|
138
145
|
}
|
|
139
146
|
violations = [
|
|
@@ -146,6 +153,16 @@ def test_removed_backend_actions_are_not_exposed_by_client_models():
|
|
|
146
153
|
assert violations == []
|
|
147
154
|
|
|
148
155
|
|
|
156
|
+
def test_description_search_is_defined_only_where_the_backend_serves_it():
|
|
157
|
+
"""`description-search/` is served for time-index-meta-tables only."""
|
|
158
|
+
from mainsequence.client.metatables import MetaTable, TimeIndexMetaTable
|
|
159
|
+
|
|
160
|
+
assert "description_search" not in vars(MetaTable)
|
|
161
|
+
assert not hasattr(MetaTable, "description_search")
|
|
162
|
+
assert "description_search" in vars(TimeIndexMetaTable)
|
|
163
|
+
assert TimeIndexMetaTable.get_object_url().endswith("/time-index-meta-tables")
|
|
164
|
+
|
|
165
|
+
|
|
149
166
|
def test_cli_does_not_reintroduce_legacy_numeric_detail_routes_or_agent_runs():
|
|
150
167
|
from mainsequence.cli import api
|
|
151
168
|
from mainsequence.client import agent_runtime_models
|
|
@@ -8197,6 +8197,40 @@ def test_time_index_table_run_query(cli_mod, runner, monkeypatch):
|
|
|
8197
8197
|
assert '"value": 1' in result.output
|
|
8198
8198
|
|
|
8199
8199
|
|
|
8200
|
+
def test_time_index_table_run_query_error_is_not_double_prefixed(cli_mod, runner, monkeypatch):
|
|
8201
|
+
"""The api layer already names the operation; the CLI must not prefix it again."""
|
|
8202
|
+
monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
|
|
8203
|
+
|
|
8204
|
+
def _run_query(storage_uid, sql, *, timeout=None):
|
|
8205
|
+
raise cli_mod.ApiError(
|
|
8206
|
+
"Time-index table query failed: 415 POST "
|
|
8207
|
+
"http://127.0.0.1:8000/api/v1/time-index-meta-tables/abc/run-query/: "
|
|
8208
|
+
'Unsupported media type "text/plain" in request.'
|
|
8209
|
+
)
|
|
8210
|
+
|
|
8211
|
+
monkeypatch.setattr(cli_mod, "run_time_index_table_query", _run_query)
|
|
8212
|
+
|
|
8213
|
+
result = runner.invoke(
|
|
8214
|
+
cli_mod.app,
|
|
8215
|
+
["time-index-table", "run_query", "abc", "SELECT 1 AS value"],
|
|
8216
|
+
)
|
|
8217
|
+
assert result.exit_code == 1
|
|
8218
|
+
assert result.output.count("Time-index table query failed:") == 1
|
|
8219
|
+
|
|
8220
|
+
|
|
8221
|
+
def test_meta_table_run_query_error_is_not_double_prefixed(cli_mod, runner, monkeypatch):
|
|
8222
|
+
monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
|
|
8223
|
+
|
|
8224
|
+
def _run_query(meta_table_uid, sql, *, timeout=None):
|
|
8225
|
+
raise cli_mod.ApiError("MetaTable query failed: boom")
|
|
8226
|
+
|
|
8227
|
+
monkeypatch.setattr(cli_mod, "run_meta_table_query", _run_query)
|
|
8228
|
+
|
|
8229
|
+
result = runner.invoke(cli_mod.app, ["meta-table", "run_query", "abc", "SELECT 1"])
|
|
8230
|
+
assert result.exit_code == 1
|
|
8231
|
+
assert result.output.count("MetaTable query failed:") == 1
|
|
8232
|
+
|
|
8233
|
+
|
|
8200
8234
|
def test_meta_table_run_query(cli_mod, runner, monkeypatch):
|
|
8201
8235
|
captured = {}
|
|
8202
8236
|
monkeypatch.setattr(cli_mod, "_require_login", lambda: {"username": "u"})
|
{mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_branch_contract.py
RENAMED
|
@@ -82,11 +82,15 @@ def test_code_repository_is_only_the_logical_aggregate_contract():
|
|
|
82
82
|
assert not hasattr(code_repository.branches[0], "code_repository_type")
|
|
83
83
|
assert not hasattr(code_repository, "repository_branch")
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
# Tolerant reading (ADR 0032): the retired field is dropped, not declared.
|
|
86
|
+
assert "repository_branch" not in models_foundry.CodeRepository.model_fields
|
|
87
|
+
assert not hasattr(
|
|
86
88
|
models_foundry.CodeRepository(
|
|
87
89
|
**logical_code_repository_payload(),
|
|
88
90
|
repository_branch="main",
|
|
89
|
-
)
|
|
91
|
+
),
|
|
92
|
+
"repository_branch",
|
|
93
|
+
)
|
|
90
94
|
|
|
91
95
|
|
|
92
96
|
def test_code_repository_requires_canonical_type_and_derived_technology():
|
{mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_code_repository_runtime_context.py
RENAMED
|
@@ -722,7 +722,7 @@ def test_metatable_request_always_submits_git_resolved_context(monkeypatch):
|
|
|
722
722
|
}
|
|
723
723
|
|
|
724
724
|
|
|
725
|
-
def
|
|
725
|
+
def test_code_repository_model_drops_removed_default_data_source_fields():
|
|
726
726
|
payload = {
|
|
727
727
|
"uid": CODE_REPOSITORY_UID,
|
|
728
728
|
"code_repository_name": "Example CodeRepository",
|
|
@@ -737,5 +737,9 @@ def test_code_repository_model_rejects_removed_default_data_source_fields():
|
|
|
737
737
|
"default_metatables_data_source_uid": DATA_SOURCE_UID,
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
-
|
|
741
|
-
|
|
740
|
+
# Tolerant reading (ADR 0032): the retired field is dropped, not declared.
|
|
741
|
+
assert "default_metatables_data_source_uid" not in models_foundry.CodeRepository.model_fields
|
|
742
|
+
assert not hasattr(
|
|
743
|
+
models_foundry.CodeRepository.model_validate(payload),
|
|
744
|
+
"default_metatables_data_source_uid",
|
|
745
|
+
)
|
{mainsequence-8.1.20.dev4 → mainsequence-8.1.20.dev8}/tests/test_environment_response_contracts.py
RENAMED
|
@@ -87,13 +87,13 @@ RUNTIME_UPDATE_FIELD = "runtime_update"
|
|
|
87
87
|
),
|
|
88
88
|
],
|
|
89
89
|
)
|
|
90
|
-
def
|
|
90
|
+
def test_backend_environment_response_contract_is_declared_by_sdk_model(
|
|
91
91
|
serializer_names,
|
|
92
92
|
model,
|
|
93
93
|
expected_fields,
|
|
94
94
|
):
|
|
95
95
|
"""Keep SDK response models aligned with ADR-0036/ADR-0037 projections."""
|
|
96
|
-
assert model.model_config.get("extra") == "
|
|
96
|
+
assert model.model_config.get("extra") == "ignore", serializer_names
|
|
97
97
|
assert expected_fields <= set(model.model_fields), serializer_names
|
|
98
98
|
|
|
99
99
|
|
|
@@ -106,13 +106,13 @@ def test_backend_environment_response_contract_is_declared_by_strict_sdk_model(
|
|
|
106
106
|
helper_models.ResourceRelease,
|
|
107
107
|
],
|
|
108
108
|
)
|
|
109
|
-
def
|
|
110
|
-
assert model.model_config.get("extra") == "
|
|
109
|
+
def test_owner_observability_code_repositoryion_is_declared_by_sdk_model(model):
|
|
110
|
+
assert model.model_config.get("extra") == "ignore"
|
|
111
111
|
assert "observability" in model.model_fields
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
def test_agent_session_runtime_capabilities_code_repositoryion_is_declared_and_typed():
|
|
115
|
-
assert agent_models.AgentSession.model_config.get("extra") == "
|
|
115
|
+
assert agent_models.AgentSession.model_config.get("extra") == "ignore"
|
|
116
116
|
assert (
|
|
117
117
|
agent_models.AgentSession.model_fields["runtime_capabilities"].annotation == dict[str, str]
|
|
118
118
|
)
|