mainsequence 8.0.7__tar.gz → 8.1.0__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.0.7/mainsequence.egg-info → mainsequence-8.1.0}/PKG-INFO +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/agent_runtime_models.py +165 -13
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/base.py +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/metatables/core.py +0 -282
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/managers.py +0 -4
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/runner.py +30 -89
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/updaters.py +0 -19
- {mainsequence-8.0.7 → mainsequence-8.1.0/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence.egg-info/SOURCES.txt +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/pyproject.toml +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_code_repository_runtime_context.py +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_environment_response_contracts.py +0 -6
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_filter_normalization.py +211 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_update_configuration_client.py +27 -26
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_update_flow.py +0 -2
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_update_runner_uid_runtime.py +52 -67
- mainsequence-8.0.7/tests/test_scheduler_client_model.py +0 -172
- {mainsequence-8.0.7 → mainsequence-8.1.0}/LICENSE +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/MANIFEST.in +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/README.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/data_publishing/time_index_table_updates/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/__main__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/bootstrap.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/api.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/code_repository_status.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/config.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/cli/ui.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/client.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/models_user.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/observability.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/client/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/code_repository_context.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/code_repository_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/defaults.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/logconf.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_refs.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/dependencies.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/namespacing.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/meta_tables/time_index_table_updates/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/repository_identity_security.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/setup.cfg +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/fixtures/time_index_table_update_hash_v2_golden.json +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_auth_precedence.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_cli_migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_client.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_code_repository_batch_jobs_from_file.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_code_repository_branch_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_code_repository_documentation_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_code_repository_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_dependency_extras.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_environment_resource_context.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_instrumentation.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_local_ops.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_logconf.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_observability_client.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_schema_names.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_secret_client_model.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_ssh_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_access_dimension_audit.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_dimension_queries.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_update_configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_time_index_table_update_hard_cut.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_update_statistics.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.0}/tests/test_update_uid_guards.py +0 -0
|
@@ -36,6 +36,11 @@ STANDARD_A2A_OUTPUT_CONTRACT_METADATA_KEY = (
|
|
|
36
36
|
)
|
|
37
37
|
STANDARD_AGENT_INFERENCE_METADATA_KEY = "https://mainsequence.ai/a2a/extensions/agent-inference/v1"
|
|
38
38
|
MAX_INLINE_A2A_FILE_BYTES = 15 * 1024 * 1024
|
|
39
|
+
TRANSIENT_RUNTIME_INTERACTION_STATES = frozenset(
|
|
40
|
+
{"checking", "starting", "waking", "updating"}
|
|
41
|
+
)
|
|
42
|
+
MIN_RUNTIME_INTERACTION_RETRY_SECONDS = 0.5
|
|
43
|
+
MAX_RUNTIME_INTERACTION_RETRY_SECONDS = 30.0
|
|
39
44
|
|
|
40
45
|
|
|
41
46
|
class AgentSessionStatus(str, Enum):
|
|
@@ -229,6 +234,93 @@ class AgentRuntimePaths(BasePydanticModel):
|
|
|
229
234
|
pass
|
|
230
235
|
|
|
231
236
|
|
|
237
|
+
class AgentRuntimeInteractionNotice(BasePydanticModel):
|
|
238
|
+
model_config = ConfigDict(extra="allow")
|
|
239
|
+
|
|
240
|
+
code: str
|
|
241
|
+
severity: str
|
|
242
|
+
title: str
|
|
243
|
+
message: str
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class AgentRuntimeInteractionAction(BasePydanticModel):
|
|
247
|
+
model_config = ConfigDict(extra="allow")
|
|
248
|
+
|
|
249
|
+
type: Literal["update_agent_runtime", "retry_runtime_wake"]
|
|
250
|
+
label: str
|
|
251
|
+
confirmation_message: str
|
|
252
|
+
interaction_revision: str
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class AgentRuntimeInteractionOperation(BasePydanticModel):
|
|
256
|
+
model_config = ConfigDict(extra="allow")
|
|
257
|
+
|
|
258
|
+
uid: str
|
|
259
|
+
status: str
|
|
260
|
+
created_at: str
|
|
261
|
+
started_at: str | None = None
|
|
262
|
+
finished_at: str | None = None
|
|
263
|
+
support_reference: str
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class AgentRuntimeInteraction(BasePydanticModel):
|
|
267
|
+
model_config = ConfigDict(extra="allow")
|
|
268
|
+
|
|
269
|
+
state: Literal[
|
|
270
|
+
"ready",
|
|
271
|
+
"warning",
|
|
272
|
+
"checking",
|
|
273
|
+
"starting",
|
|
274
|
+
"waking",
|
|
275
|
+
"update_required",
|
|
276
|
+
"updating",
|
|
277
|
+
"update_failed",
|
|
278
|
+
"unavailable",
|
|
279
|
+
]
|
|
280
|
+
can_submit: bool
|
|
281
|
+
notice: AgentRuntimeInteractionNotice | None
|
|
282
|
+
action: AgentRuntimeInteractionAction | None
|
|
283
|
+
operation: AgentRuntimeInteractionOperation | None
|
|
284
|
+
retry_after_ms: int | None = Field(None, ge=0)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class AgentRuntimePresenceReplicas(BasePydanticModel):
|
|
288
|
+
desired: int | None = Field(None, ge=0)
|
|
289
|
+
actual: int | None = Field(None, ge=0)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class AgentRuntimePresenceWake(BasePydanticModel):
|
|
293
|
+
operation_uid: str
|
|
294
|
+
state: Literal[
|
|
295
|
+
"requested",
|
|
296
|
+
"in_progress",
|
|
297
|
+
"serving",
|
|
298
|
+
"failed",
|
|
299
|
+
"expired",
|
|
300
|
+
"superseded",
|
|
301
|
+
]
|
|
302
|
+
requested_at: str
|
|
303
|
+
deadline_at: str
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class AgentRuntimePresence(BasePydanticModel):
|
|
307
|
+
phase: Literal[
|
|
308
|
+
"not_deployed",
|
|
309
|
+
"observing",
|
|
310
|
+
"idle",
|
|
311
|
+
"provisioning",
|
|
312
|
+
"pulling_image",
|
|
313
|
+
"starting",
|
|
314
|
+
"serving",
|
|
315
|
+
"redeploying",
|
|
316
|
+
"failed",
|
|
317
|
+
]
|
|
318
|
+
replicas: AgentRuntimePresenceReplicas
|
|
319
|
+
detail: str
|
|
320
|
+
observed_at: str | None
|
|
321
|
+
wake: AgentRuntimePresenceWake | None
|
|
322
|
+
|
|
323
|
+
|
|
232
324
|
class AgentSessionRuntimeAccess(BasePydanticModel):
|
|
233
325
|
model_config = ConfigDict(extra="allow")
|
|
234
326
|
|
|
@@ -287,6 +379,16 @@ class AgentSessionRuntimeAccess(BasePydanticModel):
|
|
|
287
379
|
None,
|
|
288
380
|
description="Runtime image drift payload for the resolved coding-agent runtime.",
|
|
289
381
|
)
|
|
382
|
+
runtime_interaction: AgentRuntimeInteraction = Field(
|
|
383
|
+
...,
|
|
384
|
+
description=(
|
|
385
|
+
"Authoritative backend decision for whether a new runtime message may be submitted."
|
|
386
|
+
),
|
|
387
|
+
)
|
|
388
|
+
runtime_presence: AgentRuntimePresence = Field(
|
|
389
|
+
...,
|
|
390
|
+
description="Sanitized diagnostic runtime-presence evidence.",
|
|
391
|
+
)
|
|
290
392
|
|
|
291
393
|
@property
|
|
292
394
|
def image_drift_dict(self) -> dict[str, Any] | None:
|
|
@@ -313,6 +415,27 @@ def _join_runtime_url(rpc_url: str, runtime_path: str) -> str:
|
|
|
313
415
|
return f"{rpc_url.rstrip('/')}/{runtime_path.lstrip('/')}"
|
|
314
416
|
|
|
315
417
|
|
|
418
|
+
def _runtime_access_blocked_message(access: AgentSessionRuntimeAccess) -> str:
|
|
419
|
+
notice = access.runtime_interaction.notice
|
|
420
|
+
if notice is not None and notice.message.strip():
|
|
421
|
+
return notice.message.strip()
|
|
422
|
+
if access.detail and access.detail.strip():
|
|
423
|
+
return f"Coding-agent runtime access is unavailable. {access.detail.strip()}"
|
|
424
|
+
return "Coding-agent runtime access is unavailable."
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _runtime_access_retry_seconds(access: AgentSessionRuntimeAccess) -> float:
|
|
428
|
+
retry_after_ms = access.runtime_interaction.retry_after_ms
|
|
429
|
+
if retry_after_ms is None:
|
|
430
|
+
raise ApiError(
|
|
431
|
+
"Transient runtime access response is missing runtime_interaction.retry_after_ms."
|
|
432
|
+
)
|
|
433
|
+
return min(
|
|
434
|
+
MAX_RUNTIME_INTERACTION_RETRY_SECONDS,
|
|
435
|
+
max(MIN_RUNTIME_INTERACTION_RETRY_SECONDS, retry_after_ms / 1000.0),
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
|
|
316
439
|
class Agent(
|
|
317
440
|
OwnerLogMixin,
|
|
318
441
|
OwnerResourceUsageMixin,
|
|
@@ -481,6 +604,22 @@ class Agent(
|
|
|
481
604
|
raise TypeError("Agent runtime access response must be a JSON object")
|
|
482
605
|
return AgentSessionRuntimeAccess(**data)
|
|
483
606
|
|
|
607
|
+
def _resolve_runtime_access_for_message_send(
|
|
608
|
+
self,
|
|
609
|
+
*,
|
|
610
|
+
timeout=None,
|
|
611
|
+
) -> AgentSessionRuntimeAccess:
|
|
612
|
+
"""Wait only while an active backend-owned transition blocks submission."""
|
|
613
|
+
|
|
614
|
+
while True:
|
|
615
|
+
access = self.resolve_runtime_access(timeout=timeout)
|
|
616
|
+
interaction = access.runtime_interaction
|
|
617
|
+
if interaction.can_submit:
|
|
618
|
+
return access
|
|
619
|
+
if interaction.state not in TRANSIENT_RUNTIME_INTERACTION_STATES:
|
|
620
|
+
raise ApiError(_runtime_access_blocked_message(access))
|
|
621
|
+
time.sleep(_runtime_access_retry_seconds(access))
|
|
622
|
+
|
|
484
623
|
def _build_response_body(
|
|
485
624
|
self,
|
|
486
625
|
*,
|
|
@@ -601,7 +740,7 @@ class Agent(
|
|
|
601
740
|
timeout_seconds=timeout_seconds,
|
|
602
741
|
)
|
|
603
742
|
response = self._post_response(
|
|
604
|
-
access=self.
|
|
743
|
+
access=self._resolve_runtime_access_for_message_send(timeout=timeout),
|
|
605
744
|
body=body,
|
|
606
745
|
stream=False,
|
|
607
746
|
timeout=timeout,
|
|
@@ -643,7 +782,7 @@ class Agent(
|
|
|
643
782
|
timeout_seconds=timeout_seconds,
|
|
644
783
|
)
|
|
645
784
|
response = self._post_response(
|
|
646
|
-
access=self.
|
|
785
|
+
access=self._resolve_runtime_access_for_message_send(timeout=timeout),
|
|
647
786
|
body=body,
|
|
648
787
|
stream=True,
|
|
649
788
|
timeout=timeout,
|
|
@@ -1303,14 +1442,25 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1303
1442
|
timeout=None,
|
|
1304
1443
|
) -> AgentSessionRuntimeAccess:
|
|
1305
1444
|
session_uid = cls._resolve_agent_session_uid(agent_session)
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1445
|
+
access = cls.get_cached_runtime_access(session_uid)
|
|
1446
|
+
while True:
|
|
1447
|
+
if access is None:
|
|
1448
|
+
access = cls.resolve_runtime_access(
|
|
1449
|
+
session_uid,
|
|
1450
|
+
cache=True,
|
|
1451
|
+
timeout=timeout,
|
|
1452
|
+
)
|
|
1453
|
+
interaction = access.runtime_interaction
|
|
1454
|
+
if interaction.can_submit:
|
|
1455
|
+
return access
|
|
1456
|
+
if interaction.state not in TRANSIENT_RUNTIME_INTERACTION_STATES:
|
|
1457
|
+
raise ApiError(_runtime_access_blocked_message(access))
|
|
1458
|
+
time.sleep(_runtime_access_retry_seconds(access))
|
|
1459
|
+
access = cls.resolve_runtime_access(
|
|
1460
|
+
session_uid,
|
|
1461
|
+
cache=True,
|
|
1462
|
+
timeout=timeout,
|
|
1463
|
+
)
|
|
1314
1464
|
|
|
1315
1465
|
@staticmethod
|
|
1316
1466
|
def _build_standard_a2a_message_send_body(
|
|
@@ -1513,9 +1663,8 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1513
1663
|
)
|
|
1514
1664
|
if response.status_code in (401, 403):
|
|
1515
1665
|
cls.clear_cached_runtime_access(session_uid)
|
|
1516
|
-
access = cls.
|
|
1666
|
+
access = cls._resolve_runtime_access_for_message_send(
|
|
1517
1667
|
session_uid,
|
|
1518
|
-
cache=True,
|
|
1519
1668
|
timeout=timeout,
|
|
1520
1669
|
)
|
|
1521
1670
|
response = cls._post_standard_a2a_message(
|
|
@@ -1594,7 +1743,10 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1594
1743
|
response = request(access)
|
|
1595
1744
|
if response.status_code in (401, 403):
|
|
1596
1745
|
cls.clear_cached_runtime_access(session_uid)
|
|
1597
|
-
access = cls.
|
|
1746
|
+
access = cls._resolve_runtime_access_for_message_send(
|
|
1747
|
+
session_uid,
|
|
1748
|
+
timeout=timeout,
|
|
1749
|
+
)
|
|
1598
1750
|
response = request(access)
|
|
1599
1751
|
if response.status_code in (401, 403):
|
|
1600
1752
|
cls.clear_cached_runtime_access(session_uid)
|
|
@@ -134,7 +134,6 @@ class BaseObjectOrm:
|
|
|
134
134
|
END_POINTS = {
|
|
135
135
|
"User": "users",
|
|
136
136
|
# TDAG
|
|
137
|
-
"Scheduler": "schedulers",
|
|
138
137
|
"TimeIndexMetaTable": "time-index-meta-tables",
|
|
139
138
|
"TimeIndexTableUpdate": "time-index-table-updates",
|
|
140
139
|
"TimeIndexTableUpdateDetails": "time-index-table-update-details",
|
|
@@ -9,7 +9,6 @@ import json
|
|
|
9
9
|
import math
|
|
10
10
|
import os
|
|
11
11
|
import re
|
|
12
|
-
import time
|
|
13
12
|
from collections.abc import Callable, Mapping, Sequence
|
|
14
13
|
from dataclasses import dataclass, field
|
|
15
14
|
from typing import Any, ClassVar, Literal, TypedDict
|
|
@@ -57,11 +56,8 @@ from ..dtype_codec import (
|
|
|
57
56
|
)
|
|
58
57
|
from ..exceptions import raise_for_response
|
|
59
58
|
from ..utils import (
|
|
60
|
-
TDAG_CONSTANTS,
|
|
61
59
|
DateInfo,
|
|
62
60
|
bios_uuid,
|
|
63
|
-
get_network_ip,
|
|
64
|
-
is_process_running,
|
|
65
61
|
make_request,
|
|
66
62
|
serialize_to_json,
|
|
67
63
|
)
|
|
@@ -2114,10 +2110,6 @@ class AlreadyExist(Exception):
|
|
|
2114
2110
|
pass
|
|
2115
2111
|
|
|
2116
2112
|
|
|
2117
|
-
class SchedulerDoesNotExist(Exception):
|
|
2118
|
-
pass
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
2113
|
class TimeIndexTableUpdateDoesNotExist(Exception):
|
|
2122
2114
|
pass
|
|
2123
2115
|
|
|
@@ -2532,7 +2524,6 @@ class TimeIndexTableUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
2532
2524
|
table_updater_source_code_git_hash: str | None = None
|
|
2533
2525
|
table_updater_source_code: str | None = None
|
|
2534
2526
|
update_details: TimeIndexTableUpdateDetails | None = None
|
|
2535
|
-
run_configuration: TimeIndexTableUpdateConfiguration | None = None
|
|
2536
2527
|
|
|
2537
2528
|
@property
|
|
2538
2529
|
def data_source_uid(self):
|
|
@@ -2618,7 +2609,6 @@ class TimeIndexTableUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
2618
2609
|
index_min=index_min,
|
|
2619
2610
|
multi_index_column_stats=result.get("multi_index_column_stats"),
|
|
2620
2611
|
),
|
|
2621
|
-
must_update=result["must_update"],
|
|
2622
2612
|
direct_dependency_uids=result.get("direct_dependency_uids"),
|
|
2623
2613
|
)
|
|
2624
2614
|
return table_update_run
|
|
@@ -3136,31 +3126,6 @@ class TimeIndexTableUpdate(TableUpdateNode, BaseObjectOrm):
|
|
|
3136
3126
|
)
|
|
3137
3127
|
return table_update
|
|
3138
3128
|
|
|
3139
|
-
def get_update_time_to_wait(self):
|
|
3140
|
-
next_update = self.update_details.next_update
|
|
3141
|
-
time_to_wait = 0.0
|
|
3142
|
-
if next_update is not None:
|
|
3143
|
-
time_to_wait = (
|
|
3144
|
-
pd.to_datetime(next_update) - datetime.datetime.now(datetime.UTC)
|
|
3145
|
-
).total_seconds()
|
|
3146
|
-
time_to_wait = max(0, time_to_wait)
|
|
3147
|
-
return time_to_wait, next_update
|
|
3148
|
-
|
|
3149
|
-
def wait_for_update_time(
|
|
3150
|
-
self,
|
|
3151
|
-
):
|
|
3152
|
-
if self.update_details.error_on_last_update or self.update_details.last_update is None:
|
|
3153
|
-
return None
|
|
3154
|
-
|
|
3155
|
-
time_to_wait, next_update = self.get_update_time_to_wait()
|
|
3156
|
-
if time_to_wait > 0:
|
|
3157
|
-
logger.info(f"Scheduler Waiting for ts update time at {next_update} {time_to_wait}")
|
|
3158
|
-
time.sleep(time_to_wait)
|
|
3159
|
-
else:
|
|
3160
|
-
time_to_wait = max(0, 60 - datetime.datetime.now(datetime.UTC).second)
|
|
3161
|
-
logger.info("Scheduler Waiting for ts update at start of minute")
|
|
3162
|
-
time.sleep(time_to_wait)
|
|
3163
|
-
|
|
3164
3129
|
|
|
3165
3130
|
class BaseUpdateDetails:
|
|
3166
3131
|
active_update: bool = Field(default=False, description="Flag to indicate if update is active")
|
|
@@ -3169,16 +3134,12 @@ class BaseUpdateDetails:
|
|
|
3169
3134
|
default=False, description="Flag to indicate if there was an error in the last update"
|
|
3170
3135
|
)
|
|
3171
3136
|
last_update: datetime.datetime | None = Field(None, description="Timestamp of the last update")
|
|
3172
|
-
next_update: datetime.datetime | None = Field(None, description="Timestamp of the next update")
|
|
3173
3137
|
update_statistics: dict[str, Any] | None = Field(
|
|
3174
3138
|
None, description="JSON field for update statistics"
|
|
3175
3139
|
)
|
|
3176
3140
|
active_update_status: str = Field(
|
|
3177
3141
|
default="Q", max_length=20, description="Current update status"
|
|
3178
3142
|
)
|
|
3179
|
-
active_update_scheduler_uid: str | None = Field(
|
|
3180
|
-
None, description="UID reference to the scheduler for active update"
|
|
3181
|
-
)
|
|
3182
3143
|
update_priority: int = Field(default=0, description="Priority level of the update")
|
|
3183
3144
|
last_updated_by_user_uid: str | None = Field(
|
|
3184
3145
|
None, description="UID reference to the user that last updated this record"
|
|
@@ -3189,7 +3150,6 @@ class TimeIndexTableUpdateDetails(BaseUpdateDetails, BasePydanticModel, BaseObje
|
|
|
3189
3150
|
table_update_uid: str | None = Field(
|
|
3190
3151
|
None, description="Public uid of the related TimeIndexTableUpdate"
|
|
3191
3152
|
)
|
|
3192
|
-
run_configuration: TimeIndexTableUpdateConfiguration | None = None
|
|
3193
3153
|
|
|
3194
3154
|
@classmethod
|
|
3195
3155
|
def patch_for_table_update_uid(
|
|
@@ -3843,242 +3803,6 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
3843
3803
|
)
|
|
3844
3804
|
|
|
3845
3805
|
|
|
3846
|
-
class ScheduledUpdateNode(BasePydanticModel):
|
|
3847
|
-
"""Strict read projection for an update node attached to a Scheduler.
|
|
3848
|
-
|
|
3849
|
-
Scheduler endpoints return a flattened polymorphic node projection with
|
|
3850
|
-
output-table identity. This is intentionally separate from
|
|
3851
|
-
:class:`TableUpdateNode`, which models the full update graph resource.
|
|
3852
|
-
"""
|
|
3853
|
-
|
|
3854
|
-
model_config = ConfigDict(extra="forbid")
|
|
3855
|
-
|
|
3856
|
-
uid: str = Field(..., description="Public uid of the scheduled update node")
|
|
3857
|
-
node_type: str = Field(..., min_length=1, description="Scheduled-node discriminator")
|
|
3858
|
-
update_hash: str | None = Field(
|
|
3859
|
-
...,
|
|
3860
|
-
max_length=63,
|
|
3861
|
-
description="Update identity when exposed by the concrete scheduled node",
|
|
3862
|
-
)
|
|
3863
|
-
build_configuration: dict[str, Any] | None = Field(
|
|
3864
|
-
...,
|
|
3865
|
-
description="Serialized build configuration exposed by the concrete node",
|
|
3866
|
-
)
|
|
3867
|
-
output_table_uid: str | None = Field(
|
|
3868
|
-
...,
|
|
3869
|
-
description="Canonical public uid of the scheduled node's output table",
|
|
3870
|
-
)
|
|
3871
|
-
output_table_type: str | None = Field(
|
|
3872
|
-
...,
|
|
3873
|
-
description="Output-table discriminator exposed by the backend",
|
|
3874
|
-
)
|
|
3875
|
-
output_table_physical_table_name: str | None = Field(
|
|
3876
|
-
...,
|
|
3877
|
-
description="Physical table name of the scheduled node's output",
|
|
3878
|
-
)
|
|
3879
|
-
output_table_identifier: str | None = Field(
|
|
3880
|
-
...,
|
|
3881
|
-
description="Published identifier of the scheduled node's output table",
|
|
3882
|
-
)
|
|
3883
|
-
output_table_data_source_uid: str | None = Field(
|
|
3884
|
-
...,
|
|
3885
|
-
description="Canonical data-source uid of the scheduled node's output table",
|
|
3886
|
-
)
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
class Scheduler(BasePydanticModel, BaseObjectOrm):
|
|
3890
|
-
uid: str | None = Field(None, description="Public uid of this scheduler")
|
|
3891
|
-
organization_environment_uid: str = Field(
|
|
3892
|
-
...,
|
|
3893
|
-
description=(
|
|
3894
|
-
"Read-only public UID of the Organization Environment derived by the "
|
|
3895
|
-
"backend from the scheduler's update nodes."
|
|
3896
|
-
),
|
|
3897
|
-
)
|
|
3898
|
-
name: str
|
|
3899
|
-
is_running: bool
|
|
3900
|
-
running_process_pid: int | None
|
|
3901
|
-
running_in_debug_mode: bool
|
|
3902
|
-
updates_halted: bool
|
|
3903
|
-
host: str | None
|
|
3904
|
-
api_address: str | None
|
|
3905
|
-
api_port: int | None
|
|
3906
|
-
last_heart_beat: datetime.datetime | None = None
|
|
3907
|
-
pre_loads_in_tree: list[ScheduledUpdateNode] | None = None
|
|
3908
|
-
in_active_tree: list[ScheduledUpdateNode] | None = None
|
|
3909
|
-
schedules_to: list[ScheduledUpdateNode] | None = None
|
|
3910
|
-
# for heartbeat
|
|
3911
|
-
_stop_heart_beat: bool = False
|
|
3912
|
-
_executor: object | None = None
|
|
3913
|
-
|
|
3914
|
-
def _public_uid(self) -> str:
|
|
3915
|
-
return _require_public_uid(self, "Scheduler")
|
|
3916
|
-
|
|
3917
|
-
@classmethod
|
|
3918
|
-
def get_scheduler_for_update_node(cls, update_node_uid: str):
|
|
3919
|
-
"""GET the scheduler assigned to a TimeIndexTableUpdate uid."""
|
|
3920
|
-
if update_node_uid in (None, ""):
|
|
3921
|
-
raise ValueError("update_node_uid is required.")
|
|
3922
|
-
s = cls.build_session()
|
|
3923
|
-
url = cls.get_object_url() + "/for-update-node/"
|
|
3924
|
-
r = make_request(
|
|
3925
|
-
s=s,
|
|
3926
|
-
r_type="GET",
|
|
3927
|
-
url=url,
|
|
3928
|
-
payload={"params": {"update_node_uid": str(update_node_uid)}},
|
|
3929
|
-
loaders=cls.LOADERS,
|
|
3930
|
-
)
|
|
3931
|
-
if r.status_code == 404:
|
|
3932
|
-
raise SchedulerDoesNotExist(r.json().get("detail", r.text))
|
|
3933
|
-
r.raise_for_status()
|
|
3934
|
-
return cls(**r.json())
|
|
3935
|
-
|
|
3936
|
-
@classmethod
|
|
3937
|
-
def build_and_assign_to_update_nodes(
|
|
3938
|
-
cls,
|
|
3939
|
-
scheduler_name: str,
|
|
3940
|
-
update_node_uids: list[str],
|
|
3941
|
-
delink_all_ts: bool = False,
|
|
3942
|
-
remove_from_other_schedulers: bool = True,
|
|
3943
|
-
timeout=None,
|
|
3944
|
-
**kwargs,
|
|
3945
|
-
):
|
|
3946
|
-
"""
|
|
3947
|
-
POST /schedulers/build-and-assign-to-update-nodes/
|
|
3948
|
-
body: {
|
|
3949
|
-
scheduler_name, update_node_uids, delink_all_ts?,
|
|
3950
|
-
remove_from_other_schedulers?, scheduler_kwargs?
|
|
3951
|
-
}
|
|
3952
|
-
"""
|
|
3953
|
-
s = cls.build_session()
|
|
3954
|
-
url = cls.get_object_url() + "/build-and-assign-to-update-nodes/"
|
|
3955
|
-
request_body = {
|
|
3956
|
-
"scheduler_name": scheduler_name,
|
|
3957
|
-
"delink_all_update_nodes": delink_all_ts,
|
|
3958
|
-
"remove_from_other_schedulers": remove_from_other_schedulers,
|
|
3959
|
-
"scheduler_kwargs": kwargs or {},
|
|
3960
|
-
}
|
|
3961
|
-
request_body["update_node_uids"] = [str(uid) for uid in update_node_uids]
|
|
3962
|
-
payload = {"json": request_body}
|
|
3963
|
-
r = make_request(
|
|
3964
|
-
s=s, r_type="POST", url=url, payload=payload, time_out=timeout, loaders=cls.LOADERS
|
|
3965
|
-
)
|
|
3966
|
-
if r.status_code not in [200, 201]:
|
|
3967
|
-
r.raise_for_status()
|
|
3968
|
-
return cls(**r.json())
|
|
3969
|
-
|
|
3970
|
-
def in_active_tree_connect(self, update_node_uids: list[str]):
|
|
3971
|
-
"""
|
|
3972
|
-
PATCH /schedulers/{uid}/in-active-tree/
|
|
3973
|
-
body: { update_node_uids }
|
|
3974
|
-
"""
|
|
3975
|
-
s = self.build_session()
|
|
3976
|
-
url = f"{self.get_object_url()}/{self._public_uid()}/in-active-tree/"
|
|
3977
|
-
r = make_request(
|
|
3978
|
-
s=s,
|
|
3979
|
-
r_type="PATCH",
|
|
3980
|
-
url=url,
|
|
3981
|
-
payload={"json": {"update_node_uids": [str(uid) for uid in update_node_uids]}},
|
|
3982
|
-
loaders=self.LOADERS,
|
|
3983
|
-
)
|
|
3984
|
-
if r.status_code not in (200, 204):
|
|
3985
|
-
raise Exception(f"Error in request {r.text}")
|
|
3986
|
-
|
|
3987
|
-
def assign_to_scheduler(self, update_node_uids: list[str]):
|
|
3988
|
-
"""
|
|
3989
|
-
PATCH /schedulers/{uid}/assign/
|
|
3990
|
-
body: { update_node_uids }
|
|
3991
|
-
"""
|
|
3992
|
-
s = self.build_session()
|
|
3993
|
-
url = f"{self.get_object_url()}/{self._public_uid()}/assign/"
|
|
3994
|
-
r = make_request(
|
|
3995
|
-
s=s,
|
|
3996
|
-
r_type="PATCH",
|
|
3997
|
-
url=url,
|
|
3998
|
-
payload={"json": {"update_node_uids": [str(uid) for uid in update_node_uids]}},
|
|
3999
|
-
loaders=self.LOADERS,
|
|
4000
|
-
)
|
|
4001
|
-
r.raise_for_status()
|
|
4002
|
-
return Scheduler(**r.json())
|
|
4003
|
-
|
|
4004
|
-
def is_scheduler_running_in_process(self):
|
|
4005
|
-
# test call
|
|
4006
|
-
if self.is_running and hasattr(self, "api_address"):
|
|
4007
|
-
# verify scheduler host is the same
|
|
4008
|
-
if self.api_address == get_network_ip() and is_process_running(
|
|
4009
|
-
self.running_process_pid
|
|
4010
|
-
):
|
|
4011
|
-
return True
|
|
4012
|
-
return False
|
|
4013
|
-
|
|
4014
|
-
def _heart_beat_patch(self):
|
|
4015
|
-
try:
|
|
4016
|
-
scheduler = self.patch(
|
|
4017
|
-
is_running=True,
|
|
4018
|
-
running_process_pid=os.getpid(),
|
|
4019
|
-
running_in_debug_mode=self.running_in_debug_mode,
|
|
4020
|
-
last_heart_beat=datetime.datetime.now(datetime.UTC).timestamp(),
|
|
4021
|
-
)
|
|
4022
|
-
for field, value in scheduler.__dict__.items():
|
|
4023
|
-
setattr(self, field, value)
|
|
4024
|
-
except Exception as e:
|
|
4025
|
-
logger.error(e)
|
|
4026
|
-
|
|
4027
|
-
def _heartbeat_runner(self, run_interval):
|
|
4028
|
-
"""
|
|
4029
|
-
Runs forever (until the main thread ends),
|
|
4030
|
-
calling _scheduler_heart_beat_patch every 30 seconds.
|
|
4031
|
-
"""
|
|
4032
|
-
logger.debug("Heartbeat thread started with interval = %d seconds", run_interval)
|
|
4033
|
-
|
|
4034
|
-
while True:
|
|
4035
|
-
self._heart_beat_patch()
|
|
4036
|
-
# Sleep in a loop so that if we ever decide to
|
|
4037
|
-
# add a cancellation event, we can check it in smaller intervals
|
|
4038
|
-
for _ in range(run_interval):
|
|
4039
|
-
# could check for a stop event here if not daemon
|
|
4040
|
-
if self._stop_heart_beat:
|
|
4041
|
-
return
|
|
4042
|
-
time.sleep(1)
|
|
4043
|
-
|
|
4044
|
-
def start_heart_beat(self):
|
|
4045
|
-
from concurrent.futures import ThreadPoolExecutor
|
|
4046
|
-
|
|
4047
|
-
if self._executor is None:
|
|
4048
|
-
self._executor = ThreadPoolExecutor(max_workers=1)
|
|
4049
|
-
run_interval = TDAG_CONSTANTS.SCHEDULER_HEART_BEAT_FREQUENCY_SECONDS
|
|
4050
|
-
self._heartbeat_future = self._executor.submit(self._heartbeat_runner, run_interval)
|
|
4051
|
-
|
|
4052
|
-
def stop_heart_beat(self):
|
|
4053
|
-
"""
|
|
4054
|
-
Stop the heartbeat gracefully.
|
|
4055
|
-
"""
|
|
4056
|
-
# Signal the runner loop to exit
|
|
4057
|
-
self._stop_heart_beat = True
|
|
4058
|
-
|
|
4059
|
-
# Optionally wait for the future to complete
|
|
4060
|
-
if hasattr(self, "heartbeat_future") and self._heartbeat_future:
|
|
4061
|
-
logger.info("Waiting for the heartbeat thread to finish...")
|
|
4062
|
-
self._heartbeat_future.result() # or .cancel() if you prefer
|
|
4063
|
-
|
|
4064
|
-
# Shut down the executor if no longer needed
|
|
4065
|
-
if self._executor:
|
|
4066
|
-
self._executor.shutdown(wait=True)
|
|
4067
|
-
self._executor = None
|
|
4068
|
-
|
|
4069
|
-
logger.info("Heartbeat thread stopped.")
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
class TimeIndexTableUpdateConfiguration(BasePydanticModel, BaseObjectOrm):
|
|
4073
|
-
update_schedule: str = "*/1 * * * *"
|
|
4074
|
-
table_update_details_uid: str | None = None
|
|
4075
|
-
|
|
4076
|
-
@classmethod
|
|
4077
|
-
@property
|
|
4078
|
-
def ROOT_URL(cls):
|
|
4079
|
-
return None
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
3806
|
class BaseUpdateStatistics(BaseModel):
|
|
4083
3807
|
"""
|
|
4084
3808
|
Backend-agnostic update-state envelope shared by updater types.
|
|
@@ -4886,7 +4610,6 @@ class _TableUpdateRunFields:
|
|
|
4886
4610
|
)
|
|
4887
4611
|
# extra fields for local control
|
|
4888
4612
|
update_statistics: BaseUpdateStatistics | None = None
|
|
4889
|
-
must_update: bool | None = None
|
|
4890
4613
|
direct_dependency_uids: list[str] | None = None
|
|
4891
4614
|
|
|
4892
4615
|
|
|
@@ -5104,7 +4827,6 @@ def get_session_data_source() -> DataSource:
|
|
|
5104
4827
|
|
|
5105
4828
|
TimeIndexTableUpdateDetails.model_rebuild()
|
|
5106
4829
|
TimeIndexTableUpdate.model_rebuild()
|
|
5107
|
-
TimeIndexTableUpdateConfiguration.model_rebuild()
|
|
5108
4830
|
TimeIndexedProfile.model_rebuild()
|
|
5109
4831
|
TimeIndexMetaTable.model_rebuild()
|
|
5110
4832
|
DataSource.model_rebuild()
|
|
@@ -5163,10 +4885,6 @@ __all__ = [
|
|
|
5163
4885
|
"MetaTableStatementPayload",
|
|
5164
4886
|
"MetaTableValidateContractRequest",
|
|
5165
4887
|
"PodDataSource",
|
|
5166
|
-
"TimeIndexTableUpdateConfiguration",
|
|
5167
|
-
"ScheduledUpdateNode",
|
|
5168
|
-
"Scheduler",
|
|
5169
|
-
"SchedulerDoesNotExist",
|
|
5170
4888
|
"SessionDataSource",
|
|
5171
4889
|
"SQLITE",
|
|
5172
4890
|
"COMPILED_SQL_V1",
|
|
@@ -621,10 +621,6 @@ class BaseTimeIndexTableUpdateManager:
|
|
|
621
621
|
def update_details(self) -> Any | None:
|
|
622
622
|
return self.table_update.update_details
|
|
623
623
|
|
|
624
|
-
@property
|
|
625
|
-
def run_configuration(self) -> dict | None:
|
|
626
|
-
return self.table_update.run_configuration
|
|
627
|
-
|
|
628
624
|
@property
|
|
629
625
|
def time_indexed_profile(self) -> Any | None:
|
|
630
626
|
return self._get_time_indexed_profile()
|