File size: 2,266 Bytes
73b4c3f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d8fa02
73b4c3f
9d8fa02
 
 
 
 
e0bff0f
 
73b4c3f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import pytest

from hackathon_advisor.tool_contracts import (
    ToolCall,
    ToolContractError,
    parse_xml_tool_call,
    resolve_tool_call,
    tool_schemas,
    validate_tool_call,
)


def test_tool_schemas_are_model_ready() -> None:
    schemas = tool_schemas()

    assert len(schemas) >= 8
    assert schemas[0]["type"] == "function"
    assert {schema["function"]["name"] for schema in schemas} >= {
        "search_projects",
        "find_whitespace",
        "save_idea",
        "score_idea",
        "make_plan",
        "set_goals",
    }
    schema_text = str(schemas)
    assert "set_target" not in schema_text
    assert "side_quests" not in schema_text
    assert "prize" not in schema_text.lower()
    assert "badge" not in schema_text.lower()
    assert "Build Small" not in schema_text
    assert "hackathon" not in schema_text.lower()


def test_parse_and_validate_minicpm_xml_tool_call() -> None:
    call = parse_xml_tool_call('<function name="search_projects">{"query":"lullaby audio"}</function>')

    assert validate_tool_call(call) == ToolCall("search_projects", {"query": "lullaby audio"})


def test_validate_rejects_unknown_tool() -> None:
    with pytest.raises(ToolContractError, match="unknown tool"):
        validate_tool_call(ToolCall("invent_project", {}))


def test_validate_rejects_bad_argument_type() -> None:
    with pytest.raises(ToolContractError, match="search_projects.query must be string"):
        validate_tool_call(ToolCall("search_projects", {"query": 47}))


def test_validate_rejects_extra_arguments() -> None:
    with pytest.raises(ToolContractError, match="unexpected arguments"):
        validate_tool_call(ToolCall("find_whitespace", {"query": "unused"}))


def test_resolve_defaults_to_search_when_output_is_broken() -> None:
    resolution = resolve_tool_call("<function", fallback_query="offline archive")

    assert resolution.status == "defaulted"
    assert resolution.call == ToolCall("search_projects", {"query": "offline archive"})
    assert resolution.errors


def test_resolve_defaults_to_whitespace_without_query() -> None:
    resolution = resolve_tool_call("no function here")

    assert resolution.status == "defaulted"
    assert resolution.call == ToolCall("find_whitespace", {})