fabricatio 0.2.0.dev0__py3-none-any.whl → 0.2.0.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fabricatio/__init__.py CHANGED
@@ -1,24 +1,32 @@
1
1
  """Fabricatio is a Python library for building llm app using event-based agent structure."""
2
2
 
3
3
  from fabricatio.core import env
4
+ from fabricatio.fs import magika
4
5
  from fabricatio.journal import logger
5
6
  from fabricatio.models.action import Action, WorkFlow
6
7
  from fabricatio.models.events import Event
7
8
  from fabricatio.models.role import Role
8
9
  from fabricatio.models.task import Task
9
10
  from fabricatio.models.tool import ToolBox
10
- from fabricatio.models.utils import Messages
11
- from fabricatio.parser import Capture
11
+ from fabricatio.models.utils import Message, Messages
12
+ from fabricatio.parser import Capture, CodeBlockCapture, JsonCapture, PythonCapture
13
+ from fabricatio.templates import templates_manager
12
14
 
13
15
  __all__ = [
14
16
  "Action",
15
17
  "Capture",
18
+ "CodeBlockCapture",
16
19
  "Event",
20
+ "JsonCapture",
21
+ "Message",
17
22
  "Messages",
23
+ "PythonCapture",
18
24
  "Role",
19
25
  "Task",
20
26
  "ToolBox",
21
27
  "WorkFlow",
22
28
  "env",
23
29
  "logger",
30
+ "magika",
31
+ "templates_manager",
24
32
  ]
@@ -1,5 +1,5 @@
1
1
  """module for actions."""
2
2
 
3
- from fabricatio.actions.transmission import SendTask
3
+ from fabricatio.actions.transmission import PublishTask
4
4
 
5
- __all__ = ["SendTask"]
5
+ __all__ = ["PublishTask"]
@@ -0,0 +1,13 @@
1
+ from fabricatio.models.action import Action
2
+ from fabricatio.models.task import Task
3
+
4
+
5
+ class Talk(Action):
6
+ """Action that says hello to the world."""
7
+
8
+ name: str = "talk"
9
+ output_key: str = "talk_response"
10
+
11
+ async def _execute(self, task_input: Task[str], **_) -> str:
12
+ """Execute the action."""
13
+ return await self.aask(task_input.briefing, system_message=task_input.generate_prompt())
@@ -2,15 +2,31 @@ from typing import List
2
2
 
3
3
  from fabricatio.journal import logger
4
4
  from fabricatio.models.action import Action
5
+ from fabricatio.models.events import EventLike
5
6
  from fabricatio.models.task import Task
6
7
 
7
8
 
8
- class SendTask(Action):
9
- """Action that sends a task to a user."""
9
+ class PublishTask(Action):
10
+ """An action that publishes a task to a list of targets."""
10
11
 
11
- name: str = "send_task"
12
+ name: str = "publish_task"
13
+ """The name of the action."""
14
+ description: str = "Publish a task to a list of targets."
15
+ """The description of the action."""
12
16
 
13
- async def _execute(self, send_targets: List[str], send_task: Task, **_) -> None:
17
+ async def _execute(self, send_targets: List[EventLike], send_task: Task, **_) -> None:
18
+ """Execute the action by sending the task to the specified targets."""
14
19
  logger.info(f"Sending task {send_task.name} to {send_targets}")
15
20
  for target in send_targets:
