feat: prepare ARR for controlled public deployment
This commit is contained in:
157
arr_web/agent_writeback_runtime.py
Normal file
157
arr_web/agent_writeback_runtime.py
Normal file
@@ -0,0 +1,157 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user