File size: 3,565 Bytes
c63bc31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Tests for the tool registry."""

import pytest

from cortex_ai.tools import ToolError, ToolRegistry, default_registry, registry_from_names, tool


def test_default_registry_exposes_expected_tools():
    reg = default_registry()
    assert set(reg.names()) == {"calculate", "cortex_identity", "current_time", "text_stats"}
    assert len(reg) == 4


def test_openai_schema_shape():
    reg = default_registry()
    schemas = {s["function"]["name"]: s for s in reg.to_openai_schemas()}
    calc = schemas["calculate"]
    assert calc["type"] == "function"
    assert calc["function"]["parameters"]["type"] == "object"
    assert calc["function"]["parameters"]["properties"]["expression"]["type"] == "string"
    assert calc["function"]["parameters"]["required"] == ["expression"]


def test_calculate_returns_the_right_value():
    reg = default_registry()
    assert reg.call("calculate", {"expression": "2*(3+4)"}) == "14"
    assert reg.call("calculate", {"expression": "10/4"}) == "2.5"


def test_calculate_rejects_non_arithmetic_input():
    reg = default_registry()
    # A rejected expression surfaces as an ERROR string, not an exception: the
    # model must be able to read the failure and react to it.
    assert reg.call("calculate", {"expression": "__import__('os')"}).startswith("ERROR:")
    assert reg.call("calculate", {"expression": "9**9**9"}).startswith("ERROR:")
    assert "exponentiation" in reg.call("calculate", {"expression": "9**9**9"})
    assert reg.call("calculate", {"expression": "1/0"}).startswith("ERROR:")


def test_calculate_rejects_oversized_input():
    reg = default_registry()
    assert reg.call("calculate", {"expression": "1+" * 200 + "1"}).startswith("ERROR:")


def test_arguments_may_be_a_json_string():
    reg = default_registry()
    assert reg.call("calculate", '{"expression": "3+4"}') == "7"


def test_unknown_tool_raises():
    reg = default_registry()
    with pytest.raises(ToolError, match="unknown tool"):
        reg.call("does_not_exist", {})


def test_missing_required_argument_raises():
    reg = default_registry()
    with pytest.raises(ToolError, match="missing required argument"):
        reg.call("calculate", {})


def test_unknown_argument_raises():
    reg = default_registry()
    with pytest.raises(ToolError, match="unknown argument"):
        reg.call("calculate", {"expression": "1+1", "bogus": 1})


def test_invalid_json_arguments_raise():
    reg = default_registry()
    with pytest.raises(ToolError, match="not valid JSON"):
        reg.call("calculate", "{not json")


def test_registry_from_names_restricts_and_orders():
    reg = registry_from_names(["text_stats", "calculate"])
    assert reg.names() == ["calculate", "text_stats"]


def test_registry_from_empty_names_returns_everything():
    assert len(registry_from_names([])) == 4


def test_cortex_identity_tool_reports_project_attribution():
    from cortex_ai.identity import IDENTITY_STATEMENT

    assert default_registry().call("cortex_identity", {}) == IDENTITY_STATEMENT


def test_custom_tool_via_decorator():
    @tool(description="Ajoute deux entiers.")
    def ajouter(a: int, b: int) -> str:
        return str(a + b)

    reg = ToolRegistry([ajouter])
    assert reg.names() == ["ajouter"]
    assert reg.call("ajouter", {"a": 2, "b": 3}) == "5"
    assert reg.to_openai_schemas()[0]["function"]["parameters"]["required"] == ["a", "b"]


def test_tool_returning_a_dict_is_json_encoded():
    reg = default_registry()
    out = reg.call("text_stats", {"text": "un deux trois"})
    assert '"mots": 3' in out