16
- await send_task.publish(target)
21
+ await send_task.move_to(target).publish()
22
+
23
+
24
+ class CycleTask(Action):
25
+ """An action that cycles a task through a list of targets."""
26
+
27
+ name: str = "cycle_task"
28
+ """The name of the action."""
29
+ description: str = "Cycle a task through a list of targets"
30
+
31
+ async def _execute(self, task_input: Task, **_) -> None:
32
+ """Execute the action by cycling the task through the specified targets."""
@@ -0,0 +1,92 @@
1
+ import shutil
2
+ import subprocess
3
+ import tarfile
4
+ from pathlib import Path
5
+
6
+ import requests
7
+ import typer
8
+
9
+ app = typer.Typer()
10
+
11
+
12
+ def download_file(url: str, destination: Path) -> None:
13
+ """Download a file from a given URL to a specified destination.
14
+
15
+ Args:
16
+ url (str): The URL of the file to download.
17
+ destination (Path): The path where the file should be saved.
18
+
19
+ Raises:
20
+ requests.exceptions.HTTPError: If the request to the URL fails.
21
+ """
22
+ response = requests.get(url, stream=True, timeout=120)
23
+ response.raise_for_status()
24
+ with destination.open("wb") as f:
25
+ for chunk in response.iter_content(chunk_size=8192):
26
+ f.write(chunk)
27
+
28
+
29
+ def extract_tar_gz(file_path: Path, extract_to: Path) -> None:
30
+ """Extract a tar.gz file to a specified directory.
31
+
32
+ Args:
33
+ file_path (Path): The path to the tar.gz file.
34
+ extract_to (Path): The directory where the contents should be extracted.
35
+ """
36
+ with tarfile.open(file_path, "r:gz") as tar:
37
+ tar.extractall(path=extract_to, filter="data")
38
+
39
+
40
+ def extract_with_7z(file_path: Path, extract_to: Path) -> None:
41
+ """Extract a file using the 7z command-line tool.
42
+
43
+ Args:
44
+ file_path (Path): The path to the file to extract.
45
+ extract_to (Path): The directory where the contents should be extracted.
46
+
47
+ Raises:
48
+ subprocess.CalledProcessError: If the 7z command fails.
49
+ """
50
+ subprocess.run(["7z", "x", file_path, f"-o{extract_to}"], check=True)
51
+
52
+
53
+ @app.command()
54
+ def download_and_extract(output_dir: str = typer.Argument(..., help="Directory to extract the templates to")) -> None:
55
+ """Download and extract the templates.tar.gz file from the latest release of the fabricatio GitHub repository.
56
+
57
+ Args:
58
+ output_dir (str): The directory where the templates should be extracted.
59
+
60
+ Raises:
61
+ typer.Exit: If the templates.tar.gz file is not found or extraction fails.
62
+ """
63
+ repo_url = "https://api.github.com/repos/Whth/fabricatio/releases/latest"
64
+ response = requests.get(repo_url, timeout=30)
65
+ response.raise_for_status()
66
+ latest_release = response.json()
67
+ assets = latest_release.get("assets", [])
68
+ tar_gz_asset = next((asset for asset in assets if asset["name"] == "templates.tar.gz"), None)
69
+
70
+ if not tar_gz_asset:
71
+ typer.echo("templates.tar.gz not found in the latest release.")
72
+ raise typer.Exit(code=1)
73
+
74
+ download_url = tar_gz_asset["browser_download_url"]
75
+ output_path = Path(output_dir) # Convert output_dir to Path object
76
+ tar_gz_path = output_path.joinpath("templates.tar.gz") # Use joinpath instead of os.path.join
77
+
78
+ download_file(download_url, tar_gz_path) # Directly use Path object
79
+ typer.echo(f"Downloaded {download_url} to {tar_gz_path}")
80
+
81
+ try:
82
+ if shutil.which("7z"):
83
+ extract_with_7z(tar_gz_path, output_path) # Directly use Path objects
84
+ elif shutil.which("tar"):
85
+ extract_tar_gz(tar_gz_path, output_path) # Directly use Path objects
86
+ else:
87
+ typer.echo("Neither 7z nor tar is available for extraction.")
88
+ raise typer.Exit(code=1)
89
+ typer.echo(f"Extracted templates to {output_dir}")
90
+ except RuntimeError as e:
91
+ typer.echo(f"Extraction failed: {e}")
92
+ raise typer.Exit(code=1) from e
fabricatio/config.py CHANGED
@@ -1,3 +1,4 @@
1
+ from pathlib import Path
1
2
  from typing import List, Literal, Optional
2
3
 
3
4
  from appdirs import user_config_dir
@@ -119,10 +120,13 @@ class Code2PromptConfig(BaseModel):
119
120
 
120
121
  model_config = ConfigDict(use_attribute_docstrings=True)
121
122
  template_dir: List[DirectoryPath] = Field(
122
- default_factory=lambda: [r".\c2p_templates", rf"{ROAMING_DIR}\c2p_templates"]
123
+ default_factory=lambda: [Path(r".\templates"), Path(rf"{ROAMING_DIR}\templates")]
123
124
  )
124
125
  """The directory containing the templates for code2prompt."""
125
126
 
127
+ template_suffix: str = Field(default=".hbs", frozen=True)
128
+ """The suffix of the template files for code2prompt."""
129
+
126
130
 
127
131
  class MagikaConfig(BaseModel):
128
132
  """Magika configuration class."""
fabricatio/fs/__init__.py CHANGED
@@ -0,0 +1,5 @@
1
+ """FileSystem manipulation module for Fabricatio."""
2
+
3
+ from fabricatio.fs.readers import magika
4
+
5
+ __all__ = ["magika"]
@@ -1,20 +1,22 @@
1
1
  import traceback
2
2
  from abc import abstractmethod
3
3
  from asyncio import Queue
4
- from typing import Any, Dict, Tuple, Type, Unpack
4
+ from typing import Any, Dict, Self, Tuple, Type, Unpack
5
5
 
6
6
  from pydantic import Field, PrivateAttr
7
7
 
8
8
  from fabricatio.journal import logger
