fabricatio 0.2.3.dev1__tar.gz → 0.2.3.dev3__tar.gz

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.
Files changed (95) hide show
  1. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/PKG-INFO +42 -38
  2. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/README.md +41 -37
  3. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_diary/diary.py +2 -2
  4. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/minor/hello_fabricatio.py +1 -1
  5. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/propose_task/propose.py +1 -1
  6. fabricatio-0.2.3.dev3/examples/simple_chat/chat.py +47 -0
  7. fabricatio-0.2.3.dev1/examples/simple_chat/chat.py → fabricatio-0.2.3.dev3/examples/simple_rag/simple_rag.py +1 -1
  8. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/pyproject.toml +1 -1
  9. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/__init__.py +8 -0
  10. fabricatio-0.2.3.dev3/python/fabricatio/capabilities/rag.py +225 -0
  11. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/config.py +3 -0
  12. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/task.py +8 -8
  13. fabricatio-0.2.3.dev3/templates/built-in/retrieved_display.hbs +5 -0
  14. fabricatio-0.2.3.dev3/templates.tar.gz +0 -0
  15. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_action.py +2 -3
  16. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_task.py +5 -6
  17. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_usages.py +3 -4
  18. fabricatio-0.2.3.dev3/uv.lock +1573 -0
  19. fabricatio-0.2.3.dev1/python/fabricatio/capabilities/rag.py +0 -46
  20. fabricatio-0.2.3.dev1/templates.tar.gz +0 -0
  21. fabricatio-0.2.3.dev1/uv.lock +0 -1573
  22. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/build-package.yaml +0 -0
  23. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/ruff.yaml +0 -0
  24. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/tests.yaml +0 -0
  25. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.gitignore +0 -0
  26. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.python-version +0 -0
  27. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Cargo.lock +0 -0
  28. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Cargo.toml +0 -0
  29. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/LICENSE +0 -0
  30. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Makefile +0 -0
  31. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/llm_usages/llm_usage.py +0 -0
  32. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_a_rating/rating.py +0 -0
  33. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_diary/commits.json +0 -0
  34. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/task_handle/handle_task.py +0 -0
  35. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/_rust.pyi +0 -0
  36. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/_rust_instances.py +0 -0
  37. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/__init__.py +0 -0
  38. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/communication.py +0 -0
  39. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/transmission.py +0 -0
  40. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/capabilities/rating.py +0 -0
  41. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/capabilities/task.py +0 -0
  42. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/core.py +0 -0
  43. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/decorators.py +0 -0
  44. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/__init__.py +0 -0
  45. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/curd.py +0 -0
  46. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/readers.py +0 -0
  47. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/journal.py +0 -0
  48. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/action.py +0 -0
  49. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/events.py +0 -0
  50. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/generic.py +0 -0
  51. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/kwargs_types.py +0 -0
  52. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/role.py +0 -0
  53. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/tool.py +0 -0
  54. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/usages.py +0 -0
  55. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/utils.py +0 -0
  56. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/parser.py +0 -0
  57. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/py.typed +0 -0
  58. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/__init__.py +0 -0
  59. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/arithmetic.py +0 -0
  60. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/fs.py +0 -0
  61. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/task.py +0 -0
  62. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/hash.rs +0 -0
  63. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/lib.rs +0 -0
  64. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/templates.rs +0 -0
  65. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
  66. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/claude-xml.hbs +0 -0
  67. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/clean-up-code.hbs +0 -0
  68. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
  69. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/dependencies.hbs +0 -0
  70. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/document-the-code.hbs +0 -0
  71. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_criteria.hbs +0 -0
  72. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_manual.hbs +0 -0
  73. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_weights_klee.hbs +0 -0
  74. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_tool_usage_code.hbs +0 -0
  75. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/extract_criteria_from_reasons.hbs +0 -0
  76. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/extract_reasons_from_examples.hbs +0 -0
  77. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
  78. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/fix-bugs.hbs +0 -0
  79. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/improve-performance.hbs +0 -0
  80. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/make_choice.hbs +0 -0
  81. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/make_judgment.hbs +0 -0
  82. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/propose_task.hbs +0 -0
  83. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/rate_fine_grind.hbs +0 -0
  84. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/refactor.hbs +0 -0
  85. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
  86. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/task_briefing.hbs +0 -0
  87. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/web-ctf-solver.hbs +0 -0
  88. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-git-commit.hbs +0 -0
  89. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-github-pull-request.hbs +0 -0
  90. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-github-readme.hbs +0 -0
  91. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_config.py +0 -0
  92. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_advanced.py +0 -0
  93. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_generic.py +0 -0
  94. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_role.py +0 -0
  95. {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_tool.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.3.dev1
3
+ Version: 0.2.3.dev3
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -98,32 +98,32 @@ from fabricatio import Action, Role, Task, logger
98
98
 
99
99
 
100
100
  class Hello(Action):
101
- """Action that says hello."""
101
+ """Action that says hello."""
102
102
 
103
- name: str = "hello"
104
- output_key: str = "task_output"
103
+ name: str = "hello"
104
+ output_key: str = "task_output"
105
105
 
106
- async def _execute(self, task_input: Task[str], **_) -> Any:
107
- ret = "Hello fabricatio!"
108
- logger.info("executing talk action")
109
- return ret
106
+ async def _execute(self, task_input: Task[str], **_) -> Any:
107
+ ret = "Hello fabricatio!"
108
+ logger.info("executing talk action")
109
+ return ret
110
110
 
111
111
 
112
112
  async def main() -> None:
113
- """Main function."""
114
- role = Role(
115
- name="talker",
116
- description="talker role",
117
- registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
118
- )
113
+ """Main function."""
114
+ role = Role(
115
+ name="talker",
116
+ description="talker role",
117
+ registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
118
+ )
119
119
 
120
- task = Task(name="say hello", goal="say hello", description="say hello to the world")
121
- result = await task.delegate()
122
- logger.success(f"Result: {result}")
120
+ task = Task(name="say hello", goals="say hello", description="say hello to the world")
121
+ result = await task.delegate()
122
+ logger.success(f"Result: {result}")
123
123
 
124
124
 
125
125
  if __name__ == "__main__":
126
- asyncio.run(main())
126
+ asyncio.run(main())
127
127
  ```
128
128
 
129
129
  #### Writing and Dumping Code
@@ -311,17 +311,18 @@ from fabricatio.models.task import Task
311
311
 
312
312
  toolbox_usage = ToolBoxUsage()
313
313
 
314
+
314
315
  async def handle_security_vulnerabilities():
315
- task = Task(
316
- name="Security Check",
317
- goal=["Identify security vulnerabilities"],
318
- description="Perform a thorough security review on the project.",
319
- dependencies=["./src/main.py"]
320
- )
321
-
322
- vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
323
- for vulnerability in vulnerabilities:
324
- print(f"Found vulnerability: {vulnerability.name}")
316
+ task = Task(
317
+ name="Security Check",
318
+ goals=["Identify security vulnerabilities"],
319
+ description="Perform a thorough security review on the project.",
320
+ dependencies=["./src/main.py"]
321
+ )
322
+
323
+ vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
324
+ for vulnerability in vulnerabilities:
325
+ print(f"Found vulnerability: {vulnerability.name}")
325
326
  ```
326
327
 
327
328
  #### Managing CTF Challenges
@@ -334,19 +335,22 @@ from fabricatio.models.task import Task
334
335
 
335
336
  toolbox_usage = ToolBoxUsage()
336
337
 
338
+
337
339
  async def solve_ctf_challenge(challenge_name: str, challenge_description: str, files: list[str]):
338
- task = Task(
339
- name=challenge_name,
340
- goal=[f"Solve {challenge_name} challenge"],
341
- description=challenge_description,
342
- dependencies=files
343
- )
344
-
345
- solution = await toolbox_usage.gather_tools_fine_grind(task)
346
- print(f"Challenge Solved: {solution}")
340
+ task = Task(
341
+ name=challenge_name,
342
+ goals=[f"Solve {challenge_name} challenge"],
343
+ description=challenge_description,
344
+ dependencies=files
345
+ )
346
+
347
+ solution = await toolbox_usage.gather_tools_fine_grind(task)
348
+ print(f"Challenge Solved: {solution}")
349
+
347
350
 
348
351
  if __name__ == "__main__":
349
- asyncio.run(solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
352
+ asyncio.run(
353
+ solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
350
354
  ```
351
355
 
352
356
  ### Configuration
@@ -59,32 +59,32 @@ from fabricatio import Action, Role, Task, logger
59
59
 
60
60
 
61
61
  class Hello(Action):
62
- """Action that says hello."""
62
+ """Action that says hello."""
63
63
 
64
- name: str = "hello"
65
- output_key: str = "task_output"
64
+ name: str = "hello"
65
+ output_key: str = "task_output"
66
66
 
67
- async def _execute(self, task_input: Task[str], **_) -> Any:
68
- ret = "Hello fabricatio!"
69
- logger.info("executing talk action")
70
- return ret
67
+ async def _execute(self, task_input: Task[str], **_) -> Any:
68
+ ret = "Hello fabricatio!"
69
+ logger.info("executing talk action")
70
+ return ret
71
71
 
72
72
 
73
73
  async def main() -> None:
74
- """Main function."""
75
- role = Role(
76
- name="talker",
77
- description="talker role",
78
- registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
79
- )
74
+ """Main function."""
75
+ role = Role(
76
+ name="talker",
77
+ description="talker role",
78
+ registry={Task.pending_label: WorkFlow(name="talk", steps=(Hello,))}
79
+ )
80
80
 
81
- task = Task(name="say hello", goal="say hello", description="say hello to the world")
82
- result = await task.delegate()
83
- logger.success(f"Result: {result}")
81
+ task = Task(name="say hello", goals="say hello", description="say hello to the world")
82
+ result = await task.delegate()
83
+ logger.success(f"Result: {result}")
84
84
 
85
85
 
86
86
  if __name__ == "__main__":
87
- asyncio.run(main())
87
+ asyncio.run(main())
88
88
  ```
89
89
 
90
90
  #### Writing and Dumping Code
@@ -272,17 +272,18 @@ from fabricatio.models.task import Task
272
272
 
273
273
  toolbox_usage = ToolBoxUsage()
274
274
 
275
+
275
276
  async def handle_security_vulnerabilities():
276
- task = Task(
277
- name="Security Check",
278
- goal=["Identify security vulnerabilities"],
279
- description="Perform a thorough security review on the project.",
280
- dependencies=["./src/main.py"]
281
- )
282
-
283
- vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
284
- for vulnerability in vulnerabilities:
285
- print(f"Found vulnerability: {vulnerability.name}")
277
+ task = Task(
278
+ name="Security Check",
279
+ goals=["Identify security vulnerabilities"],
280
+ description="Perform a thorough security review on the project.",
281
+ dependencies=["./src/main.py"]
282
+ )
283
+
284
+ vulnerabilities = await toolbox_usage.gather_tools_fine_grind(task)
285
+ for vulnerability in vulnerabilities:
286
+ print(f"Found vulnerability: {vulnerability.name}")
286
287
  ```
287
288
 
288
289
  #### Managing CTF Challenges
@@ -295,19 +296,22 @@ from fabricatio.models.task import Task
295
296
 
296
297
  toolbox_usage = ToolBoxUsage()
297
298
 
299
+
298
300
  async def solve_ctf_challenge(challenge_name: str, challenge_description: str, files: list[str]):
299
- task = Task(
300
- name=challenge_name,
301
- goal=[f"Solve {challenge_name} challenge"],
302
- description=challenge_description,
303
- dependencies=files
304
- )
305
-
306
- solution = await toolbox_usage.gather_tools_fine_grind(task)
307
- print(f"Challenge Solved: {solution}")
301
+ task = Task(
302
+ name=challenge_name,
303
+ goals=[f"Solve {challenge_name} challenge"],
304
+ description=challenge_description,
305
+ dependencies=files
306
+ )
307
+
308
+ solution = await toolbox_usage.gather_tools_fine_grind(task)
309
+ print(f"Challenge Solved: {solution}")
310
+
308
311
 
309
312
  if __name__ == "__main__":
310
- asyncio.run(solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
313
+ asyncio.run(
314
+ solve_ctf_challenge("Binary Exploitation", "CTF Binary Exploitation Challenge", ["./challenges/binary_exploit"]))
311
315
  ```
312
316
 
313
317
  ### Configuration
@@ -18,8 +18,8 @@ class WriteDiary(Action):
18
18
  output_key: str = "dump_text"
19
19
 
20
20
  async def _execute(self, task_input: Task[str], **_) -> str:
21
- task_input.goal.clear()
22
- task_input.goal.extend(
21
+ task_input.goals.clear()
22
+ task_input.goals.extend(
23
23
  [
24
24
  "write a Internship Diary according to the given commit messages",
25
25
  "the diary should include the main dev target of the day, and the exact content"
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from fabricatio import Action, Role, Task, WorkFlow, logger
7
7
 
8
- task = Task(name="say hello", goal=["say hello"], description="say hello to the world")
8
+ task = Task(name="say hello", goals=["say hello"], description="say hello to the world")
9
9
 
10
10
 
11
11
  class Hello(Action):
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from fabricatio import Action, Role, Task, WorkFlow, logger
7
7
 
8
- task = Task(name="say hello", goal=["say hello"], description="say hello to the world")
8
+ task = Task(name="say hello", goals=["say hello"], description="say hello to the world")
9
9
 
10
10
 
11
11
  class Talk(Action):
@@ -0,0 +1,47 @@
1
+ """Simple chat example."""
2
+
3
+ import asyncio
4
+
5
+ from fabricatio import Action, Role, Task, WorkFlow, logger
6
+ from fabricatio.models.events import Event
7
+ from questionary import text
8
+
9
+
10
+ class Talk(Action):
11
+ """Action that says hello to the world."""
12
+
13
+ name: str = "talk"
14
+ output_key: str = "task_output"
15
+
16
+ async def _execute(self, task_input: Task[str], **_) -> int:
17
+ counter = 0
18
+ try:
19
+ while True:
20
+ user_say = await text("User: ").ask_async()
21
+ gpt_say = await self.aask(
22
+ user_say,
23
+ system_message=f"You have to answer to user obeying task assigned to you:\n{task_input.briefing}",
24
+ )
25
+ print(f"GPT: {gpt_say}") # noqa: T201
26
+ counter += 1
27
+ except KeyboardInterrupt:
28
+ logger.info(f"executed talk action {counter} times")
29
+ return counter
30
+
31
+
32
+ async def main() -> None:
33
+ """Main function."""
34
+ role = Role(
35
+ name="talker",
36
+ description="talker role",
37
+ registry={Event.instantiate_from("talk").push_wildcard().push("pending"): WorkFlow(name="talk", steps=(Talk,))},
38
+ )
39
+
40
+ task = await role.propose(
41
+ "you have to act as a helpful assistant, answer to all user questions properly and patiently"
42
+ )
43
+ _ = await task.delegate("talk")
44
+
45
+
46
+ if __name__ == "__main__":
47
+ asyncio.run(main())
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from fabricatio import Action, Role, Task, WorkFlow, logger
7
7
 
8
- task = Task(name="say hello", goal=["say hello"], description="say hello to the world")
8
+ task = Task(name="say hello", goals=["say hello"], description="say hello to the world")
9
9
 
10
10
 
11
11
  class Talk(Action):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.2.3-dev1"
3
+ version = "0.2.3-dev3"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -1,5 +1,7 @@
1
1
  """Fabricatio is a Python library for building llm app using event-based agent structure."""
2
2
 
3
+ from importlib.util import find_spec
4
+
3
5
  from fabricatio._rust_instances import template_manager
4
6
  from fabricatio.core import env
5
7
  from fabricatio.fs import magika
@@ -35,3 +37,9 @@ __all__ = [
35
37
  "task_toolbox",
36
38
  "template_manager",
37
39
  ]
40
+
41
+
42
+ if find_spec("pymilvus"):
43
+ from fabricatio.capabilities.rag import Rag
44
+
45
+ __all__ += ["Rag"]
@@ -0,0 +1,225 @@
1
+ """A module for the RAG (Retrieval Augmented Generation) model."""
2
+
3
+ from functools import lru_cache
4
+ from operator import itemgetter
5
+ from os import PathLike
6
+ from pathlib import Path
7
+ from typing import Any, Callable, Dict, List, Optional, Self, Union, Unpack
8
+
9
+ from fabricatio import template_manager
10
+ from fabricatio.config import configs
11
+ from fabricatio.models.kwargs_types import LLMKwargs
12
+ from fabricatio.models.usages import LLMUsage
13
+ from fabricatio.models.utils import MilvusData
14
+ from more_itertools.recipes import flatten
15
+
16
+ try:
17
+ from pymilvus import MilvusClient
18
+ except ImportError as e:
19
+ raise RuntimeError("pymilvus is not installed. Have you installed `fabricatio[rag]` instead of `fabricatio`") from e
20
+ from pydantic import Field, PrivateAttr
21
+
22
+
23
+ @lru_cache(maxsize=None)
24
+ def create_client(
25
+ uri: Optional[str] = None, token: Optional[str] = None, timeout: Optional[float] = None
26
+ ) -> MilvusClient:
27
+ """Create a Milvus client."""
28
+ return MilvusClient(
29
+ uri=uri or configs.rag.milvus_uri.unicode_string(),
30
+ token=token or configs.rag.milvus_token.get_secret_value() if configs.rag.milvus_token else "",
31
+ timeout=timeout or configs.rag.milvus_timeout,
32
+ )
33
+
34
+
35
+ class Rag(LLMUsage):
36
+ """A class representing the RAG (Retrieval Augmented Generation) model."""
37
+
38
+ milvus_uri: Optional[str] = Field(default=None, frozen=True)
39
+ """The URI of the Milvus server."""
40
+ milvus_token: Optional[str] = Field(default=None, frozen=True)
41
+ """The token for the Milvus server."""
42
+ milvus_timeout: Optional[float] = Field(default=None, frozen=True)
43
+ """The timeout for the Milvus server."""
44
+ target_collection: Optional[str] = Field(default=None)
45
+ """The name of the collection being viewed."""
46
+
47
+ _client: MilvusClient = PrivateAttr(None)
48
+ """The Milvus client used for the RAG model."""
49
+
50
+ @property
51
+ def client(self) -> MilvusClient:
52
+ """Return the Milvus client."""
53
+ return self._client
54
+
55
+ def model_post_init(self, __context: Any) -> None:
56
+ """Initialize the RAG model by creating the collection if it does not exist."""
57
+ self._client = create_client(self.milvus_uri, self.milvus_token, self.milvus_timeout)
58
+ self.view(self.target_collection, create=True)
59
+
60
+ def view(self, collection_name: Optional[str], create: bool = False) -> Self:
61
+ """View the specified collection.
62
+
63
+ Args:
64
+ collection_name (str): The name of the collection.
65
+ create (bool): Whether to create the collection if it does not exist.
66
+ """
67
+ if create and collection_name and not self._client.has_collection(collection_name):
68
+ self._client.create_collection(collection_name)
69
+
70
+ self.target_collection = collection_name
71
+ return self
72
+
73
+ def quit_viewing(self) -> Self:
74
+ """Quit the current view.
75
+
76
+ Returns:
77
+ Self: The current instance, allowing for method chaining.
78
+ """
79
+ return self.view(None)
80
+
81
+ @property
82
+ def safe_target_collection(self) -> str:
83
+ """Get the name of the collection being viewed, raise an error if not viewing any collection.
84
+
85
+ Returns:
86
+ str: The name of the collection being viewed.
87
+ """
88
+ if self.target_collection is None:
89
+ raise RuntimeError("No collection is being viewed. Have you called `self.view()`?")
90
+ return self.target_collection
91
+
92
+ def add_document[D: Union[Dict[str, Any], MilvusData]](
93
+ self, data: D | List[D], collection_name: Optional[str] = None
94
+ ) -> Self:
95
+ """Adds a document to the specified collection.
96
+
97
+ Args:
98
+ data (Union[Dict[str, Any], MilvusData] | List[Union[Dict[str, Any], MilvusData]]): The data to be added to the collection.
99
+ collection_name (Optional[str]): The name of the collection. If not provided, the currently viewed collection is used.
100
+
101
+ Returns:
102
+ Self: The current instance, allowing for method chaining.
103
+ """
104
+ if isinstance(data, MilvusData):
105
+ data = data.prepare_insertion()
106
+ if isinstance(data, list):
107
+ data = [d.prepare_insertion() if isinstance(d, MilvusData) else d for d in data]
108
+ self._client.insert(collection_name or self.safe_target_collection, data)
109
+ return self
110
+
111
+ def consume(
112
+ self, source: PathLike, reader: Callable[[PathLike], MilvusData], collection_name: Optional[str] = None
113
+ ) -> Self:
114
+ """Consume a file and add its content to the collection.
115
+
116
+ Args:
117
+ source (PathLike): The path to the file to be consumed.
118
+ reader (Callable[[PathLike], MilvusData]): The reader function to read the file.
119
+ collection_name (Optional[str]): The name of the collection. If not provided, the currently viewed collection is used.
120
+
121
+ Returns:
122
+ Self: The current instance, allowing for method chaining.
123
+ """
124
+ data = reader(Path(source))
125
+ self.add_document(data, collection_name or self.safe_target_collection)
126
+ return self
127
+
128
+ async def afetch_document(
129
+ self,
130
+ vecs: List[List[float]],
131
+ desired_fields: List[str] | str,
132
+ collection_name: Optional[str] = None,
133
+ result_per_query: int = 10,
134
+ ) -> List[Dict[str, Any]] | List[Any]:
135
+ """Fetch data from the collection.
136
+
137
+ Args:
138
+ vecs (List[List[float]]): The vectors to search for.
139
+ desired_fields (List[str] | str): The fields to retrieve.
140
+ collection_name (Optional[str]): The name of the collection. If not provided, the currently viewed collection is used.
141
+ result_per_query (int): The number of results to return per query.
142
+
143
+ Returns:
144
+ List[Dict[str, Any]] | List[Any]: The retrieved data.
145
+ """
146
+ # Step 1: Search for vectors
147
+ search_results = self._client.search(
148
+ collection_name or self.safe_target_collection,
149
+ vecs,
150
+ output_fields=desired_fields if isinstance(desired_fields, list) else [desired_fields],
151
+ limit=result_per_query,
152
+ )
153
+
154
+ # Step 2: Flatten the search results
155
+ flattened_results = flatten(search_results)
156
+
157
+ # Step 3: Sort by distance (descending)
158
+ sorted_results = sorted(flattened_results, key=itemgetter("distance"), reverse=True)
159
+
160
+ # Step 4: Extract the entities
161
+ resp = [result["entity"] for result in sorted_results]
162
+
163
+ if isinstance(desired_fields, list):
164
+ return resp
165
+ return [r.get(desired_fields) for r in resp]
166
+
167
+ async def aretrieve(
168
+ self,
169
+ query: List[str] | str,
170
+ collection_name: Optional[str] = None,
171
+ result_per_query: int = 10,
172
+ final_limit: int = 20,
173
+ ) -> List[str]:
174
+ """Retrieve data from the collection.
175
+
176
+ Args:
177
+ query (List[str] | str): The query to be used for retrieval.
178
+ collection_name (Optional[str]): The name of the collection. If not provided, the currently viewed collection is used.
179
+ result_per_query (int): The number of results to be returned per query.
180
+ final_limit (int): The final limit on the number of results to return.
181
+
182
+ Returns:
183
+ List[str]: A list of strings containing the retrieved data.
184
+ """
185
+ if isinstance(query, str):
186
+ query = [query]
187
+ return await self.afetch_document(
188
+ vecs=(await self.vectorize(query)),
189
+ desired_fields="text",
190
+ collection_name=collection_name,
191
+ result_per_query=result_per_query,
192
+ )[:final_limit]
193
+
194
+ async def aask_retrieved(
195
+ self,
196
+ question: str | List[str],
197
+ query: List[str] | str,
198
+ collection_name: Optional[str] = None,
199
+ result_per_query: int = 10,
200
+ final_limit: int = 20,
201
+ **kwargs: Unpack[LLMKwargs],
202
+ ) -> str:
203
+ """Asks a question by retrieving relevant documents based on the provided query.
204
+
205
+ This method performs document retrieval using the given query, then asks the
206
+ specified question using the retrieved documents as context.
207
+
208
+ Args:
209
+ question (str | List[str]): The question or list of questions to be asked.
210
+ query (List[str] | str): The query or list of queries used for document retrieval.
211
+ collection_name (Optional[str]): The name of the collection to retrieve documents from.
212
+ If not provided, the currently viewed collection is used.
213
+ result_per_query (int): The number of results to return per query. Default is 10.
214
+ final_limit (int): The maximum number of retrieved documents to consider. Default is 20.
215
+ **kwargs (Unpack[LLMKwargs]): Additional keyword arguments passed to the underlying `aask` method.
216
+
217
+ Returns:
218
+ str: A string response generated after asking with the context of retrieved documents.
219
+ """
220
+ docs = await self.aretrieve(query, collection_name, result_per_query, final_limit)
221
+ return await self.aask(
222
+ question,
223
+ template_manager.render_template(configs.templates.retrieved_display_template, {"docs": docs}),
224
+ **kwargs,
225
+ )
@@ -176,6 +176,9 @@ class TemplateConfig(BaseModel):
176
176
  draft_rating_weights_klee_template: str = Field(default="draft_rating_weights_klee")
177
177
  """The name of the draft rating weights klee template which will be used to draft rating weights with Klee method."""
178
178
 
179
+ retrieved_display_template: str = Field(default="retrieved_display")
180
+ """The name of the retrieved display template which will be used to display retrieved documents."""
181
+
179
182
 
180
183
  class MagikaConfig(BaseModel):
181
184
  """Magika configuration class."""
@@ -46,19 +46,19 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
46
46
  """
47
47
 
48
48
  name: str = Field(...)
49
- """The name of the task, which should be a concise and descriptive name."""
49
+ """The name of the task, which should be concise and descriptive."""
50
50
 
51
51
  description: str = Field(default="")
52
- """The description of the task, which should provide every details and noting about the task if provided, obeying the CEFR level rule and 5W1H rule."""
52
+ """A detailed explanation of the task that includes all necessary information. Should be clear and answer what, why, when, where, who, and how questions."""
53
53
 
54
- goal: List[str] = Field(default=[])
55
- """The goal of the task, a list of strings. The goal should be a concise and clear statement of what the task is intended to achieve, goal SHALL NOT be too broad or too narrow."""
54
+ goals: List[str] = Field(default=[])
55
+ """A list of objectives that the task aims to accomplish. Each goal should be clear and specific. Complex tasks should be broken into multiple smaller goals."""
56
56
 
57
57
  namespace: List[str] = Field(default_factory=list)
58
- """The namespace of the task, a list of namespace segment, as string, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
58
+ """A list of string segments that identify the task's location in the system. If not specified, defaults to an empty list."""
59
59
 
60
60
  dependencies: List[str] = Field(default_factory=list)
61
- """A list of file paths, These file are needed to read or write to meet a specific requirement of this task, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
61
+ """A list of file paths that are needed (either reading or writing) to complete this task. If not specified, defaults to an empty list."""
62
62
 
63
63
  _output: Queue[T | None] = PrivateAttr(default_factory=Queue)
64
64
  """The output queue of the task."""
@@ -113,7 +113,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
113
113
  Returns:
114
114
  Task: A new instance of the `Task` class.
115
115
  """
116
- return cls(name=name, goal=goal, description=description)
116
+ return cls(name=name, goals=goal, description=description)
117
117
 
118
118
  def update_task(self, goal: Optional[List[str] | str] = None, description: Optional[str] = None) -> Self:
119
119
  """Update the goal and description of the task.
@@ -126,7 +126,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
126
126
  Task: The updated instance of the `Task` class.
127
127
  """
128
128
  if goal:
129
- self.goal = goal if isinstance(goal, list) else [goal]
129
+ self.goals = goal if isinstance(goal, list) else [goal]
130
130
  if description:
131
131
  self.description = description
132
132
  return self
@@ -0,0 +1,5 @@
1
+ {{#if docs}}{{#each docs}}
2
+ ----- Start of {{ @index }}th retrieved document -----
3
+ {{this}}
4
+ ----- End of {{ @index }}th retrieved document -----
5
+ {{/each}}{{/if}}
Binary file
@@ -1,5 +1,4 @@
1
1
  import pytest
2
-
3
2
  from fabricatio.models.action import Action, WorkFlow
4
3
  from fabricatio.models.task import Task, TaskStatus
5
4
 
@@ -103,7 +102,7 @@ def test_workflow_with_extra_context():
103
102
  @pytest.mark.asyncio
104
103
  async def test_workflow_serve_success():
105
104
  workflow = WorkFlow(name="TestServe", steps=[DemoAction()])
106
- task = Task(name="test", goal=["test"], description="test")
105
+ task = Task(name="test", goals=["test"], description="test")
107
106
  await workflow.serve(task)
108
107
  assert task._status == TaskStatus.Finished
109
108
 
@@ -111,6 +110,6 @@ async def test_workflow_serve_success():
111
110
  @pytest.mark.asyncio
112
111
  async def test_workflow_serve_failure():
113
112
  workflow = WorkFlow(name="TestServeFail", steps=[FailingAction()])
114
- task = Task(name="test", goal=["test"], description="test")
113
+ task = Task(name="test", goals=["test"], description="test")
115
114
  await workflow.serve(task)
116
115
  assert task