fix(flows): exempt workflow proposal tools from tokenjuice compaction (canvas renders ≥4-node graphs)#4888
Conversation
… so the canvas renders ≥4-node graphs extract_workflow_proposal (flows/ops.rs) scans agent history for a tool result JSON containing "type": "workflow_proposal". But ToolOutputMiddleware::after_tool ran the payload summarizer and tinyjuice compaction on every tool result first — tinyjuice tabulates any uniform object-array of >=3 rows over ~512 bytes into a `[json table: ...]` marker, stripping the "type" field. Small graphs (under threshold) survived; graphs with >=4 nodes got tabulated and the extractor found nothing, so the canvas rendered blank. Add COMPACTION_EXEMPT_TOOLS (propose_workflow, revise_workflow, edit_workflow, save_workflow, create_workflow) and skip both the payload-summarizer and tokenjuice stages for these tools' results, so their self-describing JSON reaches agent.history() byte-for-byte. The per-tool char cap and shared byte-cap backstop are untouched. Tests: extract_workflow_proposal_survives_large_graph (+2 sibling cases) in flows/ops_tests.rs; tool_output_leaves_propose_workflow_byte_for_byte_intact (+ baseline tabulation proof, exempt-tool-name coverage, and a COMPACTION_EXEMPT_TOOLS membership check) in tinyagents/middleware.rs.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough
ChangesWorkflow payload preservation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant ToolResult
participant ToolOutputMiddleware
participant Compaction
participant Truncation
ToolResult->>ToolOutputMiddleware: provide tool name and payload
ToolOutputMiddleware->>ToolOutputMiddleware: classify exemptions
ToolOutputMiddleware->>Compaction: process non-exempt payloads
ToolOutputMiddleware->>Truncation: truncate non-exempt payloads
Possibly related PRs
Suggested labels: Suggested reviewers: Poem
Comment |
…mpt sampling tools from compaction PR tinyhumansai#4888 exempted workflow proposal tools from tokenjuice compaction (steps 1-2 of ToolOutputMiddleware::after_tool) but left the per-tool char cap and shared ~16KiB byte-budget backstop (steps 3-4) in place. A >=10-node proposal routinely exceeds that budget, gets truncated at a UTF-8 boundary, and fails the whole-string JSON parse both flows::ops::extract_workflow_proposal and the frontend's parseWorkflowProposal do -- a second, size-triggered cause of the blank-canvas bug, distinct from the tabulation one tinyhumansai#4888 fixed. Also exempt get_tool_output_sample/get_tool_contract from compaction (steps 1-2) only: tokenjuice's array-elision tabulation hides the real response shape these tools exist to reveal, causing the model to derive a wrong/nonexistent split_out.path. They stay subject to the byte-cap backstop since their payload is intermediate context, not the turn's final output, and can be genuinely large. Split into is_compaction_exempt (proposal + sampling tools) and is_truncation_exempt (proposal tools only) so each tool family's exemption scope is explicit and independently testable.
…tabulation-main # Conflicts: # src/openhuman/flows/ops_tests.rs
… (canvas renders ≥4-node graphs) (tinyhumansai#4888)
Summary
The
workflow_buildercopilot's proposal never reached the canvas for non-trivial graphs. Root cause:extract_workflow_proposal(ops.rs) reads the post-compaction agent history, butToolOutputMiddleware::after_toolruns tinyjuice on every tool result first — and the JSON compressor tabulates any uniform node-array ≥3 rows / ~512 bytes into[json table: graph.nodes …], stripping thetype:"workflow_proposal"marker. Soserde_jsonfails, extraction returnsNone,has_proposal=false, blank canvas. Small graphs slipped under the threshold and rendered; ≥4-node graphs vanished (nondeterministically — one build recovered viatinyjuice_retrieve+ re-propose, another silently died).Solution
Exempt the proposal-/persistence-emitting tools (
propose_workflow,revise_workflow,edit_workflow,save_workflow,create_workflow) from both compaction passes (payload summarizer + tokenjuice) inToolOutputMiddleware::after_tool. Their output is a machine-readable contract that downstream extraction + the frontend canvas parse structurally; compacting it serves no purpose. The per-tool char cap and shared byte-budget backstop are untouched.Testing
extract_workflow_proposaltests (6-node graph survives, latest-of-many, ignores non-proposals).openhuman::flows::→ 363 pass;openhuman::tinyagents::middleware::→ 39 pass.GGML_NATIVE=OFF cargo checkclean;cargo fmt --checkclean.Acceptance criteria
Summary by CodeRabbit
Bug Fixes
Tests