Repository navigation
Conversation
…annot fail the request Signed-off-by: ChethanUK <chethanuk@outlook.com>
…the windowed judge cap Signed-off-by: ChethanUK <chethanuk@outlook.com>
Thanks for using CodeAnt! 🎉We're free for open-source projects. if you're enjoying it, help us grow by sharing. Share on X · |
CodeAnt-AI DescriptionKeep windowed routing requests within the judge’s size limit What Changed
Impact
💡 Usage GuideChecking Your Pull RequestEvery time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later. Talking to CodeAnt AIGot a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask: This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code. ExamplePreserve Org Learnings with CodeAntYou can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input: This helps CodeAnt AI learn and adapt to your team's coding style and standards. ExampleRetrigger reviewAsk CodeAnt AI to review the PR again, by typing: Check Your Repository HealthTo analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health. |
|
chethanuk
left a comment
There was a problem hiding this comment.
Independent review of 6a7c3f7 against base 3fbfdff. This is a comment review because GitHub does not allow approving your own PR.
Verdict: REVISE, PR body only. I found no code defect that must block this fork PR. The cap and tool pairing held in everything I ran. Two limits in the body read smaller than what I measured, and three known limits are missing from it.
Fix in the PR body
-
"Twenty or more parallel tool results" only holds for very short ids. The test fixtures use 6-character ids. With results sent as separate messages, a follow-up before the group, and results of 1000 to 40000 characters, I measured:
Call shape Last N that kept the window First N that left only the task 6-char id, search19 20 29-char id, read_file16 18 (17 not run) 40-char id, mcp__filesystem__read_file15 16 The result size made no difference. The body should say about 16, and that the number falls as ids and tool names get longer. With all results in one message, N up to 64 kept the window.
-
"A long run of large tool turns" understates the follow-up loss. With
[task, "ok", follow-up, P call and result pairs of 40000 chars]and a window of 50, the follow-up stays at P = 3 and is gone at P = 4. Four large reads after a user message is ordinary for a coding agent. A small window such as 6 already loses the follow-up at 3 pairs before the cap runs, so the cap adds this loss only for larger windows. -
Add these limits. They are missing from the body:
- The count leaves out JSON envelopes and the tool ids on results. In one probe, 15551 counted characters were 17840 characters as serialized messages. The judge system prompt is also outside the cap, so "judge payload" means the messages only.
- A result whose call sits before the opening task, sent as the newest message, leaves only the task, because
startnever moves atllm_class.rs:230. I read this from the code and did not run it. - An opening message with only tool results, too many to fit, becomes an empty text block (
llm_class.rs:211-212,unwrap_or_default). Some providers may reject that. I read this from the code, did not run it, and did not call a provider. It is also the one case where "the opening task is always kept" does not hold. The input is unusual.
Non-blocking
- Replacing an oversized unknown block with a note could split a provider's server tool pair, and no provider was tested. I did not test it either.
- Any window message over
room / (group + 1).max(4)is shortened even when dropping one old message would have been enough. The newest message can be cut to about 4470 characters this way. This follows from the design, and it is a tradeoff to know about. - A task just over the cap is cut to about half even when little else needs the room (
llm_class.rs:206). - Results in one message get far less than the room: 10 results of 5000 characters came out at 5120 characters in total.
docs/reference/toml_schema.mddescribesrecent_turn_windowin several rows and does not mention the cap. The routing doc does. Adding it there would pass the 100-line limit.- 506 test lines for 96 non-test lines in
llm_class.rsmay draw a request to trim. The rows do test distinct behavior throughTaskInput, so I would keep them. The table has no row for a realistic id length, an orphan result as the newest message, or the empty text fallback.
What I checked
- Correctness:
cap_windowruns only when a window is set.startmoves only when no result is waiting for a call, so no kept result loses its call.usedstops atroom, and the opening message is held to half the cap, so the content total stays withinWINDOW_CHARS.sharecannot divide by zero, anddraincannot invert. The window walk andtrack_tool_pairskeep the old block order. - All 4 uses of
TaskInputgo throughmessages, so the capability, custom and decision judges share the cap.trim_messageshas no other caller. - Scope: 3 files. Non-test additions are 96 in
llm_class.rs, 2 inescalation.rsand 1 in the docs, 99 in total. It stays under the limit by one line, and any fix to the first item above would need a matching removal. - Title, body and commits: I searched them, and the added lines, for issue numbers, close keywords, upstream links and mentions. I found none. Both commits are one line with a DCO sign-off and no other trailers. The body keeps the What, Why and Notes template sections and says no issue is linked.
Commands run
cargo test -p switchyard-libsy --lib algorithms::llm_class: 34 passed, exit 0. This matches the body.cargo test -p switchyard-libsy --lib: 443 passed, exit 0.cargo clippy -p switchyard-libsy --all-targets -- -D warnings: exit 0.cargo fmt --all --check: exit 0.git merge-treeagainst the latest upstream main, 5 commits ahead of this base: clean, exit 0.- PR checks: all passed, and the Pages deploy was skipped.
- Probes: 4 throwaway print tests in a scratch copy with its own build directory, exit 0. They are not in this PR.
Not run locally: cargo test --workspace, pytest, or any real provider. CI ran its own Rust and Python jobs.
| fn block_chars(block: &ContentBlock) -> usize { | ||
| match block { | ||
| ContentBlock::Text { text } => text.chars().count(), | ||
| ContentBlock::ToolResult(result) => result.content.iter().map(block_chars).sum(), |
There was a problem hiding this comment.
Suggestion: Tool result IDs and error flags are uncounted, so many empty results can make the forwarded judge messages exceed the intended character cap.
Assessment: 🟠 Major · 🔁 Occurrence: Rarely · 🏷️ Incomplete implementation
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** crates/libsy/src/algorithms/llm_class.rs
**Line:** 172:172
**Comment:**
*Incomplete Implementation: Tool result IDs and error flags are uncounted, so many empty results can make the forwarded judge messages exceed the intended character cap.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix| other => { | ||
| *other = ContentBlock::Text { | ||
| text: "[content omitted: too long]".to_string(), | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
Suggestion: A long Refusal contains text that could be shortened, but this branch replaces it entirely and loses that context from the windowed judge.
Assessment: 🟠 Major · 🔁 Occurrence: Rarely · 🏷️ Logic error
Prompt for AI Agent 🤖
This is a comment left during a code review.
**Path:** crates/libsy/src/algorithms/llm_class.rs
**Line:** 187:192
**Comment:**
*Logic Error: A long `Refusal` contains text that could be shortened, but this branch replaces it entirely and loses that context from the windowed judge.
Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix…, and correct the cap docs Signed-off-by: ChethanUK <chethanuk@outlook.com>
What
recent_turn_windowis set. Long text is shortened in the middle. Tool arguments, images and other blocks that cannot be cut are replaced with a short note. If the messages still do not fit, the oldest window messages are dropped. The opening message stays, though its content can be cut.track_tool_pairs), and the drop-oldest step keeps a tool call and its results together.MAX_REQUEST_CHARSandtruncate_middlein the escalation code becomepub(crate)so the windowed path reuses the same 18000 value and the same truncation.recent_turn_windowdocs table describes the cap and what it can drop or cut.This is a fixed character cap on the windowed path only. It is not a route-level knob and not a token budget, which others have asked for. Unwindowed requests are unchanged. Routes that set
recent_turn_windowdo change when they go over the cap. For example, a task with a 200000-character base64 image reaches the judge as its text and the note.Why
With
recent_turn_windowset, one large tool result or message can push the judge request past what the judge model accepts, and the judge call fails. By default (fail_open = true) the route then picks the capable target without a judge decision. Withfail_open = falsethe request gets the error. The escalation judge already bounds its input at 18000 characters, but the windowed path for capability, custom and decision judges had no bound.Which issue does this address?
None linked in this fork PR.
Notes for reviewers
Start at
cap_windowincrates/libsy/src/algorithms/llm_class.rs. Tests cover a single message larger than the cap, a tool call and result at the window start staying paired, one character over the cap, content under the cap staying unchanged, a follow-up surviving four 40000-character results sent in one message, an opening message of only tool results becoming empty text, and the structured judge request.Test run at 9f94343:
cargo fmt --all --checkandcargo clippy --workspace --all-targets -- -D warningsexit 0.cargo test --workspaceexits 0, with 1168 passed, 0 failed and 1 ignored.cargo test -p switchyard-libsy --lib algorithms::llm_classgives 34 passed, 0 failed.uv run pytest tests/ -vexits 1 at the first failure, becauseaddoptsinpyproject.tomlsets-x.uv run pytest tests/ -v -o addopts=exits 1, with 177 passed and 2 failed. Both are proxy tests intests/e2ethat got permission denied on the Docker socket, and both also fail on the clean base. A third test,test_interrupt_stops_only_owned_children[2-130]intests/test_nemo_gym_run.py, hit a 20-second timeout on the clean base and on the previous head, and passed in this run.Limits. The numbers come from throwaway tests, which are not in this PR. They ran against the same non-test code as this commit, since the last commit changes only tests and docs.
search, 18 with 29-character ids andread_file, and 16 with 40-character ids andmcp__filesystem__read_file. Result size from 1000 to 40000 characters made no difference. With all results in one message, 96 results kept the window in all three shapes, and 128 kept it only with the 6-character ids.server_tool_useblock stayed and itsweb_search_tool_resultbecame the note. Whether a provider rejects this was not tested. A rejection fails the judge call, with the outcome described under Why.docs/reference/toml_schema.mdanddocs/routing_algorithms/stage_router_routing.mddo not mention the cap.docs/routing_algorithms/llm_classifier_routing.mddoes. Its row now says a later user message can be dropped, a very large tool group or a newest tool result whose call came before the opening message can leave only the opening message, and the opening message can be cut to the start of its text, or to empty text if it holds only tool results.