From 49fec3684ad100e44203ded5fa2a3c2113d0094b Mon Sep 17 00:00:00 2001 From: Re-bin Date: Sun, 15 Feb 2026 08:11:33 +0000 Subject: [PATCH] fix: use json_repair for robust LLM response parsing --- README.md | 2 +- nanobot/agent/loop.py | 9 ++++++++- nanobot/providers/litellm_provider.py | 6 ++---- pyproject.toml | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c08d3af..9066d5a 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ⚡️ Delivers core agent functionality in just **~4,000** lines of code — **99% smaller** than Clawdbot's 430k+ lines. -📏 Real-time line count: **3,656 lines** (run `bash core_agent_lines.sh` to verify anytime) +📏 Real-time line count: **3,663 lines** (run `bash core_agent_lines.sh` to verify anytime) ## 📢 News diff --git a/nanobot/agent/loop.py b/nanobot/agent/loop.py index 7deef59..6342f56 100644 --- a/nanobot/agent/loop.py +++ b/nanobot/agent/loop.py @@ -3,6 +3,7 @@ import asyncio from contextlib import AsyncExitStack import json +import json_repair from pathlib import Path from typing import Any @@ -420,9 +421,15 @@ Respond with ONLY valid JSON, no markdown fences.""" model=self.model, ) text = (response.content or "").strip() + if not text: + logger.warning("Memory consolidation: LLM returned empty response, skipping") + return if text.startswith("```"): text = text.split("\n", 1)[-1].rsplit("```", 1)[0].strip() - result = json.loads(text) + result = json_repair.loads(text) + if not isinstance(result, dict): + logger.warning(f"Memory consolidation: unexpected response type, skipping. Response: {text[:200]}") + return if entry := result.get("history_entry"): memory.append_history(entry) diff --git a/nanobot/providers/litellm_provider.py b/nanobot/providers/litellm_provider.py index a39893b..ed4cf49 100644 --- a/nanobot/providers/litellm_provider.py +++ b/nanobot/providers/litellm_provider.py @@ -1,6 +1,7 @@ """LiteLLM provider implementation for multi-provider support.""" import json +import json_repair import os from typing import Any @@ -173,10 +174,7 @@ class LiteLLMProvider(LLMProvider): # Parse arguments from JSON string if needed args = tc.function.arguments if isinstance(args, str): - try: - args = json.loads(args) - except json.JSONDecodeError: - args = {"raw": args} + args = json_repair.loads(args) tool_calls.append(ToolCallRequest( id=tc.id, diff --git a/pyproject.toml b/pyproject.toml index 17c739f..147e799 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ dependencies = [ "python-socks[asyncio]>=2.4.0", "prompt-toolkit>=3.0.0", "mcp>=1.0.0", + "json-repair>=0.30.0", ] [project.optional-dependencies]