Skip to content

Handle malformed NEAR streaming tool args as repairable - #5867

Merged
serrrfirat merged 1 commit into
mainfrom
codex/nearai-streaming-tool-args-repair
Jul 9, 2026
Merged

serrrfirat merged 1 commit into
mainfrom
codex/nearai-streaming-tool-args-repair

Conversation

@serrrfirat

@serrrfirat serrrfirat commented Jul 9, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • preserve malformed streamed NEAR tool-call arguments as arguments_parse_error instead of failing the provider stream
  • treat streamed empty arguments fields as malformed while preserving the existing omitted-arguments {} fallback
  • reject malformed provider tool arguments in the Reborn model gateway before capability registration/execution
  • route those failures through the existing repairable invalid tool-output path with safe placeholder arguments
  • include the full raw malformed streamed argument string in the model-only repair tool result so the model has maximum context to recover
  • keep raw malformed arguments out of the safe summary/log path and out of replayed structured tool-call fields

Root cause

The streamed NEAR parser concatenated tool-call argument deltas and strict-parsed them after [DONE]. If NEAR emitted malformed JSON after assistant text had already streamed, the provider returned InvalidResponse; the retry layer refused to retry after emitted text, so the run failed instead of giving the model a repair turn. A present-but-empty streamed arguments field also collapsed to {}, which could bypass the repair metadata.

Recovery behavior

For malformed streamed NEAR tool arguments, the gateway now rejects the whole provider tool-call batch before any capability is registered or executed. The repair request sent back to the model replays the assistant tool call with safe placeholder arguments, then attaches a tool result containing the parse failure plus the full raw malformed argument text verbatim. That raw detail is model-only repair context; safe summaries/logs use only the sanitized first parse-error line.

Testing tier note

The malformed-argument repair behavior sits at the provider/gateway seam before capability registration. The Reborn integration harness does not currently expose a production-wired fixture that can inject provider-internal ToolCall.arguments_parse_error metadata from a streamed provider response. The crate-tier llm_gateway test drives the production LlmProviderModelGateway::stream_model_with_capabilities_and_progress caller boundary with a streaming provider fake and the real gateway capability surface fake, which is the narrowest reachable seam for this provider metadata path without adding test-only integration wiring.

Validation

  • cargo fmt --check
  • git diff --check
  • cargo test -p ironclaw_llm complete_with_tools_streaming -- --nocapture
  • cargo test -p ironclaw_reborn --features root-llm-provider,libsql-secrets,libsql-restart-tests,webui-user-store provider_tool_arguments_parse_error_summary -- --nocapture
  • cargo test -p ironclaw_reborn --features root-llm-provider,libsql-secrets,libsql-restart-tests,webui-user-store --test llm_gateway gateway_repairs -- --nocapture
  • cargo clippy -p ironclaw_llm --all-targets --all-features -- -D warnings
  • cargo clippy -p ironclaw_reborn --features root-llm-provider,libsql-secrets,libsql-restart-tests,webui-user-store --test llm_gateway -- -D warnings

@ironloopai

ironloopai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

🔎 IronLoop Review Status

Head: f87811adeeb8cbda2ecffbaf473482d567ae15de
Result: No reviewer jobs are scheduled yet.
Next: Run @ironloopai review to start reviewers.
Updated: 2026-07-09T11:39:03.432Z

Current reviewers:

Reviewer State Verdict Findings Last update
none Queued N/A No reviewer jobs scheduled yet. N/A
Reviewer summaries
Reviewer Detail
none No reviewer jobs scheduled yet.
Recent activity
Time Reviewer State Detail
N/A N/A Waiting No progress events recorded yet.
Available commands
  • @ironloopai help
  • @ironloopai agents
  • @ironloopai review
  • @ironloopai review --agent <agent>
  • @ironloopai status
Run metadata

Admission: webhook accepted the request and IronLoop persisted reviewer state before this projection.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of malformed tool-call arguments so requests can still complete instead of failing outright.
    • Added safer recovery when tool arguments can’t be parsed, preserving the original call flow and returning a usable fallback.
    • Better repair behavior for provider tool errors, including clearer recovery from invalid or empty tool arguments.
    • Expanded test coverage for streamed and non-streamed tool-call repair scenarios.

