"""ASV benchmarks for action_arguments table ORM round-trip performance. Measures the performance of: - ActionArgument construction and serialization for ORM child rows - Bulk argument serialization for actions with many arguments - ActionArgumentModel construction patterns """ from __future__ import annotations import json import sys from pathlib import Path try: from cleveragents.domain.models.core.action import ( Action, ActionArgument, ArgumentRequirement, ArgumentType, ) from cleveragents.domain.models.core.plan import NamespacedName from cleveragents.infrastructure.database.models import ( ActionArgumentModel, LifecycleActionModel, ) except ModuleNotFoundError: sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) from cleveragents.domain.models.core.action import ( Action, ActionArgument, ArgumentRequirement, ArgumentType, ) from cleveragents.domain.models.core.plan import NamespacedName from cleveragents.infrastructure.database.models import ( ActionArgumentModel, LifecycleActionModel, ) def _make_argument(name: str, idx: int) -> ActionArgument: """Create a typed argument cycling through types by index.""" types = list(ArgumentType) arg_type = types[idx % len(types)] return ActionArgument( name=name, arg_type=arg_type, requirement=ArgumentRequirement.REQUIRED if idx % 2 == 0 else ArgumentRequirement.OPTIONAL, description=f"Argument {name} for benchmarking", default_value="default" if idx % 2 == 1 else None, min_value=0 if arg_type == ArgumentType.INTEGER else None, max_value=100 if arg_type == ArgumentType.INTEGER else None, validation_pattern=r"^[a-z]+$" if arg_type == ArgumentType.STRING else None, ) def _make_action_with_n_args(n: int) -> Action: """Create an action with N arguments.""" return Action( namespaced_name=NamespacedName( server=None, namespace="local", name=f"multi-arg-{n}" ), description=f"Action with {n} arguments", definition_of_done="All args validated", strategy_actor="openai/gpt-4", execution_actor="openai/gpt-4", arguments=[_make_argument(f"arg_{i}", i) for i in range(n)], ) class ArgumentModelConstructionSuite: """Benchmark ActionArgumentModel construction patterns.""" def setup(self) -> None: self.args = [_make_argument(f"arg_{i}", i) for i in range(20)] def time_construct_single_model(self) -> None: """Construct a single ActionArgumentModel from values.""" arg = self.args[0] ActionArgumentModel( action_name="local/bench-action", name=arg.name, arg_type=arg.arg_type.value, requirement=arg.requirement.value, description=arg.description, default_value_json=json.dumps(arg.default_value) if arg.default_value is not None else None, min_value=arg.min_value, max_value=arg.max_value, validation_pattern=arg.validation_pattern, position=0, ) def time_construct_20_models(self) -> None: """Construct 20 ActionArgumentModel instances.""" for i, arg in enumerate(self.args): ActionArgumentModel( action_name="local/bench-action", name=arg.name, arg_type=arg.arg_type.value, requirement=arg.requirement.value, description=arg.description, default_value_json=json.dumps(arg.default_value) if arg.default_value is not None else None, min_value=arg.min_value, max_value=arg.max_value, validation_pattern=arg.validation_pattern, position=i, ) class ArgumentRoundTripSuite: """Benchmark action ORM round-trip with varying argument counts.""" def setup(self) -> None: self.action_2_args = _make_action_with_n_args(2) self.action_10_args = _make_action_with_n_args(10) self.action_20_args = _make_action_with_n_args(20) def time_round_trip_2_args(self) -> None: """Round-trip an action with 2 arguments through ORM.""" model = LifecycleActionModel.from_domain(self.action_2_args) model.to_domain() def time_round_trip_10_args(self) -> None: """Round-trip an action with 10 arguments through ORM.""" model = LifecycleActionModel.from_domain(self.action_10_args) model.to_domain() def time_round_trip_20_args(self) -> None: """Round-trip an action with 20 arguments through ORM.""" model = LifecycleActionModel.from_domain(self.action_20_args) model.to_domain() class ArgumentSerializationSuite: """Benchmark JSON serialization of argument default values.""" def setup(self) -> None: self.string_val = "default-string-value" self.int_val = 42 self.float_val = 3.14159 self.bool_val = True self.list_val = ["alpha", "beta", "gamma", "delta"] def time_serialize_string(self) -> None: """Serialize a string default value to JSON.""" json.dumps(self.string_val) def time_serialize_integer(self) -> None: """Serialize an integer default value to JSON.""" json.dumps(self.int_val) def time_serialize_list(self) -> None: """Serialize a list default value to JSON.""" json.dumps(self.list_val) def time_deserialize_string(self) -> None: """Deserialize a JSON string default value.""" json.loads('"default-string-value"') def time_deserialize_list(self) -> None: """Deserialize a JSON list default value.""" json.loads('["alpha", "beta", "gamma", "delta"]')