fabricatio 0.2.0.dev14__tar.gz → 0.2.0.dev16__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 (110) hide show
  1. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/PKG-INFO +1 -1
  2. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/examples/llm_usages/llm_usage.py +15 -5
  3. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/examples/minor/hello_fabricatio.py +2 -0
  4. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/examples/propose_task/propose.py +2 -0
  5. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/examples/simple_chat/chat.py +2 -0
  6. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/pyproject.toml +3 -4
  7. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/_rust.pyi +11 -11
  8. fabricatio-0.2.0.dev16/python/fabricatio/_rust_instances.py +4 -0
  9. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/actions/communication.py +2 -0
  10. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/actions/transmission.py +2 -0
  11. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/config.py +40 -14
  12. fabricatio-0.2.0.dev16/python/fabricatio/decorators.py +72 -0
  13. fabricatio-0.2.0.dev16/python/fabricatio/fs/curd.py +110 -0
  14. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/fs/readers.py +2 -0
  15. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/action.py +5 -4
  16. fabricatio-0.2.0.dev16/python/fabricatio/models/advanced.py +96 -0
  17. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/events.py +4 -2
  18. fabricatio-0.2.0.dev16/python/fabricatio/models/generic.py +110 -0
  19. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/role.py +3 -2
  20. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/task.py +2 -29
  21. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/tool.py +65 -49
  22. fabricatio-0.2.0.dev16/python/fabricatio/models/usages.py +464 -0
  23. fabricatio-0.2.0.dev16/python/fabricatio/toolboxes/fs.py +14 -0
  24. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/toolboxes/task.py +2 -0
  25. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/src/templates.rs +5 -1
  26. fabricatio-0.2.0.dev16/templates.tar.gz +0 -0
  27. fabricatio-0.2.0.dev16/tests/test_config.py +50 -0
  28. fabricatio-0.2.0.dev16/tests/test_models/test_action.py +53 -0
  29. fabricatio-0.2.0.dev16/tests/test_models/test_advanced.py +19 -0
  30. fabricatio-0.2.0.dev16/tests/test_models/test_generic.py +43 -0
  31. fabricatio-0.2.0.dev16/tests/test_models/test_role.py +34 -0
  32. fabricatio-0.2.0.dev16/tests/test_models/test_task.py +32 -0
  33. fabricatio-0.2.0.dev16/tests/test_models/test_tool.py +34 -0
  34. fabricatio-0.2.0.dev16/tests/test_models/test_usages.py +20 -0
  35. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/uv.lock +1 -1
  36. fabricatio-0.2.0.dev14/python/fabricatio/_rust_instances.py +0 -4
  37. fabricatio-0.2.0.dev14/python/fabricatio/decorators.py +0 -35
  38. fabricatio-0.2.0.dev14/python/fabricatio/models/generic.py +0 -530
  39. fabricatio-0.2.0.dev14/templates.tar.gz +0 -0
  40. fabricatio-0.2.0.dev14/tests/__init__.py +0 -0
  41. fabricatio-0.2.0.dev14/tests/test_config.py +0 -18
  42. fabricatio-0.2.0.dev14/tests/test_core.py +0 -56
  43. fabricatio-0.2.0.dev14/tests/test_decorators.py +0 -23
  44. fabricatio-0.2.0.dev14/tests/test_events.py +0 -41
  45. fabricatio-0.2.0.dev14/tests/test_examples.py +0 -30
  46. fabricatio-0.2.0.dev14/tests/test_models/test_action.py +0 -41
  47. fabricatio-0.2.0.dev14/tests/test_models/test_communication.py +0 -19
  48. fabricatio-0.2.0.dev14/tests/test_models/test_events.py +0 -27
  49. fabricatio-0.2.0.dev14/tests/test_models/test_generic.py +0 -22
  50. fabricatio-0.2.0.dev14/tests/test_models/test_llm_usage.py +0 -21
  51. fabricatio-0.2.0.dev14/tests/test_models/test_propose_task.py +0 -22
  52. fabricatio-0.2.0.dev14/tests/test_models/test_role.py +0 -23
  53. fabricatio-0.2.0.dev14/tests/test_models/test_role_with_actions.py +0 -23
  54. fabricatio-0.2.0.dev14/tests/test_models/test_task.py +0 -40
  55. fabricatio-0.2.0.dev14/tests/test_models/test_template_manager.py +0 -29
  56. fabricatio-0.2.0.dev14/tests/test_models/test_tool.py +0 -56
  57. fabricatio-0.2.0.dev14/tests/test_models/test_transmission.py +0 -29
  58. fabricatio-0.2.0.dev14/tests/test_models/test_utils.py +0 -12
  59. fabricatio-0.2.0.dev14/tests/test_models/test_with_briefing.py +0 -17
  60. fabricatio-0.2.0.dev14/tests/test_models/test_with_briefing_and_json_example.py +0 -22
  61. fabricatio-0.2.0.dev14/tests/test_models/test_with_briefing_and_llm_usage.py +0 -22
  62. fabricatio-0.2.0.dev14/tests/test_models/test_with_briefing_and_llm_usage_and_json_example.py +0 -29
  63. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency.py +0 -19
  64. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_briefing.py +0 -22
  65. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_briefing_and_json_example.py +0 -29
  66. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_briefing_and_llm_usage.py +0 -25
  67. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_json_example.py +0 -22
  68. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_llm_usage.py +0 -22
  69. fabricatio-0.2.0.dev14/tests/test_models/test_with_dependency_and_llm_usage_and_json_example.py +0 -25
  70. fabricatio-0.2.0.dev14/tests/test_models/test_with_json_example.py +0 -17
  71. fabricatio-0.2.0.dev14/tests/test_models/test_workflow.py +0 -30
  72. fabricatio-0.2.0.dev14/tests/test_parser.py +0 -9
  73. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/.github/workflows/build-package.yaml +0 -0
  74. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/.github/workflows/ruff.yaml +0 -0
  75. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/.github/workflows/tests.yaml +0 -0
  76. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/.gitignore +0 -0
  77. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/.python-version +0 -0
  78. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/Cargo.lock +0 -0
  79. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/Cargo.toml +0 -0
  80. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/LICENSE +0 -0
  81. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/Makefile +0 -0
  82. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/README.md +0 -0
  83. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/__init__.py +0 -0
  84. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/actions/__init__.py +0 -0
  85. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/core.py +0 -0
  86. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/fs/__init__.py +0 -0
  87. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/journal.py +0 -0
  88. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/models/utils.py +0 -0
  89. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/parser.py +0 -0
  90. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/py.typed +0 -0
  91. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/toolboxes/__init__.py +0 -0
  92. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/python/fabricatio/toolboxes/arithmetic.py +0 -0
  93. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/src/lib.rs +0 -0
  94. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
  95. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/claude-xml.hbs +0 -0
  96. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/clean-up-code.hbs +0 -0
  97. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
  98. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/document-the-code.hbs +0 -0
  99. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
  100. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/fix-bugs.hbs +0 -0
  101. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/improve-performance.hbs +0 -0
  102. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/make_choice.hbs +0 -0
  103. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/make_judgment.hbs +0 -0
  104. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/propose_task.hbs +0 -0
  105. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/refactor.hbs +0 -0
  106. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
  107. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/web-ctf-solver.hbs +0 -0
  108. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/write-git-commit.hbs +0 -0
  109. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/write-github-pull-request.hbs +0 -0
  110. {fabricatio-0.2.0.dev14 → fabricatio-0.2.0.dev16}/templates/built-in/write-github-readme.hbs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.0.dev14