Walkthrough

Streaming tool-call argument parsing now preserves malformed arguments as parse errors. The gateway rejects those tool calls as invalid output, marks them repairable, and reconstructs repair requests with an invalid-arguments payload. Tests cover batch and streaming repair paths.

Changes

Malformed tool-call arguments handling

Layer / File(s) Summary
Lossy provider parsing
crates/ironclaw_llm/src/nearai_chat.rs
Streaming tool-call parsing uses parse_tool_call_args_lossy, stores JSON parse failures in arguments_parse_error, and keeps blank arguments as an empty object; the streaming test now expects a successful tool call with the parse error preserved.
Gateway rejection and repair payloads
crates/ironclaw_reborn/src/model_gateway.rs
Provider tool calls carrying arguments_parse_error are rejected as InvalidOutput with sanitized safe summaries, treated as repairable, and rebuilt for repair with the dedicated invalid-marker arguments payload while clearing the parse-error field.
Repair-path tests
crates/ironclaw_reborn/tests/llm_gateway.rs
Repair helpers are added and gateway tests now cover malformed provider tool arguments before registration in batch and streaming mode, including repaired reply selection, usage accumulation, no capability registration, and rejected tool-result content.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • nearai/ironclaw#4805: Shares the same gateway repair-before-registration flow and invalid-arguments replay shape in model_gateway.rs and llm_gateway.rs.
  • nearai/ironclaw#5292: Also changes provider_tool_call_from_llm validation at the model boundary in crates/ironclaw_reborn/src/model_gateway.rs.

Suggested reviewers: think-in-universe, henrypark133

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning It covers summary/root cause/validation, but omits most required template sections like Change Type, Linked Issue, Security Impact, Rollback Plan, and Review Track. Add the missing template sections and fill them out, especially Change Type, Linked Issue, Security Impact, Blast Radius, Rollback Plan, and Review Track.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly matches the PR's main change: malformed NEAR streaming tool args are made repairable.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5867 July 9, 2026 10:29 Destroyed
@github-actions github-actions Bot added scope: docs Documentation size: XL 500+ changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jul 9, 2026
@serrrfirat
serrrfirat force-pushed the codex/nearai-streaming-tool-args-repair branch from 84dc29b to d57a255 Compare July 9, 2026 10:31
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5867 July 9, 2026 10:31 Destroyed

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces streaming support across the LLM provider, gateway, and WebUI layers, specifically implementing OpenAI-compatible SSE streaming for the NEAR AI chat provider. It adds streaming retry logic, progress-tracking sinks, and event subscription capabilities to allow live assistant text updates to stream directly to the browser. Feedback on the changes highlights a performance and network efficiency bottleneck in ProviderStreamSink::text_delta, where cloning, sanitizing, and sending the entire accumulated text on every delta results in quadratic complexity.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

I am having trouble creating individual review comments. Click here to see my feedback.

crates/ironclaw_reborn/src/model_gateway.rs (1065-1080)

high

Performance & Network Efficiency Bottleneck ($O(N^2)$ Complexity)

In ProviderStreamSink::text_delta, on every single token/character delta received from the LLM provider, the entire accumulated string is cloned and passed to sanitize_model_visible_text (which performs regex/pattern scanning for secrets), and then the entire accumulated string is sent over the network via safe_text_update.

This introduces two major issues:

  1. CPU Overhead: For a response of $N$ tokens, cloning and scanning the entire growing string on every delta results in $O(N^2)$ complexity, which will severely bottleneck CPU performance on long responses.
  2. Network Bandwidth: Since the full accumulated text is sent on every single token, the total data transferred over the SSE connection grows quadratically.

Recommendation

If this implementation is intentionally inefficient for the sake of initial correctness, please document the deferred optimization (e.g., incremental updates) as a known future task. Otherwise, consider implementing a throttling mechanism or incremental updates to avoid the $O(N^2)$ overhead.

