Make built-in actors virtual (resolved on-demand from provider registry, not persisted to database) #10924

Closed
opened 2026-04-29 05:20:19 +00:00 by hurui200320 · 1 comment
Member

Metadata

Commit Message: feat(actor): make built-in actors virtual, resolved on-demand from provider registry
Branch: feature/m3-virtual-built-in-actors

Background and Context

Built-in actors (e.g. openai/gpt-4o, anthropic/claude-sonnet-4-20250514) represent LLM providers configured via API keys. They are conceptually derived data — their existence and configuration come from the ProviderRegistry, not from user-created definitions.

The specification (docs/specification.md) shows agents actor list output that includes built-in actors alongside custom actors, implying they should always be visible when providers are configured. However, the current implementation has an architectural contradiction:

  1. ensure_built_in_actors() persists built-in actors to the SQLite database via upsert_actor() (database write)
  2. list() and list_actors() intentionally skip calling ensure_built_in_actors() to avoid triggering writes from a read-only operation (bug #797)
  3. Result: after agents init --yes, running agents actor list shows "No actors configured" even though providers are configured with valid API keys

The built-in actors only appear after a write operation (e.g. actor set-default, actor add) triggers ensure_built_in_actors() as a side effect.

Current Behavior

  • agents init --yes creates the database but does not materialize built-in actors
  • agents actor list returns "No actors configured" because it does not call ensure_built_in_actors()
  • Built-in actors only appear in the database after a write operation triggers lazy materialization
  • The ActorRegistry.list() method has a comment explicitly stating it does not call ensure_built_in_actors() to avoid database writes from read-only operations (bug #797)

Expected Behavior

Built-in actors should be truly virtual — resolved on-demand from the ProviderRegistry at query time and merged with persisted custom actors in memory. No database writes should occur for built-in actors.

Actor resolution order (when looking up an actor by name):

  1. Search the database for a custom actor with the exact name
  2. If not found, resolve from configured providers as a virtual built-in actor
  3. If still not found, throw an error

This allows user-registered actors to take precedence over built-in ones (though the spec reserves provider namespaces like openai/ and anthropic/ for built-in actors, so registration of a custom actor with those namespaces should be rejected at actor add time — a pre-existing gap, not in scope here).

This means:

  • agents actor list shows built-in actors immediately after init (if providers are configured)
  • agents actor list output shows built-in actors with Built-in: ✓ column populated
  • agents actor show openai/gpt-4o works without prior materialization
  • agents actor run openai/gpt-4o "hello" works without prior materialization
  • The database only stores custom actors (those added via actor add)
  • Built-in actors cannot be removed or updated
  • set-default persists only the actor name string; the name can reference a virtual built-in actor that is resolved from the provider registry at runtime
  • All dead code and database schema artifacts related to persisting built-in actors are removed in this same change

Acceptance Criteria

  • agents actor list displays built-in actors from configured providers without any prior write operation
  • agents actor list output shows built-in actors with Built-in: ✓ column populated
  • agents actor show <provider>/<model> returns details for a built-in actor without it being persisted
  • agents actor run <provider>/<model> "prompt" executes using a virtual built-in actor
  • agents actor remove <provider>/<model> returns an error (built-in actors cannot be removed)
  • agents actor set-default <provider>/<model> persists the actor name and resolves it to the virtual built-in actor at runtime
  • agents actor get-default returns the virtual built-in actor when the persisted default name matches a provider-registry actor
  • Actor resolution follows DB-first order: database → virtual built-in → error
  • The ActorRegistry.list() and list_actors() methods resolve virtual actors without calling any persistence method
  • The ensure_built_in_actors() method is removed entirely — no code path persists built-in actors to the database
  • Custom actors (added via actor add) continue to be persisted and listed correctly
  • The is_built_in flag on the Actor model is preserved for virtual actors returned from the provider registry
  • ActorRepository.upsert_built_in() is removed (no longer needed)
  • The is_built_in column is removed from the actors database table via a new Alembic migration
  • All references to is_built_in in the repository layer are cleaned up
  • All existing Behave scenarios for actor listing pass with the new virtual behavior
  • Coverage remains ≥ 97% via nox -s coverage_report
  • Full nox suite passes with zero errors

Supporting Information

  • Specification: docs/specification.md lines 5497-5644 (agents actor list examples show built-in actors in output)
  • Specification: docs/specification.md lines 1330-1487 (agents init output does NOT include built-in actors)
  • Current code: src/cleveragents/actor/registry.py lines 164-231 (ensure_built_in_actors() persists to DB — to be removed)
  • Current code: src/cleveragents/actor/registry.py lines 439-466 (list() skips ensure_built_in_actors() — to be changed)
  • Bug reference: Bug #797 — the fix that removed ensure_built_in_actors() calls from list() to prevent read-triggered writes
  • Related: src/cleveragents/infrastructure/database/repositories.pyActorRepository has is_built_in column and upsert_built_in() method (to be removed)
  • Related: src/cleveragents/domain/models/core/actor.pyActor model has is_built_in field (keep for virtual resolution, remove from DB mapping)
  • Related: src/cleveragents/application/services/actor_service.py — service methods that persist built-in actors (to be reviewed)
  • Related: src/cleveragents/infrastructure/database/migrations/ — new migration needed to drop is_built_in column

Subtasks

  • Design: Define the virtual actor resolution strategy — how ProviderRegistry.get_configured_providers() maps to Actor domain objects in memory, including yaml_text generation for v3 compatibility
  • Refactor: Remove ensure_built_in_actors() from ActorRegistry and all call sites that invoke it
  • Refactor: Remove ActorRepository.upsert_built_in() and all repository-level built-in actor persistence logic
  • Implement: Add _resolve_virtual_builtin_actors() method to ActorRegistry that generates virtual Actor objects from configured providers (in-memory only)
  • Implement: Update ActorRegistry.list() / list_actors() to merge virtual built-in actors with persisted custom actors
  • Implement: Update ActorRegistry.get() / get_actor() to resolve virtual built-in actors by name before falling back to the database
  • Implement: Ensure set_default_actor() persists only the actor name string (no actor row created for built-ins)
  • Implement: Ensure get_default_actor() resolves the persisted name to a virtual built-in actor if it matches a configured provider
  • Implement: Ensure remove_actor() rejects built-in actor names with a clear error message
  • Implement: Ensure update() rejects built-in actor names with a clear error message
  • DB Migration: Create Alembic migration to drop is_built_in column from the actors table
  • Cleanup: Remove all dead code references to is_built_in persistence across the codebase (repository, service, registry, tests)
  • Tests (Behave): Add scenarios for virtual built-in actor listing, showing, and running
  • Tests (Behave): Add scenarios confirming built-in actors cannot be removed or updated
  • Tests (Behave): Add scenarios for set-default and get-default with virtual built-in actors
  • Tests (Robot): Add integration test confirming no database writes occur during actor list
  • Verify coverage ≥ 97% via nox -s coverage_report
  • Run full nox suite, fix any errors

Definition of Done

This issue is complete when:

  • All subtasks above are completed and checked off.
  • A Git commit is created where the first line matches the Commit Message in Metadata exactly.
  • The commit is pushed to the branch matching the Branch in Metadata exactly.
  • The commit is submitted as a PR to master, reviewed, and merged.
## Metadata ``` Commit Message: feat(actor): make built-in actors virtual, resolved on-demand from provider registry Branch: feature/m3-virtual-built-in-actors ``` ## Background and Context Built-in actors (e.g. `openai/gpt-4o`, `anthropic/claude-sonnet-4-20250514`) represent LLM providers configured via API keys. They are conceptually derived data — their existence and configuration come from the `ProviderRegistry`, not from user-created definitions. The specification (`docs/specification.md`) shows `agents actor list` output that includes built-in actors alongside custom actors, implying they should always be visible when providers are configured. However, the current implementation has an architectural contradiction: 1. `ensure_built_in_actors()` **persists** built-in actors to the SQLite database via `upsert_actor()` (database write) 2. `list()` and `list_actors()` intentionally **skip** calling `ensure_built_in_actors()` to avoid triggering writes from a read-only operation (bug #797) 3. Result: after `agents init --yes`, running `agents actor list` shows "No actors configured" even though providers are configured with valid API keys The built-in actors only appear after a write operation (e.g. `actor set-default`, `actor add`) triggers `ensure_built_in_actors()` as a side effect. ## Current Behavior - `agents init --yes` creates the database but does not materialize built-in actors - `agents actor list` returns "No actors configured" because it does not call `ensure_built_in_actors()` - Built-in actors only appear in the database after a write operation triggers lazy materialization - The `ActorRegistry.list()` method has a comment explicitly stating it does not call `ensure_built_in_actors()` to avoid database writes from read-only operations (bug #797) ## Expected Behavior Built-in actors should be **truly virtual** — resolved on-demand from the `ProviderRegistry` at query time and merged with persisted custom actors in memory. No database writes should occur for built-in actors. **Actor resolution order** (when looking up an actor by name): 1. Search the database for a custom actor with the exact name 2. If not found, resolve from configured providers as a virtual built-in actor 3. If still not found, throw an error This allows user-registered actors to take precedence over built-in ones (though the spec reserves provider namespaces like `openai/` and `anthropic/` for built-in actors, so registration of a custom actor with those namespaces should be rejected at `actor add` time — a pre-existing gap, not in scope here). This means: - `agents actor list` shows built-in actors immediately after `init` (if providers are configured) - `agents actor list` output shows built-in actors with `Built-in: ✓` column populated - `agents actor show openai/gpt-4o` works without prior materialization - `agents actor run openai/gpt-4o "hello"` works without prior materialization - The database only stores **custom** actors (those added via `actor add`) - Built-in actors cannot be removed or updated - `set-default` persists only the actor name string; the name can reference a virtual built-in actor that is resolved from the provider registry at runtime - All dead code and database schema artifacts related to persisting built-in actors are removed in this same change ## Acceptance Criteria - [ ] `agents actor list` displays built-in actors from configured providers without any prior write operation - [ ] `agents actor list` output shows built-in actors with `Built-in: ✓` column populated - [ ] `agents actor show <provider>/<model>` returns details for a built-in actor without it being persisted - [ ] `agents actor run <provider>/<model> "prompt"` executes using a virtual built-in actor - [ ] `agents actor remove <provider>/<model>` returns an error (built-in actors cannot be removed) - [ ] `agents actor set-default <provider>/<model>` persists the actor name and resolves it to the virtual built-in actor at runtime - [ ] `agents actor get-default` returns the virtual built-in actor when the persisted default name matches a provider-registry actor - [ ] Actor resolution follows DB-first order: database → virtual built-in → error - [ ] The `ActorRegistry.list()` and `list_actors()` methods resolve virtual actors without calling any persistence method - [ ] The `ensure_built_in_actors()` method is removed entirely — no code path persists built-in actors to the database - [ ] Custom actors (added via `actor add`) continue to be persisted and listed correctly - [ ] The `is_built_in` flag on the `Actor` model is preserved for virtual actors returned from the provider registry - [ ] `ActorRepository.upsert_built_in()` is removed (no longer needed) - [ ] The `is_built_in` column is removed from the `actors` database table via a new Alembic migration - [ ] All references to `is_built_in` in the repository layer are cleaned up - [ ] All existing Behave scenarios for actor listing pass with the new virtual behavior - [ ] Coverage remains ≥ 97% via `nox -s coverage_report` - [ ] Full `nox` suite passes with zero errors ## Supporting Information - **Specification**: `docs/specification.md` lines 5497-5644 (`agents actor list` examples show built-in actors in output) - **Specification**: `docs/specification.md` lines 1330-1487 (`agents init` output does NOT include built-in actors) - **Current code**: `src/cleveragents/actor/registry.py` lines 164-231 (`ensure_built_in_actors()` persists to DB — to be removed) - **Current code**: `src/cleveragents/actor/registry.py` lines 439-466 (`list()` skips `ensure_built_in_actors()` — to be changed) - **Bug reference**: Bug #797 — the fix that removed `ensure_built_in_actors()` calls from `list()` to prevent read-triggered writes - **Related**: `src/cleveragents/infrastructure/database/repositories.py` — `ActorRepository` has `is_built_in` column and `upsert_built_in()` method (to be removed) - **Related**: `src/cleveragents/domain/models/core/actor.py` — `Actor` model has `is_built_in` field (keep for virtual resolution, remove from DB mapping) - **Related**: `src/cleveragents/application/services/actor_service.py` — service methods that persist built-in actors (to be reviewed) - **Related**: `src/cleveragents/infrastructure/database/migrations/` — new migration needed to drop `is_built_in` column ## Subtasks - [ ] Design: Define the virtual actor resolution strategy — how `ProviderRegistry.get_configured_providers()` maps to `Actor` domain objects in memory, including `yaml_text` generation for v3 compatibility - [ ] Refactor: Remove `ensure_built_in_actors()` from `ActorRegistry` and all call sites that invoke it - [ ] Refactor: Remove `ActorRepository.upsert_built_in()` and all repository-level built-in actor persistence logic - [ ] Implement: Add `_resolve_virtual_builtin_actors()` method to `ActorRegistry` that generates virtual `Actor` objects from configured providers (in-memory only) - [ ] Implement: Update `ActorRegistry.list()` / `list_actors()` to merge virtual built-in actors with persisted custom actors - [ ] Implement: Update `ActorRegistry.get()` / `get_actor()` to resolve virtual built-in actors by name before falling back to the database - [ ] Implement: Ensure `set_default_actor()` persists only the actor name string (no actor row created for built-ins) - [ ] Implement: Ensure `get_default_actor()` resolves the persisted name to a virtual built-in actor if it matches a configured provider - [ ] Implement: Ensure `remove_actor()` rejects built-in actor names with a clear error message - [ ] Implement: Ensure `update()` rejects built-in actor names with a clear error message - [ ] DB Migration: Create Alembic migration to drop `is_built_in` column from the `actors` table - [ ] Cleanup: Remove all dead code references to `is_built_in` persistence across the codebase (repository, service, registry, tests) - [ ] Tests (Behave): Add scenarios for virtual built-in actor listing, showing, and running - [ ] Tests (Behave): Add scenarios confirming built-in actors cannot be removed or updated - [ ] Tests (Behave): Add scenarios for `set-default` and `get-default` with virtual built-in actors - [ ] Tests (Robot): Add integration test confirming no database writes occur during `actor list` - [ ] Verify coverage ≥ 97% via `nox -s coverage_report` - [ ] Run full `nox` suite, fix any errors ## Definition of Done This issue is complete when: - All subtasks above are completed and checked off. - A Git commit is created where the first line matches the Commit Message in Metadata exactly. - The commit is pushed to the branch matching the Branch in Metadata exactly. - The commit is submitted as a PR to master, reviewed, and merged.
Author
Member

close as dup to #10923

close as dup to #10923
hurui200320 2026-04-29 05:22:29 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#10924
No description provided.