Files
wyndham-ARR/arr_web/agent_writeback_runtime.py
2026-07-29 16:38:05 +08:00

158 lines
5.5 KiB
Python

"""Production composition for authenticated Agent result callbacks."""
from __future__ import annotations
import importlib.util
import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Optional
from arr_ingestion.postgres import DatabaseConfig, PostgresIngestionRepository
from arr_ingestion.service import IngestionService
from arr_ingestion.validation import DeliveryValidator, ProcessorPolicy
from arr_processing.callbacks import AgentResultWriteback
from arr_processing.config import ResultVerificationConfig
from arr_processing.errors import ProcessingTransportError
from arr_processing.postgres import PostgresProcessingState, ProcessingDatabaseConfig
from arr_processing.registration import ProcessingOutputRegistrar, RemoteFilePort
from arr_processing.remote_files import PrefixRemoteFilePort
from arr_processing.runner import ProcessingRunner, RemoteRunSnapshot
from arr_storage.aliyun_oss_v2 import AliyunOssConfig, AliyunOssV2Client
from arr_storage.contracts import ObjectKeyPolicy
from arr_storage.exchange import OutputExchangeConfig
from arr_storage.remote import CloudObjectBackend
from arr_storage.store import ManagedObjectStore
from arr_web.services import ProcessingAgentResultCoordinator
class CallbackOnlyTransport:
"""Callbacks do not require remote Agent network authority in this process."""
@staticmethod
def _disabled() -> None:
raise ProcessingTransportError(
"PROCESSING_REMOTE_DISABLED",
retryable=False,
)
def submit(self, request: Any, idempotency_key: str) -> RemoteRunSnapshot:
del request, idempotency_key
self._disabled()
def get(self, request: Any, remote_run_id: str) -> RemoteRunSnapshot:
del request, remote_run_id
self._disabled()
def cancel(self, request: Any, remote_run_id: str) -> RemoteRunSnapshot:
del request, remote_run_id
self._disabled()
@dataclass
class AgentWritebackRuntime:
coordinator: ProcessingAgentResultCoordinator
close_callback: Optional[Callable[[], None]] = None
def close(self) -> None:
if self.close_callback is not None:
self.close_callback()
def load_processor_policy(project_root: Path) -> ProcessorPolicy:
skill_root = (project_root / "arr-opera-daily-ingest").resolve()
script = skill_root / "scripts" / "process_daily.py"
module_name = "_arr_runtime_process_daily"
specification = importlib.util.spec_from_file_location(module_name, script)
if specification is None or specification.loader is None:
raise ValueError("ARR daily processor identity is unavailable")
module = importlib.util.module_from_spec(specification)
prior = sys.modules.get(module_name)
sys.modules[module_name] = module
try:
specification.loader.exec_module(module)
processor_version = str(module.PROCESSOR_VERSION)
rule_set_sha256 = str(module.rule_set_sha256())
finally:
if prior is None:
sys.modules.pop(module_name, None)
else:
sys.modules[module_name] = prior
return ProcessorPolicy(
processor_version=processor_version,
rule_set_sha256=rule_set_sha256,
skill_root=skill_root,
)
def compose_agent_writeback(
*,
object_store: ManagedObjectStore,
remote_files: RemoteFilePort,
processor_policy: ProcessorPolicy,
verification: ResultVerificationConfig,
connect: Optional[Callable[[str], Any]] = None,
) -> AgentWritebackRuntime:
processing_config = (
ProcessingDatabaseConfig("controlled")
if connect is not None
else ProcessingDatabaseConfig.from_environment()
)
ingestion_config = (
DatabaseConfig("controlled")
if connect is not None
else DatabaseConfig.from_environment()
)
state = PostgresProcessingState(processing_config, connect=connect)
runner = ProcessingRunner(
CallbackOnlyTransport(),
state,
verification.verifier(),
)
repository = PostgresIngestionRepository(ingestion_config, connect=connect)
writeback = AgentResultWriteback(
runner,
state,
ProcessingOutputRegistrar(remote_files, object_store),
IngestionService(
DeliveryValidator(object_store, processor_policy),
repository,
),
outcome_resolver=state,
)
return AgentWritebackRuntime(ProcessingAgentResultCoordinator(writeback))
def compose_oss_agent_writeback(
*,
project_root: Path,
connect: Optional[Callable[[str], Any]] = None,
) -> AgentWritebackRuntime:
oss_client = AliyunOssV2Client(AliyunOssConfig.from_environment())
try:
oss_client.assert_immutable_writes_supported()
key_policy = ObjectKeyPolicy(os.environ.get("ARR_OBJECT_PREFIX", "arr"))
object_store = ManagedObjectStore(
CloudObjectBackend(oss_client),
key_policy,
)
runtime = compose_agent_writeback(
object_store=object_store,
remote_files=PrefixRemoteFilePort(
oss_client,
OutputExchangeConfig.from_environment(),
),
processor_policy=load_processor_policy(project_root),
verification=ResultVerificationConfig.from_environment(),
connect=connect,
)
runtime.close_callback = oss_client.close
return runtime
except Exception:
try:
oss_client.close()
except Exception:
pass
raise