"""Utilities for orchestrating high-volume marketing process refreshes.""" from __future__ import annotations import argparse import sys import time from dataclasses import dataclass from typing import Any, Dict from services.seeder_api.app import MarketingQuery, marketing_processes @dataclass(slots=True) class BulkUpdatePlan: """Summary metrics describing a bulk update execution.""" iterations: int batch_size: int executed_iterations: int dry_run: bool sample_runtime_seconds: float estimated_total_runtime_seconds: float def to_dict(self) -> Dict[str, Any]: return { "iterations": self.iterations, "batch_size": self.batch_size, "executed_iterations": self.executed_iterations, "dry_run": self.dry_run, "sample_runtime_seconds": self.sample_runtime_seconds, "estimated_total_runtime_seconds": self.estimated_total_runtime_seconds, } DEFAULT_PROMPT = ( "Scale omni-channel revenue loops for autonomous marketing agents with global payments coverage" ) def execute_bulk_updates( *, iterations: int = 1_000_000, batch_size: int = 1_000, prompt: str = DEFAULT_PROMPT, limit: int = 2, dry_run: bool = True, ) -> BulkUpdatePlan: """Drive repeated marketing process orchestrations. Args: iterations: Total number of update cycles requested. batch_size: Frequency for progress logging and throttling. prompt: Prompt forwarded into the marketing processes orchestration. limit: How many campaign recommendations to request per iteration. dry_run: When true, only run a small sample to compute projections. Returns: A :class:`BulkUpdatePlan` summarising the execution profile. """ if iterations <= 0: raise ValueError("iterations must be positive") if batch_size <= 0: raise ValueError("batch_size must be positive") if limit <= 0: raise ValueError("limit must be positive") sample_iterations = min(5, iterations) start = time.perf_counter() executed = 0 for _ in range(sample_iterations): marketing_processes(MarketingQuery(prompt=prompt, limit=limit)) executed += 1 sample_runtime = time.perf_counter() - start per_iteration = sample_runtime / executed if executed else 0.0 estimated_total = per_iteration * iterations if dry_run: return BulkUpdatePlan( iterations=iterations, batch_size=batch_size, executed_iterations=executed, dry_run=True, sample_runtime_seconds=sample_runtime, estimated_total_runtime_seconds=estimated_total, ) processed = executed while processed < iterations: marketing_processes(MarketingQuery(prompt=prompt, limit=limit)) processed += 1 if processed % batch_size == 0 or processed == iterations: remaining = iterations - processed eta = max(0.0, estimated_total - per_iteration * processed) print( f"Processed {processed:,}/{iterations:,} updates | remaining {remaining:,} | " f"est. remaining {eta:0.1f}s", file=sys.stderr, ) total_runtime = time.perf_counter() - start return BulkUpdatePlan( iterations=iterations, batch_size=batch_size, executed_iterations=processed, dry_run=False, sample_runtime_seconds=sample_runtime, estimated_total_runtime_seconds=total_runtime, ) def _positive_int(value: str) -> int: try: parsed = int(value) except ValueError as exc: # pragma: no cover - argparse already ensures message raise argparse.ArgumentTypeError(str(exc)) from exc if parsed <= 0: raise argparse.ArgumentTypeError("value must be positive") return parsed def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description=( "Simulate or execute large-scale marketing process updates using the " "FastAPI orchestration stack." ) ) parser.add_argument( "--iterations", type=_positive_int, default=1_000_000, help="Total update cycles to run (default: 1,000,000).", ) parser.add_argument( "--batch-size", type=_positive_int, default=1_000, help="How often to log progress when executing updates (default: 1,000).", ) parser.add_argument( "--limit", type=_positive_int, default=2, help="Campaign recommendation limit per iteration (default: 2).", ) parser.add_argument( "--prompt", default=DEFAULT_PROMPT, help="Prompt describing the marketing objective (default: preconfigured omni-channel brief).", ) parser.add_argument( "--execute", action="store_true", help="Execute the full run instead of returning a dry-run projection.", ) args = parser.parse_args(argv) plan = execute_bulk_updates( iterations=args.iterations, batch_size=args.batch_size, prompt=args.prompt, limit=args.limit, dry_run=not args.execute, ) payload = json_dumps(plan.to_dict()) print(payload) return 0 def json_dumps(data: Dict[str, Any]) -> str: # Local helper to avoid importing json at module import time. import json return json.dumps(data, indent=2, sort_keys=True) if __name__ == "__main__": # pragma: no cover - CLI entry point raise SystemExit(main())