3
+ Version: 0.2.0.dev16
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -1,7 +1,10 @@
1
+ """Example of using the library."""
2
+
1
3
  import asyncio
2
- from typing import Any
4
+ from typing import Any, Callable
3
5
 
4
6
  from fabricatio import Action, Role, Task, WorkFlow, logger
7
+ from fabricatio.parser import PythonCapture
5
8
 
6
9
  task = Task(name="say hello", goal="say hello", description="say hello to the world")
7
10
 
@@ -13,9 +16,18 @@ class Talk(Action):
13
16
  output_key: str = "task_output"
14
17
 
15
18
  async def _execute(self, task_input: Task[str], **_) -> Any:
16
- ret = "Hello fabricatio!"
19
+ def _validator(res: str) -> Callable[[float, float], float] | None:
20
+ code = PythonCapture.capture(res)
21
+ exec(code, None, locals())
22
+ if "addup" in locals():
23
+ return locals().get("addup")
24
+ return None
25
+
26
+ func = await self.aask_validate(
27
+ "make a python function which can compute addition of two numbers, with good typing, the function name shall be `addup`"
28
+ )
17
29
  logger.info("executing talk action")
18
- return ret
30
+ return func
19
31
 
20
32
 
21
33
  async def main() -> None:
@@ -23,8 +35,6 @@ async def main() -> None:
23
35
  role = Role(
24
36
  name="talker", description="talker role", registry={task.pending_label: WorkFlow(name="talk", steps=(Talk,))}
25
37
  )
