mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-28 14:07:55 +00:00
* fix(channels): wire channel→agent handler and fix Telegram send pipeline * format code * add supported tests
182 lines
5.9 KiB
Python
182 lines
5.9 KiB
Python
"""Tests for the shared build_tool_descriptions() builder."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from openjarvis.core.types import ToolResult
|
|
from openjarvis.tools._stubs import (
|
|
BaseTool,
|
|
ToolSpec,
|
|
build_tool_descriptions,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class _CalcTool(BaseTool):
|
|
tool_id = "calculator"
|
|
|
|
@property
|
|
def spec(self) -> ToolSpec:
|
|
return ToolSpec(
|
|
name="calculator",
|
|
description="Evaluate a mathematical expression safely.",
|
|
parameters={
|
|
"type": "object",
|
|
"properties": {
|
|
"expression": {
|
|
"type": "string",
|
|
"description": (
|
|
"Math expression to evaluate (e.g. '2+3*4', 'sqrt(16)')"
|
|
),
|
|
},
|
|
},
|
|
"required": ["expression"],
|
|
},
|
|
category="math",
|
|
cost_estimate=0.0,
|
|
latency_estimate=0.0,
|
|
)
|
|
|
|
def execute(self, **params) -> ToolResult:
|
|
return ToolResult(tool_name="calculator", content="0", success=True)
|
|
|
|
|
|
class _WebSearchTool(BaseTool):
|
|
tool_id = "web_search"
|
|
|
|
@property
|
|
def spec(self) -> ToolSpec:
|
|
return ToolSpec(
|
|
name="web_search",
|
|
description="Search the web for real-time information.",
|
|
parameters={
|
|
"type": "object",
|
|
"properties": {
|
|
"query": {
|
|
"type": "string",
|
|
"description": "Search query string",
|
|
},
|
|
"max_results": {
|
|
"type": "integer",
|
|
"description": "Maximum results to return",
|
|
},
|
|
},
|
|
"required": ["query"],
|
|
},
|
|
category="utility",
|
|
cost_estimate=0.001,
|
|
latency_estimate=2.0,
|
|
)
|
|
|
|
def execute(self, **params) -> ToolResult:
|
|
return ToolResult(tool_name="web_search", content="", success=True)
|
|
|
|
|
|
class _NoCategoryTool(BaseTool):
|
|
tool_id = "think"
|
|
|
|
@property
|
|
def spec(self) -> ToolSpec:
|
|
return ToolSpec(
|
|
name="think",
|
|
description="Internal reasoning scratchpad.",
|
|
parameters={
|
|
"type": "object",
|
|
"properties": {
|
|
"thought": {"type": "string"},
|
|
},
|
|
},
|
|
)
|
|
|
|
def execute(self, **params) -> ToolResult:
|
|
return ToolResult(tool_name="think", content="", success=True)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestBuildToolDescriptions:
|
|
def test_empty_list_returns_no_tools(self):
|
|
assert build_tool_descriptions([]) == "No tools available."
|
|
|
|
def test_single_tool_name_present(self):
|
|
result = build_tool_descriptions([_CalcTool()])
|
|
assert "### calculator" in result
|
|
|
|
def test_description_present(self):
|
|
result = build_tool_descriptions([_CalcTool()])
|
|
assert "Evaluate a mathematical expression safely." in result
|
|
|
|
def test_parameter_type_and_required(self):
|
|
result = build_tool_descriptions([_CalcTool()])
|
|
assert "expression" in result
|
|
assert "string" in result
|
|
assert "required" in result
|
|
|
|
def test_parameter_description(self):
|
|
result = build_tool_descriptions([_CalcTool()])
|
|
assert "Math expression to evaluate" in result
|
|
|
|
def test_optional_parameter_no_required_marker(self):
|
|
result = build_tool_descriptions([_WebSearchTool()])
|
|
# max_results is optional
|
|
assert "max_results" in result
|
|
# The line for max_results should not say "required"
|
|
for line in result.splitlines():
|
|
if "max_results" in line:
|
|
assert "required" not in line
|
|
|
|
def test_category_shown_by_default(self):
|
|
result = build_tool_descriptions([_CalcTool()])
|
|
assert "Category: math" in result
|
|
|
|
def test_category_hidden_when_disabled(self):
|
|
result = build_tool_descriptions([_CalcTool()], include_category=False)
|
|
assert "Category:" not in result
|
|
|
|
def test_empty_category_not_shown(self):
|
|
result = build_tool_descriptions([_NoCategoryTool()])
|
|
assert "Category:" not in result
|
|
|
|
def test_cost_hidden_by_default(self):
|
|
result = build_tool_descriptions([_WebSearchTool()])
|
|
assert "Cost estimate:" not in result
|
|
|
|
def test_cost_shown_when_enabled(self):
|
|
result = build_tool_descriptions([_WebSearchTool()], include_cost=True)
|
|
assert "Cost estimate:" in result
|
|
assert "Latency estimate:" in result
|
|
|
|
def test_zero_cost_not_shown(self):
|
|
result = build_tool_descriptions([_CalcTool()], include_cost=True)
|
|
# cost_estimate is 0.0, so should not show
|
|
assert "Cost estimate:" not in result
|
|
|
|
def test_multiple_tools(self):
|
|
result = build_tool_descriptions([_CalcTool(), _WebSearchTool()])
|
|
assert "### calculator" in result
|
|
assert "### web_search" in result
|
|
|
|
def test_tool_without_parameters(self):
|
|
"""Tool with empty parameters dict."""
|
|
|
|
class _EmptyParamTool(BaseTool):
|
|
tool_id = "noop"
|
|
|
|
@property
|
|
def spec(self) -> ToolSpec:
|
|
return ToolSpec(name="noop", description="Does nothing.")
|
|
|
|
def execute(self, **params) -> ToolResult:
|
|
return ToolResult(tool_name="noop", content="", success=True)
|
|
|
|
result = build_tool_descriptions([_EmptyParamTool()])
|
|
assert "### noop" in result
|
|
assert "Does nothing." in result
|
|
# No Parameters section since no properties
|
|
assert "Parameters:" not in result
|