9
9
  from fabricatio.models.generic import LLMUsage, WithBriefing
10
- from fabricatio.models.task import Task
10
+ from fabricatio.models.task import ProposeTask, Task
11
11
 
12
12
 
13
- class Action(WithBriefing, LLMUsage):
13
+ class Action(ProposeTask):
14
14
  """Class that represents an action to be executed in a workflow."""
15
15
 
16
+ personality: str = Field(default="")
17
+ """The personality of whom the action belongs to."""
16
18
  output_key: str = Field(default="")
17
- """ The key of the output data."""
19
+ """The key of the output data."""
18
20
 
19
21
  @abstractmethod
20
22
  async def _execute(self, **cxt: Unpack) -> Any:
@@ -40,21 +42,30 @@ class Action(WithBriefing, LLMUsage):
40
42
  cxt[self.output_key] = ret
41
43
  return cxt
42
44
 
45
+ def briefing(self) -> str:
46
+ """Return a brief description of the action."""
47
+ if self.personality:
48
+ return f"## Your personality: \n{self.personality}\n# The action you are going to perform: \n{super().briefing}"
49
+ return f"# The action you are going to perform: \n{super().briefing}"
43
50
 
44
- class WorkFlow(WithBriefing, LLMUsage):
51
+
52
+ class WorkFlow[A: Type[Action] | Action](WithBriefing, LLMUsage):
45
53
  """Class that represents a workflow to be executed in a task."""
46
54
 
47
55
  _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
48
56
  """ The context dictionary to be used for workflow execution."""
49
57
 
50
58
  _instances: Tuple[Action, ...] = PrivateAttr(...)
59
+ """ The instances of the workflow steps."""
51
60
 
52
- steps: Tuple[Type[Action], ...] = Field(...)
53
- """ The steps to be executed in the workflow."""
61
+ steps: Tuple[A, ...] = Field(...)
62
+ """ The steps to be executed in the workflow, actions or action classes."""
54
63
  task_input_key: str = Field(default="task_input")
55
64
  """ The key of the task input data."""
56
65
  task_output_key: str = Field(default="task_output")
57
66
  """ The key of the task output data."""
67
+ extra_init_context: Dict[str, Any] = Field(default_factory=dict, frozen=True)
68
+ """ The extra context dictionary to be used for workflow initialization."""
58
69
 
59
70
  def model_post_init(self, __context: Any) -> None:
60
71
  """Initialize the workflow by setting fallbacks for each step.
@@ -62,9 +73,24 @@ class WorkFlow(WithBriefing, LLMUsage):
62
73
  Args:
63
74
  __context: The context to be used for initialization.
64
75
  """
65
- self._instances = tuple(step() for step in self.steps)
66
- for step in self._instances:
67
- step.fallback_to(self)
76
+ temp = []
77
+ for step in self.steps:
78
+ temp.append(step if isinstance(step, Action) else step())
79
+ self._instances = tuple(temp)
80
+
81
+ def inject_personality(self, personality: str) -> Self:
82
+ """Inject the personality of the workflow.
83
+
84
+ Args:
85
+ personality: The personality to be injected.
86
+
87
+ Returns:
88
+ Self: The instance of the workflow with the injected personality.
89
+ """
90
+ for a in self._instances:
91
+ if not a.personality:
92
+ a.personality = personality
93
+ return self
68
94
 
69
95
  async def serve(self, task: Task) -> None:
70
96
  """Serve the task by executing the workflow steps.
@@ -73,13 +99,13 @@ class WorkFlow(WithBriefing, LLMUsage):
73
99
  task: The task to be served.
74
100
  """
75
101
  await task.start()
76
- await self._context.put({self.task_input_key: task})
102
+ await self._init_context()
77
103
  current_action = None
78
104
  try:
79
105
  for step in self._instances:
80
106
  logger.debug(f"Executing step: {step.name}")
81
- ctx = await self._context.get()
82
- modified_ctx = await step.act(ctx)
107
+ cxt = await self._context.get()
108
+ modified_ctx = await step.act(cxt)
83
109
  await self._context.put(modified_ctx)
84
110
  current_action = step.name
85
111
  logger.info(f"Finished executing workflow: {self.name}")
@@ -88,3 +114,14 @@ class WorkFlow(WithBriefing, LLMUsage):
88
114
  logger.error(f"Error during task: {current_action} execution: {e}") # Log the exception
89
115
  logger.error(traceback.format_exc()) # Add this line to log the traceback
90
116
  await task.fail() # Mark the task as failed
