Files
DemonGatanjieu-Anomalous_Mo…/tests/test_tool_budget.py
T
DemonGatanjieuandClaude Opus 5.5 6586c3766e Give the AI Anomalous's manual on demand, and free room in the tool list
read_guide (api/mcp_guide.py) hands the AI one topic at a time: the README's parts between
<!-- guide:name --> marks (both languages, same names; the Chinese one carries the words the
Chinese window shows), Model Check's rules by section, and the fields of show_panel's blocks.
Long topics come in parts that fit an answer. Nothing is written twice: the topics are the
README's and the rules' own text.

To make room for what 1.64 adds next: show_panel names its blocks and the guide has their
fields (a bad block's error points there), update_panel's details moved there too,
download_status became run_status(downloads=true), track_run's text is shorter. The tool list
went from 43,957 to 42,574 characters with one tool more (53 tools); show_panel no longer needs
a longer-description exception in the budget test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 00:10:51 +08:00

51 lines
2.3 KiB
Python

"""The tool budget: what AI apps are handed stays small enough for a light model (Gemini Flash is the
yardstick). On 2026-10-26 Gemini 3.8 Flash ran about 570 calls over the full set in Antigravity with
hardly a wrong one, so the set as it is, is the ceiling: a new tool, or a longer description, has to
make room by merging or cutting. Raise a number here only together with a new Flash run that
shows it still goes well (docs/architecture/tool-exam.md)."""
import json
import sys
import unittest
from pathlib import Path
PLUGIN = Path(__file__).resolve().parents[1]
sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])]
from api import mcp_server # noqa: E402
MAX_TOOLS = 53
MAX_CHARS = 44000 # the full list as AI apps get it (about 11k tokens)
MAX_DESCRIPTION = 700 # a tool's own description; details an AI needs only now and then go to read_guide
MAX_REQUIRED = 2 # what a call needs at least; the rest has a default
INSTRUCTIONS_CHARS = 3400
def listed():
return mcp_server._tool_list(True)["tools"]
class BudgetTests(unittest.TestCase):
def test_the_set_does_not_grow(self):
tools = listed()
self.assertLessEqual(len(tools), MAX_TOOLS, "a new tool: merge or cut one")
self.assertLessEqual(len(json.dumps(tools, ensure_ascii=False)), MAX_CHARS)
self.assertLessEqual(len(mcp_server.INSTRUCTIONS), INSTRUCTIONS_CHARS)
def test_each_tool_is_short_and_needs_little(self):
for tool in listed():
with self.subTest(tool=tool["name"]):
self.assertLessEqual(len(tool.get("description") or ""), MAX_DESCRIPTION)
self.assertLessEqual(len(tool["inputSchema"].get("required") or []), MAX_REQUIRED)
self.assertTrue(tool.get("description"), "every tool says what it is for")
def test_each_step_has_a_name_in_both_languages(self):
# The timeline names a step by aiTool_<tool> (web/modules/ai_task_view.js toolKey); the card's own calls are not steps.
words = (PLUGIN / "web" / "modules" / "locales.js").read_text(encoding="utf-8")
for name in set(mcp_server.TOOLS) - set(mcp_server.mcp_apps.APP_TOOLS):
with self.subTest(tool=name):
self.assertEqual(words.count(f"aiTool_{name}:"), 2)
if __name__ == "__main__":
unittest.main()