diff --git a/README.md b/README.md
index ac72b24ac2..ff86ea6022 100644
--- a/README.md
+++ b/README.md
@@ -2279,6 +2279,11 @@ DeerFlow is model-agnostic — it works with any LLM that implements the OpenAI-
- **Multimodal inputs** for image understanding and video comprehension
- **Strong tool-use** for reliable function calling and structured outputs
+MindIE XML tool-call arguments support signed, fractional, and exponent JSON
+numbers. If a number overflows, underflows to zero, or exceeds Python's integer
+digit limit, the complete argument stays a string, including nested JSON lists
+and objects, so parsing does not silently change its value.
+
## Embedded Python Client
For `DeerFlowClient(agent_name="researcher")`, the named agent's `mcp_plugins`
diff --git a/backend/packages/harness/deerflow/models/AGENTS.md b/backend/packages/harness/deerflow/models/AGENTS.md
index 7b187a00ac..5b181276d4 100644
--- a/backend/packages/harness/deerflow/models/AGENTS.md
+++ b/backend/packages/harness/deerflow/models/AGENTS.md
@@ -1,3 +1,13 @@
+### MindIE XML numeric arguments (`mindie_provider.py`)
+
+The XML parser recognizes JSON numeric syntax, including signed, fractional,
+and exponent values. Invalid numeric forms such as `007`, `3.`, and `+3` remain
+strings. Numeric conversion failures preserve the entire argument, including
+lists or objects with overflowing or underflowing floats or oversized integers.
+Only JSON syntax errors may use the existing Python-literal fallback; never retry
+numeric conversion failures through `ast.literal_eval`, which can silently turn
+nested underflow into zero. Regression coverage is in `tests/test_mindie_provider.py`.
+
### Model Factory (`packages/harness/deerflow/models/factory.py`)
Request-admission waits follow the next scheduled admission and configured
diff --git a/backend/packages/harness/deerflow/models/mindie_provider.py b/backend/packages/harness/deerflow/models/mindie_provider.py
index 7c6d68364f..7ceae51be2 100644
--- a/backend/packages/harness/deerflow/models/mindie_provider.py
+++ b/backend/packages/harness/deerflow/models/mindie_provider.py
@@ -1,15 +1,42 @@
import ast
import html
import json
+import math
import re
import uuid
from collections.abc import Iterator
+from decimal import Decimal, InvalidOperation
import httpx
from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage, ToolMessage
from langchain_core.outputs import ChatGenerationChunk, ChatResult
from langchain_openai import ChatOpenAI
+_JSON_NUMBER_RE = re.compile(r"^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$")
+
+
+def _parse_json_float(value: str) -> float:
+ """Parse a JSON float without silently overflowing or underflowing."""
+ try:
+ precise = Decimal(value)
+ except InvalidOperation as exc:
+ raise ValueError(f"invalid JSON number: {value}") from exc
+ converted = float(precise)
+ if not math.isfinite(converted) or (precise != 0 and converted == 0.0):
+ raise ValueError(f"JSON number is outside the representable float range: {value}")
+ return converted
+
+
+def _safe_literal(value: object) -> bool:
+ """Reject non-finite floats nested in an AST fallback value."""
+ if isinstance(value, float):
+ return math.isfinite(value)
+ if isinstance(value, list):
+ return all(_safe_literal(item) for item in value)
+ if isinstance(value, dict):
+ return all(_safe_literal(item) for item in value.values())
+ return True
+
def _fix_messages(messages: list) -> list:
"""Sanitize incoming messages for MindIE compatibility.
@@ -122,14 +149,22 @@ def _parse_xml_tool_call_to_dict(content: str) -> tuple[str, list[dict]]:
# Attempt to deserialize string values into native Python types
# to satisfy downstream Pydantic validation.
parsed_value = raw_value
- if raw_value.startswith(("[", "{")) or raw_value in ("true", "false", "null") or raw_value.isdigit():
+ if raw_value.startswith(("[", "{")) or raw_value in ("true", "false", "null") or _JSON_NUMBER_RE.fullmatch(raw_value):
try:
- parsed_value = json.loads(raw_value)
+ parsed_value = json.loads(raw_value, parse_float=_parse_json_float)
except json.JSONDecodeError:
try:
- parsed_value = ast.literal_eval(raw_value)
+ candidate = ast.literal_eval(raw_value)
+ if _safe_literal(candidate):
+ parsed_value = candidate
except (ValueError, SyntaxError):
pass
+ except ValueError:
+ # Preserve the entire argument when JSON numeric conversion
+ # rejects overflow, underflow, or the integer digit limit.
+ # Retrying containers with literal_eval would turn nested
+ # underflowing numbers into zero and bypass this validation.
+ pass
args[key] = parsed_value
diff --git a/backend/tests/test_mindie_provider.py b/backend/tests/test_mindie_provider.py
index ad7fa3d86e..cd0276593e 100644
--- a/backend/tests/test_mindie_provider.py
+++ b/backend/tests/test_mindie_provider.py
@@ -330,6 +330,66 @@ class TestParseXmlToolCalls:
_, calls = _parse_xml_tool_call_to_dict(content)
assert calls[0]["args"]["n"] == 42
+ @pytest.mark.parametrize(
+ ("raw_value", "expected"),
+ [("-3", -3), ("3.14", 3.14), ("1e-3", 1e-3)],
+ )
+ def test_signed_fractional_and_exponent_params_deserialised(self, raw_value, expected):
+ content = f"{raw_value}"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == expected
+
+ @pytest.mark.parametrize("raw_value", ["9" * 5000, "1e400", "1e-400"])
+ def test_unsafe_numeric_params_stay_strings(self, raw_value):
+ content = f"{raw_value}"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == raw_value
+
+ def test_leading_zero_param_stays_string(self):
+ content = "007"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == "007"
+
+ def test_trailing_dot_param_stays_string(self):
+ content = "3."
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == "3."
+
+ def test_leading_plus_param_stays_string(self):
+ content = "+3"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == "+3"
+
+ @pytest.mark.parametrize(
+ "raw_value",
+ ["[1e-400]", '{"n":1e-400}', '[{"n":-1e-400}]', '{"n":[0.5,1e-400]}', '{"n":[1e400]}'],
+ )
+ def test_unsafe_nested_json_numbers_preserve_entire_argument(self, raw_value):
+ content = f"{raw_value}"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == raw_value
+
+ @pytest.mark.parametrize(
+ ("raw_value", "expected"),
+ [
+ ("[0.0,1e-308,5e-324]", [0.0, 1e-308, 5e-324]),
+ ('{"n":-1e-3,"zero":0.0}', {"n": -1e-3, "zero": 0.0}),
+ ],
+ )
+ def test_representable_nested_json_numbers_deserialised(self, raw_value, expected):
+ content = f"{raw_value}"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == expected
+
+ @pytest.mark.parametrize(
+ ("raw_value", "expected"),
+ [("[True,1.5]", [True, 1.5]), ("{'n':1.5}", {"n": 1.5}), ("[1e400,]", "[1e400,]")],
+ )
+ def test_python_literal_fallback_preserves_finite_value_policy(self, raw_value, expected):
+ content = f"{raw_value}"
+ _, calls = _parse_xml_tool_call_to_dict(content)
+ assert calls[0]["args"]["n"] == expected
+
def test_list_param_deserialised(self):
content = '["a","b"]'
_, calls = _parse_xml_tool_call_to_dict(content)