References
  1. When an implementation is intentionally inefficient for the sake of initial correctness, document the deferred optimization (e.g., incremental updates) as a known future task.

@github-actions github-actions Bot added size: M 50-199 changed lines and removed size: XL 500+ changed lines labels Jul 9, 2026
@serrrfirat
serrrfirat marked this pull request as ready for review July 9, 2026 10:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_reborn/src/model_gateway.rs`:
- Around line 1826-1834: Use a fixed safe summary for provider parse errors in
provider_tool_arguments_parse_error_summary and
HostManagedModelError::safe_summary: do not return any provider-authored parse
text even when it matches the failed to parse tool-call arguments JSON prefix,
and always fall back to PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY. Keep the
detailed parse_error content only in the repair-message path that builds the
tool-call retry message, so the summary stays fully fixed and safe while the
repair flow still gets the full error context.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80c86249-f3c6-4357-a12e-0c38d54347a1

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd5aa8 and d57a255.

📒 Files selected for processing (3)
  • crates/ironclaw_llm/src/nearai_chat.rs
  • crates/ironclaw_reborn/src/model_gateway.rs
  • crates/ironclaw_reborn/tests/llm_gateway.rs

Comment thread crates/ironclaw_reborn/src/model_gateway.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
crates/ironclaw_reborn/src/model_gateway.rs (1)

1827-1837: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Safe-summary leak: provider parse-error text still passes through unchanged.

This reintroduces the exact issue flagged on a prior commit of this PR: when the sanitized parse error starts with the prefix and passes LoopSafeSummary::new, it's returned verbatim as the safe_summary. sanitize_model_visible_text only redacts secret-shaped tokens, not arbitrary provider-authored text, so ordinary serde_json messages (line/column info, "trailing characters", etc.) leak through as host safe-summary content. As per path instructions, "When constructing CapabilityResultMessage.safe_summary or any host safe-summary, use fixed host-authored text only; do not interpolate model/user-controlled data such as ... provider strings." The detailed message is already carried separately through the repair-message path, so this passthrough isn't needed.

🔒 Proposed fix: always return the fixed summary
 fn provider_tool_arguments_parse_error_summary(parse_error: &str) -> String {
-    let sanitized = sanitize_model_visible_text(parse_error.to_string());
-    let sanitized = sanitized.trim();
-    if sanitized.starts_with(PROVIDER_TOOL_ARGUMENTS_PARSE_ERROR_PREFIX)
-        && LoopSafeSummary::new(sanitized).is_ok()
-    {
-        sanitized.to_string()
-    } else {
-        PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY.to_string()
-    }
+    let _ = parse_error; // detailed text flows through the repair-message path only
+    PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY.to_string()
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_reborn/src/model_gateway.rs` around lines 1827 - 1837, The
safe-summary helper currently lets provider-authored parse-error text pass
through unchanged when it matches the prefix and satisfies LoopSafeSummary::new.
Update provider_tool_arguments_parse_error_summary to always return the fixed
host-authored PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY instead of forwarding
sanitized parse_error content, keeping any detailed provider text only in the
separate repair-message path.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@crates/ironclaw_reborn/src/model_gateway.rs`:
- Around line 1827-1837: The safe-summary helper currently lets
provider-authored parse-error text pass through unchanged when it matches the
prefix and satisfies LoopSafeSummary::new. Update
provider_tool_arguments_parse_error_summary to always return the fixed
host-authored PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY instead of forwarding
sanitized parse_error content, keeping any detailed provider text only in the
separate repair-message path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c5bc59c3-e84a-44bb-95c2-a090fade6e64

📥 Commits

Reviewing files that changed from the base of the PR and between d57a255 and d06d2ab.

📒 Files selected for processing (3)
  • crates/ironclaw_llm/src/nearai_chat.rs
  • crates/ironclaw_reborn/src/model_gateway.rs
  • crates/ironclaw_reborn/tests/llm_gateway.rs

@railway-app

railway-app Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-5867 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Jul 9, 2026 at 11:47 am

@github-actions

github-actions Bot commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

Coverage ratchet

Ratchet mode: ENFORCING

RATCHET PASS: global
  observed: 85.13% (283983 / 333600 lines)
  floor:    85.3% (tolerance 0.5pp -> effective floor 84.8%)
  denominator: 333600 lines now vs 320188 at floor capture (+13412 lines, +4.19%) — not a material change

⚠️ 3 Reborn crate(s) have 0 int-tier coverage (target: 0) — ironclaw_prompt_envelope, ironclaw_scripts, ironclaw_skill_learning

Reborn integration-tier coverage

Line coverage (Reborn crates): 85.13% — 283983 / 333600 lines

Per-crate breakdown (65 crates, lowest-covered first)
Crate Line % Covered / Total
ironclaw_prompt_envelope 0% 0 / 88
ironclaw_scripts 0% 0 / 347
ironclaw_skill_learning 0% 0 / 61
ironclaw_wasm_sandbox_core 7.37% 7 / 95
ironclaw_runtime_policy 33.2% 80 / 241
ironclaw_event_projections 43.34% 673 / 1553
ironclaw_run_state 52.36% 222 / 424
ironclaw_authorization 53.54% 461 / 861
ironclaw_triggers 59.79% 1740 / 2910
ironclaw_observability 61.54% 16 / 26
ironclaw_webui_v2 62.62% 2632 / 4203
ironclaw_reborn_cli 62.84% 3816 / 6073
ironclaw_mcp 63.15% 581 / 920
ironclaw_reborn_migration 67.01% 1172 / 1749
ironclaw_memory 67.12% 747 / 1113
ironclaw_dispatcher 67.15% 92 / 137
ironclaw_filesystem 67.44% 3815 / 5657
ironclaw_trust 72.88% 661 / 907
ironclaw_capabilities 74.08% 1658 / 2238
ironclaw_wasm_limiter 74.6% 47 / 63
ironclaw_reborn_event_store 74.61% 958 / 1284
ironclaw_extractors 74.72% 538 / 720
ironclaw_first_party_extensions 77.62% 5411 / 6971
ironclaw_llm 78.16% 20072 / 25680
ironclaw_product_context 78.57% 11 / 14
ironclaw_wasm_product_adapters 80.58% 1510 / 1874
ironclaw_process_sandbox 80.65% 671 / 832
ironclaw_reborn_openai_compat 80.95% 956 / 1181
ironclaw_memory_native 81.86% 3226 / 3941
ironclaw_wasm 82.54% 950 / 1151
ironclaw_secrets 82.7% 2791 / 3375
ironclaw_events 83.47% 1762 / 2111
ironclaw_processes 84.06% 965 / 1148
ironclaw_turns 84.26% 13127 / 15579
ironclaw_host_api 85.17% 2549 / 2993
ironclaw_product_workflow 85.34% 10748 / 12594
ironclaw_projects 85.92% 659 / 767
ironclaw_network 86.12% 670 / 778
ironclaw_common 86.46% 1514 / 1751
ironclaw_threads 86.62% 4132 / 4770
ironclaw_slack_v2_adapter 86.79% 1806 / 2081
ironclaw_auth 86.94% 2995 / 3445
ironclaw_reborn_config 86.98% 1730 / 1989
ironclaw_product_adapters 86.98% 3207 / 3687
ironclaw_reborn_identity 87.03% 557 / 640
ironclaw_skills 87.35% 4336 / 4964
ironclaw_hooks 87.84% 9916 / 11289
ironclaw_product_adapter_registry 87.96% 526 / 598
ironclaw_reborn_traces 88.21% 11931 / 13526
ironclaw_extensions 88.26% 2631 / 2981
ironclaw_reborn_composition 88.76% 70139 / 79019
ironclaw_host_runtime 88.94% 17522 / 19700
ironclaw_reborn 89.15% 15846 / 17774
ironclaw_conversations 90% 2924 / 3249
ironclaw_approvals 90.51% 1507 / 1665
ironclaw_event_streams 91.48% 1009 / 1103
ironclaw_loop_support 92.44% 14731 / 15936
ironclaw_attachments 93.06% 630 / 677
ironclaw_resources 93.09% 4637 / 4981
ironclaw_reborn_webui_ingress 93.19% 2217 / 2379
ironclaw_telegram_v2_adapter 93.87% 2452 / 2612
ironclaw_agent_loop 94.58% 8776 / 9279
ironclaw_safety 94.8% 3668 / 3869
ironclaw_first_party_extension_ports 95% 3094 / 3257
ironclaw_outbound 95.59% 3556 / 3720

This table itself is informational and never gates the PR on its own — not the percentage, not the per-crate holes, not the 0-coverage callout. A separate coverage ratchet (dry-run until enforce=true; see tests/integration/coverage-floor.toml) can fail the build on specific configured floors.

Exemptions (4 entry/entries excluded from the accounting above)
Module / Crate Reason Issue
crate: ironclaw_embeddings v1-only: consumed only by root ironclaw (src/app.rs, src/tools/builtin/memory.rs, src/workspace/mod.rs, src/config/{mod,embeddings}.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_gateway v1-only: consumed only by root ironclaw (src/channels/web/platform/static_files.rs, src/channels/web/handlers/frontend.rs); no crates/* dependents. Covered by "Tests (Legacy)". #5657
crate: ironclaw_oauth v1-only: consumed only by root ironclaw (src/auth/oauth.rs); no crates/* dependents. Crate's own doc comment confirms v1-only. Covered by "Tests (Legacy)". #5657
crate: ironclaw_tui v1-only: consumed only by root ironclaw (src/main.rs, src/channels/tui.rs); no crates/* dependents. Crate's own doc comment confirms it bridges INTO v1, not Reborn. Covered by "Tests (Legacy)". #5657

@serrrfirat
serrrfirat force-pushed the codex/nearai-streaming-tool-args-repair branch from d06d2ab to 122004c Compare July 9, 2026 11:25
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5867 July 9, 2026 11:25 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/ironclaw_llm/src/nearai_chat.rs (1)

1562-1593: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

into_tool_call is now infallible but still returns Result.

parse_tool_call_args_lossy (crates/ironclaw_llm/src/tool_args.rs:60-71) never returns an Err, so every path in into_tool_call now produces Ok(...). The Result<Option<ToolCall>, LlmError> signature and the caller's state.into_tool_call()? (line 641) are vestigial and imply a failure mode that no longer exists.

♻️ Proposed simplification
-    fn into_tool_call(self) -> Result<Option<ToolCall>, LlmError> {
+    fn into_tool_call(self) -> Option<ToolCall> {
         if self.id.is_empty()
             && self.name.is_empty()
             && self.arguments.is_empty()
             && !self.arguments_delta_seen
         {
-            return Ok(None);
+            return None;
         }
         let (arguments, arguments_parse_error) =
             if self.arguments.is_empty() && !self.arguments_delta_seen {
                 (serde_json::Value::Object(Default::default()), None)
             } else {
                 parse_tool_call_args_lossy(&self.arguments)
             };
-        Ok(Some(ToolCall {
+        Some(ToolCall {
             id: self.id,
             name: self.name,
             arguments,
             reasoning: None,
             signature: None,
             arguments_parse_error,
-        }))
+        })
     }

And at the call site: if let Some(tool_call) = state.into_tool_call() { ... }.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_llm/src/nearai_chat.rs` around lines 1562 - 1593,
`NearAiStreamingToolCallState::into_tool_call` is now infallible, so remove the
unnecessary `Result`/`LlmError` from its signature and return `Option<ToolCall>`
directly. Update the logic to keep returning `None` for the empty state and
`Some(ToolCall { ... })` otherwise, and change the caller that currently uses
`state.into_tool_call()?` to handle the plain `Option` (for example with `if let
Some(tool_call) = ...`). Also remove any now-unused `LlmError` import or related
error handling in `nearai_chat.rs`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ironclaw_reborn/src/model_gateway.rs`:
- Around line 1941-1950: The repairability check in
is_provider_tool_arguments_parse_error_summary is stringly-typed and can
silently break if safe_summary wording changes. Update
HostManagedModelError/HostManagedModelErrorKind handling so
is_repairable_provider_tool_output_error and
is_provider_tool_arguments_parse_error_summary rely on a typed enum variant or
dedicated error kind instead of starts_with/== on safe_summary, then keep the
InvalidOutput classification wired through that typed signal.

---

Outside diff comments:
In `@crates/ironclaw_llm/src/nearai_chat.rs`:
- Around line 1562-1593: `NearAiStreamingToolCallState::into_tool_call` is now
infallible, so remove the unnecessary `Result`/`LlmError` from its signature and
return `Option<ToolCall>` directly. Update the logic to keep returning `None`
for the empty state and `Some(ToolCall { ... })` otherwise, and change the
caller that currently uses `state.into_tool_call()?` to handle the plain
`Option` (for example with `if let Some(tool_call) = ...`). Also remove any
now-unused `LlmError` import or related error handling in `nearai_chat.rs`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1bf6d69f-db21-43b6-8d7b-836bb4f344e9

📥 Commits

Reviewing files that changed from the base of the PR and between d06d2ab and 122004c.

📒 Files selected for processing (3)
  • crates/ironclaw_llm/src/nearai_chat.rs
  • crates/ironclaw_reborn/src/model_gateway.rs
  • crates/ironclaw_reborn/tests/llm_gateway.rs

Comment on lines 1941 to 1950
fn is_repairable_provider_tool_output_error(error: &HostManagedModelError) -> bool {
error.kind == HostManagedModelErrorKind::InvalidOutput
&& is_provider_arguments_too_large_summary(&error.safe_summary)
&& (is_provider_arguments_too_large_summary(&error.safe_summary)
|| is_provider_tool_arguments_parse_error_summary(&error.safe_summary))
}

fn is_provider_tool_arguments_parse_error_summary(safe_summary: &str) -> bool {
safe_summary.starts_with(PROVIDER_TOOL_ARGUMENTS_PARSE_ERROR_PREFIX)
|| safe_summary == PROVIDER_TOOL_ARGUMENTS_INVALID_SUMMARY
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔵 Trivial | ⚖️ Poor tradeoff

Repairability classification keyed off safe_summary string equality.

is_provider_tool_arguments_parse_error_summary matches on starts_with/== against the safe-summary string rather than a typed error variant/kind on HostManagedModelError. This mirrors the pre-existing is_provider_arguments_too_large_summary pattern, so it's consistent with current file conventions, but as per coding guidelines, "If code matches on string literals, the underlying type should be an enum; do not leave it as a stringly-typed value." Any future safe-summary wording change silently breaks the retry classification with no compiler signal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_reborn/src/model_gateway.rs` around lines 1941 - 1950, The
repairability check in is_provider_tool_arguments_parse_error_summary is
stringly-typed and can silently break if safe_summary wording changes. Update
HostManagedModelError/HostManagedModelErrorKind handling so
is_repairable_provider_tool_output_error and
is_provider_tool_arguments_parse_error_summary rely on a typed enum variant or
dedicated error kind instead of starts_with/== on safe_summary, then keep the
InvalidOutput classification wired through that typed signal.

Source: Coding guidelines

@serrrfirat
serrrfirat force-pushed the codex/nearai-streaming-tool-args-repair branch from 122004c to f87811a Compare July 9, 2026 11:39
@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5867 July 9, 2026 11:39 Destroyed
@github-actions github-actions Bot added size: L 200-499 changed lines and removed size: M 50-199 changed lines labels Jul 9, 2026
@serrrfirat
serrrfirat merged commit bf717e7 into main Jul 9, 2026
62 checks passed
@serrrfirat
serrrfirat deleted the codex/nearai-streaming-tool-args-repair branch July 9, 2026 12:00

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-5867 — f87811ad Deployed Jul 9, 2026 by railway-app[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: docs Documentation size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant