fabricatio 0.2.4.dev3__tar.gz → 0.2.5.dev0__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 (107) hide show
  1. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/PKG-INFO +2 -1
  2. fabricatio-0.2.5.dev0/examples/write_outline/.gitignore +2 -0
  3. fabricatio-0.2.5.dev0/examples/write_outline/write_outline.py +24 -0
  4. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/pyproject.toml +6 -5
  5. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/__init__.py +12 -5
  6. fabricatio-0.2.5.dev0/python/fabricatio/actions/article.py +81 -0
  7. fabricatio-0.2.5.dev0/python/fabricatio/actions/output.py +21 -0
  8. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/capabilities/rag.py +40 -8
  9. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/capabilities/rating.py +6 -15
  10. fabricatio-0.2.5.dev0/python/fabricatio/capabilities/review.py +141 -0
  11. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/config.py +6 -0
  12. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/fs/__init__.py +11 -1
  13. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/fs/curd.py +14 -8
  14. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/fs/readers.py +5 -2
  15. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/action.py +2 -3
  16. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/extra.py +81 -9
  17. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/generic.py +25 -1
  18. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/kwargs_types.py +9 -1
  19. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/role.py +2 -2
  20. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/task.py +0 -14
  21. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/tool.py +3 -2
  22. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/usages.py +36 -0
  23. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/toolboxes/__init__.py +1 -3
  24. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/toolboxes/fs.py +15 -1
  25. fabricatio-0.2.5.dev0/python/fabricatio/workflows/articles.py +15 -0
  26. fabricatio-0.2.4.dev3/python/fabricatio/workflows/articles.py → fabricatio-0.2.5.dev0/python/fabricatio/workflows/rag.py +1 -1
  27. fabricatio-0.2.5.dev0/templates/built-in/pathstr.hbs +3 -0
  28. fabricatio-0.2.5.dev0/templates/built-in/review_string.hbs +14 -0
  29. fabricatio-0.2.5.dev0/templates.tar.gz +0 -0
  30. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/uv.lock +9 -1
  31. fabricatio-0.2.4.dev3/python/fabricatio/actions/article.py +0 -49
  32. fabricatio-0.2.4.dev3/python/fabricatio/toolboxes/task.py +0 -6
  33. fabricatio-0.2.4.dev3/templates.tar.gz +0 -0
  34. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/.github/workflows/build-package.yaml +0 -0
  35. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/.github/workflows/ruff.yaml +0 -0
  36. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/.github/workflows/tests.yaml +0 -0
  37. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/.gitignore +0 -0
  38. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/.python-version +0 -0
  39. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/Cargo.lock +0 -0
  40. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/Cargo.toml +0 -0
  41. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/LICENSE +0 -0
  42. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/Makefile +0 -0
  43. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/README.md +0 -0
  44. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/extract_article/extract.py +0 -0
  45. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/llm_usages/llm_usage.py +0 -0
  46. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/make_a_rating/rating.py +0 -0
  47. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/make_diary/commits.json +0 -0
  48. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/make_diary/diary.py +0 -0
  49. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/minor/hello_fabricatio.py +0 -0
  50. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/propose_task/propose.py +0 -0
  51. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/simple_chat/chat.py +0 -0
  52. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/simple_rag/simple_rag.py +0 -0
  53. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/examples/task_handle/handle_task.py +0 -0
  54. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/_rust.pyi +0 -0
  55. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/_rust_instances.py +0 -0
  56. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/actions/rag.py +0 -0
  57. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/capabilities/propose.py +0 -0
  58. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/capabilities/task.py +0 -0
  59. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/core.py +0 -0
  60. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/decorators.py +0 -0
  61. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/journal.py +0 -0
  62. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/events.py +0 -0
  63. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/models/utils.py +0 -0
  64. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/parser.py +0 -0
  65. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/py.typed +0 -0
  66. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/python/fabricatio/toolboxes/arithmetic.py +0 -0
  67. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/src/hash.rs +0 -0
  68. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/src/hbs_helpers.rs +0 -0
  69. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/src/lib.rs +0 -0
  70. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/src/templates.rs +0 -0
  71. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
  72. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/claude-xml.hbs +0 -0
  73. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/clean-up-code.hbs +0 -0
  74. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/create_json_obj.hbs +0 -0
  75. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
  76. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/dependencies.hbs +0 -0
  77. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/document-the-code.hbs +0 -0
  78. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/draft_rating_criteria.hbs +0 -0
  79. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/draft_rating_manual.hbs +0 -0
  80. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/draft_rating_weights_klee.hbs +0 -0
  81. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/draft_tool_usage_code.hbs +0 -0
  82. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/extract_criteria_from_reasons.hbs +0 -0
  83. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/extract_reasons_from_examples.hbs +0 -0
  84. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
  85. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/fix-bugs.hbs +0 -0
  86. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/improve-performance.hbs +0 -0
  87. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/liststr.hbs +0 -0
  88. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/make_choice.hbs +0 -0
  89. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/make_judgment.hbs +0 -0
  90. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/rate_fine_grind.hbs +0 -0
  91. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/refactor.hbs +0 -0
  92. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/refined_query.hbs +0 -0
  93. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/retrieved_display.hbs +0 -0
  94. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
  95. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/task_briefing.hbs +0 -0
  96. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/web-ctf-solver.hbs +0 -0
  97. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/write-git-commit.hbs +0 -0
  98. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/write-github-pull-request.hbs +0 -0
  99. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/templates/built-in/write-github-readme.hbs +0 -0
  100. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_config.py +0 -0
  101. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_action.py +0 -0
  102. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_advanced.py +0 -0
  103. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_generic.py +0 -0
  104. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_role.py +0 -0
  105. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_task.py +0 -0
  106. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_tool.py +0 -0
  107. {fabricatio-0.2.4.dev3 → fabricatio-0.2.5.dev0}/tests/test_models/test_usages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.4.dev3
