mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 05:01:57 +00:00
e46a3f0d62
运行 87daca70:一个计时器,外层每一拍里内层跑两个 50ms 的子拍。5 秒该推进 50 拍, 结果推进了 138 拍。循环节点走 done 出口时计数原样留着,外层下一轮再把它带进来,就从 上次停下的地方接着往上数:内层 max_iterations 是 5,第一、二拍各跑 2 个子拍,第三拍 1 个,从第四拍起一进来就撞上限直接退出,子拍一次都不跑(撞上限的 warn 136 条)。 嵌套的 foreach 更糟:外层第二轮时内层游标已经在末尾,内层一项都不再跑,也不报错, 结果只是悄悄少了一大截。 改法:走 done 时记 finished,再被带进来时从 0 数。index 仍停在最后一轮,结束后读 loops.x.index 的照旧拿到轮数;画布上内层的"第 N/上限 轮"也随之每轮重新数起。 测试:test_nested_loops 两条——嵌套 while 合计 6 次(修复前 3 次)、嵌套 foreach 走完 3×2 项(修复前只有 1a、1b)。用 87daca70 的原图在临时库里真沙箱重跑:内层每拍都是 "跑、跑、结束",撞上限 0 次。后端 473 passed ×2。
106 lines
4.6 KiB
Python
106 lines
4.6 KiB
Python
"""嵌套循环:内层循环每被外层带进来一次,都从头数。
|
|
|
|
运行 87daca70:一个计时器,外层每一拍里内层跑两个 50ms 的子拍。5 秒该推进 50 拍,
|
|
结果推进了 138 拍——内层循环的计数走 done 出口时原样留着,外层下一轮再把它带进来,
|
|
就从上次停下的地方接着往上数。内层 max_iterations 是 5:第一拍用掉 2 次,第二拍
|
|
2 次,第三拍 1 次,从第四拍起一进来就撞上限直接退出,子拍一次都不跑(撞上限的
|
|
warn 136 条)。max_iterations 于是成了"整次运行合计多少轮",而不是"每进来一次
|
|
最多多少轮"。
|
|
|
|
嵌套的 foreach 更糟:外层第二轮时内层游标已经在末尾,内层一项都不再跑,没有报错,
|
|
结果只是悄悄少了一大截。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from app.db.base import SessionLocal
|
|
from app.db.models import Run
|
|
from app.engine.runner import run_manager
|
|
|
|
|
|
def node(nid, ntype, **config):
|
|
return {"id": nid, "type": ntype, "data": {"label": nid, "config": config}}
|
|
|
|
|
|
def edge(source, target, handle=None):
|
|
return {"source": source, "target": target, "sourceHandle": handle}
|
|
|
|
|
|
@pytest.fixture
|
|
async def engine_up():
|
|
await run_manager.setup()
|
|
yield
|
|
await run_manager.shutdown()
|
|
|
|
|
|
async def _finish(graph) -> Run:
|
|
run = await run_manager.start(graph=graph, input_payload={})
|
|
for _ in range(600):
|
|
async with SessionLocal() as session:
|
|
row = await session.get(Run, run.id)
|
|
if row.status in ("succeeded", "failed", "cancelled", "interrupted"):
|
|
return row
|
|
await asyncio.sleep(0.05)
|
|
raise AssertionError(f"没跑完:{row.status}")
|
|
|
|
|
|
async def test_an_inner_while_restarts_on_every_outer_round(engine_up):
|
|
"""87daca70 的形状:外层 3 拍,每拍内层 2 个子拍,合计 6 次。
|
|
|
|
内层上限给 3(够一拍用,不够三拍合计用):计数不复位的话第二拍只跑 1 次、
|
|
第三拍一次不跑,合计 3。
|
|
"""
|
|
row = await _finish({
|
|
"nodes": [
|
|
node("start", "input", fields=[]),
|
|
node("init_i", "transform", expression="0", assign_to="i"),
|
|
node("init_total", "transform", expression="0", assign_to="total"),
|
|
node("outer", "loop", mode="while", condition="vars.i < 3", max_iterations=10),
|
|
node("reset_j", "transform", expression="0", assign_to="j"),
|
|
node("inner", "loop", mode="while", condition="vars.j < 2", max_iterations=3),
|
|
node("bump_j", "transform", expression="vars.j + 1", assign_to="j"),
|
|
node("count", "transform", expression="vars.total + 1", assign_to="total"),
|
|
node("bump_i", "transform", expression="vars.i + 1", assign_to="i"),
|
|
node("out", "output", fields=[{"name": "结果", "value": "{{ vars.total }}"}]),
|
|
],
|
|
"edges": [
|
|
edge("start", "init_i"), edge("init_i", "init_total"), edge("init_total", "outer"),
|
|
edge("outer", "reset_j", "body"), edge("reset_j", "inner"),
|
|
edge("inner", "bump_j", "body"), edge("bump_j", "count"), edge("count", "inner"),
|
|
edge("inner", "bump_i", "done"), edge("bump_i", "outer"),
|
|
edge("outer", "out", "done"),
|
|
],
|
|
})
|
|
assert row.status == "succeeded", row.error
|
|
assert row.output["结果"] == "6"
|
|
|
|
|
|
async def test_an_inner_foreach_walks_its_items_on_every_outer_round(engine_up):
|
|
"""外层 3 项 × 内层 2 项。游标不复位的话,外层第二轮起内层一项都不跑,只剩 1a、1b。"""
|
|
row = await _finish({
|
|
"nodes": [
|
|
node("start", "input", fields=[]),
|
|
node("init_log", "transform", expression="[]", assign_to="log"),
|
|
node("outer", "loop", mode="foreach", items="[1, 2, 3]", item_var="o",
|
|
max_iterations=10),
|
|
node("inner", "loop", mode="foreach", items='["a", "b"]', item_var="x",
|
|
max_iterations=10),
|
|
node("add", "transform", expression="vars.log + [str(vars.o) + vars.x]",
|
|
assign_to="log"),
|
|
node("out", "output", fields=[{"name": "结果", "value": "{{ vars.log | json }}"}]),
|
|
],
|
|
"edges": [
|
|
edge("start", "init_log"), edge("init_log", "outer"),
|
|
edge("outer", "inner", "body"),
|
|
edge("inner", "add", "body"), edge("add", "inner"),
|
|
edge("inner", "outer", "done"),
|
|
edge("outer", "out", "done"),
|
|
],
|
|
})
|
|
assert row.status == "succeeded", row.error
|
|
assert json.loads(row.output["结果"]) == ["1a", "1b", "2a", "2b", "3a", "3b"]
|