diff --git a/benchmarks/a2a_facade_bench.py b/benchmarks/a2a_facade_bench.py index 53ba04261..19580d5d6 100644 --- a/benchmarks/a2a_facade_bench.py +++ b/benchmarks/a2a_facade_bench.py @@ -40,11 +40,11 @@ class FacadeDispatchSuite: def setup(self) -> None: self.facade = A2aLocalFacade() - self.session_req = A2aRequest(operation="session.create") + self.session_req = A2aRequest(method="session.create", params={}) self.plan_req = A2aRequest( - operation="plan.execute", params={"plan_id": "BENCH001"} + method="plan.execute", params={"plan_id": "BENCH001"} ) - self.context_req = A2aRequest(operation="context.get") + self.context_req = A2aRequest(method="context.get", params={}) def time_dispatch_session_create(self) -> None: self.facade.dispatch(self.session_req) @@ -57,7 +57,7 @@ class FacadeDispatchSuite: def time_dispatch_all_operations(self) -> None: for op in self.facade.list_operations(): - req = A2aRequest(operation=op) + req = A2aRequest(method=op, params={}) self.facade.dispatch(req) def time_list_operations(self) -> None: diff --git a/benchmarks/m6_acceptance_bench.py b/benchmarks/m6_acceptance_bench.py index 86c070ef8..d1130845b 100644 --- a/benchmarks/m6_acceptance_bench.py +++ b/benchmarks/m6_acceptance_bench.py @@ -50,17 +50,17 @@ class M6FacadeDispatchSuite: def time_session_create(self) -> None: """Benchmark session.create dispatch.""" - self._facade.dispatch(A2aRequest(operation="session.create", params={})) + self._facade.dispatch(A2aRequest(method="session.create", params={})) def time_plan_create(self) -> None: """Benchmark plan.create dispatch.""" - self._facade.dispatch(A2aRequest(operation="plan.create", params={})) + self._facade.dispatch(A2aRequest(method="plan.create", params={})) def time_plan_execute(self) -> None: """Benchmark plan.execute dispatch.""" self._facade.dispatch( A2aRequest( - operation="plan.execute", + method="plan.execute", params={"plan_id": "01M6SM0KE00000000000000001"}, ) ) diff --git a/docs/api/action.md b/docs/api/action.md new file mode 100644 index 000000000..81cfcbb3b --- /dev/null +++ b/docs/api/action.md @@ -0,0 +1,283 @@ +# `cleveragents.action` — Action Configuration Schema + +The `action` package provides the YAML configuration schema, validation, and +loading utilities for CleverAgents action definitions. + +Actions are the top-level automation units in CleverAgents: each action binds +lifecycle actors, typed arguments, invariants, and completion criteria into a +named, reusable workflow template. + +See the [Action Configuration Files](../schema/action.schema.yaml) schema and +`examples/actions/` for reference examples. + +--- + +## Quick Start + +```python +from cleveragents.action.schema import ActionConfigSchema + +# Parse from a YAML string +config = ActionConfigSchema.from_yaml(""" +name: local/my-action +description: "Refactor a Python module" +strategy_actor: openai/gpt-4o +execution_actor: openai/gpt-4o-mini +definition_of_done: "All tests pass and coverage >= 90%" +arguments: + - name: target_module + type: string + required: true + description: "Module path to refactor" +invariants: + - "Do not modify public API signatures" +""") + +print(config.name) # "local/my-action" +print(config.arguments[0]) # ActionArgumentSchema(name='target_module', ...) + +# Load from a file +config = ActionConfigSchema.from_yaml_file("examples/actions/refactor.yaml") +``` + +--- + +## `ActionConfigSchema` + +```python +from cleveragents.action.schema import ActionConfigSchema +``` + +Pydantic model that validates a complete action YAML configuration. + +### Factory Methods + +#### `ActionConfigSchema.from_yaml(yaml_string: str) → ActionConfigSchema` + +Parse and validate an action YAML string. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `yaml_string` | `str` | Raw YAML content | + +**Returns:** Validated `ActionConfigSchema` instance. + +**Raises:** +- `ValueError` — if the YAML is empty, `None`, or not a mapping +- `pydantic.ValidationError` — if schema validation fails + +**Processing pipeline:** +1. Parse YAML with `yaml.safe_load` +2. Normalize camelCase keys → snake_case (with deprecation warnings) +3. Interpolate `${ENV_VAR}` placeholders from environment variables +4. Validate with Pydantic + +--- + +#### `ActionConfigSchema.from_yaml_file(path: str | Path) → ActionConfigSchema` + +Load and validate an action YAML file from disk. + +| Parameter | Type | Description | +|-----------|------|-------------| +| `path` | `str \| Path` | Path to the YAML file | + +**Returns:** Validated `ActionConfigSchema` instance. + +**Raises:** +- `FileNotFoundError` — if the file does not exist +- `ValueError` — if the path is `None` or does not point to a regular file +- `pydantic.ValidationError` — if schema validation fails + +--- + +### Fields + +#### Identity + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `schema_version` | `str` | No (default: `"1"`) | Schema version for forward compatibility | +| `name` | `str` | **Yes** | Namespaced action name — `namespace/name` format | +| `description` | `str` | **Yes** | Short description of the action | +| `long_description` | `str \| None` | No | Detailed multi-line description | + +The `name` field must match `^[a-z0-9][a-z0-9_-]*/[a-z0-9][a-z0-9_-]*$`. +Examples: `local/my-action`, `builtin/refactor`. + +#### Lifecycle Actors + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `strategy_actor` | `str` | **Yes** | Actor for the Strategize phase | +| `execution_actor` | `str` | **Yes** | Actor for the Execute phase | +| `estimation_actor` | `str \| None` | No | Optional actor for cost/risk estimation | +| `review_actor` | `str \| None` | No | Optional actor for code review / QA | +| `apply_actor` | `str \| None` | No | Optional actor for the Apply phase | +| `invariant_actor` | `str \| None` | No | Optional actor for invariant reconciliation | + +All actor names must follow the `namespace/name` format. + +#### Completion Criteria + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `definition_of_done` | `str` | **Yes** | Testable criteria defining task completion | + +#### Behaviour + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `reusable` | `bool` | `True` | Whether the action persists after use | +| `read_only` | `bool` | `False` | Restrict the action to read-only operations | +| `state` | `str` | `"available"` | Action state: `available` or `archived` | + +#### Arguments + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `arguments` | `list[ActionArgumentSchema]` | `[]` | Typed arguments accepted by the action | + +#### Automation & Invariants + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `automation_profile` | `str \| None` | `None` | Default automation profile for derived plans | +| `invariants` | `list[str]` | `[]` | Constraints carried as plan-level invariants | +| `inputs_schema` | `dict[str, Any] \| None` | `None` | Optional JSON Schema for advanced input validation | + +Invariants are automatically trimmed, de-duplicated, and blank entries are +dropped during validation. + +--- + +## `ActionArgumentSchema` + +```python +from cleveragents.action.schema import ActionArgumentSchema +``` + +Pydantic model for a single typed action argument. Arguments are values +supplied by the user at plan-use time (`agents plan use --arg name=value`). + +### Fields + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `name` | `str` | **Required** | Argument name (1–64 chars, valid identifier) | +| `type` | `str` | `"string"` | Data type: `string`, `integer`, `float`, `boolean`, `list` | +| `required` | `bool` | `False` | Whether the argument must be provided | +| `description` | `str` | `""` | Human-readable description (max 500 chars) | +| `default` | `str \| int \| float \| bool \| list[str] \| None` | `None` | Default value when argument is not provided | +| `validation_pattern` | `str \| None` | `None` | Regex pattern for string argument validation | +| `min_value` | `float \| None` | `None` | Minimum value for numeric arguments | +| `max_value` | `float \| None` | `None` | Maximum value for numeric arguments | + +The `name` field must be a valid Python identifier (alphanumeric, underscores, +or hyphens). + +--- + +## camelCase Compatibility + +The schema accepts both `snake_case` and `camelCase` keys for backward +compatibility. camelCase keys are automatically normalized with a deprecation +warning: + +| camelCase | snake_case | +|-----------|------------| +| `strategyActor` | `strategy_actor` | +| `executionActor` | `execution_actor` | +| `definitionOfDone` | `definition_of_done` | +| `longDescription` | `long_description` | +| `estimationActor` | `estimation_actor` | +| `reviewActor` | `review_actor` | +| `applyActor` | `apply_actor` | +| `invariantActor` | `invariant_actor` | +| `readOnly` | `read_only` | +| `automationProfile` | `automation_profile` | +| `inputsSchema` | `inputs_schema` | +| `validationPattern` | `validation_pattern` | +| `minValue` | `min_value` | +| `maxValue` | `max_value` | +| `schemaVersion` | `schema_version` | + +--- + +## Environment Variable Interpolation + +String values in the YAML may reference environment variables using +`${VAR_NAME}` syntax. Missing variables are left as-is (no error): + +```yaml +name: local/deploy +description: "Deploy to ${TARGET_ENV}" +strategy_actor: openai/gpt-4o +execution_actor: openai/gpt-4o-mini +definition_of_done: "Deployment to ${TARGET_ENV} verified" +``` + +```python +import os +os.environ["TARGET_ENV"] = "staging" +config = ActionConfigSchema.from_yaml(yaml_string) +# config.description == "Deploy to staging" +``` + +--- + +## Database Integrity Note + +When updating an action that already has arguments or invariants registered, +`ActionRepository.update()` uses explicit `sa_delete()` + `session.flush()` +before re-inserting child rows. This avoids the SQLAlchemy `.clear()` + +`.append()` pattern that could trigger a `UNIQUE constraint failed` error +on `(action_name, name)` pairs. See [#4197](https://git.cleverthis.com/cleveragents/cleveragents-core/pulls/4197). + +--- + +## Complete Example + +```yaml +# examples/actions/refactor.yaml +schema_version: "1" +name: local/refactor +description: "Refactor a Python module to improve code quality" +long_description: | + Analyses the target module for code smells, applies refactoring + patterns, and ensures all tests continue to pass. + +strategy_actor: openai/gpt-4o +execution_actor: openai/gpt-4o-mini +review_actor: anthropic/claude-4-sonnet +estimation_actor: local/estimator + +definition_of_done: | + - All existing tests pass + - Coverage >= 90% + - No new linting errors + - Public API unchanged + +reusable: true +read_only: false +state: available + +arguments: + - name: target_module + type: string + required: true + description: "Python module path to refactor (e.g. src/myapp/utils.py)" + - name: max_complexity + type: integer + required: false + default: 10 + description: "Maximum cyclomatic complexity threshold" + min_value: 1 + max_value: 50 + +invariants: + - "Do not modify public API signatures" + - "Preserve all existing test assertions" + +automation_profile: standard +``` diff --git a/docs/api/index.md b/docs/api/index.md index 8da0de416..db9de4b12 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -10,6 +10,7 @@ classes, functions, and exceptions with signatures and usage examples. |--------|-------------| | [`cleveragents.core`](core.md) | Exception hierarchy, error handling, retry patterns, async cleanup | | [`cleveragents.a2a`](a2a.md) | Agent-to-Agent (A2A) protocol facade, transport, models, and events | +| [`cleveragents.action`](action.md) | Action YAML configuration schema, validation, argument types, and env-var interpolation | | [`cleveragents.actor`](actor.md) | Actor registry, configuration, loader, and compiler | | [`cleveragents.skills`](skills.md) | Skill framework — schema, protocol, registry, discovery, and inline executor | | [`cleveragents.tool`](tool.md) | Tool runtime, lifecycle, registry, router, and container executor | diff --git a/mkdocs.yml b/mkdocs.yml index 48a98aa71..3e4caa5e2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,6 +15,7 @@ nav: - Overview: api/index.md - Core Utilities: api/core.md - A2A Protocol: api/a2a.md + - Action Schema: api/action.md - Actor System: api/actor.md - Skills: api/skills.md - Tools: api/tool.md