3
+ Version: 0.2.5.dev0
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -9,6 +9,7 @@ Classifier: Framework :: AsyncIO
9
9
  Classifier: Framework :: Pydantic :: 2
10
10
  Classifier: Typing :: Typed
11
11
  Requires-Dist: appdirs>=1.4.4
12
+ Requires-Dist: async-cache>=1.1.1
12
13
  Requires-Dist: asyncio>=3.4.3
13
14
  Requires-Dist: asyncstdlib>=3.13.0
14
15
  Requires-Dist: litellm>=1.60.0
@@ -0,0 +1,2 @@
1
+ article_briefing.txt
2
+ out.typ
@@ -0,0 +1,24 @@
1
+ """Example of using the library."""
2
+
3
+ import asyncio
4
+
5
+ from fabricatio import Event, Role, WriteOutlineWorkFlow, logger
6
+
7
+
8
+ async def main() -> None:
9
+ """Main function."""
10
+ role = Role(
11
+ name="Undergraduate Researcher",
12
+ description="Write an outline for an article in typst format.",
13
+ registry={Event.quick_instantiate(ns := "article"): WriteOutlineWorkFlow},
14
+ )
15
+
16
+ proposed_task = await role.propose_task(
17
+ "You need to read the `./article_briefing.txt` file and write an outline for the article in typst format. The outline should be saved in the `./out.typ` file.",
18
+ )
19
+ path = await proposed_task.delegate(ns)
20
+ logger.success(f"The outline is saved in:\n{path}")
21
+
22
+
23
+ if __name__ == "__main__":
24
+ asyncio.run(main())
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.2.4-dev3"
3
+ version = "0.2.5-dev0"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -28,6 +28,7 @@ keywords = [
28
28
  requires-python = ">=3.12,<3.13"
29
29
  dependencies = [
30
30
  "appdirs>=1.4.4",
31
+ "async-cache>=1.1.1",
31
32
  "asyncio>=3.4.3",
32
33
  "asyncstdlib>=3.13.0",
33
34
  "litellm>=1.60.0",
@@ -138,6 +139,10 @@ testpaths = [
138
139
  asyncio_mode = "auto"
139
140
  asyncio_default_fixture_loop_scope = "function"
140
141
 
142
+ [[tool.uv.index]]
143
+ url = "https://mirrors.bfsu.edu.cn/pypi/web/simple"
144
+ default = true
145
+
141
146
  [[tool.uv.index]]
142
147
  url = "https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple"
143
148
 
@@ -147,7 +152,3 @@ url = "https://mirrors.huaweicloud.com/repository/pypi/simple"
147
152
  [[tool.uv.index]]
148
153
  url = "https://mirrors.aliyun.com/pypi/simple/"
149
154
 
150
- [[tool.uv.index]]
151
- url = "https://mirrors.bfsu.edu.cn/pypi/web/simple"
152
- default = true
153
-
@@ -3,9 +3,10 @@
3
3
  from importlib.util import find_spec
4
4
 
5
5
  from fabricatio._rust_instances import template_manager
6
- from fabricatio.actions.article import ExtractArticleEssence
6
+ from fabricatio.actions.article import ExtractArticleEssence, GenerateArticleProposal, GenerateOutline
7
+ from fabricatio.actions.output import DumpFinalizedOutput
7
8
  from fabricatio.core import env
8
- from fabricatio.fs import magika
9
+ from fabricatio.fs import magika, safe_json_read, safe_text_read
9
10
  from fabricatio.journal import logger
10
11
  from fabricatio.models.action import Action, WorkFlow
11
12
  from fabricatio.models.events import Event
@@ -15,15 +16,19 @@ from fabricatio.models.task import Task
15
16
  from fabricatio.models.tool import ToolBox
16
17
  from fabricatio.models.utils import Message, Messages
17
18
  from fabricatio.parser import Capture, CodeBlockCapture, JsonCapture, PythonCapture
18
- from fabricatio.toolboxes import arithmetic_toolbox, basic_toolboxes, fs_toolbox, task_toolbox
19
+ from fabricatio.toolboxes import arithmetic_toolbox, basic_toolboxes, fs_toolbox
20
+ from fabricatio.workflows.articles import WriteOutlineWorkFlow
19
21
 
20
22
  __all__ = [
21
23
  "Action",
22
24
  "ArticleEssence",
23
25
  "Capture",
24
26
  "CodeBlockCapture",
27
+ "DumpFinalizedOutput",
25
28
  "Event",
26
29
  "ExtractArticleEssence",
30
+ "GenerateArticleProposal",
31
+ "GenerateOutline",
27
32
  "JsonCapture",
28
33
  "Message",
29
34
  "Messages",
@@ -32,13 +37,15 @@ __all__ = [
32
37
  "Task",
33
38
  "ToolBox",
34
39
  "WorkFlow",
40
+ "WriteOutlineWorkFlow",
35
41
  "arithmetic_toolbox",
36
42
  "basic_toolboxes",
37
43
  "env",
38
44
  "fs_toolbox",
39
45
  "logger",
40
46
  "magika",
41
- "task_toolbox",
47
+ "safe_json_read",
48
+ "safe_text_read",
42
49
  "template_manager",
43
50
  ]
44
51
 
@@ -46,6 +53,6 @@ __all__ = [
46
53
  if find_spec("pymilvus"):
47
54
  from fabricatio.actions.rag import InjectToDB
48
55
  from fabricatio.capabilities.rag import RAG
49
- from fabricatio.workflows.articles import StoreArticle
56
+ from fabricatio.workflows.rag import StoreArticle
50
57
 
51
58
  __all__ += ["RAG", "InjectToDB", "StoreArticle"]
@@ -0,0 +1,81 @@
1
+ """Actions for transmitting tasks to targets."""
2
+
3
+ from os import PathLike
4
+ from pathlib import Path
5
+ from typing import Callable, List
6
+
7
+ from fabricatio.fs import safe_text_read
8
+ from fabricatio.journal import logger
9
+ from fabricatio.models.action import Action
10
+ from fabricatio.models.extra import ArticleEssence, ArticleOutline, ArticleProposal
11
+ from fabricatio.models.task import Task
12
+
13
+
14
+ class ExtractArticleEssence(Action):
15
+ """Extract the essence of article(s) in text format from the paths specified in the task dependencies.
16
+
17
+ Notes:
18
+ This action is designed to extract vital information from articles with Markdown format, which is pure text, and
19
+ which is converted from pdf files using `magic-pdf` from the `MinerU` project, see https://github.com/opendatalab/MinerU
20
+ """
21
+
22
+ output_key: str = "article_essence"
23
+ """The key of the output data."""
24
+
25
+ async def _execute[P: PathLike | str](
26
+ self,
27
+ task_input: Task,
28
+ reader: Callable[[P], str] = lambda p: Path(p).read_text(encoding="utf-8"),
29
+ **_,
30
+ ) -> List[ArticleEssence]:
31
+ if not task_input.dependencies:
32
+ logger.info(err := "Task not approved, since no dependencies are provided.")
33
+ raise RuntimeError(err)
34
+
35
+ # trim the references
36
+ contents = ["References".join(c.split("References")[:-1]) for c in map(reader, task_input.dependencies)]
37
+ return await self.propose(
38
+ ArticleEssence,
39
+ contents,
40
+ system_message=f"# your personal briefing: \n{self.briefing}",
41
+ )
42
+
43
+
44
+ class GenerateArticleProposal(Action):
45
+ """Generate an outline for the article based on the extracted essence."""
46
+
47
+ output_key: str = "article_proposal"
48
+ """The key of the output data."""
49
+
50
+ async def _execute(
51
+ self,
52
+ task_input: Task,
53
+ **_,
54
+ ) -> ArticleProposal:
55
+ input_path = await self.awhich_pathstr(
56
+ f"{task_input.briefing}\nExtract the path of file, which contains the article briefing that I need to read."
57
+ )
58
+
59
+ return await self.propose(
60
+ ArticleProposal,
61
+ safe_text_read(input_path),
62
+ system_message=f"# your personal briefing: \n{self.briefing}",
63
+ )
64
+
65
+
66
+ class GenerateOutline(Action):
67
+ """Generate the article based on the outline."""
68
+
69
+ output_key: str = "article"
70
+ """The key of the output data."""
71
+
72
+ async def _execute(
73
+ self,
74
+ article_proposal: ArticleProposal,
75
+ **_,
76
+ ) -> ArticleOutline:
77
+ return await self.propose(
78
+ ArticleOutline,
79
+ article_proposal.display(),
80
+ system_message=f"# your personal briefing: \n{self.briefing}",
81
+ )
@@ -0,0 +1,21 @@
1
+ """Dump the finalized output to a file."""
2
+
3
+ from typing import Unpack
4
+
5
+ from fabricatio.models.action import Action
6
+ from fabricatio.models.generic import FinalizedDumpAble
7
+ from fabricatio.models.task import Task
8
+
9
+
10
+ class DumpFinalizedOutput(Action):
11
+ """Dump the finalized output to a file."""
12
+
13
+ output_key: str = "dump_path"
14
+
15
+ async def _execute(self, task_input: Task, to_dump: FinalizedDumpAble, **cxt: Unpack) -> str:
16
+ dump_path = await self.awhich_pathstr(
17
+ f"{task_input.briefing}\n\nExtract a single path of the file, to which I will dump the data."
18
+ )
19
+
20
+ to_dump.finalized_dump_to(dump_path)
21
+ return dump_path
@@ -242,7 +242,6 @@ class RAG(EmbeddingUsage):
242
242
  async def aretrieve(
243
243
  self,
244
244
  query: List[str] | str,
245
- collection_name: Optional[str] = None,
246
245
  final_limit: int = 20,
247
246
  **kwargs: Unpack[FetchKwargs],
248
247
  ) -> List[str]:
@@ -250,7 +249,6 @@ class RAG(EmbeddingUsage):
250
249
 
251
250
  Args:
252
251
  query (List[str] | str): The query to be used for retrieval.
253
- collection_name (Optional[str]): The name of the collection. If not provided, the currently viewed collection is used.
254
252
  final_limit (int): The final limit on the number of results to return.
255
253
  **kwargs (Unpack[FetchKwargs]): Additional keyword arguments for retrieval.
256
254
 
@@ -263,15 +261,14 @@ class RAG(EmbeddingUsage):
263
261
  await self.afetch_document(
264
262
  vecs=(await self.vectorize(query)),
265
263
  desired_fields="text",
266
- collection_name=collection_name,
267
264
  **kwargs,
268
265
  )
269
266
  )[:final_limit]
270
267
 
271
268
  async def aask_retrieved(
272
269
  self,
273
- question: str | List[str],
274
- query: List[str] | str,
270
+ question: str,
271
+ query: Optional[List[str] | str] = None,
275
272
  collection_name: Optional[str] = None,
276
273
  extra_system_message: str = "",
277
274
  result_per_query: int = 10,
@@ -285,7 +282,7 @@ class RAG(EmbeddingUsage):
285
282
  specified question using the retrieved documents as context.
286
283
 
287
284
  Args:
288
- question (str | List[str]): The question or list of questions to be asked.
285
+ question (str): The question to be asked.
289
286
  query (List[str] | str): The query or list of queries used for document retrieval.
290
287
  collection_name (Optional[str]): The name of the collection to retrieve documents from.
291
288
  If not provided, the currently viewed collection is used.
@@ -299,9 +296,9 @@ class RAG(EmbeddingUsage):
299
296
  str: A string response generated after asking with the context of retrieved documents.
300
297
  """
301
298
  docs = await self.aretrieve(
302
- query,
303
- collection_name,
299
+ query or question,
304
300
  final_limit,
301
+ collection_name=collection_name,
305
302
  result_per_query=result_per_query,
306
303
  similarity_threshold=similarity_threshold,
307
304
  )
@@ -332,3 +329,38 @@ class RAG(EmbeddingUsage):
332
329
  ),
333
330
  **kwargs,
334
331
  )
332
+
333
+ async def aask_refined(
334
+ self,
335
+ question: str,
336
+ collection_name: Optional[str] = None,
337
+ extra_system_message: str = "",
338
+ result_per_query: int = 10,
339
+ final_limit: int = 20,
340
+ similarity_threshold: float = 0.37,
341
+ **kwargs: Unpack[LLMKwargs],
342
+ ) -> str:
343
+ """Asks a question using a refined query based on the provided question.
344
+
345
+ Args:
346
+ question (str): The question to be asked.
347
+ collection_name (Optional[str]): The name of the collection to retrieve documents from.
348
+ extra_system_message (str): An additional system message to be included in the prompt.
349
+ result_per_query (int): The number of results to return per query. Default is 10.
350
+ final_limit (int): The maximum number of retrieved documents to consider. Default is 20.
351
+ similarity_threshold (float): The threshold for similarity, only results above this threshold will be returned.
352
+ **kwargs (Unpack[LLMKwargs]): Additional keyword arguments passed to the underlying `aask` method.
353
+
354
+ Returns:
355
+ str: A string response generated after asking with the refined question.
356
+ """
357
+ return await self.aask_retrieved(
358
+ question,
359
+ await self.arefined_query(question, **kwargs),
360
+ collection_name=collection_name,
361
+ extra_system_message=extra_system_message,
362
+ result_per_query=result_per_query,
363
+ final_limit=final_limit,
364
+ similarity_threshold=similarity_threshold,
365
+ **kwargs,
366
+ )
@@ -4,7 +4,7 @@ from asyncio import gather
4
4
  from itertools import permutations
5
5
  from typing import Dict, List, Set, Tuple, Union, Unpack, overload
6
6
 
7
- import orjson
7
+ from cache import AsyncLRU
8
8
  from fabricatio._rust_instances import template_manager
9
9
  from fabricatio.config import configs
10
10
  from fabricatio.journal import logger
@@ -40,10 +40,8 @@ class GiveRating(WithBriefing, LLMUsage):
40
40
 
41
41
  def _validator(response: str) -> Dict[str, float] | None:
42
42
  if (
43
- (json_data := JsonCapture.convert_with(response, orjson.loads)) is not None
44
- and isinstance(json_data, dict)
43
+ (json_data := JsonCapture.validate_with(response, dict, str))
45
44
  and json_data.keys() == rating_manual.keys()
46
- and all(isinstance(v, float) for v in json_data.values())
47
45
  and all(score_range[0] <= v <= score_range[1] for v in json_data.values())
48
46
  ):
49
47
  return json_data
@@ -115,6 +113,7 @@ class GiveRating(WithBriefing, LLMUsage):
115
113
  return await gather(*[self.rate_fine_grind(item, manual, score_range, **kwargs) for item in to_rate])
116
114
  raise ValueError("to_rate must be a string or a list of strings")
117
115
 
116
+ @AsyncLRU(maxsize=32)
118
117
  async def draft_rating_manual(
119
118
  self, topic: str, criteria: Set[str], **kwargs: Unpack[ValidateKwargs]
120
119
  ) -> Dict[str, str]:
@@ -169,16 +168,6 @@ class GiveRating(WithBriefing, LLMUsage):
169
168
  Returns:
170
169
  Set[str]: A set of rating dimensions.
171
170
  """
172
-
173
- def _validator(response: str) -> Set[str] | None:
174
- if (
175
- json_data := JsonCapture.validate_with(
176
- response, target_type=list, elements_type=str, length=criteria_count
177
- )
178
- ) is not None:
179
- return set(json_data)
180
- return None
181
-
182
171
  return await self.aask_validate(
183
172
  question=(
184
173
  template_manager.render_template(
@@ -189,7 +178,9 @@ class GiveRating(WithBriefing, LLMUsage):
189
178
  },
190
179
  )
191
180
  ),
192
- validator=_validator,
181
+ validator=lambda resp: set(out)
182
+ if (out := JsonCapture.validate_with(resp, list, str, criteria_count))
183
+ else out,
193
184
  system_message=f"# your personal briefing: \n{self.briefing}",
194
185
  **kwargs,
195
186
  )
@@ -0,0 +1,141 @@
1
+ """A module that provides functionality to rate tasks based on a rating manual and score range."""
2
+
3
+ from typing import List, Optional, Set, Unpack
4
+
5
+ from fabricatio import template_manager
6
+ from fabricatio.capabilities.propose import Propose
7
+ from fabricatio.capabilities.rating import GiveRating
8
+ from fabricatio.config import configs
9
+ from fabricatio.models.generic import Display, ProposedAble, WithBriefing
10
+ from fabricatio.models.kwargs_types import GenerateKwargs, ReviewKwargs
11
+ from fabricatio.models.task import Task
12
+ from pydantic import PrivateAttr
13
+
14
+
15
+ class ReviewResult[T](ProposedAble, Display):
16
+ """Class that represents the result of a review.
17
+
18
+ This class holds the problems identified in a review and their proposed solutions,
19
+ along with a reference to the reviewed object.
20
+
21
+ Attributes:
22
+ existing_problems (List[str]): List of problems identified in the review.
23
+ proposed_solutions (List[str]): List of proposed solutions to the problems identified in the review.
24
+
25
+ Type Parameters:
26
+ T: The type of the object being reviewed.
27
+ """
28
+
29
+ existing_problems: List[str]
30
+ """List of problems identified in the review."""
31
+
32
+ proposed_solutions: List[str]
33
+ """List of proposed solutions to the problems identified in the review."""
34
+
35
+ _ref: T = PrivateAttr(None)
36
+ """Reference to the object being reviewed."""
37
+
38
+ def update_ref[K](self, ref: K) -> "ReviewResult[K]":
39
+ """Update the reference to the object being reviewed.
40
+
41
+ Args:
42
+ ref (K): The new reference to the object being reviewed.
43
+
44
+ Returns:
45
+ ReviewResult[K]: The updated instance of the ReviewResult class with the new reference type.
46
+ """
47
+ self._ref = ref
48
+ return self
49
+
50
+ def deref(self) -> T:
51
+ """Dereference the object being reviewed.
52
+
53
+ Returns:
54
+ T: The object being reviewed.
55
+ """
56
+ return self._ref
57
+
58
+
59
+ class Review(GiveRating, Propose):
60
+ """Class that provides functionality to review tasks and strings using a language model.
61
+
62
+ This class extends GiveRating and Propose capabilities to analyze content,
63
+ identify problems, and suggest solutions based on specified criteria.
64
+
65
+ The review process can be applied to Task objects or plain strings with
66
+ appropriate topic and criteria.
67
+ """
68
+
69
+ async def review_task[T](self, task: Task[T], **kwargs: Unpack[ReviewKwargs]) -> ReviewResult[Task[T]]:
70
+ """Review a task using specified review criteria.
71
+
72
+ This method analyzes a task object to identify problems and propose solutions
73
+ based on the criteria provided in kwargs.
74
+
75
+ Args:
76
+ task (Task[T]): The task object to be reviewed.
77
+ **kwargs (Unpack[ReviewKwargs]): Additional keyword arguments for the review process,
78
+ including topic and optional criteria.
79
+
80
+ Returns:
81
+ ReviewResult[Task[T]]: A review result containing identified problems and proposed solutions,
82
+ with a reference to the original task.
83
+ """
84
+ return await self.review_obj(task, **kwargs)
85
+
86
+ async def review_string(
87
+ self, text: str, topic: str, criteria: Optional[Set[str]] = None, **kwargs: Unpack[GenerateKwargs]
88
+ ) -> ReviewResult[str]:
89
+ """Review a string based on specified topic and criteria.
90
+
91
+ This method analyzes a text string to identify problems and propose solutions
92
+ based on the given topic and criteria.
93
+
94
+ Args:
95
+ text (str): The text content to be reviewed.
96
+ topic (str): The subject topic for the review criteria.
97
+ criteria (Optional[Set[str]], optional): A set of criteria for the review.
98
+ If not provided, criteria will be drafted automatically. Defaults to None.
99
+ **kwargs (Unpack[GenerateKwargs]): Additional keyword arguments for the LLM usage.
100
+
101
+ Returns:
102
+ ReviewResult[str]: A review result containing identified problems and proposed solutions,
103
+ with a reference to the original text.
104
+ """
105
+ criteria = criteria or (await self.draft_rating_criteria(topic))
106
+ manual = await self.draft_rating_manual(topic, criteria)
107
+ res: ReviewResult[str] = await self.propose(
108
+ ReviewResult,
109
+ template_manager.render_template(
110
+ configs.templates.review_string_template, {"text": text, "topic": topic, "criteria_manual": manual}
111
+ ),
112
+ **kwargs,
113
+ )
114
+ return res.update_ref(text)
115
+
116
+ async def review_obj[M: (Display, WithBriefing)](self, obj: M, **kwargs: Unpack[ReviewKwargs]) -> ReviewResult[M]:
117
+ """Review an object that implements Display or WithBriefing interface.
118
+
119
+ This method extracts displayable text from the object and performs a review
120
+ based on the criteria provided in kwargs.
121
+
122
+ Args:
123
+ obj (M): The object to be reviewed, which must implement either Display or WithBriefing.
124
+ **kwargs (Unpack[ReviewKwargs]): Additional keyword arguments for the review process,
125
+ including topic and optional criteria.
126
+
127
+ Raises:
128
+ TypeError: If the object does not implement Display or WithBriefing.
129
+
130
+ Returns:
131
+ ReviewResult[M]: A review result containing identified problems and proposed solutions,
132
+ with a reference to the original object.
133
+ """
134
+ if isinstance(obj, Display):
135
+ text = obj.display()
136
+ elif isinstance(obj, WithBriefing):
137
+ text = obj.briefing
138
+ else:
139
+ raise TypeError(f"Unsupported type for review: {type(obj)}")
140
+
141
+ return (await self.review_string(text, **kwargs)).update_ref(obj)
@@ -212,6 +212,12 @@ class TemplateConfig(BaseModel):
212
212
  refined_query_template: str = Field(default="refined_query")
213
213
  """The name of the refined query template which will be used to refine a query."""
214
214
 
215
+ pathstr_template: str = Field(default="pathstr")
216
+ """The name of the pathstr template which will be used to acquire a path of strings."""
217
+
218
+ review_string_template: str = Field(default="review_string")
219
+ """The name of the review string template which will be used to review a string."""
220
+
215
221
 
216
222
  class MagikaConfig(BaseModel):
217
223
  """Magika configuration class."""
@@ -1,9 +1,19 @@
1
1
  """FileSystem manipulation module for Fabricatio."""
2
2
 
3
- from fabricatio.fs.curd import copy_file, create_directory, delete_directory, delete_file, dump_text, move_file, tree
3
+ from fabricatio.fs.curd import (
4
+ absolute_path,
5
+ copy_file,
6
+ create_directory,
7
+ delete_directory,
8
+ delete_file,
9
+ dump_text,
10
+ move_file,
11
+ tree,
12
+ )
4
13
  from fabricatio.fs.readers import magika, safe_json_read, safe_text_read
5
14
 
6
15
  __all__ = [
16
+ "absolute_path",
7
17
  "copy_file",
8
18
  "create_directory",
9
19
  "delete_directory",
@@ -2,14 +2,14 @@
2
2
 
3
3
  import shutil
4
4
  import subprocess
5
+ from os import PathLike
5
6
  from pathlib import Path
6
7
  from typing import Union
7
8
 
8
- from fabricatio.decorators import depend_on_external_cmd, logging_execution_info
9
+ from fabricatio.decorators import depend_on_external_cmd
9
10
  from fabricatio.journal import logger
10
11
 
11
12
 
12
- @logging_execution_info
13
13
  def dump_text(path: Union[str, Path], text: str) -> None:
14
14
  """Dump text to a file. you need to make sure the file's parent directory exists.
15
15
 
@@ -23,7 +23,6 @@ def dump_text(path: Union[str, Path], text: str) -> None:
23
23
  Path(path).write_text(text, encoding="utf-8", errors="ignore")
24
24
 
25
25
 
26
- @logging_execution_info
27
26
  def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
28
27
  """Copy a file from source to destination.
29
28
 
@@ -43,7 +42,6 @@ def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
43
42
  raise
44
43
 
45
44
 
46
- @logging_execution_info
47
45
  def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
48
46
  """Move a file from source to destination.
49
47
 
@@ -63,7 +61,6 @@ def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
63
61
  raise
64
62
 
65
63
 
66
- @logging_execution_info
67
64
  def delete_file(file_path: Union[str, Path]) -> None:
68
65
  """Delete a file.
69
66
 
@@ -82,7 +79,6 @@ def delete_file(file_path: Union[str, Path]) -> None:
82
79
  raise
83
80
 
84
81
 
85
- @logging_execution_info
86
82
  def create_directory(dir_path: Union[str, Path], parents: bool = True, exist_ok: bool = True) -> None:
87
83
  """Create a directory.
88
84
 
@@ -99,7 +95,6 @@ def create_directory(dir_path: Union[str, Path], parents: bool = True, exist_ok:
99
95
  raise
100
96
 
101
97
 
102
- @logging_execution_info
103
98
  @depend_on_external_cmd(
104
99
  "erd",
105
100
  "Please install `erd` using `cargo install erdtree` or `scoop install erdtree`.",
@@ -111,7 +106,6 @@ def tree(dir_path: Union[str, Path]) -> str:
111
106
  return subprocess.check_output(("erd", dir_path.as_posix()), encoding="utf-8") # noqa: S603
112
107
 
113
108
 
114
- @logging_execution_info
115
109
  def delete_directory(dir_path: Union[str, Path]) -> None:
116
110
  """Delete a directory and its contents.
117
111
 
@@ -128,3 +122,15 @@ def delete_directory(dir_path: Union[str, Path]) -> None:
128
122
  except OSError as e:
129
123
  logger.error(f"Failed to delete directory {dir_path}: {e!s}")
130
124
  raise
125
+
126
+
127
+ def absolute_path(path: str | Path | PathLike) -> str:
128
+ """Get the absolute path of a file or directory.
129
+
130
+ Args:
131
+ path (str, Path, PathLike): The path to the file or directory.
132
+
133
+ Returns:
134
+ str: The absolute path of the file or directory.
135
+ """
136
+ return Path(path).expanduser().resolve().as_posix()
@@ -7,6 +7,7 @@ from magika import Magika
7
7
  from orjson import orjson
8
8
 
9
9
  from fabricatio.config import configs
10
+ from fabricatio.journal import logger
10
11
 
11
12
  magika = Magika(model_dir=configs.magika.model_dir)
12
13
 
@@ -23,7 +24,8 @@ def safe_text_read(path: Path | str) -> str:
23
24
  path = Path(path)
24
25
  try:
25
26
  return path.read_text(encoding="utf-8")
26
- except (UnicodeDecodeError, IsADirectoryError, FileNotFoundError):
27
+ except (UnicodeDecodeError, IsADirectoryError, FileNotFoundError) as e:
28
+ logger.error(f"Failed to read file {path}: {e!s}")
27
29
  return ""
28
30
 
29
31
 
@@ -39,5 +41,6 @@ def safe_json_read(path: Path | str) -> Dict:
39
41
  path = Path(path)
40
42
  try:
41
43
  return orjson.loads(path.read_text(encoding="utf-8"))
42
- except (orjson.JSONDecodeError, IsADirectoryError, FileNotFoundError):
44
+ except (orjson.JSONDecodeError, IsADirectoryError, FileNotFoundError) as e:
45
+ logger.error(f"Failed to read file {path}: {e!s}")
43
46
  return {}