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.
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/PKG-INFO +42 -38
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/README.md +41 -37
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_diary/diary.py +2 -2
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/minor/hello_fabricatio.py +1 -1
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/propose_task/propose.py +1 -1
- fabricatio-0.2.3.dev3/examples/simple_chat/chat.py +47 -0
- fabricatio-0.2.3.dev1/examples/simple_chat/chat.py → fabricatio-0.2.3.dev3/examples/simple_rag/simple_rag.py +1 -1
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/pyproject.toml +1 -1
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/__init__.py +8 -0
- fabricatio-0.2.3.dev3/python/fabricatio/capabilities/rag.py +225 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/config.py +3 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/task.py +8 -8
- fabricatio-0.2.3.dev3/templates/built-in/retrieved_display.hbs +5 -0
- fabricatio-0.2.3.dev3/templates.tar.gz +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_action.py +2 -3
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_task.py +5 -6
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_usages.py +3 -4
- fabricatio-0.2.3.dev3/uv.lock +1573 -0
- fabricatio-0.2.3.dev1/python/fabricatio/capabilities/rag.py +0 -46
- fabricatio-0.2.3.dev1/templates.tar.gz +0 -0
- fabricatio-0.2.3.dev1/uv.lock +0 -1573
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/build-package.yaml +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/ruff.yaml +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.github/workflows/tests.yaml +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.gitignore +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/.python-version +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Cargo.lock +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Cargo.toml +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/LICENSE +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/Makefile +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/llm_usages/llm_usage.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_a_rating/rating.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/make_diary/commits.json +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/examples/task_handle/handle_task.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/_rust.pyi +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/_rust_instances.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/__init__.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/communication.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/actions/transmission.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/capabilities/rating.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/capabilities/task.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/core.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/decorators.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/__init__.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/curd.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/fs/readers.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/journal.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/action.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/events.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/generic.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/kwargs_types.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/role.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/tool.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/usages.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/models/utils.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/parser.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/py.typed +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/__init__.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/arithmetic.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/fs.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/python/fabricatio/toolboxes/task.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/hash.rs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/lib.rs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/src/templates.rs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/claude-xml.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/clean-up-code.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/dependencies.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/document-the-code.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_criteria.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_manual.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_rating_weights_klee.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/draft_tool_usage_code.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/extract_criteria_from_reasons.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/extract_reasons_from_examples.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/fix-bugs.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/improve-performance.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/make_choice.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/make_judgment.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/propose_task.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/rate_fine_grind.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/refactor.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/task_briefing.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/web-ctf-solver.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-git-commit.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-github-pull-request.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/templates/built-in/write-github-readme.hbs +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_config.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_advanced.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_generic.py +0 -0
- {fabricatio-0.2.3.dev1 → fabricatio-0.2.3.dev3}/tests/test_models/test_role.py +0 -0
- {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.
|
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
|
-
|
101
|
+
"""Action that says hello."""
|
102
102
|
|
103
|
-
|
104
|
-
|
103
|
+
name: str = "hello"
|
104
|
+
output_key: str = "task_output"
|
105
105
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
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
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
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
|
-
|
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
|
-
|
62
|
+
"""Action that says hello."""
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
name: str = "hello"
|
65
|
+
output_key: str = "task_output"
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
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
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
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
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
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
|
-
|
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.
|
22
|
-
task_input.
|
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",
|
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",
|
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",
|
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,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
|
49
|
+
"""The name of the task, which should be concise and descriptive."""
|
50
50
|
|
51
51
|
description: str = Field(default="")
|
52
|
-
"""
|
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
|
-
|
55
|
-
"""
|
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
|
-
"""
|
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
|
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,
|
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.
|
129
|
+
self.goals = goal if isinstance(goal, list) else [goal]
|
130
130
|
if description:
|
131
131
|
self.description = description
|
132
132
|
return self
|
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",
|
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",
|
113
|
+
task = Task(name="test", goals=["test"], description="test")
|
115
114
|
await workflow.serve(task)
|
116
115
|
assert task
|