quick-agent 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- quick_agent/agent_registry.py +5 -25
- quick_agent/llms.txt +1 -1
- quick_agent/models/loaded_agent_file.py +136 -1
- quick_agent/models/output_spec.py +1 -1
- quick_agent/prompting.py +33 -15
- quick_agent/quick_agent.py +99 -38
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/business-extract-structured.md +1 -1
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/business-extract.md +1 -1
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/function-spec-validator.md +1 -1
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/subagent-validate-eval-list.md +1 -1
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/subagent-validator-contains.md +8 -1
- {quick_agent-0.1.2.data → quick_agent-0.1.3.data}/data/quick_agent/agents/template.md +12 -1
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/METADATA +5 -1
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/RECORD +21 -20
- tests/test_agent.py +273 -9
- tests/test_httpx_tools.py +295 -0
- tests/test_orchestrator.py +353 -28
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/WHEEL +0 -0
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/entry_points.txt +0 -0
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {quick_agent-0.1.2.dist-info → quick_agent-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
quick_agent/__init__.py,sha256=lgTMjPhbumIZ1Pna33khCaAs4DSjErnYoCkvK_YxBCc,362
|
|
2
2
|
quick_agent/agent_call_tool.py,sha256=yyXpWWpQlhSV8qxkeA1tLdrfcHhYg2cmhf4sxx8FfvA,2195
|
|
3
|
-
quick_agent/agent_registry.py,sha256=
|
|
3
|
+
quick_agent/agent_registry.py,sha256=VqDFYsH6-kwUwtuiQyHpvmDeWVcSbn62pCvqE8D7IZE,1818
|
|
4
4
|
quick_agent/agent_tools.py,sha256=GwaPkoG_vy1f6hatvRQFoDj0IJ-6nIYV746qc8y-yMg,1475
|
|
5
5
|
quick_agent/cli.py,sha256=nzVF4paXxugaPzmHTMwCBkbQkfkkv9hqGQH4Fh0jJ_U,1973
|
|
6
6
|
quick_agent/directory_permissions.py,sha256=r6Lc56oIrGhVVUVrCoPInue_BCdGZsD5oDgoAHJWiyE,1955
|
|
7
7
|
quick_agent/input_adaptors.py,sha256=ZaK0Mm27cGMGb5y6fsgV0gXRPMW2zmHn9l9dXKzOufA,865
|
|
8
8
|
quick_agent/io_utils.py,sha256=BUmUoZepL4lSYe0JbKxLm4mFFQ6Zt6MZQzprzgoBweU,1200
|
|
9
9
|
quick_agent/json_utils.py,sha256=G6uP9SrdEw5WtA9--dBqdJcTYD2JgdgotPJdleErB_c,1003
|
|
10
|
-
quick_agent/llms.txt,sha256=
|
|
10
|
+
quick_agent/llms.txt,sha256=_jJvgJDdGyuIc-sT3XoadCGxDhzi61Cnb3Fq6zpsE_M,6488
|
|
11
11
|
quick_agent/orchestrator.py,sha256=SGa5oeRgbjkKXdFL5kszZT-R6-jIgyOkHEMmjLrDWjc,1321
|
|
12
|
-
quick_agent/prompting.py,sha256=
|
|
12
|
+
quick_agent/prompting.py,sha256=ZxKZ-qKfbStj7160xi84RYJWB5BxhYD01-ruiTN4iQA,1317
|
|
13
13
|
quick_agent/py.typed,sha256=3Q9vdii9v-_pbj9sglgDzJAIelvARaYaDJlB2u-NDqg,38
|
|
14
|
-
quick_agent/quick_agent.py,sha256=
|
|
14
|
+
quick_agent/quick_agent.py,sha256=P5qlJrdTwwYAafkm_ML5DJT3w49QRtEvUpF3fiJjR6Q,12850
|
|
15
15
|
quick_agent/tools_loader.py,sha256=oveR-EGAZo3Q7NPJ2aWKU3d8xd8kIpibOrjInpUZwQg,2642
|
|
16
16
|
quick_agent/models/__init__.py,sha256=YseFAVWLarh0wZCVUL0fmkjVgA-FnYJLEiVinG1-6Tk,737
|
|
17
17
|
quick_agent/models/agent_spec.py,sha256=YsCrs9DSKJJJBsqnMAxb_19zG6UuNHtXwiuTZjbFg5U,882
|
|
18
18
|
quick_agent/models/chain_step_spec.py,sha256=NcdYFmgJhA5ODIwCTgTb2fmmJJesAZCVAIy3cGC5dhs,342
|
|
19
19
|
quick_agent/models/handoff_spec.py,sha256=46Pt4JGU8-6f-cdbDR0oqWuJbg5ENJcXrFN1IK3chvs,280
|
|
20
|
-
quick_agent/models/loaded_agent_file.py,sha256=
|
|
20
|
+
quick_agent/models/loaded_agent_file.py,sha256=kOOrzfoYqCgoifcnOtn2us_MNPSVg4kRWgmyeb21BEY,5159
|
|
21
21
|
quick_agent/models/model_spec.py,sha256=spnIZ8BNtXT5t8YRL1nh7cXrgWwNSptt_BMnAB8w1Lg,425
|
|
22
|
-
quick_agent/models/output_spec.py,sha256=
|
|
22
|
+
quick_agent/models/output_spec.py,sha256=ZGYmHXAh82SoD5g8VPJRu_2uTjL_W_NM0gCXrAfiYYI,223
|
|
23
23
|
quick_agent/models/run_input.py,sha256=WpV-QPlOPXMmZR6eIvP7vJOuCeBt3xWe4j4ngEbmSxg,282
|
|
24
24
|
quick_agent/models/tool_impl_spec.py,sha256=7B161m8nFZCNjslb4VZdSi-mdAZM0jhp4kV4uaq8X1s,216
|
|
25
25
|
quick_agent/models/tool_json.py,sha256=eYFBPCqxmRfyo7GYHBXCfog7kUzpjginfjt9grQy4ic,274
|
|
@@ -31,21 +31,22 @@ quick_agent/tools/filesystem/read_text.py,sha256=owpZxwCFoYbljABhwpNKU4g7-8xDPJD
|
|
|
31
31
|
quick_agent/tools/filesystem/write_text.py,sha256=lwsMCk_p8xurVxychM2UBieXDp0SgN3C6rufLBUzDxo,584
|
|
32
32
|
quick_agent/tools/filesystem.read_text/tool.json,sha256=UtkOxcpi6qYd611tJx-zFXVUjOwx2ZbmxEWsF4cAVpo,246
|
|
33
33
|
quick_agent/tools/filesystem.write_text/tool.json,sha256=EcGmB_M_KDTqOrnxk_NVhKRoW_KADFFksgJ7fmw472s,275
|
|
34
|
-
quick_agent-0.1.
|
|
35
|
-
quick_agent-0.1.
|
|
36
|
-
quick_agent-0.1.
|
|
37
|
-
quick_agent-0.1.
|
|
38
|
-
quick_agent-0.1.
|
|
39
|
-
quick_agent-0.1.
|
|
40
|
-
quick_agent-0.1.
|
|
41
|
-
tests/test_agent.py,sha256=
|
|
34
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/business-extract-structured.md,sha256=yeNIZFw60-2u93ZdI6nnQnTOGrllCN8SQLJPApvqXdU,1168
|
|
35
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/business-extract.md,sha256=SZAgpRI8Z4HnkMBAwvMLrErkNQ5HkHqerVMSisn-gNU,993
|
|
36
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/function-spec-validator.md,sha256=ls3r0ejswgM_9SljMLxMjk75x9XJna7ceqHX3pWcgnk,3279
|
|
37
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/subagent-validate-eval-list.md,sha256=IxFwHGIsnGbnaCK-j7znrp4llShepRfRlhTJ5sjqe-g,4105
|
|
38
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/subagent-validator-contains.md,sha256=1yY3Z5copRoCIvq_taVSjzvp8prFab49s1-ybaDcdpk,3107
|
|
39
|
+
quick_agent-0.1.3.data/data/quick_agent/agents/template.md,sha256=4_ldsHj46qmDcer4SCfwfxRJqoesZsp87XHSAimtaLo,2571
|
|
40
|
+
quick_agent-0.1.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
41
|
+
tests/test_agent.py,sha256=1FQteGMCftv40bC-i_iNETaLR-7hmUz9kIlcJYvnazU,10999
|
|
42
42
|
tests/test_directory_permissions.py,sha256=-9h7W1VO0LekPLCxEqIAGgebquNks2F8M02BdZDLlQY,2815
|
|
43
|
+
tests/test_httpx_tools.py,sha256=xqIQ376STyNGwd5YA8VZPZS4nsr1EsSDYCrrcoOuXBE,10500
|
|
43
44
|
tests/test_input_adaptors.py,sha256=sle9zrumq3CUVGtDPZi2pf0om-Z7v4SI6khnTf7Rz4c,918
|
|
44
45
|
tests/test_integration.py,sha256=XbI6abGeoZm3r_BqpQyhxhF36N7VYtNGcJ9-xNxZysM,9044
|
|
45
|
-
tests/test_orchestrator.py,sha256=
|
|
46
|
+
tests/test_orchestrator.py,sha256=Tc1iynzPMf_ghbHitlcKDS9dks91t-ojbG3l7qInsbA,41716
|
|
46
47
|
tests/test_tools.py,sha256=Yc6AKCz79XJwHqiRV8dUBE6GVNdspUU0hIeGCXx-Rvc,936
|
|
47
|
-
quick_agent-0.1.
|
|
48
|
-
quick_agent-0.1.
|
|
49
|
-
quick_agent-0.1.
|
|
50
|
-
quick_agent-0.1.
|
|
51
|
-
quick_agent-0.1.
|
|
48
|
+
quick_agent-0.1.3.dist-info/METADATA,sha256=x_ButLyAVP8hACb2fwZsHzPAQbYQQ6Li-TgsUP55hpE,47339
|
|
49
|
+
quick_agent-0.1.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
50
|
+
quick_agent-0.1.3.dist-info/entry_points.txt,sha256=bij-xFaQMSrMj7zearzi-bMfcyweum_GvURFMVZGde8,53
|
|
51
|
+
quick_agent-0.1.3.dist-info/top_level.txt,sha256=KT1ID0FVC0OLzQnoBKRIHbXLRNugiU2gcgj_f4DtAsw,18
|
|
52
|
+
quick_agent-0.1.3.dist-info/RECORD,,
|
tests/test_agent.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import logging
|
|
2
3
|
import sys
|
|
3
4
|
import types
|
|
4
5
|
from pathlib import Path
|
|
@@ -79,16 +80,24 @@ def test_write_output_creates_parent(tmp_path: Path) -> None:
|
|
|
79
80
|
|
|
80
81
|
def test_make_user_prompt_contains_sections() -> None:
|
|
81
82
|
run_input = RunInput(source_path="file.txt", kind="text", text="hi", data=None)
|
|
82
|
-
prompt = prompting.make_user_prompt(
|
|
83
|
+
prompt = prompting.make_user_prompt(run_input, {"steps": {"x": 1}})
|
|
83
84
|
|
|
84
85
|
assert "# Task Input" in prompt
|
|
85
86
|
assert "source_path: file.txt" in prompt
|
|
86
87
|
assert "## Input Content" in prompt
|
|
87
88
|
assert "hi" in prompt
|
|
88
|
-
assert "## Chain State (
|
|
89
|
-
assert
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
assert "## Chain State (YAML)" in prompt
|
|
90
|
+
assert "x: 1" in prompt
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_make_user_prompt_inline_without_state_hides_headers() -> None:
|
|
94
|
+
run_input = RunInput(source_path="inline_input.txt", kind="text", text="hello", data=None)
|
|
95
|
+
prompt = prompting.make_user_prompt(run_input, {})
|
|
96
|
+
|
|
97
|
+
assert "# Task Input" not in prompt
|
|
98
|
+
assert "## Input Content" not in prompt
|
|
99
|
+
assert "## Chain State" not in prompt
|
|
100
|
+
assert prompt.strip() == "hello"
|
|
92
101
|
|
|
93
102
|
|
|
94
103
|
def test_resolve_schema_valid_missing_and_invalid() -> None:
|
|
@@ -110,7 +119,7 @@ def test_resolve_schema_valid_missing_and_invalid() -> None:
|
|
|
110
119
|
chain=[ChainStepSpec(id="s1", kind="text", prompt_section="step:one")],
|
|
111
120
|
schemas={"Good": "schemas.tmp:GoodSchema", "Bad": "schemas.tmp:NotSchema"},
|
|
112
121
|
)
|
|
113
|
-
loaded = LoadedAgentFile(spec=spec,
|
|
122
|
+
loaded = LoadedAgentFile.from_parts(spec=spec, instructions="", system_prompt="", step_prompts={})
|
|
114
123
|
|
|
115
124
|
try:
|
|
116
125
|
assert resolve_schema(loaded, "Good") is GoodSchema
|
|
@@ -137,6 +146,10 @@ chain:
|
|
|
137
146
|
prompt_section: step:one
|
|
138
147
|
---
|
|
139
148
|
|
|
149
|
+
## Instructions
|
|
150
|
+
|
|
151
|
+
system.
|
|
152
|
+
|
|
140
153
|
## step:one
|
|
141
154
|
|
|
142
155
|
body.
|
|
@@ -144,8 +157,9 @@ body.
|
|
|
144
157
|
md_path = tmp_path / "agent.md"
|
|
145
158
|
md_path.write_text(md, encoding="utf-8")
|
|
146
159
|
|
|
147
|
-
loaded =
|
|
160
|
+
loaded = LoadedAgentFile(md_path)
|
|
148
161
|
assert loaded.spec.name == "Test"
|
|
162
|
+
assert loaded.instructions == "system."
|
|
149
163
|
assert loaded.step_prompts["step:one"] == "body."
|
|
150
164
|
|
|
151
165
|
|
|
@@ -166,7 +180,7 @@ body.
|
|
|
166
180
|
md_path = tmp_path / "agent.md"
|
|
167
181
|
md_path.write_text(md, encoding="utf-8")
|
|
168
182
|
|
|
169
|
-
loaded =
|
|
183
|
+
loaded = LoadedAgentFile(md_path)
|
|
170
184
|
assert loaded.spec.model.base_url == "https://api.openai.com/v1"
|
|
171
185
|
assert loaded.spec.model.api_key_env == "OPENAI_API_KEY"
|
|
172
186
|
assert loaded.spec.model.model_name == "gpt-5.2"
|
|
@@ -189,8 +203,258 @@ body.
|
|
|
189
203
|
md_path = tmp_path / "agent.md"
|
|
190
204
|
md_path.write_text(md, encoding="utf-8")
|
|
191
205
|
|
|
192
|
-
loaded =
|
|
206
|
+
loaded = LoadedAgentFile(md_path)
|
|
193
207
|
assert loaded.spec.model.base_url == "https://api.openai.com/v1"
|
|
194
208
|
assert loaded.spec.model.api_key_env == "OPENAI_API_KEY"
|
|
195
209
|
assert loaded.spec.model.model_name == "gpt-5.2"
|
|
196
210
|
assert loaded.spec.model.provider == "openai-compatible"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def test_load_agent_file_parses_instructions_and_system_prompt(tmp_path: Path) -> None:
|
|
214
|
+
md = """---
|
|
215
|
+
name: Sections
|
|
216
|
+
model:
|
|
217
|
+
base_url: http://localhost
|
|
218
|
+
model_name: test
|
|
219
|
+
chain: []
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## instructions
|
|
223
|
+
|
|
224
|
+
Use the tool.
|
|
225
|
+
|
|
226
|
+
## Notes
|
|
227
|
+
|
|
228
|
+
ignored.
|
|
229
|
+
|
|
230
|
+
## System prompt
|
|
231
|
+
|
|
232
|
+
You are concise.
|
|
233
|
+
"""
|
|
234
|
+
md_path = tmp_path / "agent.md"
|
|
235
|
+
md_path.write_text(md, encoding="utf-8")
|
|
236
|
+
|
|
237
|
+
loaded = LoadedAgentFile(md_path)
|
|
238
|
+
assert "Use the tool." in loaded.instructions
|
|
239
|
+
assert "## Notes" in loaded.instructions
|
|
240
|
+
assert "ignored." in loaded.instructions
|
|
241
|
+
assert loaded.system_prompt == "You are concise."
|
|
242
|
+
assert loaded.step_prompts == {}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@pytest.mark.parametrize(
|
|
246
|
+
("header", "expected"),
|
|
247
|
+
[
|
|
248
|
+
("## Instructions", "Do it."),
|
|
249
|
+
("## instructions", "Do it."),
|
|
250
|
+
("## INSTRUCTIONS", "Do it."),
|
|
251
|
+
],
|
|
252
|
+
)
|
|
253
|
+
def test_load_agent_file_accepts_instruction_header_case(
|
|
254
|
+
tmp_path: Path, header: str, expected: str
|
|
255
|
+
) -> None:
|
|
256
|
+
md = f"""---
|
|
257
|
+
name: Case Instructions
|
|
258
|
+
model:
|
|
259
|
+
base_url: http://localhost
|
|
260
|
+
model_name: test
|
|
261
|
+
chain: []
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
{header}
|
|
265
|
+
|
|
266
|
+
{expected}
|
|
267
|
+
"""
|
|
268
|
+
md_path = tmp_path / "agent.md"
|
|
269
|
+
md_path.write_text(md, encoding="utf-8")
|
|
270
|
+
|
|
271
|
+
loaded = LoadedAgentFile(md_path)
|
|
272
|
+
assert loaded.instructions == expected
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
@pytest.mark.parametrize(
|
|
276
|
+
("header", "expected"),
|
|
277
|
+
[
|
|
278
|
+
("# System prompt", "Be brief."),
|
|
279
|
+
("## System prompt", "Be brief."),
|
|
280
|
+
("### System prompt", "Be brief."),
|
|
281
|
+
("## system prompt", "Be brief."),
|
|
282
|
+
("## SYSTEM PROMPT", "Be brief."),
|
|
283
|
+
("## system_prompt", "Be brief."),
|
|
284
|
+
],
|
|
285
|
+
)
|
|
286
|
+
def test_load_agent_file_accepts_system_prompt_header_case(
|
|
287
|
+
tmp_path: Path, header: str, expected: str
|
|
288
|
+
) -> None:
|
|
289
|
+
md = f"""---
|
|
290
|
+
name: Case System
|
|
291
|
+
model:
|
|
292
|
+
base_url: http://localhost
|
|
293
|
+
model_name: test
|
|
294
|
+
chain: []
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
{header}
|
|
298
|
+
|
|
299
|
+
{expected}
|
|
300
|
+
"""
|
|
301
|
+
md_path = tmp_path / "agent.md"
|
|
302
|
+
md_path.write_text(md, encoding="utf-8")
|
|
303
|
+
|
|
304
|
+
loaded = LoadedAgentFile(md_path)
|
|
305
|
+
assert loaded.system_prompt == expected
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
@pytest.mark.parametrize(
|
|
309
|
+
"header",
|
|
310
|
+
[
|
|
311
|
+
"## step:one",
|
|
312
|
+
"## STEP:one",
|
|
313
|
+
"## Step:one",
|
|
314
|
+
],
|
|
315
|
+
)
|
|
316
|
+
def test_load_agent_file_accepts_step_header_case(tmp_path: Path, header: str) -> None:
|
|
317
|
+
md = f"""---
|
|
318
|
+
name: Case Step
|
|
319
|
+
model:
|
|
320
|
+
base_url: http://localhost
|
|
321
|
+
model_name: test
|
|
322
|
+
chain:
|
|
323
|
+
- id: one
|
|
324
|
+
kind: text
|
|
325
|
+
prompt_section: step:one
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
{header}
|
|
329
|
+
|
|
330
|
+
Say hi.
|
|
331
|
+
"""
|
|
332
|
+
md_path = tmp_path / "agent.md"
|
|
333
|
+
md_path.write_text(md, encoding="utf-8")
|
|
334
|
+
|
|
335
|
+
loaded = LoadedAgentFile(md_path)
|
|
336
|
+
assert loaded.step_prompts["step:one"] == "Say hi."
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def test_load_agent_file_allows_instructions_only_no_steps(tmp_path: Path) -> None:
|
|
340
|
+
md = """---
|
|
341
|
+
name: No Steps
|
|
342
|
+
model:
|
|
343
|
+
base_url: http://localhost
|
|
344
|
+
model_name: test
|
|
345
|
+
chain: []
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Instructions
|
|
349
|
+
|
|
350
|
+
Just answer.
|
|
351
|
+
"""
|
|
352
|
+
md_path = tmp_path / "agent.md"
|
|
353
|
+
md_path.write_text(md, encoding="utf-8")
|
|
354
|
+
|
|
355
|
+
loaded = LoadedAgentFile(md_path)
|
|
356
|
+
assert loaded.instructions == "Just answer."
|
|
357
|
+
assert loaded.step_prompts == {}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def test_load_agent_file_raises_without_sections(tmp_path: Path) -> None:
|
|
361
|
+
md = """---
|
|
362
|
+
name: Empty
|
|
363
|
+
model:
|
|
364
|
+
base_url: http://localhost
|
|
365
|
+
model_name: test
|
|
366
|
+
chain: []
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
just text.
|
|
370
|
+
"""
|
|
371
|
+
md_path = tmp_path / "agent.md"
|
|
372
|
+
md_path.write_text(md, encoding="utf-8")
|
|
373
|
+
|
|
374
|
+
with pytest.raises(ValueError, match="instructions, system prompt, or step sections"):
|
|
375
|
+
LoadedAgentFile(md_path)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def test_load_agent_file_warns_on_preamble_before_instructions(tmp_path: Path, caplog: pytest.LogCaptureFixture) -> None:
|
|
379
|
+
md = """---
|
|
380
|
+
name: Preamble
|
|
381
|
+
model:
|
|
382
|
+
base_url: http://localhost
|
|
383
|
+
model_name: test
|
|
384
|
+
chain: []
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
Preamble text.
|
|
388
|
+
|
|
389
|
+
## Instructions
|
|
390
|
+
|
|
391
|
+
Do the thing.
|
|
392
|
+
"""
|
|
393
|
+
md_path = tmp_path / "agent.md"
|
|
394
|
+
md_path.write_text(md, encoding="utf-8")
|
|
395
|
+
|
|
396
|
+
with caplog.at_level(logging.WARNING):
|
|
397
|
+
LoadedAgentFile(md_path)
|
|
398
|
+
|
|
399
|
+
assert "Ignored text before instructions or system prompt" in caplog.text
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def test_load_agent_file_preserves_subsections_in_all_sections(tmp_path: Path) -> None:
|
|
403
|
+
md = """---
|
|
404
|
+
name: Subsections
|
|
405
|
+
model:
|
|
406
|
+
base_url: http://localhost
|
|
407
|
+
model_name: test
|
|
408
|
+
chain:
|
|
409
|
+
- id: one
|
|
410
|
+
kind: text
|
|
411
|
+
prompt_section: step:one
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Instructions
|
|
415
|
+
|
|
416
|
+
Intro line.
|
|
417
|
+
|
|
418
|
+
## Constraints
|
|
419
|
+
|
|
420
|
+
- Be concise.
|
|
421
|
+
- Keep scope tight.
|
|
422
|
+
|
|
423
|
+
## System prompt
|
|
424
|
+
|
|
425
|
+
System intro.
|
|
426
|
+
|
|
427
|
+
## Rules
|
|
428
|
+
|
|
429
|
+
Always follow the rules.
|
|
430
|
+
|
|
431
|
+
## step:one
|
|
432
|
+
|
|
433
|
+
Step intro.
|
|
434
|
+
|
|
435
|
+
## Details
|
|
436
|
+
|
|
437
|
+
Explain details here.
|
|
438
|
+
|
|
439
|
+
## step:two
|
|
440
|
+
|
|
441
|
+
Step intro two.
|
|
442
|
+
|
|
443
|
+
## Details
|
|
444
|
+
|
|
445
|
+
Explain details here.
|
|
446
|
+
"""
|
|
447
|
+
md_path = tmp_path / "agent.md"
|
|
448
|
+
md_path.write_text(md, encoding="utf-8")
|
|
449
|
+
|
|
450
|
+
loaded = LoadedAgentFile(md_path)
|
|
451
|
+
assert "Intro line." in loaded.instructions
|
|
452
|
+
assert "## Constraints" in loaded.instructions
|
|
453
|
+
assert "Be concise." in loaded.instructions
|
|
454
|
+
assert "System intro." in loaded.system_prompt
|
|
455
|
+
assert "## Rules" in loaded.system_prompt
|
|
456
|
+
assert "Always follow the rules." in loaded.system_prompt
|
|
457
|
+
assert "Step intro." in loaded.step_prompts["step:one"]
|
|
458
|
+
assert "## Details" in loaded.step_prompts["step:one"]
|
|
459
|
+
assert "Explain details here." in loaded.step_prompts["step:one"]
|
|
460
|
+
assert "## Details" in loaded.step_prompts["step:two"]
|