26
- logger.info(Task.json_example())
27
- logger.info(f"proposed task: {await role.propose('write a rust clap cli that can download a html page')}")
28
38
 
29
39
 
30
40
  if __name__ == "__main__":
@@ -1,3 +1,5 @@
1
+ """Example of a simple hello world program using fabricatio."""
2
+
1
3
  import asyncio
2
4
  from typing import Any
3
5
 
@@ -1,3 +1,5 @@
1
+ """Example of proposing a task to a role."""
2
+
1
3
  import asyncio
2
4
  from typing import Any
3
5
 
@@ -1,3 +1,5 @@
1
+ """Simple chat example."""
2
+
1
3
  import asyncio
2
4
  from typing import Any
3
5
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.2.0-dev.14"
3
+ version = "0.2.0-dev.16"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -82,7 +82,7 @@ full = [
82
82
 
83
83
 
84
84
  [tool.ruff]
85
- include = ["pyproject.toml", "python/fabricatio/*.py", "examples/*.py"]
85
+ include = ["pyproject.toml", "python/fabricatio/*.py","python/fabricatio/*.pyi", "examples/*.py"]
86
86
  line-length = 120
87
87
  target-version = "py312"
88
88
  [tool.ruff.format]
@@ -117,11 +117,10 @@ select = [
117
117
  "RUF", # Ruff-specific rules
118
118
  ]
119
119
  ignore = [
120
- "S101",
121
120
  "ANN401",
122
121
  "ANN003",
123
- "D100",
124
122
  "PYI063",
123
+ "PYI021",
125
124
  "ASYNC109"
126
125
  ]
127
126
  [tool.ruff.lint.pydocstyle]
@@ -1,13 +1,13 @@
1
1
  from pathlib import Path
2
- from typing import Dict, List, Any
3
-
2
+ from typing import Any, Dict, List, Optional
4
3
 
5
4
  class TemplateManager:
6
- def __init__(self, template_dirs: List[Path]) -> None:
7
- """
8
- Initialize the template manager.
5
+ def __init__(self, template_dirs: List[Path], suffix: Optional[str] = None) -> None:
6
+ """Initialize the template manager.
7
+
9
8
  Args:
10
9
  template_dirs (List[Path]): A list of paths to directories containing templates.
10
+ suffix (str, optional): The suffix of template files. None means 'hbs' suffix .
11
11
  """
12
12
 
13
13
  @property
@@ -19,8 +19,8 @@ class TemplateManager:
19
19
  """Get a list of template names."""
20
20
 
21
21
  def get_template(self, name: str) -> str:
22
- """
23
- Get a template by name.
22
+ """Get a template by name.
23
+
24
24
  Args:
25
25
  name (str): The name of the template to retrieve.
26
26
 
@@ -29,8 +29,8 @@ class TemplateManager:
29
29
  """
30
30
 
31
31
  def get_template_source(self, name: str) -> str:
32
- """
33
- Get the source path of a template by name.
32
+ """Get the source path of a template by name.
33
+
34
34
  Args:
35
35
  name (str): The name of the template to retrieve.
36
36
 
@@ -42,8 +42,8 @@ class TemplateManager:
42
42
  """Discover templates in the specified directories."""
43
43
 
44
44
  def render_template(self, name: str, data: Dict[str, Any]) -> str:
45
- """
46
- Render a template with the given name and data.
45
+ """Render a template with the given name and data.
46
+
47
47
  Args:
48
48
  name (str): The name of the template to render.
49
49
  data (Dict[str, Any]): The data to pass to the template.
@@ -0,0 +1,4 @@
1
+ from fabricatio._rust import TemplateManager
2
+ from fabricatio.config import configs
3
+
4
+ template_manager = TemplateManager(template_dirs=configs.templates.template_dir, suffix=configs.templates.template_suffix)
@@ -1,3 +1,5 @@
1
+ """Actions that involve communication with the user."""
2
+
1
3
  from fabricatio.models.action import Action
2
4
  from fabricatio.models.task import Task
3
5
 
@@ -1,3 +1,5 @@
1
+ """Actions for transmitting tasks to targets."""
2
+
1
3
  from typing import List
2
4
 
3
5
  from fabricatio.journal import logger
@@ -116,17 +116,17 @@ class DebugConfig(BaseModel):
116
116
  """The log file of the application."""
117
117
 
118
118
 
119
- class Code2PromptConfig(BaseModel):
120
- """Code2Prompt configuration class."""
119
+ class TemplateConfig(BaseModel):
120
+ """Template configuration class."""
121
121
 
122
122
  model_config = ConfigDict(use_attribute_docstrings=True)
123
123
  template_dir: List[DirectoryPath] = Field(
124
124
  default_factory=lambda: [DirectoryPath(r".\templates"), DirectoryPath(rf"{ROAMING_DIR}\templates")]
125
125
  )
126
- """The directory containing the templates for code2prompt."""
126
+ """The directory containing the templates."""
127
127
 
128
- template_suffix: str = Field(default=".hbs", frozen=True)
129
- """The suffix of the template files for code2prompt."""
128
+ template_suffix: str = Field(default="hbs", frozen=True)
129
+ """The suffix of the templates."""
130
130
 
131
131
 
132
132
  class MagikaConfig(BaseModel):
@@ -137,6 +137,26 @@ class MagikaConfig(BaseModel):
137
137
  """The directory containing the models for magika."""
138
138
 
139
139
 
140
+ class GeneralConfig(BaseModel):
141
+ """Global configuration class."""
142
+
143
+ model_config = ConfigDict(use_attribute_docstrings=True)
144
+ workspace: DirectoryPath = Field(default=DirectoryPath(r"."))
145
+ """The workspace directory for the application."""
146
+
147
+ confirm_on_fs_ops: bool = Field(default=True)
148
+ """Whether to confirm on file system operations."""
149
+
150
+
151
+ class ToolBoxConfig(BaseModel):
152
+ """Toolbox configuration class."""
153
+
154
+ model_config = ConfigDict(use_attribute_docstrings=True)
155
+
156
+ tool_module_name: str = Field(default="Toolbox")
157
+ """The name of the module containing the toolbox."""
158
+
159
+
140
160
  class Settings(BaseSettings):
141
161
  """Application settings class.
142
162
 
@@ -144,7 +164,7 @@ class Settings(BaseSettings):
144
164
  llm (LLMConfig): LLM Configuration
145
165
  debug (DebugConfig): Debug Configuration
146
166
  pymitter (PymitterConfig): Pymitter Configuration
147
- code2prompt (Code2PromptConfig): Code2Prompt Configuration
167
+ templates (TemplateConfig): Template Configuration
148
168
  magika (MagikaConfig): Magika Configuration
149
169
  """
150
170
 
@@ -167,20 +187,26 @@ class Settings(BaseSettings):
167
187
  pymitter: PymitterConfig = Field(default_factory=PymitterConfig)
168
188
  """Pymitter Configuration"""
169
189
 
170
- code2prompt: Code2PromptConfig = Field(default_factory=Code2PromptConfig)
171
- """Code2Prompt Configuration"""
190
+ templates: TemplateConfig = Field(default_factory=TemplateConfig)
191
+ """Template Configuration"""
172
192
 
173
193
  magika: MagikaConfig = Field(default_factory=MagikaConfig)
174
194
  """Magika Configuration"""
175
195
 
196
+ general: GeneralConfig = Field(default_factory=GeneralConfig)
197
+ """General Configuration"""
198
+
199
+ toolbox: ToolBoxConfig = Field(default_factory=ToolBoxConfig)
200
+ """Toolbox Configuration"""
201
+
176
202
  @classmethod
177
203
  def settings_customise_sources(
178
- cls,
179
- settings_cls: type[BaseSettings],
180
- init_settings: PydanticBaseSettingsSource,
181
- env_settings: PydanticBaseSettingsSource,
182
- dotenv_settings: PydanticBaseSettingsSource,
183
- file_secret_settings: PydanticBaseSettingsSource,
204
+ cls,
205
+ settings_cls: type[BaseSettings],
206
+ init_settings: PydanticBaseSettingsSource,
207
+ env_settings: PydanticBaseSettingsSource,
208
+ dotenv_settings: PydanticBaseSettingsSource,
209
+ file_secret_settings: PydanticBaseSettingsSource,
184
210
  ) -> tuple[PydanticBaseSettingsSource, ...]:
185
211
  """Customize settings sources.
186
212
 
@@ -0,0 +1,72 @@
1
+ """Decorators for Fabricatio."""
2
+
3
+ from functools import wraps
4
+ from inspect import signature
5
+ from shutil import which
6
+ from typing import Callable, Optional
7
+
8
+ from questionary import confirm
9
+
10
+ from fabricatio.config import configs
11
+ from fabricatio.journal import logger
12
+
13
+
14
+ def depend_on_external_cmd[**P, R](
15
+ bin_name: str, install_tip: Optional[str], homepage: Optional[str] = None
16
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]:
17
+ """Decorator to check for the presence of an external command.
18
+
19
+ Args:
20
+ bin_name (str): The name of the required binary.
21
+ install_tip (Optional[str]): Installation instructions for the required binary.
22
+ homepage (Optional[str]): The homepage of the required binary.
23
+
24
+ Returns:
25
+ Callable[[Callable[P, R]], Callable[P, R]]: A decorator that wraps the function to check for the binary.
26
+
27
+ Raises:
28
+ RuntimeError: If the required binary is not found.
29
+ """
30
+
31
+ def _decorator(func: Callable[P, R]) -> Callable[P, R]:
32
+ @wraps(func)
33
+ def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
34
+ if which(bin_name) is None:
35
+ err = f"`{bin_name}` is required to run {func.__name__}{signature(func)}, please install it the to `PATH` first."
36
+ if install_tip is not None:
37
+ err += f"\nInstall tip: {install_tip}"
38
+ if homepage is not None:
39
+ err += f"\nHomepage: {homepage}"
40
+ logger.error(err)
41
+ raise RuntimeError(err)
42
+ return func(*args, **kwargs)
43
+
44
+ return _wrapper
45
+
46
+ return _decorator
47
+
48
+
49
+ def confirm_to_execute[**P, R](func: Callable[P, R]) -> Callable[P, Optional[R]] | Callable[P, R]:
50
+ """Decorator to confirm before executing a function.
51
+
52
+ Args:
53
+ func (Callable): The function to be executed
54
+
55
+ Returns:
56
+ Callable: A decorator that wraps the function to confirm before execution.
57
+ """
58
+ if not configs.general.confirm_on_fs_ops:
59
+ # Skip confirmation if the configuration is set to False
60
+ return func
61
+
62
+ @wraps(func)
63
+ def _wrapper(*args: P.args, **kwargs: P.kwargs) -> Optional[R]:
64
+ if confirm(
65
+ f"Are you sure to execute function: {func.__name__}{signature(func)} \n📦 Args:{args}\n🔑 Kwargs:{kwargs}\n",
66
+ instruction="Please input [Yes/No] to proceed (default: Yes):",
67
+ ).ask():
68
+ return func(*args, **kwargs)
69
+ logger.warning(f"Function: {func.__name__}{signature(func)} canceled by user.")
70
+ return None
71
+
72
+ return _wrapper
@@ -0,0 +1,110 @@
1
+ """File system create, update, read, delete operations."""
2
+
3
+ import shutil
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Union
7
+
8
+ from fabricatio.decorators import depend_on_external_cmd
9
+ from fabricatio.journal import logger
10
+
11
+
12
+ def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
13
+ """Copy a file from source to destination.
14
+
15
+ Args:
16
+ src: Source file path
17
+ dst: Destination file path
18
+
19
+ Raises:
20
+ FileNotFoundError: If source file doesn't exist
21
+ shutil.SameFileError: If source and destination are the same
22
+ """
23
+ try:
24
+ shutil.copy(src, dst)
25
+ logger.info(f"Copied file from {src} to {dst}")
26
+ except OSError as e:
27
+ logger.error(f"Failed to copy file from {src} to {dst}: {e!s}")
28
+ raise
29
+
30
+
31
+ def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
32
+ """Move a file from source to destination.
33
+
34
+ Args:
35
+ src: Source file path
36
+ dst: Destination file path
37
+
38
+ Raises:
39
+ FileNotFoundError: If source file doesn't exist
40
+ shutil.SameFileError: If source and destination are the same
41
+ """
42
+ try:
43
+ shutil.move(src, dst)
44
+ logger.info(f"Moved file from {src} to {dst}")
45
+ except OSError as e:
46
+ logger.error(f"Failed to move file from {src} to {dst}: {e!s}")
47
+ raise
48
+
49
+
50
+ def delete_file(file_path: Union[str, Path]) -> None:
51
+ """Delete a file.
52
+
53
+ Args:
54
+ file_path: Path to the file to be deleted
55
+
56
+ Raises:
57
+ FileNotFoundError: If file doesn't exist
58
+ PermissionError: If no permission to delete the file
59
+ """
60
+ try:
61
+ Path(file_path).unlink()
62
+ logger.info(f"Deleted file: {file_path}")
63
+ except OSError as e:
64
+ logger.error(f"Failed to delete file {file_path}: {e!s}")
65
+ raise
66
+
67
+
68
+ def create_directory(dir_path: Union[str, Path], parents: bool = True, exist_ok: bool = True) -> None:
69
+ """Create a directory.
70
+
71
+ Args:
72
+ dir_path: Path to the directory to create
73
+ parents: Create parent directories if they don't exist
74
+ exist_ok: Don't raise error if directory already exists
75
+ """
76
+ try:
77
+ Path(dir_path).mkdir(parents=parents, exist_ok=exist_ok)
78
+ logger.info(f"Created directory: {dir_path}")
79
+ except OSError as e:
80
+ logger.error(f"Failed to create directory {dir_path}: {e!s}")
81
+ raise
82
+
83
+
84
+ @depend_on_external_cmd(
85
+ "erd",
86
+ "Please install `erd` using `cargo install erdtree` or `scoop install erdtree`.",
87
+ "https://github.com/solidiquis/erdtree",
88
+ )
89
+ def tree(dir_path: Union[str, Path]) -> str:
90
+ """Generate a tree representation of the directory structure. Requires `erd` to be installed."""
91
+ dir_path = Path(dir_path)
92
+ return subprocess.check_output(("erd", dir_path.as_posix()), encoding="utf-8") # noqa: S603
93
+
94
+
95
+ def delete_directory(dir_path: Union[str, Path]) -> None:
96
+ """Delete a directory and its contents.
97
+
98
+ Args:
99
+ dir_path: Path to the directory to delete
100
+
101
+ Raises:
102
+ FileNotFoundError: If directory doesn't exist
103
+ OSError: If directory is not empty and can't be removed
104
+ """
105
+ try:
106
+ shutil.rmtree(dir_path)
107
+ logger.info(f"Deleted directory: {dir_path}")
108
+ except OSError as e:
109
+ logger.error(f"Failed to delete directory {dir_path}: {e!s}")
110
+ raise
@@ -1,3 +1,5 @@
1
+ """Filesystem readers for Fabricatio."""
2
+
1
3
  from magika import Magika
2
4
 
3
5
  from fabricatio.config import configs
@@ -6,9 +6,10 @@ from asyncio import Queue
6
6
  from typing import Any, Dict, Self, Tuple, Type, Unpack
7
7
 
8
8
  from fabricatio.journal import logger
9
- from fabricatio.models.generic import LLMUsage, WithBriefing
10
- from fabricatio.models.task import ProposeTask, Task
11
- from fabricatio.models.tool import ToolBoxUsage
9
+ from fabricatio.models.advanced import ProposeTask
10
+ from fabricatio.models.generic import WithBriefing
11
+ from fabricatio.models.task import Task
12
+ from fabricatio.models.usages import ToolBoxUsage
12
13
  from pydantic import Field, PrivateAttr
13
14
 
14
15
 
@@ -51,7 +52,7 @@ class Action(ProposeTask, ToolBoxUsage):
51
52
  return f"# The action you are going to perform: \n{super().briefing}"
52
53
 
53
54
 
54
- class WorkFlow[A: Type[Action] | Action](WithBriefing, LLMUsage, ToolBoxUsage):
55
+ class WorkFlow[A: Type[Action] | Action](WithBriefing, ToolBoxUsage):
55
56
  """Class that represents a workflow to be executed in a task."""
56
57
 
57
58
  _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
@@ -0,0 +1,96 @@
1
+ """A module for advanced models and functionalities."""
2
+
3
+ from typing import List
4
+
5
+ from fabricatio._rust_instances import template_manager
6
+ from fabricatio.models.generic import WithBriefing
7
+ from fabricatio.models.task import Task
8
+ from fabricatio.models.usages import LLMUsage, ToolBoxUsage
9
+ from fabricatio.parser import JsonCapture
10
+ from loguru import logger
11
+ from pydantic import NonNegativeFloat, PositiveInt, ValidationError
12
+
13
+
14
+ class ProposeTask(LLMUsage, WithBriefing):
15
+ """A class that proposes a task based on a prompt."""
16
+
17
+ async def propose(
18
+ self,
19
+ prompt: str,
20
+ max_validations: PositiveInt = 2,
21
+ model: str | None = None,
22
+ temperature: NonNegativeFloat | None = None,
23
+ stop: str | List[str] | None = None,
24
+ top_p: NonNegativeFloat | None = None,
25
+ max_tokens: PositiveInt | None = None,
26
+ stream: bool | None = None,
27
+ timeout: PositiveInt | None = None,
28
+ max_retries: PositiveInt | None = None,
29
+ ) -> Task:
30
+ """Asynchronously proposes a task based on a given prompt and parameters.
31
+
32
+ Parameters:
33
+ prompt: The prompt text for proposing a task, which is a string that must be provided.
34
+ max_validations: The maximum number of validations allowed, default is 2.
35
+ model: The model to be used, default is None.
36
+ temperature: The sampling temperature, default is None.
37
+ stop: The stop sequence(s) for generation, default is None.
38
+ top_p: The nucleus sampling parameter, default is None.
39
+ max_tokens: The maximum number of tokens to be generated, default is None.
40
+ stream: Whether to stream the output, default is None.
41
+ timeout: The timeout for the operation, default is None.
42
+ max_retries: The maximum number of retries for the operation, default is None.
43
+
44
+ Returns:
45
+ A Task object based on the proposal result.
46
+ """
47
+ if not prompt:
48
+ err = f"{self.name}: Prompt must be provided."
49
+ logger.error(err)
50
+ raise ValueError(err)
51
+
52
+ def _validate_json(response: str) -> None | Task:
53
+ try:
54
+ cap = JsonCapture.capture(response)
55
+ logger.debug(f"Response: \n{response}")
56
+ logger.info(f"Captured JSON: \n{cap}")
57
+ return Task.model_validate_json(cap)
58
+ except ValidationError as e:
59
+ logger.error(f"Failed to parse task from JSON: {e}")
60
+ return None
61
+
62
+ template_data = {"prompt": prompt, "json_example": Task.json_example()}
63
+ return await self.aask_validate(
64
+ question=template_manager.render_template("propose_task", template_data),
65
+ validator=_validate_json,
66
+ system_message=f"# your personal briefing: \n{self.briefing}",
67
+ max_validations=max_validations,
68
+ model=model,
69
+ temperature=temperature,
70
+ stop=stop,
71
+ top_p=top_p,
72
+ max_tokens=max_tokens,
73
+ stream=stream,
74
+ timeout=timeout,
75
+ max_retries=max_retries,
76
+ )
77
+
78
+
79
+ class HandleTask(WithBriefing, ToolBoxUsage):
80
+ """A class that handles a task based on a task object."""
81
+
82
+ async def handle[T](
83
+ self,
84
+ task: Task[T],
85
+ max_validations: PositiveInt = 2,
86
+ model: str | None = None,
87
+ temperature: NonNegativeFloat | None = None,
88
+ stop: str | List[str] | None = None,
89
+ top_p: NonNegativeFloat | None = None,
90
+ max_tokens: PositiveInt | None = None,
91
+ stream: bool | None = None,
92
+ timeout: PositiveInt | None = None,
93
+ max_retries: PositiveInt | None = None,
94
+ ) -> T:
95
+ """Asynchronously handles a task based on a given task object and parameters."""
96
+ # TODO: Implement the handle method
@@ -47,8 +47,10 @@ class Event(BaseModel):
47
47
 
48
48
  def push(self, segment: str) -> Self:
49
49
  """Push a segment to the event."""
50
- assert segment, "The segment must not be empty."
51
- assert configs.pymitter.delimiter not in segment, "The segment must not contain the delimiter."
50
+ if not segment:
51
+ raise ValueError("The segment must not be empty.")
52
+ if configs.pymitter.delimiter in segment:
53
+ raise ValueError("The segment must not contain the delimiter.")
52
54
 
53
55
  self.segments.append(segment)
54
56
  return self