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.
@@ -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=laHxiB3ko2oaQDhN7xG7u7Kc7WTy7j-rPqvfiWdpxEY,2473
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=SNxYm6oFbqvc4mzGsxSpWg1l0iwT-Be0saVw3rtoruQ,6459
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=XMlnRut3aU2FEabhXuvb4Z_DXKzi1psZfbbx4iDyg_4,645
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=3Cok1VPzYZo4Nityyprub2XaKVRBkaoeTm6fs67BRZw,10319
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=rWRxrm0KjgmNIiYfooMM95eohMB1zCZC-eqrp1mf8w0,313
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=bvKFU1ONeza95qethNIiVgrsnT7l1TKMsdHjfXVVJnc,229
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.2.data/data/quick_agent/agents/business-extract-structured.md,sha256=mhieyFbkJ23kjZPvpKguW4xorkTgzwAev1EZCUkcS44,1182
35
- quick_agent-0.1.2.data/data/quick_agent/agents/business-extract.md,sha256=2SQdD9a4Bg_PXqL5-D5-kJMzZOYAigi0N54XsnoeNCU,996
36
- quick_agent-0.1.2.data/data/quick_agent/agents/function-spec-validator.md,sha256=Dlc4j1vFpDHj7w2vcWrvC83EEg_sWwwt1GOaRgcsm6Y,3289
37
- quick_agent-0.1.2.data/data/quick_agent/agents/subagent-validate-eval-list.md,sha256=K83L9BKvGcttPRINwD6VSPopEHnB_aw4tpfSI6KM2u8,4119
38
- quick_agent-0.1.2.data/data/quick_agent/agents/subagent-validator-contains.md,sha256=3VxtKpyogwcJ4Er5tQFyYNe2nJBtANErb5D-_snBATw,3115
39
- quick_agent-0.1.2.data/data/quick_agent/agents/template.md,sha256=6IBuVZjK5OPIchI8ruvxao8CdLy0ZOi0meh6d1exI0E,2458
40
- quick_agent-0.1.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
41
- tests/test_agent.py,sha256=tcPpH9WmGPQjMo_jkFJlF8kgOZ3QktIvUHTAJaH1hWA,5533
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=fWeZzTF_cQj6nbeEVh0cC5uKwgWP56deDQxkcCO1uNk,31001
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.2.dist-info/METADATA,sha256=1xL8o4mz4qW2dLuotIPTGJxuHRHm1Uxp42j-w6nsQ-8,47103
48
- quick_agent-0.1.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
49
- quick_agent-0.1.2.dist-info/entry_points.txt,sha256=bij-xFaQMSrMj7zearzi-bMfcyweum_GvURFMVZGde8,53
50
- quick_agent-0.1.2.dist-info/top_level.txt,sha256=KT1ID0FVC0OLzQnoBKRIHbXLRNugiU2gcgj_f4DtAsw,18
51
- quick_agent-0.1.2.dist-info/RECORD,,
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("do thing", run_input, {"x": 1})
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 (JSON)" in prompt
89
- assert '"x": 1' in prompt
90
- assert "## Step Instructions" in prompt
91
- assert "do thing" in prompt
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, body="", step_prompts={})
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 = agent_registry.load_agent_file(md_path)
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 = agent_registry.load_agent_file(md_path)
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 = agent_registry.load_agent_file(md_path)
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"]