mainsequence 8.0.7__tar.gz → 8.1.1__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.1}/PKG-INFO +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/agent_runtime_models.py +192 -13
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/base.py +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/metatables/core.py +0 -282
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/managers.py +0 -4
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/runner.py +30 -89
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/updaters.py +0 -19
- {mainsequence-8.0.7 → mainsequence-8.1.1/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence.egg-info/SOURCES.txt +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/pyproject.toml +1 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_code_repository_runtime_context.py +0 -1
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_environment_response_contracts.py +3 -8
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_filter_normalization.py +345 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_observability_client.py +5 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_update_configuration_client.py +27 -26
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_update_flow.py +0 -2
- {mainsequence-8.0.7 → mainsequence-8.1.1}/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.1}/LICENSE +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/MANIFEST.in +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/README.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/data_publishing/time_index_table_updates/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/__main__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/bootstrap.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/api.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/code_repository_status.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/config.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/cli/ui.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/client.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/models_user.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/observability.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/client/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/code_repository_context.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/code_repository_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/defaults.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/logconf.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_refs.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/__init__.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/dependencies.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/namespacing.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/meta_tables/time_index_table_updates/utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/repository_identity_security.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/setup.cfg +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/fixtures/time_index_table_update_hash_v2_golden.json +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_auth_precedence.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_cli.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_cli_migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_client.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_code_repository_batch_jobs_from_file.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_code_repository_branch_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_code_repository_documentation_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_code_repository_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_dependency_extras.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_environment_resource_context.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_instrumentation.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_local_ops.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_logconf.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_schema_names.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_secret_client_model.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_ssh_utils.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_access_dimension_audit.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_dimension_queries.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_update_configuration.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_time_index_table_update_hard_cut.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/tests/test_update_statistics.py +0 -0
- {mainsequence-8.0.7 → mainsequence-8.1.1}/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):
|
|
@@ -117,6 +122,23 @@ class AgentHarnessProtocol(str, Enum):
|
|
|
117
122
|
TAU_SESSION_V1 = "tau-session-v1"
|
|
118
123
|
|
|
119
124
|
|
|
125
|
+
class AgentRuntimeUpdateRemediation(BasePydanticModel):
|
|
126
|
+
tool: Literal["agent.update_runtime"]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class AgentRuntimeUpdate(BasePydanticModel):
|
|
130
|
+
state: Literal[
|
|
131
|
+
"current",
|
|
132
|
+
"update_required",
|
|
133
|
+
"updating",
|
|
134
|
+
"update_failed",
|
|
135
|
+
"not_deployed",
|
|
136
|
+
"unknown",
|
|
137
|
+
]
|
|
138
|
+
needs_redeploy: bool | None
|
|
139
|
+
remediation: AgentRuntimeUpdateRemediation | None
|
|
140
|
+
|
|
141
|
+
|
|
120
142
|
class AgentSemanticSearchResult(BasePydanticModel):
|
|
121
143
|
uid: str = Field(..., description="Public UID of the matched agent.")
|
|
122
144
|
name: str = Field(..., description="Human-readable display name of the matched agent.")
|
|
@@ -145,6 +167,10 @@ class AgentSemanticSearchResult(BasePydanticModel):
|
|
|
145
167
|
...,
|
|
146
168
|
description="Name of the Organization Environment that scoped the search result.",
|
|
147
169
|
)
|
|
170
|
+
runtime_update: AgentRuntimeUpdate = Field(
|
|
171
|
+
...,
|
|
172
|
+
description="Backend-owned runtime-currency state for the matched Agent.",
|
|
173
|
+
)
|
|
148
174
|
semantic_score: float = Field(
|
|
149
175
|
...,
|
|
150
176
|
description="Vector similarity component of the semantic-search ranking.",
|
|
@@ -229,6 +255,93 @@ class AgentRuntimePaths(BasePydanticModel):
|
|
|
229
255
|
pass
|
|
230
256
|
|
|
231
257
|
|
|
258
|
+
class AgentRuntimeInteractionNotice(BasePydanticModel):
|
|
259
|
+
model_config = ConfigDict(extra="allow")
|
|
260
|
+
|
|
261
|
+
code: str
|
|
262
|
+
severity: str
|
|
263
|
+
title: str
|
|
264
|
+
message: str
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class AgentRuntimeInteractionAction(BasePydanticModel):
|
|
268
|
+
model_config = ConfigDict(extra="allow")
|
|
269
|
+
|
|
270
|
+
type: Literal["update_agent_runtime", "retry_runtime_wake"]
|
|
271
|
+
label: str
|
|
272
|
+
confirmation_message: str
|
|
273
|
+
interaction_revision: str
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class AgentRuntimeInteractionOperation(BasePydanticModel):
|
|
277
|
+
model_config = ConfigDict(extra="allow")
|
|
278
|
+
|
|
279
|
+
uid: str
|
|
280
|
+
status: str
|
|
281
|
+
created_at: str
|
|
282
|
+
started_at: str | None = None
|
|
283
|
+
finished_at: str | None = None
|
|
284
|
+
support_reference: str
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class AgentRuntimeInteraction(BasePydanticModel):
|
|
288
|
+
model_config = ConfigDict(extra="allow")
|
|
289
|
+
|
|
290
|
+
state: Literal[
|
|
291
|
+
"ready",
|
|
292
|
+
"warning",
|
|
293
|
+
"checking",
|
|
294
|
+
"starting",
|
|
295
|
+
"waking",
|
|
296
|
+
"update_required",
|
|
297
|
+
"updating",
|
|
298
|
+
"update_failed",
|
|
299
|
+
"unavailable",
|
|
300
|
+
]
|
|
301
|
+
can_submit: bool
|
|
302
|
+
notice: AgentRuntimeInteractionNotice | None
|
|
303
|
+
action: AgentRuntimeInteractionAction | None
|
|
304
|
+
operation: AgentRuntimeInteractionOperation | None
|
|
305
|
+
retry_after_ms: int | None = Field(None, ge=0)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class AgentRuntimePresenceReplicas(BasePydanticModel):
|
|
309
|
+
desired: int | None = Field(None, ge=0)
|
|
310
|
+
actual: int | None = Field(None, ge=0)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class AgentRuntimePresenceWake(BasePydanticModel):
|
|
314
|
+
operation_uid: str
|
|
315
|
+
state: Literal[
|
|
316
|
+
"requested",
|
|
317
|
+
"in_progress",
|
|
318
|
+
"serving",
|
|
319
|
+
"failed",
|
|
320
|
+
"expired",
|
|
321
|
+
"superseded",
|
|
322
|
+
]
|
|
323
|
+
requested_at: str
|
|
324
|
+
deadline_at: str
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
class AgentRuntimePresence(BasePydanticModel):
|
|
328
|
+
phase: Literal[
|
|
329
|
+
"not_deployed",
|
|
330
|
+
"observing",
|
|
331
|
+
"idle",
|
|
332
|
+
"provisioning",
|
|
333
|
+
"pulling_image",
|
|
334
|
+
"starting",
|
|
335
|
+
"serving",
|
|
336
|
+
"redeploying",
|
|
337
|
+
"failed",
|
|
338
|
+
]
|
|
339
|
+
replicas: AgentRuntimePresenceReplicas
|
|
340
|
+
detail: str
|
|
341
|
+
observed_at: str | None
|
|
342
|
+
wake: AgentRuntimePresenceWake | None
|
|
343
|
+
|
|
344
|
+
|
|
232
345
|
class AgentSessionRuntimeAccess(BasePydanticModel):
|
|
233
346
|
model_config = ConfigDict(extra="allow")
|
|
234
347
|
|
|
@@ -287,6 +400,16 @@ class AgentSessionRuntimeAccess(BasePydanticModel):
|
|
|
287
400
|
None,
|
|
288
401
|
description="Runtime image drift payload for the resolved coding-agent runtime.",
|
|
289
402
|
)
|
|
403
|
+
runtime_interaction: AgentRuntimeInteraction = Field(
|
|
404
|
+
...,
|
|
405
|
+
description=(
|
|
406
|
+
"Authoritative backend decision for whether a new runtime message may be submitted."
|
|
407
|
+
),
|
|
408
|
+
)
|
|
409
|
+
runtime_presence: AgentRuntimePresence = Field(
|
|
410
|
+
...,
|
|
411
|
+
description="Sanitized diagnostic runtime-presence evidence.",
|
|
412
|
+
)
|
|
290
413
|
|
|
291
414
|
@property
|
|
292
415
|
def image_drift_dict(self) -> dict[str, Any] | None:
|
|
@@ -313,6 +436,27 @@ def _join_runtime_url(rpc_url: str, runtime_path: str) -> str:
|
|
|
313
436
|
return f"{rpc_url.rstrip('/')}/{runtime_path.lstrip('/')}"
|
|
314
437
|
|
|
315
438
|
|
|
439
|
+
def _runtime_access_blocked_message(access: AgentSessionRuntimeAccess) -> str:
|
|
440
|
+
notice = access.runtime_interaction.notice
|
|
441
|
+
if notice is not None and notice.message.strip():
|
|
442
|
+
return notice.message.strip()
|
|
443
|
+
if access.detail and access.detail.strip():
|
|
444
|
+
return f"Coding-agent runtime access is unavailable. {access.detail.strip()}"
|
|
445
|
+
return "Coding-agent runtime access is unavailable."
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _runtime_access_retry_seconds(access: AgentSessionRuntimeAccess) -> float:
|
|
449
|
+
retry_after_ms = access.runtime_interaction.retry_after_ms
|
|
450
|
+
if retry_after_ms is None:
|
|
451
|
+
raise ApiError(
|
|
452
|
+
"Transient runtime access response is missing runtime_interaction.retry_after_ms."
|
|
453
|
+
)
|
|
454
|
+
return min(
|
|
455
|
+
MAX_RUNTIME_INTERACTION_RETRY_SECONDS,
|
|
456
|
+
max(MIN_RUNTIME_INTERACTION_RETRY_SECONDS, retry_after_ms / 1000.0),
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
|
|
316
460
|
class Agent(
|
|
317
461
|
OwnerLogMixin,
|
|
318
462
|
OwnerResourceUsageMixin,
|
|
@@ -427,6 +571,10 @@ class Agent(
|
|
|
427
571
|
"CodeRepositoryBranch, or null when the Agent is not environment-scoped."
|
|
428
572
|
),
|
|
429
573
|
)
|
|
574
|
+
runtime_update: AgentRuntimeUpdate = Field(
|
|
575
|
+
...,
|
|
576
|
+
description="Read-only backend-owned runtime-currency state for this Agent.",
|
|
577
|
+
)
|
|
430
578
|
observability: ObservabilityLinks | None = Field(
|
|
431
579
|
default=None,
|
|
432
580
|
description="Backend-owned runtime observability and related-resource capabilities.",
|
|
@@ -481,6 +629,22 @@ class Agent(
|
|
|
481
629
|
raise TypeError("Agent runtime access response must be a JSON object")
|
|
482
630
|
return AgentSessionRuntimeAccess(**data)
|
|
483
631
|
|
|
632
|
+
def _resolve_runtime_access_for_message_send(
|
|
633
|
+
self,
|
|
634
|
+
*,
|
|
635
|
+
timeout=None,
|
|
636
|
+
) -> AgentSessionRuntimeAccess:
|
|
637
|
+
"""Wait only while an active backend-owned transition blocks submission."""
|
|
638
|
+
|
|
639
|
+
while True:
|
|
640
|
+
access = self.resolve_runtime_access(timeout=timeout)
|
|
641
|
+
interaction = access.runtime_interaction
|
|
642
|
+
if interaction.can_submit:
|
|
643
|
+
return access
|
|
644
|
+
if interaction.state not in TRANSIENT_RUNTIME_INTERACTION_STATES:
|
|
645
|
+
raise ApiError(_runtime_access_blocked_message(access))
|
|
646
|
+
time.sleep(_runtime_access_retry_seconds(access))
|
|
647
|
+
|
|
484
648
|
def _build_response_body(
|
|
485
649
|
self,
|
|
486
650
|
*,
|
|
@@ -601,7 +765,7 @@ class Agent(
|
|
|
601
765
|
timeout_seconds=timeout_seconds,
|
|
602
766
|
)
|
|
603
767
|
response = self._post_response(
|
|
604
|
-
access=self.
|
|
768
|
+
access=self._resolve_runtime_access_for_message_send(timeout=timeout),
|
|
605
769
|
body=body,
|
|
606
770
|
stream=False,
|
|
607
771
|
timeout=timeout,
|
|
@@ -643,7 +807,7 @@ class Agent(
|
|
|
643
807
|
timeout_seconds=timeout_seconds,
|
|
644
808
|
)
|
|
645
809
|
response = self._post_response(
|
|
646
|
-
access=self.
|
|
810
|
+
access=self._resolve_runtime_access_for_message_send(timeout=timeout),
|
|
647
811
|
body=body,
|
|
648
812
|
stream=True,
|
|
649
813
|
timeout=timeout,
|
|
@@ -1303,14 +1467,25 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1303
1467
|
timeout=None,
|
|
1304
1468
|
) -> AgentSessionRuntimeAccess:
|
|
1305
1469
|
session_uid = cls._resolve_agent_session_uid(agent_session)
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1470
|
+
access = cls.get_cached_runtime_access(session_uid)
|
|
1471
|
+
while True:
|
|
1472
|
+
if access is None:
|
|
1473
|
+
access = cls.resolve_runtime_access(
|
|
1474
|
+
session_uid,
|
|
1475
|
+
cache=True,
|
|
1476
|
+
timeout=timeout,
|
|
1477
|
+
)
|
|
1478
|
+
interaction = access.runtime_interaction
|
|
1479
|
+
if interaction.can_submit:
|
|
1480
|
+
return access
|
|
1481
|
+
if interaction.state not in TRANSIENT_RUNTIME_INTERACTION_STATES:
|
|
1482
|
+
raise ApiError(_runtime_access_blocked_message(access))
|
|
1483
|
+
time.sleep(_runtime_access_retry_seconds(access))
|
|
1484
|
+
access = cls.resolve_runtime_access(
|
|
1485
|
+
session_uid,
|
|
1486
|
+
cache=True,
|
|
1487
|
+
timeout=timeout,
|
|
1488
|
+
)
|
|
1314
1489
|
|
|
1315
1490
|
@staticmethod
|
|
1316
1491
|
def _build_standard_a2a_message_send_body(
|
|
@@ -1513,9 +1688,8 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1513
1688
|
)
|
|
1514
1689
|
if response.status_code in (401, 403):
|
|
1515
1690
|
cls.clear_cached_runtime_access(session_uid)
|
|
1516
|
-
access = cls.
|
|
1691
|
+
access = cls._resolve_runtime_access_for_message_send(
|
|
1517
1692
|
session_uid,
|
|
1518
|
-
cache=True,
|
|
1519
1693
|
timeout=timeout,
|
|
1520
1694
|
)
|
|
1521
1695
|
response = cls._post_standard_a2a_message(
|
|
@@ -1594,7 +1768,10 @@ class AgentSession(OwnerLogMixin, BaseObjectOrm, BasePydanticModel):
|
|
|
1594
1768
|
response = request(access)
|
|
1595
1769
|
if response.status_code in (401, 403):
|
|
1596
1770
|
cls.clear_cached_runtime_access(session_uid)
|
|
1597
|
-
access = cls.
|
|
1771
|
+
access = cls._resolve_runtime_access_for_message_send(
|
|
1772
|
+
session_uid,
|
|
1773
|
+
timeout=timeout,
|
|
1774
|
+
)
|
|
1598
1775
|
response = request(access)
|
|
1599
1776
|
if response.status_code in (401, 403):
|
|
1600
1777
|
cls.clear_cached_runtime_access(session_uid)
|
|
@@ -1974,6 +2151,8 @@ __all__ = [
|
|
|
1974
2151
|
"AgentA2AProfile",
|
|
1975
2152
|
"AgentRuntimeImageDrift",
|
|
1976
2153
|
"AgentRuntimeImageDriftCheck",
|
|
2154
|
+
"AgentRuntimeUpdate",
|
|
2155
|
+
"AgentRuntimeUpdateRemediation",
|
|
1977
2156
|
"AgentHarnessKind",
|
|
1978
2157
|
"AgentHarnessProtocol",
|
|
1979
2158
|
"AgentSessionInsights",
|
|
@@ -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()
|