117
+
118
+ async def _init_context(self) -> None:
119
+ """Initialize the context dictionary for workflow execution."""
120
+ logger.debug(f"Initializing context for workflow: {self.name}")
121
+ await self._context.put({self.task_input_key: None, **dict(self.extra_init_context)})
122
+
123
+ def fallback_to_self(self) -> Self:
124
+ """Set the fallback for each step to the workflow itself."""
125
+ for step in self._instances:
126
+ step.fallback_to(self)
127
+ return self
fabricatio/models/role.py CHANGED
@@ -1,17 +1,15 @@
1
1
  from typing import Any
2
2
 
3
- from pydantic import Field, ValidationError
3
+ from pydantic import Field
4
4
 
5
5
  from fabricatio.core import env
6
6
  from fabricatio.journal import logger
7
7
  from fabricatio.models.action import WorkFlow
8
8
  from fabricatio.models.events import Event
9
- from fabricatio.models.generic import LLMUsage, Memorable, WithBriefing, WithToDo
10
- from fabricatio.models.task import Task
11
- from fabricatio.parser import JsonCapture
9
+ from fabricatio.models.task import ProposeTask
12
10
 
13
11
 
14
- class Role(Memorable, WithBriefing, WithToDo, LLMUsage):
12
+ class Role(ProposeTask):
15
13
  """Class that represents a role with a registry of events and workflows."""
16
14
 
17
15
  registry: dict[Event | str, WorkFlow] = Field(...)
@@ -20,31 +18,8 @@ class Role(Memorable, WithBriefing, WithToDo, LLMUsage):
20
18
  def model_post_init(self, __context: Any) -> None:
21
19
  """Register the workflows in the role to the event bus."""
22
20
  for event, workflow in self.registry.items():
23
- workflow.fallback_to(self)
21
+ workflow.fallback_to(self).fallback_to_self().inject_personality(self.briefing)
24
22
  logger.debug(
25
23
  f"Registering workflow: {workflow.name} for event: {event.collapse() if isinstance(event, Event) else event}"
26
24
  )
27
25
  env.on(event, workflow.serve)
28
-
29
- async def propose(self, prompt: str) -> Task:
30
- """Propose a task based on the provided prompt."""
31
- assert prompt, "Prompt must be provided."
32
-
33
- def _validate_json(response: str) -> None | Task:
34
- try:
35
- cap = JsonCapture.capture(response)
36
- logger.debug(f"Response: \n{response}")
37
- logger.info(f"Captured JSON: \n{cap[0]}")
38
- return Task.model_validate_json(cap[0] if cap else response)
39
- except ValidationError as e:
40
- logger.error(f"Failed to parse task from JSON: {e}")
41
- return None
42
-
43
- return await self.aask_validate(
44
- f"{prompt} \n\nBased on requirement above, "
45
- f"you need to construct a task to satisfy that requirement in JSON format "
46
- f"written like this: \n\n```json\n{Task.json_example()}\n```\n\n"
47
- f"No extra explanation needed. ",
48
- _validate_json,
49
- system_message=f"# your personal briefing: \n{self.briefing}",
50
- )
fabricatio/models/task.py CHANGED
@@ -7,12 +7,13 @@ from asyncio import Queue
7
7
  from enum import Enum
8
8
  from typing import Any, List, Optional, Self
9
9
 
10
- from pydantic import Field, PrivateAttr
10
+ from pydantic import Field, PrivateAttr, ValidationError
11
11
 
12
12
  from fabricatio.core import env
13
13
  from fabricatio.journal import logger
14
- from fabricatio.models.events import Event
15
- from fabricatio.models.generic import WithBriefing, WithDependency, WithJsonExample
14
+ from fabricatio.models.events import Event, EventLike
15
+ from fabricatio.models.generic import LLMUsage, WithBriefing, WithDependency, WithJsonExample
16
+ from fabricatio.parser import JsonCapture
16
17
 
17
18
 
18
19
  class TaskStatus(Enum):
@@ -69,7 +70,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
69
70
  """Initialize the task with a namespace event."""
70
71
  self._namespace.segments.extend(self.namespace)
71
72
 
72
- def move_to(self, new_namespace: List[str]) -> Self:
73
+ def move_to(self, new_namespace: EventLike) -> Self:
73
74
  """Move the task to a new namespace.
74
75
 
75
76
  Args:
@@ -254,3 +255,30 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
254
255
  str: The briefing of the task.
255
256
  """
256
257
  return f"{super().briefing}\n{self.goal}"
258
+
259
+
260
+ class ProposeTask(LLMUsage, WithBriefing):
261
+ """A class that proposes a task based on a prompt."""
262
+
263
+ async def propose(self, prompt: str) -> Task:
264
+ """Propose a task based on the provided prompt."""
265
+ assert prompt, "Prompt must be provided."
266
+
267
+ def _validate_json(response: str) -> None | Task:
268
+ try:
269
+ cap = JsonCapture.capture(response)
270
+ logger.debug(f"Response: \n{response}")
271
+ logger.info(f"Captured JSON: \n{cap[0]}")
272
+ return Task.model_validate_json(cap[0] if cap else response)
273
+ except ValidationError as e:
274
+ logger.error(f"Failed to parse task from JSON: {e}")
275
+ return None
276
+
277
+ return await self.aask_validate(
278
+ f"{prompt} \n\nBased on requirement above, "
279
+ f"you need to construct a task to satisfy that requirement in JSON format "
280
+ f"written like this: \n\n```json\n{Task.json_example()}\n```\n\n"
281
+ f"No extra explanation needed. ",
282
+ _validate_json,
283
+ system_message=f"# your personal briefing: \n{self.briefing}",
284
+ )
fabricatio/models/tool.py CHANGED
@@ -24,7 +24,7 @@ class Tool[**P, R](WithBriefing):
24
24
  assert self.name, "The tool must have a name."
25
25
  self.description = self.description or self.source.__doc__ or ""
26
26
 
27
- def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
27
+ def invoke(self, *args: P.args, **kwargs: P.kwargs) -> R:
28
28
  """Invoke the tool's source function with the provided arguments."""
29
29
  return self.source(*args, **kwargs)
30
30
 
@@ -82,20 +82,18 @@ class ToolBox(WithBriefing):
82
82
  toc = f"## {self.name}: {self.description}\n## {len(self.tools)} tools available:"
83
83
  return f"{toc}\n\n{list_out}"
84
84
 
85
- def invoke_tool[**P, R](self, name: str, *args: P.args, **kwargs: P.kwargs) -> R:
85
+ def get[**P, R](self, name: str) -> Tool[P, R]:
86
86
  """Invoke a tool by name with the provided arguments.
87
87
 
88
88
  Args:
89
89
  name (str): The name of the tool to invoke.
90
- *args (P.args): Positional arguments to pass to the tool.
91
- **kwargs (P.kwargs): Keyword arguments to pass to the tool.
92
90
 
93
91
  Returns:
94
- R: The result of the tool's execution.
92
+ Tool: The tool instance with the specified name.
95
93
 
96
94
  Raises:
97
95
  AssertionError: If no tool with the specified name is found.
98
96
  """
99
97
  tool = next((tool for tool in self.tools if tool.name == name), None)
100
98
  assert tool, f"No tool named {name} found."
101
- return tool(*args, **kwargs)
99
+ return tool
@@ -0,0 +1,39 @@
1
+ from typing import Any, Dict, List, Self
2
+
3
+ from pydantic import BaseModel, ConfigDict, DirectoryPath, Field, FilePath, PrivateAttr
4
+
5
+ from fabricatio.config import configs
6
+ from fabricatio.journal import logger
7
+
8
+
9
+ class TemplateManager(BaseModel):
10
+ """A class that manages templates for code generation."""
11
+
12
+ model_config = ConfigDict(use_attribute_docstrings=True)
13
+ templates_dir: List[DirectoryPath] = Field(default_factory=lambda: list(configs.code2prompt.template_dir))
14
+ """The directories containing the templates. first element has the highest override priority."""
15
+ _discovered_templates: Dict[str, FilePath] = PrivateAttr(default_factory=dict)
16
+
17
+ def model_post_init(self, __context: Any) -> None:
18
+ """Post-initialization method for the model."""
19
+ self.discover_templates()
20
+
21
+ def discover_templates(self) -> Self:
22
+ """Discover the templates in the template directories."""
23
+ discovered = [
24
+ f
25
+ for d in self.templates_dir[::-1]
26
+ for f in d.rglob(f"*{configs.code2prompt.template_suffix}", case_sensitive=False)
27
+ if f.is_file()
28
+ ]
29
+
30
+ self._discovered_templates = {f.stem: f for f in discovered}
31
+ logger.info(f"Discovered {len(self._discovered_templates)} templates.")
32
+ return self
33
+
34
+ def get_template(self, name: str) -> FilePath | None:
35
+ """Get the template with the specified name."""
36
+ return self._discovered_templates.get(name, None)
37
+
38
+
39
+ templates_manager = TemplateManager()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.0.dev0
3
+ Version: 0.2.0.dev2
4
4
  Summary: A LLM multi-agent framework.
5
5
  Author-email: Whth <zettainspector@foxmail.com>
6
6
  License: MIT License
@@ -43,18 +43,61 @@ Requires-Dist: litellm>=1.60.0
43
43
  Requires-Dist: loguru>=0.7.3
44
44
  Requires-Dist: magika>=0.5.1
45
45
  Requires-Dist: orjson>=3.10.15
46
+ Requires-Dist: pybars3>=0.9.7
46
47
  Requires-Dist: pydantic-settings>=2.7.1
47
48
  Requires-Dist: pydantic>=2.10.6
48
49
  Requires-Dist: pymitter>=1.0.0
49
50
  Requires-Dist: questionary>=2.1.0
50
51
  Requires-Dist: regex>=2024.11.6
51
52
  Requires-Dist: rich>=13.9.4
53
+ Requires-Dist: typer>=0.15.1
52
54
  Description-Content-Type: text/markdown
53
55
 
56
+ # Fabricatio
57
+
58
+ ---
59
+
60
+ Fabricatio is a powerful framework designed to facilitate the creation and management of tasks, actions, and workflows. It leverages modern Python features and libraries to provide a robust and flexible environment for building applications that require task automation and orchestration.
61
+
62
+ ## Table of Contents
63
+
64
+ - [Installation](#installation)
65
+ - [Usage](#usage)
66
+ - [Defining a Task](#defining-a-task)
67
+ - [Creating an Action](#creating-an-action)
68
+ - [Assigning a Role](#assigning-a-role)
69
+ - [Logging](#logging)
70
+ - [Configuration](#configuration)
71
+ - [LLM Configuration](#llm-configuration)
72
+ - [Debug Configuration](#debug-configuration)
73
+ - [Examples](#examples)
74
+ - [Simple Task Example](#simple-task-example)
75
+ - [Complex Workflow Example](#complex-workflow-example)
76
+ - [Contributing](#contributing)
77
+ - [License](#license)
78
+
79
+ ## Installation
80
+ To install Fabricatio, you can use pip:
81
+
82
+ ```bash
83
+ pip install fabricatio
84
+ ```
85
+
86
+ Alternatively, you can clone the repository and install it manually:
87
+
88
+ ```bash
89
+ git clone https://github.com/your-repo/fabricatio.git
90
+ cd fabricatio
91
+ pip install .
92
+ ```
93
+
94
+
54
95
  ## Usage
55
96
 
56
97
  ### Defining a Task
57
98
 
99
+ A task in Fabricatio is defined using the `Task` class. You can specify the name, goal, and description of the task.
100
+
58
101
  ```python
59
102
  from fabricatio.models.task import Task
60
103
 
@@ -64,12 +107,14 @@ task = Task(name="say hello", goal="say hello", description="say hello to the wo
64
107
 
65
108
  ### Creating an Action
66
109
 
110
+ Actions are the building blocks of workflows. They perform specific tasks and can be asynchronous.
111
+
67
112
  ```python
68
113
  from fabricatio import Action, logger
69
114
  from fabricatio.models.task import Task
70
115
 
71
116
  class Talk(Action):
72
- async def _execute(self, task_input: Task[str], **_) -> Any:
117
+ async def _execute(self, task_input: Task[str], **_) -> str:
73
118
  ret = "Hello fabricatio!"
74
119
  logger.info("executing talk action")
75
120
  return ret
@@ -78,6 +123,8 @@ class Talk(Action):
78
123
 
79
124
  ### Assigning a Role
80
125
 
126
+ Roles in Fabricatio are responsible for executing workflows. You can define a role with a set of actions.
127
+
81
128
  ```python
82
129
  from fabricatio.models.role import Role
83
130
  from fabricatio.models.action import WorkFlow
@@ -104,19 +151,91 @@ debug_config = DebugConfig(log_level="DEBUG", log_file="fabricatio.log")
104
151
 
105
152
  Fabricatio uses Pydantic for configuration management. You can define your settings in the `config.py` file.
106
153
 
154
+ ### LLM Configuration
155
+
156
+ The Large Language Model (LLM) configuration is managed by the `LLMConfig` class.
157
+
107
158
  ```python
108
- from fabricatio.config import Settings
159
+ from fabricatio.config import LLMConfig
109
160
 
110
- settings = Settings(llm=LLMConfig(api_endpoint="https://api.example.com"))
161
+ llm_config = LLMConfig(api_endpoint="https://api.example.com")
111
162
  ```
112
163
 
113
164
 
114
- ## Testing
165
+ ### Debug Configuration
115
166
 
116
- Fabricatio includes a set of tests to ensure the framework works as expected. You can run the tests using `pytest`.
167
+ The debug configuration is managed by the `DebugConfig` class.
117
168
 
118
- ```bash
119
- pytest
169
+ ```python
170
+ from fabricatio.config import DebugConfig
171
+
172
+ debug_config = DebugConfig(log_level="DEBUG", log_file="fabricatio.log")
173
+ ```
174
+
175
+
176
+ ## Examples
177
+
178
+ ### Simple Task Example
179
+
180
+ Here is a simple example of a task that prints "Hello fabricatio!".
181
+
182
+ ```python
183
+ import asyncio
184
+ from fabricatio import Action, Role, Task, WorkFlow, logger
185
+
186
+ task = Task(name="say hello", goal="say hello", description="say hello to the world")
187
+
188
+ class Talk(Action):
189
+ async def _execute(self, task_input: Task[str], **_) -> Any:
190
+ ret = "Hello fabricatio!"
191
+ logger.info("executing talk action")
192
+ return ret
193
+
194
+ class TestWorkflow(WorkFlow):
195
+ pass
196
+
197
+ role = Role(name="Test Role", actions=[TestWorkflow()])
198
+
199
+ async def main() -> None:
200
+ await role.act(task)
201
+
202
+ if __name__ == "__main__":
203
+ asyncio.run(main())
204
+ ```
205
+
206
+
207
+ ### Complex Workflow Example
208
+
209
+ Here is a more complex example that demonstrates how to create a workflow with multiple actions.
210
+
211
+ ```python
212
+ import asyncio
213
+ from fabricatio import Action, Role, Task, WorkFlow, logger
214
+
215
+ task = Task(name="complex task", goal="perform complex operations", description="a task with multiple actions")
216
+
217
+ class ActionOne(Action):
218
+ async def _execute(self, task_input: Task[str], **_) -> Any:
219
+ ret = "Action One executed"
220
+ logger.info(ret)
221
+ return ret
222
+
223
+ class ActionTwo(Action):
224
+ async def _execute(self, task_input: Task[str], **_) -> Any:
225
+ ret = "Action Two executed"
226
+ logger.info(ret)
227
+ return ret
228
+
229
+ class ComplexWorkflow(WorkFlow):
230
+ actions = [ActionOne(), ActionTwo()]
231
+
232
+ role = Role(name="Complex Role", actions=[ComplexWorkflow()])
233
+
234
+ async def main() -> None:
235
+ await role.act(task)
236
+
237
+ if __name__ == "__main__":
238
+ asyncio.run(main())
120
239
  ```
121
240
 
122
241
 
@@ -0,0 +1,28 @@
1
+ fabricatio/__init__.py,sha256=eznZiLDVe3pw0NwX36Rari-DK0zxDTpa_wwAmWGnto8,909
2
+ fabricatio/config.py,sha256=61wo7pingdp8vaxzI78QLSLWtuTdMbsa0FgOTJbnnZM,7979
3
+ fabricatio/core.py,sha256=B6KBIfBRF023HF0UUaUprEkQd6sT7G_pexGXQ9btJnE,5788
4
+ fabricatio/decorators.py,sha256=Qsyb-_cDwtXY5yhyLrYZEAlrHh5ZuEooQ8vEOUAxj8k,1855
5
+ fabricatio/journal.py,sha256=CW9HePtgTiboOyPTExq9GjG5BseZcbc-S6lxDXrpmv0,667
6
+ fabricatio/parser.py,sha256=6PNFNNyHNkNG7vTS1ejFc4vCzl1OCy4bqB0e4Ty_-Qs,2365
7
+ fabricatio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ fabricatio/templates.py,sha256=7HnWGp2a_Z4ILBHb0Hxh0sOBs8fVD8V6wsnb_OIkYww,1553
9
+ fabricatio/actions/__init__.py,sha256=eFmFVPQvtNgFynIXBVr3eP-vWQDWCPng60YY5LXvZgg,115
10
+ fabricatio/actions/communication.py,sha256=tmsr3H_w-V-b2WxLEyWByGuwSCLgHIHTdHYAgHrdUxc,425
11
+ fabricatio/actions/transmission.py,sha256=PedZ6XsflKdT5ikzaqWr_6h8jci0kekAHfwygzKBUns,1188
12
+ fabricatio/bin/template_download.py,sha256=JcertOKBIHHorXrQURdqH2wgy2LLzqps5ADp7QbYNgc,3415
13
+ fabricatio/fs/__init__.py,sha256=lWcKYg0v3mv2LnnSegOQaTtlVDODU0vtw_s6iKU5IqQ,122
14
+ fabricatio/fs/readers.py,sha256=mw0VUH3P7Wk0SMlcQm2yOfjEz5C3mQ_kjduAjecaxgY,123
15
+ fabricatio/models/action.py,sha256=M5EfKRJr6puUeKPMm8SGxtKIQWcDcR6dZYs1VTN0ihw,4977
16
+ fabricatio/models/events.py,sha256=pW3sfxEFWTafbP4bn4hVFSe8qloYrrzOgi9oMCqAEgw,2604
17
+ fabricatio/models/generic.py,sha256=noHow8dUjMnNiasl2ZHaZeCMpxzDme9QvxTetoyB45w,19539
18
+ fabricatio/models/role.py,sha256=PM4yBMkHIjYVGsUKlWFbKSOUnhJ8pMXSBySG3Ub4e60,972
19
+ fabricatio/models/task.py,sha256=VPK7ey5AFTeH_Ufr-qwDXEhdwA6GRfBElLIiVH6azDA,9431
20
+ fabricatio/models/tool.py,sha256=1hxDvE92uiWCaxrb0c81EGCkNrt8Ce_V9JDBz7b3qVo,3349
21
+ fabricatio/models/utils.py,sha256=2mgXla9_K3dnRrz6hIKzmltTYPmvDk0MBjjEBkCXTdg,2474
22
+ fabricatio/toolboxes/__init__.py,sha256=bjefmPd7wBaWhbZzdMPXvrjMTeRzlUh_Dev2PUAc124,158
23
+ fabricatio/toolboxes/task.py,sha256=xgyPetm2R_HlQwpzE8YPnBN7QOYLd0-T8E6QPZG1PPQ,204
24
+ fabricatio-0.2.0.dev2.dist-info/METADATA,sha256=7sQ65HU4g26s-Vd1dXOUTQ0bygyZPU9eLTsfgOs1jZc,7113
25
+ fabricatio-0.2.0.dev2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ fabricatio-0.2.0.dev2.dist-info/entry_points.txt,sha256=qfO9AWRzHvppZwBQ9LKc88-Y9jf2o_0rqEEnM7daM-8,84
27
+ fabricatio-0.2.0.dev2.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
28
+ fabricatio-0.2.0.dev2.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ templates = fabricatio.bin.template_download:download_and_extract
@@ -1,24 +0,0 @@
1
- fabricatio/__init__.py,sha256=nFPtohqceECRYzU-WlVT6o4oSaKN0vGok-w9JIaiJfs,644
2
- fabricatio/config.py,sha256=emkIpaXRQykAX_C2ND8tKR2DQUJJEN0mvdMDTklwOXw,7823
3
- fabricatio/core.py,sha256=B6KBIfBRF023HF0UUaUprEkQd6sT7G_pexGXQ9btJnE,5788
4
- fabricatio/decorators.py,sha256=Qsyb-_cDwtXY5yhyLrYZEAlrHh5ZuEooQ8vEOUAxj8k,1855
5
- fabricatio/journal.py,sha256=CW9HePtgTiboOyPTExq9GjG5BseZcbc-S6lxDXrpmv0,667
6
- fabricatio/parser.py,sha256=6PNFNNyHNkNG7vTS1ejFc4vCzl1OCy4bqB0e4Ty_-Qs,2365
7
- fabricatio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- fabricatio/actions/__init__.py,sha256=n3lwq9FPNtvfLu2L1pX4UkwiPITU7luk-b4aMJyjIC8,109
9
- fabricatio/actions/transmission.py,sha256=Azog4ItVk7aASdYzTwzyckzYG2hDFSXctnA7qp-Qlq0,502
10
- fabricatio/fs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- fabricatio/fs/readers.py,sha256=mw0VUH3P7Wk0SMlcQm2yOfjEz5C3mQ_kjduAjecaxgY,123
12
- fabricatio/models/action.py,sha256=M-12dc-nQiNJU6Y9j-dr4Ef3642vRvzHlzxekBepzaU,3358
13
- fabricatio/models/events.py,sha256=pW3sfxEFWTafbP4bn4hVFSe8qloYrrzOgi9oMCqAEgw,2604
14
- fabricatio/models/generic.py,sha256=noHow8dUjMnNiasl2ZHaZeCMpxzDme9QvxTetoyB45w,19539
15
- fabricatio/models/role.py,sha256=jdabuYRXwgvpYoNwvazygDiZHGGQApUIIKltniu78O8,2151
16
- fabricatio/models/task.py,sha256=Qc16UeeTMaFjG2-sXafyOrjOS7H_0RluE9sFNwArDlI,8179
17
- fabricatio/models/tool.py,sha256=UkEp1Nzbl5wZX21q_Z2VkpiJmVDSdoGDzINQniO8hSY,3536
18
- fabricatio/models/utils.py,sha256=2mgXla9_K3dnRrz6hIKzmltTYPmvDk0MBjjEBkCXTdg,2474
19
- fabricatio/toolboxes/__init__.py,sha256=bjefmPd7wBaWhbZzdMPXvrjMTeRzlUh_Dev2PUAc124,158
20
- fabricatio/toolboxes/task.py,sha256=xgyPetm2R_HlQwpzE8YPnBN7QOYLd0-T8E6QPZG1PPQ,204
21
- fabricatio-0.2.0.dev0.dist-info/METADATA,sha256=keLz2I0t_VqaU0Xx5rgSIW66aZqSeJLKIsi9Vjw89vc,3858
22
- fabricatio-0.2.0.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- fabricatio-0.2.0.dev0.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
24
- fabricatio-0.2.0.dev0.dist-info/RECORD,,