Skip to content

WIP: \tool call hardening small models - #614

Closed
michaelneale wants to merge 2 commits into
mainfrom
micn/tool-call-hardening-small-models
Closed

WIP: \tool call hardening small models#614
michaelneale wants to merge 2 commits into
mainfrom
micn/tool-call-hardening-small-models

Conversation

@michaelneale

Copy link
Copy Markdown
Collaborator

cc @ndizazzo this is using that forge pattern/design to rescue tool calls (a new crate) - FYI, not sure if will keep this approach but showing signs of promise, may be fun to try. I imagine we need a way to make this automatic for some small models (not sure where the cut is)

michaelneale and others added 2 commits May 21, 2026 12:58
…ctx compaction

Adds a new crate `mesh-llm-guardrails` that ports forge v0.6.0's
tool-call rescue and tiered context compaction into two transparent
`OpenAiBackend` decorators:

- `GuardrailBackend` — engaged when the host's model is single-digit
  B (small tier). Rescues bare-text tool calls (JSON, rehearsal
  `tool[ARGS]{}`, Qwen3-Coder XML), validates names against the
  request's tool catalog, retries with a corrective nudge, and falls
  through as text on budget exhaustion instead of 5xx-ing.

- `CompactingBackend` — engaged when the host's `n_ctx` is small
  (default ≤ 8192). Three-phase compaction (drop nudges → drop tool
  results → drop reasoning, last gated behind a flag) plus escalating
  context-warning injection. Prefers `usage.total_tokens` reported by
  the inner backend over a char/4 heuristic once a turn has been seen
  — matches forge's ContextManager and catches dense tool-schema
  bloat that char/4 underestimates.

Wired into `SkippyModelHandle::{load_with_hooks, load_stage0_config}`
so every consumer (direct `/v1`, MoA workers, virtual-LLM consults,
`/v1/responses`) gets the wrap for free without per-call-site logic.
Wrap decision is made once at construction based on (model name, n_ctx).

Also lifts the single-digit-B heuristic from `mesh-mixture-of-agents`
into `mesh_llm_routing::is_small_tier_name` so the guardrail decorator
and the MoA router can share one source of truth. (Switching the MoA
call site to delegate is a small mechanical follow-up.)

Per-request override fields `mesh_guardrails` and `mesh_compact` in
`request.extra` follow the existing `mesh_hooks` pattern.

Out of scope (deferred):
- Streaming guardrails (buffered-tool first cut; SSE passes through).
- StepEnforcer for ingress (config exposes it; defaults empty).
- `respond()` tool injection (config exposes it; default off).
- TOML / CLI config (uses WrapConfig::default for now).
- Forge validation gates / sampling defaults (separate workstream).

Verified end-to-end: goose driving `Qwen3-8B-Q4_K_M` through the mesh
ran tool calls cleanly with no false-positive interference from the
decorator (validator saw structured `tool_calls`, all names known,
Execute path).

Tests: 43 unit tests in mesh-llm-guardrails (rescue strategies,
validator, error tracker, step enforcer, nudges, respond, compaction
phases, threshold warning, backend decorators with a scripted inner
backend, usage tracking across turns) + 2 in mesh-llm-routing for the
small-tier helper. All passing. Clippy clean. cargo fmt clean.

See docs/design/FORGE_INTEGRATION.md for the full design.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants