dify/api/core/agent
dparkmit24 2c162109c5 test(agent): pin the colliding case and state why the rule is shaped this way
The occurrence selector tells a per-call list from a single call's list
value by length alone. One test pinned the side where that is
unambiguous -- a 3-element list against 2 calls, replayed whole. The side
that decides whether the rule is safe is the other one: a tool called n
times whose single stored value is a list of length n. That case was
decided by the rule and described by no test, so it read as an oversight
rather than a decision.

Pin it at both readers. The replay reader in base_agent_runner and the
display reader on MessageAgentThought each get a two-call record whose
one stored value is a two-element list, asserting that call 1 reads
element 0 and call 2 reads element 1 -- what the rule does today. The
name says what the case concedes rather than what it asserts.

The docstrings say why the asymmetry is tolerable. observation values
are always str: ToolEngine.agent_invoke is typed
-> tuple[str, list[str], ToolInvokeMeta] and both runners store element
0, so a list under a tool name is not a shape any writer produces and
the length check is defensive there. tool_input values are json.loads of
the model's arguments with no shape check, so a legacy list-valued input
is possible in principle, and that is the side the collision can reach.

The selector is defined twice, identically, because models/ importing
from core/agent/ is the worse layering trade and the reverse is odd.
Neither copy is in the wrong place, so each now names the other and says
the duplication is deliberate -- enough for a future editor to find both.

No behaviour change: the condition, the ordering and the fallback are
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:57:51 -07:00
..
output_parser chore: reorg imports (#35308) 2026-04-16 08:50:02 +00:00
prompt Enhance Code Consistency Across Repository with .editorconfig (#19023) 2025-04-29 18:04:33 +08:00
strategy refactor: add missing @override decorator to agent runners, tool caches, and logging extensions (#36511) 2026-05-22 06:41:48 +00:00
__init__.py FEAT: NEW WORKFLOW ENGINE (#3160) 2024-04-08 18:51:46 +08:00
base_agent_runner.py test(agent): pin the colliding case and state why the rule is shaped this way 2026-08-05 18:57:51 -07:00
cot_agent_runner.py refactor: explicit DB session propagation across backend paths (#38559) 2026-07-15 06:48:28 +00:00
cot_chat_agent_runner.py refactor: add missing @override decorator to agent runners, tool caches, and logging extensions (#36511) 2026-05-22 06:41:48 +00:00
cot_completion_agent_runner.py refactor: convert remaining isinstance chains to match/case (part 9) (#35902) (#37340) 2026-06-11 16:14:30 +00:00
entities.py chore: add ast-grep rule to convert Optional[T] to T | None (#25560) 2025-09-15 13:06:33 +08:00
errors.py refactor(workflow): move agent node back to core workflow (#33431) 2026-03-14 22:33:13 +08:00
fc_agent_runner.py fix(agent): show one log entry per call when a tool repeats in a turn 2026-08-05 09:47:17 -07:00
plugin_entities.py refactor: replace bare dict with typed annotations in app_config/extension/provider (#35099) 2026-04-14 06:11:00 +00:00
publish_visibility.py fix(agent): block unpublished agents in workflows (#39532) 2026-07-24 11:02:43 +00:00