mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
fix(simulator): restore input/output block passthrough in dry-run
Re-add the passthrough logic for AgentInputBlock and AgentOutputBlock in simulate_block. These blocks are trivial passthroughs that don't need LLM simulation -- forwarding input values directly is faster, deterministic, and doesn't require API keys (which aren't available in CI).
This commit is contained in:
@@ -691,7 +691,78 @@ def test_prepare_dry_run_regular_block_returns_none():
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
# NOTE: Input/output block passthrough tests were removed because the simulator
|
||||
# no longer special-cases AgentInputBlock/AgentOutputBlock. These blocks are
|
||||
# now LLM-simulated like any other block (using the run() source code in the
|
||||
# prompt), so the passthrough assertions no longer apply.
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulate_agent_input_block_passthrough():
|
||||
"""AgentInputBlock should pass through the value directly, no LLM call."""
|
||||
from backend.blocks.io import AgentInputBlock
|
||||
|
||||
block = AgentInputBlock()
|
||||
outputs = []
|
||||
async for name, data in simulate_block(
|
||||
block, {"value": "hello world", "name": "q"}
|
||||
):
|
||||
outputs.append((name, data))
|
||||
|
||||
assert outputs == [("result", "hello world")]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulate_agent_dropdown_input_block_passthrough():
|
||||
"""AgentDropdownInputBlock (subclass of AgentInputBlock) should pass through."""
|
||||
from backend.blocks.io import AgentDropdownInputBlock
|
||||
|
||||
block = AgentDropdownInputBlock()
|
||||
outputs = []
|
||||
async for name, data in simulate_block(
|
||||
block,
|
||||
{
|
||||
"value": "Option B",
|
||||
"name": "sev",
|
||||
"placeholder_values": ["Option A", "Option B"],
|
||||
},
|
||||
):
|
||||
outputs.append((name, data))
|
||||
|
||||
assert outputs == [("result", "Option B")]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulate_agent_input_block_none_value():
|
||||
"""AgentInputBlock with value=None should yield nothing."""
|
||||
from backend.blocks.io import AgentInputBlock
|
||||
|
||||
block = AgentInputBlock()
|
||||
outputs = []
|
||||
async for name, data in simulate_block(block, {"value": None, "name": "q"}):
|
||||
outputs.append((name, data))
|
||||
|
||||
assert outputs == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulate_agent_output_block_passthrough():
|
||||
"""AgentOutputBlock should pass through value as output."""
|
||||
from backend.blocks.io import AgentOutputBlock
|
||||
|
||||
block = AgentOutputBlock()
|
||||
outputs = []
|
||||
async for name, data in simulate_block(
|
||||
block, {"value": "result text", "name": "out1"}
|
||||
):
|
||||
outputs.append((name, data))
|
||||
|
||||
assert ("output", "result text") in outputs
|
||||
assert ("name", "out1") in outputs
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulate_agent_output_block_no_name():
|
||||
"""AgentOutputBlock without name in input should still yield output."""
|
||||
from backend.blocks.io import AgentOutputBlock
|
||||
|
||||
block = AgentOutputBlock()
|
||||
outputs = []
|
||||
async for name, data in simulate_block(block, {"value": 42}):
|
||||
outputs.append((name, data))
|
||||
|
||||
assert outputs == [("output", 42)]
|
||||
|
||||
@@ -10,8 +10,8 @@ Special cases (no LLM simulation needed):
|
||||
(iterations capped to 1).
|
||||
- AgentExecutorBlock executes for real so it can spawn child graph executions
|
||||
(whose blocks are then simulated).
|
||||
- AgentInputBlock, AgentOutputBlock, and other simple blocks are simulated
|
||||
using the same LLM prompt (which includes the block's run() source code).
|
||||
- AgentInputBlock (and all subclasses) and AgentOutputBlock are pure
|
||||
passthrough -- they forward their input values directly.
|
||||
- MCPToolBlock uses a specialised LLM prompt grounded in the tool's schema.
|
||||
|
||||
OrchestratorBlock and AgentExecutorBlock are handled in manager.py via
|
||||
@@ -34,6 +34,7 @@ from collections.abc import AsyncGenerator
|
||||
from typing import Any
|
||||
|
||||
from backend.blocks.agent import AgentExecutorBlock
|
||||
from backend.blocks.io import AgentInputBlock, AgentOutputBlock
|
||||
from backend.blocks.mcp.block import MCPToolBlock
|
||||
from backend.blocks.orchestrator import OrchestratorBlock
|
||||
from backend.util.clients import get_openai_client
|
||||
@@ -387,6 +388,24 @@ async def simulate_block(
|
||||
yield output
|
||||
return
|
||||
|
||||
# Input/output blocks are pure passthrough -- they just forward their
|
||||
# input values. No LLM simulation needed.
|
||||
if isinstance(block, AgentInputBlock):
|
||||
# AgentInputBlock and all subclasses (AgentDropdownInputBlock,
|
||||
# AgentFileInputBlock, AgentShortTextInputBlock, etc.) yield
|
||||
# "result" with the provided value.
|
||||
value = input_data.get("value")
|
||||
if value is not None:
|
||||
yield "result", value
|
||||
return
|
||||
|
||||
if isinstance(block, AgentOutputBlock):
|
||||
# AgentOutputBlock passes through "value" as "output" (+ "name").
|
||||
yield "output", input_data.get("value")
|
||||
if "name" in input_data:
|
||||
yield "name", input_data["name"]
|
||||
return
|
||||
|
||||
output_schema = block.output_schema.jsonschema()
|
||||
output_properties: dict[str, Any] = output_schema.get("properties", {})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user