CallMeDaniel Claude Opus 4.6 (1M context) commited on
Commit
7278899
·
1 Parent(s): ab640da

test: add prompt building, routing, and JSON parsing tests

Browse files

28 tests covering parse_mentions, route_by_keywords,
parse_orchestrator_response, build_orchestrator_system_prompt,
and build_chat_messages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. tests/test_prompts.py +181 -0
tests/test_prompts.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for agents/prompts.py — prompt building, routing, parsing."""
2
+
3
+ from agents.prompts import (
4
+ parse_mentions,
5
+ route_by_keywords,
6
+ parse_orchestrator_response,
7
+ build_orchestrator_system_prompt,
8
+ build_chat_messages,
9
+ CAD_TRIGGER_KEYWORDS,
10
+ )
11
+
12
+
13
+ class TestParseMentions:
14
+ def test_no_mentions(self):
15
+ cleaned, mentions = parse_mentions("I need a bracket")
16
+ assert cleaned == "I need a bracket"
17
+ assert mentions == []
18
+
19
+ def test_single_mention(self):
20
+ cleaned, mentions = parse_mentions("@design what shape?")
21
+ assert "design" in mentions
22
+ assert "@design" not in cleaned
23
+
24
+ def test_multiple_mentions(self):
25
+ cleaned, mentions = parse_mentions("@design @engineering check this")
26
+ assert "design" in mentions
27
+ assert "engineering" in mentions
28
+ assert "@design" not in cleaned
29
+ assert "@engineering" not in cleaned
30
+
31
+ def test_cad_mention(self):
32
+ cleaned, mentions = parse_mentions("@cad generate a preview")
33
+ assert "cad" in mentions
34
+
35
+ def test_case_insensitive(self):
36
+ cleaned, mentions = parse_mentions("@Design what do you think?")
37
+ assert "design" in mentions
38
+
39
+ def test_mention_mid_sentence(self):
40
+ cleaned, mentions = parse_mentions("Can @engineering check the wall thickness?")
41
+ assert "engineering" in mentions
42
+ assert "Can" in cleaned
43
+ assert "check the wall thickness?" in cleaned
44
+
45
+
46
+ class TestRouteByKeywords:
47
+ def test_design_keywords(self):
48
+ agents = route_by_keywords("I want a sleek design with smooth shape")
49
+ assert "design" in agents
50
+
51
+ def test_engineering_keywords(self):
52
+ agents = route_by_keywords("Use M6 bolts with 3mm wall thickness in aluminum")
53
+ assert "engineering" in agents
54
+
55
+ def test_cnc_keywords(self):
56
+ agents = route_by_keywords("Can this be machined on a 3-axis CNC mill?")
57
+ assert "cnc" in agents
58
+
59
+ def test_cad_trigger(self):
60
+ agents = route_by_keywords("Generate a preview of the part")
61
+ assert "cad" in agents
62
+
63
+ def test_default_when_no_match(self):
64
+ agents = route_by_keywords("hello there")
65
+ assert agents == ["design", "engineering"]
66
+
67
+ def test_max_three_agents(self):
68
+ agents = route_by_keywords(
69
+ "design shape in aluminum for CNC machining, generate preview"
70
+ )
71
+ assert len(agents) <= 3
72
+
73
+ def test_sorted_by_relevance(self):
74
+ agents = route_by_keywords("M4 M6 tolerance clearance aluminum steel wall")
75
+ assert agents[0] == "engineering"
76
+
77
+
78
+ class TestParseOrchestratorResponse:
79
+ def test_valid_json(self):
80
+ resp = '{"agents": [{"id": "design", "message": "Nice bracket."}]}'
81
+ parsed = parse_orchestrator_response(resp)
82
+ assert len(parsed) == 1
83
+ assert parsed[0]["id"] == "design"
84
+ assert parsed[0]["message"] == "Nice bracket."
85
+ assert parsed[0]["code"] is None
86
+
87
+ def test_json_with_code(self):
88
+ resp = '{"agents": [{"id": "cad", "message": "Done.", "code": "result = cq.Workplane().box(10,10,10)"}]}'
89
+ parsed = parse_orchestrator_response(resp)
90
+ assert parsed[0]["code"] == "result = cq.Workplane().box(10,10,10)"
91
+
92
+ def test_json_in_markdown_fence(self):
93
+ resp = '```json\n{"agents": [{"id": "engineering", "message": "Use 3mm walls."}]}\n```'
94
+ parsed = parse_orchestrator_response(resp)
95
+ assert len(parsed) == 1
96
+ assert parsed[0]["id"] == "engineering"
97
+
98
+ def test_multiple_agents(self):
99
+ resp = '{"agents": [{"id": "design", "message": "A"}, {"id": "cnc", "message": "B"}]}'
100
+ parsed = parse_orchestrator_response(resp)
101
+ assert len(parsed) == 2
102
+ assert parsed[0]["id"] == "design"
103
+ assert parsed[1]["id"] == "cnc"
104
+
105
+ def test_invalid_json_fallback(self):
106
+ resp = "I think you should use aluminum."
107
+ parsed = parse_orchestrator_response(resp)
108
+ assert len(parsed) == 1
109
+ assert parsed[0]["id"] == "design"
110
+ assert parsed[0]["message"] == resp
111
+
112
+ def test_empty_agents_fallback(self):
113
+ resp = '{"agents": []}'
114
+ parsed = parse_orchestrator_response(resp)
115
+ assert len(parsed) == 1
116
+ assert parsed[0]["id"] == "design"
117
+
118
+ def test_missing_fields_skipped(self):
119
+ resp = '{"agents": [{"id": "design"}, {"id": "cnc", "message": "OK"}]}'
120
+ parsed = parse_orchestrator_response(resp)
121
+ assert len(parsed) == 1
122
+ assert parsed[0]["id"] == "cnc"
123
+
124
+
125
+ class TestBuildOrchestratorSystemPrompt:
126
+ def test_default_agents(self):
127
+ prompt = build_orchestrator_system_prompt()
128
+ assert "Design Agent" in prompt
129
+ assert "Engineering Agent" in prompt
130
+ assert "CNC Agent" in prompt
131
+ assert '### CAD Coder' not in prompt # persona block excluded
132
+
133
+ def test_specific_agents(self):
134
+ prompt = build_orchestrator_system_prompt(active_agents=["cad"])
135
+ assert "CAD Coder" in prompt
136
+ assert "Design Agent" not in prompt
137
+
138
+ def test_includes_json_format(self):
139
+ prompt = build_orchestrator_system_prompt()
140
+ assert '"agents"' in prompt
141
+ assert "JSON" in prompt
142
+
143
+ def test_cad_context_included(self):
144
+ prompt = build_orchestrator_system_prompt(
145
+ active_agents=["cad"], include_cad_context=True
146
+ )
147
+ assert "CadQuery" in prompt
148
+
149
+
150
+ class TestBuildChatMessages:
151
+ def test_returns_system_and_user(self):
152
+ msgs = build_chat_messages("hello", [], "You are a bot.")
153
+ assert len(msgs) == 2
154
+ assert msgs[0]["role"] == "system"
155
+ assert msgs[0]["content"] == "You are a bot."
156
+ assert msgs[1]["role"] == "user"
157
+
158
+ def test_history_included_in_user_message(self, sample_history):
159
+ msgs = build_chat_messages("new msg", sample_history, "system prompt")
160
+ user_content = msgs[1]["content"]
161
+ assert "servo bracket" in user_content
162
+ assert "new msg" in user_content
163
+
164
+ def test_design_state_included(self):
165
+ msgs = build_chat_messages(
166
+ "make it wider", [], "system prompt",
167
+ design_state_text="Part: bracket\nMaterial: aluminum"
168
+ )
169
+ user_content = msgs[1]["content"]
170
+ assert "bracket" in user_content
171
+ assert "aluminum" in user_content
172
+
173
+ def test_history_truncation(self):
174
+ long_history = [
175
+ {"role": "user", "content": f"msg {i}"}
176
+ for i in range(50)
177
+ ]
178
+ msgs = build_chat_messages("latest", long_history, "sys", max_history=5)
179
+ user_content = msgs[1]["content"]
180
+ assert "msg 49" in user_content
181
+ assert "msg 0" not in user_content