feat(copilot): add dummy agent generator for testing#12071
Conversation
WalkthroughThis PR introduces a dummy agent generator module for testing purposes, adding stub implementations of core agent generation functions. A new configuration flag enables switching between external and dummy modes, with service-layer routing logic to delegate calls appropriately based on the active mode. Changes
Sequence DiagramsequenceDiagram
participant Client
participant Service
participant DummyImpl
participant ExternalAPI
Client->>Service: decompose_goal() / generate_agent() / etc.
activate Service
Service->>Service: _is_dummy_mode()?
alt Dummy Mode Enabled
Service->>DummyImpl: delegate to dummy implementation
activate DummyImpl
DummyImpl->>DummyImpl: log & construct response
DummyImpl-->>Service: return mock result
deactivate DummyImpl
else External Mode
Service->>ExternalAPI: call actual external service
activate ExternalAPI
ExternalAPI-->>Service: return actual result
deactivate ExternalAPI
end
Service-->>Client: return result
deactivate Service
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| patched = current_agent.copy() | ||
| patched["description"] = ( | ||
| f"{current_agent.get('description', '')} (updated: {update_request})" |
There was a problem hiding this comment.
shallow copy may not preserve nested structures in agent JSON
current_agent.copy() creates a shallow copy - nested dicts/lists are shared references. If the agent has nested nodes or links, modifying the description could potentially affect the original agent in unexpected ways.
| patched = current_agent.copy() | |
| patched["description"] = ( | |
| f"{current_agent.get('description', '')} (updated: {update_request})" | |
| patched = copy.deepcopy(current_agent) | |
| patched["description"] = ( |
| logger.info("Using dummy agent generator for customize_template") | ||
| customized = template_agent.copy() | ||
| customized["description"] = ( |
There was a problem hiding this comment.
shallow copy may not preserve nested structures
Same shallow copy issue - use copy.deepcopy() instead of .copy() for complex nested agent structures.
| logger.info("Using dummy agent generator for customize_template") | |
| customized = template_agent.copy() | |
| customized["description"] = ( | |
| customized = copy.deepcopy(template_agent) | |
| customized["description"] = ( |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In
`@autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py`:
- Around line 90-97: The function decompose_goal_dummy returns a shallow copy of
DUMMY_DECOMPOSITION_RESULT which leaks nested mutable state; change it to return
a deep copy (e.g., use copy.deepcopy on DUMMY_DECOMPOSITION_RESULT) or
reconstruct the dict inline so callers cannot mutate the module-level constant,
and ensure you add the necessary import for copy if using deepcopy.
🧹 Nitpick comments (2)
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.py (1)
104-123:_is_dummy_mode()is called on every request — consider caching the result.
_is_dummy_mode()reads from the settings singleton on every external call. Since the dummy flag won't change at runtime, you could cache the resolved boolean once instead of re-reading and re-checking on every invocation. This is a minor optimization and readability nit — not blocking.Optional: cache dummy mode at module level
-_dummy_mode_warned = False +_dummy_mode: bool | None = None def _is_dummy_mode() -> bool: """Check if dummy mode is enabled for testing.""" - global _dummy_mode_warned - settings = _get_settings() - is_dummy = bool(settings.config.agentgenerator_use_dummy) - if is_dummy and not _dummy_mode_warned: - logger.warning( - "Agent Generator running in DUMMY MODE - returning mock responses. " - "Do not use in production!" - ) - _dummy_mode_warned = True - return is_dummy + global _dummy_mode + if _dummy_mode is None: + settings = _get_settings() + _dummy_mode = bool(settings.config.agentgenerator_use_dummy) + if _dummy_mode: + logger.warning( + "Agent Generator running in DUMMY MODE - returning mock responses. " + "Do not use in production!" + ) + return _dummy_modeautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py (1)
37-39: Hardcoded block IDs duplicate definitions inbackend/blocks/io.py.These constants match the source definitions (
c0a8e994-ebf1-4a9c-a4d8-89d09c86741band363ae599-353e-4804-937e-b2ee3cef3da4), but duplicating them creates a maintenance risk. If the block IDs in io.py ever change, these hardcoded references will silently become stale and break the agent generator.Consider importing directly from
backend.blocks.io(e.g.,from backend.blocks.io import AgentInputBlock, AgentOutputBlock) and referencing their.idattributes, or at minimum add a comment linking to the canonical import path (backend.blocks.io:AgentInputBlockandbackend.blocks.io:AgentOutputBlock) so the relationship is explicit.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/util/settings.py
🧰 Additional context used
📓 Path-based instructions (6)
autogpt_platform/backend/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development
Files:
autogpt_platform/backend/backend/util/settings.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
autogpt_platform/backend/**/*.{py,txt}
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use
poetry runprefix for all Python commands, including testing, linting, formatting, and migrations
Files:
autogpt_platform/backend/backend/util/settings.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
autogpt_platform/backend/backend/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings
Files:
autogpt_platform/backend/backend/util/settings.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
autogpt_platform/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Format Python code with
poetry run format
Files:
autogpt_platform/backend/backend/util/settings.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
autogpt_platform/backend/backend/api/features/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Update routes in '/backend/backend/api/features/' and add/update Pydantic models in the same directory for API development
When modifying API routes, update corresponding Pydantic models in the same directory and write tests alongside the route file
Files:
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
autogpt_platform/backend/backend/api/**/*.py
📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)
autogpt_platform/backend/backend/api/**/*.py: Use FastAPI for building REST and WebSocket endpoints
Use JWT-based authentication with Supabase integration
Files:
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.pyautogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py
🧬 Code graph analysis (1)
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.py (1)
autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py (6)
customize_template_dummy(127-138)decompose_goal_dummy(90-97)generate_agent_dummy(100-108)generate_agent_patch_dummy(111-124)get_blocks_dummy(141-147)health_check_dummy(150-152)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: types
- GitHub Check: test (3.12)
- GitHub Check: test (3.13)
- GitHub Check: test (3.11)
- GitHub Check: Check PR Status
🔇 Additional comments (5)
autogpt_platform/backend/backend/util/settings.py (1)
371-374: LGTM!Clean addition, logically grouped with the other
agentgenerator_*fields, sensible default ofFalse, and consistent use ofFieldwith description.autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py (2)
42-87: LGTM!The helper generates a fresh, structurally valid minimal agent each call with unique UUIDs, and the node/link wiring is internally consistent.
100-152: LGTM!The remaining dummy functions (
generate_agent_dummy,generate_agent_patch_dummy,customize_template_dummy,get_blocks_dummy,health_check_dummy) are clean stubs with signatures matching the real service call sites.autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/service.py (2)
15-27: LGTM — clean routing setup.Unconditional import is fine (negligible overhead from loading dummy stubs), and the one-time warning via
_dummy_mode_warnedis a nice touch to prevent log noise.
167-168: LGTM — consistent dummy-mode routing across all external functions.Each function gates on
_is_dummy_mode()before any HTTP client usage, argument forwarding matches the dummy signatures, and the early return prevents any network I/O in dummy mode.Also applies to: 259-262, 335-338, 426-429, 493-494, 529-530
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| async def decompose_goal_dummy( | ||
| description: str, | ||
| context: str = "", | ||
| library_agents: list[dict[str, Any]] | None = None, | ||
| ) -> dict[str, Any]: | ||
| """Return dummy decomposition result.""" | ||
| logger.info("Using dummy agent generator for decompose_goal") | ||
| return DUMMY_DECOMPOSITION_RESULT.copy() |
There was a problem hiding this comment.
Shallow copy of DUMMY_DECOMPOSITION_RESULT leaks shared mutable nested state.
dict.copy() is shallow — the "steps" list (and its inner dicts) remains shared with the module-level constant. If any downstream caller mutates the nested structures (e.g., appending to steps or modifying a step dict), it corrupts the constant for all subsequent calls.
Use copy.deepcopy or reconstruct the dict inline.
Proposed fix
+import copy
+
async def decompose_goal_dummy(
description: str,
context: str = "",
library_agents: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
"""Return dummy decomposition result."""
logger.info("Using dummy agent generator for decompose_goal")
- return DUMMY_DECOMPOSITION_RESULT.copy()
+ return copy.deepcopy(DUMMY_DECOMPOSITION_RESULT)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async def decompose_goal_dummy( | |
| description: str, | |
| context: str = "", | |
| library_agents: list[dict[str, Any]] | None = None, | |
| ) -> dict[str, Any]: | |
| """Return dummy decomposition result.""" | |
| logger.info("Using dummy agent generator for decompose_goal") | |
| return DUMMY_DECOMPOSITION_RESULT.copy() | |
| import copy | |
| async def decompose_goal_dummy( | |
| description: str, | |
| context: str = "", | |
| library_agents: list[dict[str, Any]] | None = None, | |
| ) -> dict[str, Any]: | |
| """Return dummy decomposition result.""" | |
| logger.info("Using dummy agent generator for decompose_goal") | |
| return copy.deepcopy(DUMMY_DECOMPOSITION_RESULT) |
🤖 Prompt for AI Agents
In
`@autogpt_platform/backend/backend/api/features/chat/tools/agent_generator/dummy.py`
around lines 90 - 97, The function decompose_goal_dummy returns a shallow copy
of DUMMY_DECOMPOSITION_RESULT which leaks nested mutable state; change it to
return a deep copy (e.g., use copy.deepcopy on DUMMY_DECOMPOSITION_RESULT) or
reconstruct the dict inline so callers cannot mutate the module-level constant,
and ensure you add the necessary import for copy if using deepcopy.
|
This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request. |
316822a to
b9aac42
Compare
Summary
Add an optional dummy agent generator that returns mock responses matching the expected format from the external Agent Generator service. This enables local CoPilot testing without needing the external service running.
Changes
settings.py: Addagentgenerator_use_dummysetting (default:false)dummy.py(new): Mock implementations of all service functions:decompose_goal_dummy()- Returns static instructionsgenerate_agent_dummy()- Returns minimal valid 2-node agent (Input → Output)generate_agent_patch_dummy()- Returns current agent with updated descriptioncustomize_template_dummy()- Returns template with updated descriptionget_blocks_dummy()- Returns AgentInputBlock and AgentOutputBlockhealth_check_dummy()- Always returns healthyservice.py: Route to dummy functions whenAGENTGENERATOR_USE_DUMMY=trueis_external_service_configured()to return true in dummy modeUsage
Set environment variable:
Or in
.env:Testing
AGENTGENERATOR_USE_DUMMY=trueNotes
false- requires explicit opt-inbackend/blocks/io.pyRequested by @majdyz
Greptile Overview
Greptile Summary
Adds a dummy mode for the Agent Generator service to enable local testing without the external service. When
AGENTGENERATOR_USE_DUMMY=true, all agent generator functions return mock responses with valid structure.Key Changes
agentgenerator_use_dummyboolean setting insettings.py(defaults tofalse)dummy.pywith mock implementations returning minimal valid agent structuresservice.pyto check dummy mode and route to dummy functions when enabledIssues Found
generate_agent_patch_dummy()andcustomize_template_dummy()- using.copy()instead ofcopy.deepcopy()could cause unintended mutations of nested agent structuresConfidence Score: 4/5
generate_agent_patch_dummyandcustomize_template_dummy. These should usecopy.deepcopy()instead of.copy()for nested structures. The feature is opt-in (defaultfalse) with clear warnings, reducing production risk.dummy.pylines 120 and 133 - fix shallow copy issues before mergingSequence Diagram
sequenceDiagram participant Client as CoPilot Client participant Core as core.py participant Service as service.py participant Dummy as dummy.py participant External as External Agent Generator Client->>Core: decompose_goal("description") Core->>Service: is_external_service_configured() alt Dummy Mode Enabled Service->>Service: _is_dummy_mode() returns True Service-->>Core: True (configured) Core->>Service: decompose_goal_external() Service->>Service: Check _is_dummy_mode() Service->>Dummy: decompose_goal_dummy() Dummy-->>Service: {type: "instructions", steps: [...]} Service-->>Core: Mock response else External Service Service-->>Core: True (configured) Core->>Service: decompose_goal_external() Service->>External: POST /api/decompose-description External-->>Service: Real response Service-->>Core: Agent data end Core-->>Client: Agent instructions/questions