From 4a6f39a6561e8355d13222bfdfac254b57d14264 Mon Sep 17 00:00:00 2001 From: yuandonghao Date: Sun, 14 Jun 2026 19:58:06 +0800 Subject: [PATCH] refactor(tools): add src/tools/ package with shared _common Slice 1 Task 2 (#4082/#4071). Adds the package skeleton and moves the shared _parse_tool_args helper into src/tools/_common.py. Domain modules will import from here. tool_implementations.py is untouched at this step. --- src/tools/__init__.py | 6 ++++++ src/tools/_common.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 src/tools/__init__.py create mode 100644 src/tools/_common.py diff --git a/src/tools/__init__.py b/src/tools/__init__.py new file mode 100644 index 000000000..c431160ad --- /dev/null +++ b/src/tools/__init__.py @@ -0,0 +1,6 @@ +"""Tool implementation package, split by domain (slice 1, #4082/#4071). + +Public tool functions live in domain modules. ``src.tool_implementations`` +re-exports from here for backward compatibility. +""" +from src.tools._common import _parse_tool_args # noqa: F401 diff --git a/src/tools/_common.py b/src/tools/_common.py new file mode 100644 index 000000000..856d498f2 --- /dev/null +++ b/src/tools/_common.py @@ -0,0 +1,39 @@ +"""Shared helpers used across tool implementation domains. + +Extracted from tool_implementations.py as part of slice 1 (#4082/#4071). +Domain modules under src/tools/ import from here. +""" +import json + + +def _parse_tool_args(content): + """Parse a tool-call argument blob. + + Accepts either a JSON string or an already-decoded dict. Unwraps the + common `{"body": {...}}` envelope that smaller models emit when they + read tool descriptions like "Body is JSON: {...}" literally — they + pass `body` as a field name rather than treating it as a noun. + + Returns a dict on success, raises ValueError on bad JSON. + """ + if isinstance(content, str): + try: + args = json.loads(content) if content.strip() else {} + except (json.JSONDecodeError, TypeError) as e: + raise ValueError(str(e)) + elif isinstance(content, dict): + args = content + else: + args = {} + # Unwrap {"body": {...}} envelope — but only if `body` is the sole key + # and points at a dict. We don't want to clobber a legitimate `body` + # field on tools where it's a real arg (e.g. send_email body text). + if ( + isinstance(args, dict) + and len(args) == 1 + and "body" in args + and isinstance(args["body"], dict) + and "action" in args["body"] # extra safety: only unwrap if the inner dict looks like a tool call + ): + args = args["body"] + return args