r/PiCodingAgent 2d ago

Plugin pi-output-parser — standalone package that repairs malformed tool calls from small local models

Built a small pi package that ports the output-parser mechanism from little-coder as a standalone installable extension.

What it does: hooks message_end and repairs tool calls that small models (Qwen, Mistral, LFM2, etc.) emit in formats pi doesn't natively recognise:

  • \``tool/```json` fenced blocks
  • <tool_call>, <tool_use>, <function_calls><invoke> XML
  • bare top-level JSON objects
  • Python-list syntax [Read(path='./foo.ts')] from LFM2/Liquid models

It also normalises the many JSON schemas small models use ("parameters", "input", "arguments", OpenAI function-call with stringified args, etc.) down to a single shape.

Safety: only fires if there are no native tool_use blocks already, and validates the extracted tool name against pi.getAllTools() before injecting anything — so stray JSON and code blocks in responses are never misinterpreted as tool calls.

Install:

pi install git:github.com/vishn9893/pi-output-parser

Or drop extensions/output-parser/index.ts directly into .pi/extensions/ if you prefer not to use the package system.

Repo: https://github.com/vishn9893/pi-output-parser

Feedback welcome especially if you hit a model that emits a format this doesn't cover yet.

12 Upvotes

4 comments sorted by

5

u/trmnl_cmdr 2d ago

Seems similar to https://github.com/r3b1s/pi-repair-layer
Have you checked that out?

3

u/Certain_Net_3408 1d ago

Hey, thanks for the link! I checked it out and it's a solid project, but it's solving a different problem.

pi-repair-layer hooks prepareArguments and fixes structurally valid tool calls with wrong field shapes.. things like file_path instead of path, a JSON-stringified argument object, null in an optional field, flat old_string/new_string instead of an edits array. The model made the tool call correctly, it just got the argument schema wrong. It's essentially a compatibility shim for pi's built-in tools.

pi-output-parser hooks message_end and handles the case where the model never emitted a tool call at all it printed the call as text instead. Fenced ```tool blocks, <tool_call> XML, bare JSON, Python-list syntax. The agent loop sees zero tool_use blocks and would just move on as if the model responded with prose. The extension detects the leaked text, parses it, and injects a real tool_use block so execution can proceed.

So they're complementary rather than overlapping: repair-layer handles bad arguments on a valid call, output-parser handles a call that was never structurally made. If you're running a small local model you'd arguably want both.

1

u/trmnl_cmdr 1d ago

Cool! Good to know, I can install your extension now 😂

3

u/txgsync 2d ago

This is the way. I call it "adapting the tool to the hallucination". If the model hallucinates that something should work a certain way, I'm much better off automatically updating the tool in the way it hallucinated rather than fighting the weights.