revert 4e86ffe596
CI / push-validation (pull_request) Successful in 21s
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 3m48s
CI / lint (pull_request) Successful in 3m57s
CI / quality (pull_request) Successful in 4m24s
CI / typecheck (pull_request) Successful in 4m30s
CI / security (pull_request) Successful in 4m46s
CI / e2e_tests (pull_request) Successful in 7m13s
CI / integration_tests (pull_request) Successful in 7m53s
CI / coverage (pull_request) Successful in 15m15s
CI / unit_tests (pull_request) Successful in 8m48s
CI / docker (pull_request) Successful in 1m33s
CI / coverage (push) Blocked by required conditions
CI / docker (push) Blocked by required conditions
CI / status-check (push) Blocked by required conditions
CI / benchmark-regression (push) Waiting to run
CI / benchmark-publish (push) Waiting to run
CI / status-check (pull_request) Successful in 3s
CI / push-validation (push) Successful in 23s
CI / helm (push) Successful in 30s
CI / build (push) Successful in 3m50s
CI / quality (push) Successful in 4m20s
CI / typecheck (push) Successful in 4m35s
CI / security (push) Successful in 4m48s
CI / e2e_tests (push) Successful in 7m5s
CI / integration_tests (push) Successful in 10m1s
CI / unit_tests (push) Successful in 12m41s
CI / lint (push) Failing after 15m36s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1h6m42s

revert docs: document LLM provider fallback behavior (Anthropic Sonnet fallback) [AUTO-DOCS-7]
This commit was merged in pull request #10810.
This commit is contained in:
2026-04-21 18:28:04 +00:00
committed by Forgejo
parent aaf7625c18
commit e19af527e6
3 changed files with 132 additions and 226 deletions
+132 -21
View File
@@ -5,8 +5,86 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Added
- **Plan diff shows worktree branch changes** (#9231): `plan diff` now detects
the worktree branch `cleveragents/plan-<id>` created during `plan execute`
and runs `git diff HEAD...<branch>` to display actual file changes. Falls
back to changeset-based diff when no worktree branch exists. Git operations
delegated to `GitWorktreeSandbox.diff_against_head()` in the Infrastructure
layer.
### Fixed
- **Stale worktree branch cleanup on re-execute** (#7271): Re-executing a plan
after a failed attempt or diff review crashed with `fatal: a branch named
'cleveragents/plan-<id>' already exists`. Added `GitWorktreeSandbox.cleanup_stale()`
classmethod that idempotently removes stale worktree directories and branches
before creating a fresh sandbox.
- **Worktree sandbox cleanup on plan cancel** (#9230): `plan cancel` now removes
the git worktree branch and directory created during execute, preventing resource
leaks from dangling worktrees. Delegates to `GitWorktreeSandbox.cleanup_stale()`
in the infrastructure layer.
- **`plan apply` merge conflict cleanup** (#7250): When `git merge` fails due to
a conflict during `plan apply`, the apply command now reads the actual conflict
detail from `CalledProcessError.stdout` (git writes conflict info to stdout, not
stderr), runs `git merge --abort` to restore the repo to a clean state, transitions
the plan to `constrained` state per spec §18334-18336 (may revert to Strategize
for re-planning), and prints user-friendly guidance. Also handles
`subprocess.TimeoutExpired` on both merge and abort calls. Previously, merge
conflicts left conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in project files
and the plan remained in `apply/queued` indefinitely.
- **Actor v3 YAML Schema Validation in CLI** (#5869): The `agents actor add --config`
command now validates v3 YAML files using `ActorConfigSchema`, ensuring proper
schema compliance including cycle detection for GRAPH actors, required field
validation, and enum validation. v3 YAML is detected by the presence of ANY
`type` field (any value — invalid type values are then rejected by schema
validation) or a `version` field whose string value starts with `"3"` (e.g.
`"3"`, `"3.0"`, `"3.0.0"`). Configs with `type: null` are not treated as v3.
Invalid v3 actors are rejected with clear error messages before registration.
- **Alembic Files Missing from Wheel Distribution** (#4180): Alembic configuration
(`alembic.ini`) and migration files are now part of the Python package structure
at `src/cleveragents/infrastructure/database/migrations/`. Previously, when
`agents init` was run in Docker containers or any wheel-based installation,
`FileNotFoundError` was raised because alembic files were stored at the
repository root and excluded from the wheel distribution. Now alembic files
follow standard Python packaging conventions and are automatically included.
`MigrationRunner._find_alembic_ini()` has been updated to search the new package
location as the primary anchor point. This fix enables `agents init` to work
correctly in all deployment modes: Docker containers, local pip installs
(wheel or editable), and development environments.
- **TDD Non-AssertionError Guard Visibility** (#8294): `apply_tdd_inversion` in
- **bug-hunt-pool-supervisor Non-Blocking Tracking** (#8835): The automation-tracking-manager
call in step 5 was blocking the main loop indefinitely, causing 3+ consecutive initialization
failures. Step 5 now explicitly marks tracking as best-effort -- if the call does not complete
within a reasonable time or fails, it is skipped and the supervisor continues to the next
cycle. A new Rule 9 reinforces that tracking must never block the main loop; core
functionality (module mapping, worker dispatch, monitoring) takes priority over status
reporting.
`features/environment.py` now emits its non-assertion exception guard warning to
both the structured logger and `stderr` via a new `_warning_with_stderr` helper.
This makes the guard firing visible in standard Behave console output and CI log
snippets where the structured logging sink may not be displayed. BDD infrastructure
coverage added: a new scenario in `tdd_expected_fail_infrastructure.feature`
asserts that the warning is emitted to stderr when a non-AssertionError exception
is encountered in an `@tdd_expected_fail` scenario, and a second scenario asserts
the warning is NOT emitted when the exception is an `AssertionError`. The
`CONTRIBUTING.md` now documents that `@tdd_expected_fail` step definitions must
signal expected failures via `AssertionError`.
- **Parallel Behave Runner Log Noise Reduction** (#8351): The parallel behave
runner now suppresses captured stdout/stderr for passing worker chunks and
only replays diagnostics for failed, errored, or crashed chunks. This makes
failure output significantly easier to spot in CI and local runs. A worker
crash (unhandled exception) is detected via an all-zero summary and the
captured traceback is always surfaced.
- **Directory Clustering Absolute Path Fix** (#9401): Fixed `DecompositionService._directory_key` to correctly handle absolute file paths by computing relative paths before extracting directory keys. Previously, the function used a fixed depth of 2 path components, causing all absolute paths to collapse into a single bucket (e.g., `/home` for every file on the system), making directory-based clustering completely ineffective. The fix adds an optional `root` parameter to `_directory_key()` and `ClusteringStrategy.cluster_by_directory()`, and updates `DecompositionService._build_hierarchy()` to compute the common root and pass it through, ensuring directory clustering groups paths by their actual directory hierarchy in production use.
- **Automation Profile Silent Fallback** (#8232): `_resolve_profile_for_plan` in
`PlanLifecycleService` now raises a clear `ValidationError` when a plan's
automation profile name is not a known built-in profile, instead of silently
@@ -15,6 +93,37 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
message listing available built-in profiles. The resolved profile name is also
logged at debug level for observability.
- **CheckpointManager rollback_to always returned False** (#7488): Fixed a data
integrity bug in `CheckpointManager.create_checkpoint()` where `sandbox_path`
was computed from `sandbox.context.sandbox_path` but never stored in the
checkpoint metadata. As a result, `rollback_to()` always found
`checkpoint.metadata.get("sandbox_path")` returning `None` and silently
skipped the rollback, returning `False`. The fix adds `sandbox_path` to the
metadata dict before constructing the `SandboxCheckpoint`, enabling
`rollback_to()` to correctly restore the sandbox filesystem state.
- **Strategy Actor Code Review Follow-ups** (#10267): Implemented comprehensive
fixes for all 9 code review findings from PR #1175 strategy actor implementation:
- Module-level imports: Moved `json` import to module-level in
`plan_executor_coverage_steps.py` following Python conventions and ruff/isort
standards.
- Exception handling clarity: Removed redundant `json.JSONDecodeError` from
exception clause (Exception already subsumes it).
- Silent fallback logging: Added warning log when `actor.default.strategy` config
resolution fails, providing operator visibility into configuration issues.
- Structured content block handling: Enhanced `_extract_content()` in
`strategy_actor.py` to properly extract text from LangChain `MessageContentBlock`
dicts, preventing JSON parsing failures when structured message content is
encountered.
- Deduplicated constant: Removed local `_DEFAULT_ACTOR_NAME` definition from
`strategy_actor.py` and imported from canonical source in `strategy_resolution.py`
to prevent future drift.
- Test decoupling: Added `strategy_tree: StrategyTree | None` field to
`StrategizeResult` to allow tests to inspect the tree without calling private
`_execute_with_llm()` method, eliminating divergent ULID generation from
double-invocation pattern. Updated 6 step functions in `strategy_actor_llm_steps.py`
to use the public API instead of coupling to private implementation details.
### Added
- **TDD Issue-Capture Test Activation** (#7025): Replaced 234 bare `@skip` tags
@@ -69,27 +178,6 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Priority-based filtering (Critical/High/Medium/Low) reduces noise. Backlog-groomer
performs intelligent cleanup with age thresholds by priority.
- **OpenAI Quota Fallback to Anthropic Claude Sonnet** (#10042): Implemented graceful
degradation for E2E robot integration tests when OpenAI API hits quota limit errors
(429, insufficient_quota, rate_limit). The `StrategyActor` now detects quota-specific
errors via `_is_quota_error()` and automatically falls back to
`anthropic/claude-sonnet-4-20250514` for strategy decisions, ensuring CI/CD pipelines
complete E2E tests even when the primary provider hits capacity limits. Includes
intelligent recovery: the fallback LLM instance is cached (`_fallback_llm`), fallback
mode is tracked (`_using_fallback`), and the primary provider is re-attempted after a
5-minute recovery interval (`_QUOTA_RECOVERY_INTERVAL = 300`). Added comprehensive
logging for quota error detection and provider fallback transitions, plus E2E test
scenarios for fallback verification. Implemented in
`StrategyActor._execute_with_llm()` in
`src/cleveragents/application/services/strategy_actor.py` (commit `f5712787`).
- **Documentation: LLM Provider Fallback Behavior** [AUTO-DOCS-7]: Added
`docs/reference/llm_provider_fallback.md` documenting the automatic Anthropic Claude
Sonnet fallback when OpenAI quota is exhausted. Covers trigger conditions, fallback
state machine, recovery logic, logging reference, configuration constants, and
prerequisites. Updated `README.md` with an "Automatic quota fallback" subsection
under LLM provider configuration.
- **PR Agent Reorganization**: All PR-related agents renamed and reorganized to follow
the `*-pool-supervisor` naming pattern. New agents added: `pr-editor` (safe PR editing
with description preservation), `pr-manager` (unified PR interface), and
@@ -172,6 +260,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
prefix check using OS path separators. Added regression test tagged
`@tdd_issue_7558`.
- **File Edit Encoding Parameter** (#7559): `_handle_file_edit()` now correctly
respects the `encoding` parameter when reading and writing files. Previously the
function ignored the caller-supplied encoding and fell back to the platform default,
causing data corruption with non-UTF-8 files. The fix extracts `encoding` from the
tool inputs (defaulting to `"utf-8"`) and passes it to both `path.read_text()` and
`path.write_text()`. The `FILE_EDIT_SPEC` input schema was updated to declare the
`encoding` field. BDD scenarios were added to cover explicit encoding and the
UTF-8 default.
- **Validation Gate Empty-Run Guard** (#7508): Fixed `ApplyValidationSummary.all_required_passed`
returning `True` when zero validations were run, silently bypassing the apply gate. The property
now returns `False` when the validation result set is empty (`is_empty` is `True`), ensuring
@@ -214,6 +311,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
`pr-merge-pool-supervisor` to the product-builder's supervisor launch list (18 total
supervisors). Updated all numeric references, pre-flight checklists, and validation logic.
- `ActionRepository.update()` now uses explicit bulk `sa_delete()` + `session.flush()`
before re-inserting child rows for `action_arguments` and `action_invariants`, fixing
a `sqlite3.IntegrityError: UNIQUE constraint failed` crash when `agents plan use` was
called on an action that already had arguments registered via `action create`. (#4197)
- Fixed CheckpointManager not wired into PlanExecutor — checkpoints were
never created during plan execution because `_get_plan_executor()` in
the CLI constructed PlanExecutor without a CheckpointManager (defaulted
to None, silently skipping all checkpoint hooks). `_get_plan_executor()`
now resolves the container singleton so CLI `plan execute` and `plan
rollback` share the same registry, and `_try_create_checkpoint()` raises
`PlanError` if checkpoint metadata cannot be persisted. Writable sandboxable
resources and write-capable tools now default to `checkpointable=True`, and
new Behave scenarios cover DI wiring, rollback, and capability defaults. (#1253)
---
## [3.8.0] — 2026-04-05
-12
View File
@@ -170,15 +170,3 @@ Set `CLEVERAGENTS_DEFAULT_PROVIDER` to pin the global provider (for example `exp
- Built-in actors (`<provider>/<model>`) are immutable, custom actors must be named `local/<id>`, and the default actor cannot be removed. Use `--unsafe` when adding/updating configs marked unsafe; runtime only warns when invoking unsafe actors.
- `CLEVERAGENTS_TESTING_USE_MOCK_AI=true` forces the in-repo mock provider so Behave/Robot suites never hit external APIs.
- The full capability matrix (streaming, tool calls, JSON mode, etc.) is documented in `docs/reference/providers.md`.
### Automatic quota fallback
When the primary LLM provider (e.g. OpenAI) returns a quota error (HTTP 429,
`insufficient_quota`, or `rate_limit`), `StrategyActor` automatically falls back to
**Anthropic Claude Sonnet (`claude-sonnet-4-20250514`)** for strategy decisions.
The fallback is transparent — plan operations continue without interruption. The
system re-attempts the primary provider after a 5-minute recovery interval.
Requires `ANTHROPIC_API_KEY` to be set for the fallback to succeed. See
[`docs/reference/llm_provider_fallback.md`](docs/reference/llm_provider_fallback.md)
for full details including state machine, logging, and configuration constants.
-193
View File
@@ -1,193 +0,0 @@
# LLM Provider Fallback Behavior
## Overview
CleverAgents implements automatic LLM provider fallback to ensure resilience when the
primary provider (typically OpenAI) exhausts its API quota. When a quota error is
detected, the system transparently switches to **Anthropic Claude Sonnet
(`claude-sonnet-4-20250514`)** as the fallback provider, allowing plan strategize
operations to continue without interruption.
This behavior was introduced in commit `f5712787` (feat: add fallback to Anthropic
Sonnet when OpenAI quota is exhausted) and is implemented in
`StrategyActor._execute_with_llm()` located in
`src/cleveragents/application/services/strategy_actor.py`.
---
## Trigger Conditions
The fallback activates when the primary LLM call raises an exception whose string
representation matches any of the following patterns (case-insensitive):
| Pattern | Typical Source |
|---------|---------------|
| `insufficient_quota` | OpenAI quota exhaustion |
| `429` | HTTP 429 Too Many Requests |
| `quota` | Generic quota error from any provider |
| `rate_limit` | Rate-limit errors from any provider |
Detection is performed by the module-level helper function `_is_quota_error(exc)` in
`src/cleveragents/application/services/strategy_actor.py`.
---
## Fallback Provider
| Constant | Value | Description |
|----------|-------|-------------|
| `_FALLBACK_PROVIDER` | `"anthropic"` | Provider identifier |
| `_FALLBACK_MODEL` | `"claude-sonnet-4-20250514"` | Model identifier |
| `_QUOTA_RECOVERY_INTERVAL` | `300` (seconds) | Time before re-attempting primary provider |
The fallback LLM instance is created lazily on first use and then **cached** as
`StrategyActor._fallback_llm` to avoid per-call re-initialization overhead.
---
## Fallback State Machine
`StrategyActor` tracks fallback state using three instance variables:
| Variable | Type | Description |
|----------|------|-------------|
| `_fallback_llm` | `Any \| None` | Cached fallback LLM instance |
| `_last_quota_error_time` | `float \| None` | Unix timestamp of the last quota error |
| `_using_fallback` | `bool` | Whether the actor is currently in fallback mode |
### State Transitions
```
Normal mode (_using_fallback=False)
|
| Primary provider raises quota error
v
Fallback mode (_using_fallback=True, _last_quota_error_time=<now>)
|
| _QUOTA_RECOVERY_INTERVAL (300 s) elapses
v
Recovery attempt (_using_fallback=False, primary re-tried)
|
+-- Primary succeeds --> Normal mode
+-- Primary raises quota error again --> Fallback mode
```
### Recovery Logic
Once in fallback mode, `_execute_with_llm()` skips the primary provider call for
subsequent invocations until `_QUOTA_RECOVERY_INTERVAL` seconds have elapsed since
the last quota error. This prevents hammering the primary provider with repeated
quota errors. After the interval, the primary provider is re-attempted automatically.
---
## Behavior from the User Perspective
From the user's perspective, the fallback is **transparent**:
- Plan strategize operations continue to produce valid strategy trees.
- Response quality and format are identical — both OpenAI and Anthropic Claude Sonnet
support the structured JSON output required by the strategy prompt.
- No user action is required to trigger or disable the fallback.
- The fallback is **temporary**: the system automatically attempts to recover to the
primary provider after 5 minutes.
### When Both Providers Are Unavailable
If the fallback provider (Anthropic Claude Sonnet) also fails, the original quota
exception from the primary provider is re-raised (chained from the fallback
exception). The `StrategyActor.execute()` method catches this and falls back to the
deterministic stub strategy (`_execute_stub()`), ensuring the plan lifecycle is never
blocked by LLM unavailability.
In E2E test contexts, when both providers are unavailable, tests fail with a clear
message indicating that the test outcome cannot be verified — rather than silently
skipping and creating false confidence in test coverage.
---
## Logging
The fallback mechanism emits structured log events at the following levels:
| Level | Event | Key Fields |
|-------|-------|-----------|
| `WARNING` | Quota error detected on primary provider | `plan_id`, `original_error` |
| `WARNING` | Creating fallback LLM instance | `plan_id` |
| `WARNING` | Reusing cached fallback LLM instance | `plan_id` |
| `WARNING` | Fallback recovery successful | `plan_id`, `fallback_provider`, `fallback_model` |
| `ERROR` | Fallback provider also failed | `plan_id`, `fallback_provider`, `fallback_model`, `fallback_error`, `fallback_error_type` |
| `DEBUG` | Still in quota fallback mode (with time remaining) | `plan_id` |
| `INFO` | Quota recovery interval elapsed, attempting primary | `plan_id`, `time_since_error` |
All log events include `plan_id` for correlation. Enable `DEBUG` level logging to
observe fallback mode transitions in detail.
---
## Configuration
The fallback behavior is controlled by module-level constants in
`src/cleveragents/application/services/strategy_actor.py`. These are not currently
exposed as environment variables or configuration keys. To change the fallback
provider or recovery interval, modify the constants directly:
```python
# src/cleveragents/application/services/strategy_actor.py
_FALLBACK_PROVIDER = "anthropic" # Provider identifier
_FALLBACK_MODEL = "claude-sonnet-4-20250514" # Model identifier
_QUOTA_RECOVERY_INTERVAL = 300 # Seconds before re-trying primary
```
> **Note:** The fallback provider requires a valid `ANTHROPIC_API_KEY` environment
> variable to be set. If the key is absent, the fallback LLM creation will fail and
> the original quota exception will be re-raised.
---
## Prerequisites
To enable the Anthropic fallback, ensure the following environment variable is set:
```bash
export ANTHROPIC_API_KEY=<your-anthropic-api-key>
```
The primary provider (e.g. OpenAI) is configured as usual:
```bash
export OPENAI_API_KEY=<your-openai-api-key>
```
See the [LLM provider configuration](../../README.md#llm-provider-configuration)
section of the README for the full list of supported providers and environment
variables.
---
## Implementation Reference
| Symbol | Location | Description |
|--------|----------|-------------|
| `_is_quota_error(exc)` | `src/cleveragents/application/services/strategy_actor.py` | Detects quota errors by string matching |
| `StrategyActor._execute_with_llm()` | `src/cleveragents/application/services/strategy_actor.py` | Orchestrates primary call, quota detection, and fallback |
| `StrategyActor._fallback_llm` | `src/cleveragents/application/services/strategy_actor.py` | Cached fallback LLM instance |
| `StrategyActor._last_quota_error_time` | `src/cleveragents/application/services/strategy_actor.py` | Timestamp of last quota error |
| `StrategyActor._using_fallback` | `src/cleveragents/application/services/strategy_actor.py` | Fallback mode flag |
| `_FALLBACK_PROVIDER` | `src/cleveragents/application/services/strategy_actor.py` | `"anthropic"` |
| `_FALLBACK_MODEL` | `src/cleveragents/application/services/strategy_actor.py` | `"claude-sonnet-4-20250514"` |
| `_QUOTA_RECOVERY_INTERVAL` | `src/cleveragents/application/services/strategy_actor.py` | `300` seconds |
**Introduced in:** commit `f5712787` — *feat: add fallback to Anthropic Sonnet when
OpenAI quota is exhausted*
**Fixes:** issue #10042
---
## Related Documentation
- [Retry Policy](retry_policy.md) — service-level retry and circuit breaker configuration
- [Error Recovery](error_recovery.md) — plan-level error classification and recovery actions
- [Error Handling](error_handling.md) — error codes, classification, and secret redaction
- [Observability](observability.md) — structured logging and tracing configuration