fabricatio 0.2.6__cp39-cp39-win_amd64.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.
Files changed (42) hide show
  1. fabricatio/__init__.py +43 -0
  2. fabricatio/_rust.cp39-win_amd64.pyd +0 -0
  3. fabricatio/_rust.pyi +115 -0
  4. fabricatio/_rust_instances.py +10 -0
  5. fabricatio/actions/article.py +128 -0
  6. fabricatio/actions/output.py +19 -0
  7. fabricatio/actions/rag.py +71 -0
  8. fabricatio/capabilities/correct.py +115 -0
  9. fabricatio/capabilities/propose.py +49 -0
  10. fabricatio/capabilities/rag.py +384 -0
  11. fabricatio/capabilities/rating.py +339 -0
  12. fabricatio/capabilities/review.py +278 -0
  13. fabricatio/capabilities/task.py +113 -0
  14. fabricatio/config.py +405 -0
  15. fabricatio/core.py +181 -0
  16. fabricatio/decorators.py +179 -0
  17. fabricatio/fs/__init__.py +29 -0
  18. fabricatio/fs/curd.py +149 -0
  19. fabricatio/fs/readers.py +46 -0
  20. fabricatio/journal.py +21 -0
  21. fabricatio/models/action.py +230 -0
  22. fabricatio/models/events.py +120 -0
  23. fabricatio/models/extra.py +655 -0
  24. fabricatio/models/generic.py +406 -0
  25. fabricatio/models/kwargs_types.py +169 -0
  26. fabricatio/models/role.py +72 -0
  27. fabricatio/models/task.py +299 -0
  28. fabricatio/models/tool.py +189 -0
  29. fabricatio/models/usages.py +718 -0
  30. fabricatio/models/utils.py +192 -0
  31. fabricatio/parser.py +151 -0
  32. fabricatio/py.typed +0 -0
  33. fabricatio/toolboxes/__init__.py +15 -0
  34. fabricatio/toolboxes/arithmetic.py +62 -0
  35. fabricatio/toolboxes/fs.py +31 -0
  36. fabricatio/workflows/articles.py +26 -0
  37. fabricatio/workflows/rag.py +11 -0
  38. fabricatio-0.2.6.data/scripts/tdown.exe +0 -0
  39. fabricatio-0.2.6.dist-info/METADATA +432 -0
  40. fabricatio-0.2.6.dist-info/RECORD +42 -0
  41. fabricatio-0.2.6.dist-info/WHEEL +4 -0
  42. fabricatio-0.2.6.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,230 @@
1
+ """Module that contains the classes for actions and workflows.
2
+
3
+ This module defines the Action and WorkFlow classes, which are used for
4
+ creating and executing sequences of actions in a task-based context.
5
+ """
6
+
7
+ import traceback
8
+ from abc import abstractmethod
9
+ from asyncio import Queue, create_task
10
+ from typing import Any, Dict, Self, Tuple, Type, Union, final
11
+
12
+ from fabricatio.capabilities.correct import Correct
13
+ from fabricatio.capabilities.task import HandleTask, ProposeTask
14
+ from fabricatio.journal import logger
15
+ from fabricatio.models.generic import WithBriefing
16
+ from fabricatio.models.task import Task
17
+ from fabricatio.models.usages import ToolBoxUsage
18
+ from pydantic import Field, PrivateAttr
19
+
20
+
21
+ class Action(HandleTask, ProposeTask, Correct):
22
+ """Class that represents an action to be executed in a workflow.
23
+
24
+ Actions are the atomic units of work in a workflow. Each action performs
25
+ a specific operation and can modify the shared context data.
26
+ """
27
+
28
+ name: str = Field(default="")
29
+ """The name of the action."""
30
+
31
+ description: str = Field(default="")
32
+ """The description of the action."""
33
+
34
+ personality: str = Field(default="")
35
+ """The personality traits or context for the action executor."""
36
+
37
+ output_key: str = Field(default="")
38
+ """The key used to store this action's output in the context dictionary."""
39
+
40
+ @final
41
+ def model_post_init(self, __context: Any) -> None:
42
+ """Initialize the action by setting default name and description if not provided.
43
+
44
+ Args:
45
+ __context: The context to be used for initialization.
46
+ """
47
+ self.name = self.name or self.__class__.__name__
48
+ self.description = self.description or self.__class__.__doc__ or ""
49
+
50
+ @abstractmethod
51
+ async def _execute(self,*_, **cxt) -> Any: # noqa: ANN002
52
+ """Execute the action logic with the provided context arguments.
53
+
54
+ This method must be implemented by subclasses to define the actual behavior.
55
+
56
+ Args:
57
+ **cxt: The context dictionary containing input and output data.
58
+
59
+ Returns:
60
+ Any: The result of the action execution.
61
+ """
62
+ pass
63
+
64
+ @final
65
+ async def act(self, cxt: Dict[str, Any]) -> Dict[str, Any]:
66
+ """Perform the action and update the context with results.
67
+
68
+ Args:
69
+ cxt: The context dictionary containing input and output data.
70
+
71
+ Returns:
72
+ Dict[str, Any]: The updated context dictionary.
73
+ """
74
+ ret = await self._execute(**cxt)
75
+
76
+ if self.output_key:
77
+ logger.debug(f"Setting output: {self.output_key}")
78
+ cxt[self.output_key] = ret
79
+
80
+ return cxt
81
+
82
+ @property
83
+ def briefing(self) -> str:
84
+ """Return a formatted description of the action including personality context if available.
85
+
86
+ Returns:
87
+ str: Formatted briefing text with personality and action description.
88
+ """
89
+ if self.personality:
90
+ return f"## Your personality: \n{self.personality}\n# The action you are going to perform: \n{super().briefing}"
91
+ return f"# The action you are going to perform: \n{super().briefing}"
92
+
93
+
94
+ class WorkFlow(WithBriefing, ToolBoxUsage):
95
+ """Class that represents a sequence of actions to be executed for a task.
96
+
97
+ A workflow manages the execution of multiple actions in sequence, passing
98
+ a shared context between them and handling task lifecycle events.
99
+ """
100
+
101
+ _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
102
+ """Queue for storing the workflow execution context."""
103
+
104
+ _instances: Tuple[Action, ...] = PrivateAttr(default_factory=tuple)
105
+ """Instantiated action objects to be executed in this workflow."""
106
+
107
+ steps: Tuple[Union[Type[Action], Action], ...] = Field(...)
108
+ """The sequence of actions to be executed, can be action classes or instances."""
109
+
110
+ task_input_key: str = Field(default="task_input")
111
+ """Key used to store the input task in the context dictionary."""
112
+
113
+ task_output_key: str = Field(default="task_output")
114
+ """Key used to extract the final result from the context dictionary."""
115
+
116
+ extra_init_context: Dict[str, Any] = Field(default_factory=dict, frozen=True)
117
+ """Additional initial context values to be included at workflow start."""
118
+
119
+ def model_post_init(self, __context: Any) -> None:
120
+ """Initialize the workflow by instantiating any action classes.
121
+
122
+ Args:
123
+ __context: The context to be used for initialization.
124
+ """
125
+ # Convert any action classes to instances
126
+ self._instances = tuple(step if isinstance(step, Action) else step() for step in self.steps)
127
+
128
+ def inject_personality(self, personality: str) -> Self:
129
+ """Set the personality for all actions that don't have one defined.
130
+
131
+ Args:
132
+ personality: The personality text to inject.
133
+
134
+ Returns:
135
+ Self: The workflow instance for method chaining.
136
+ """
137
+ for action in filter(lambda a: not a.personality, self._instances):
138
+ action.personality = personality
139
+ return self
140
+
141
+ async def serve(self, task: Task) -> None:
142
+ """Execute the workflow to fulfill the given task.
143
+
144
+ This method manages the complete lifecycle of processing a task through
145
+ the workflow's sequence of actions.
146
+
147
+ Args:
148
+ task: The task to be processed.
149
+ """
150
+ await task.start()
151
+ await self._init_context(task)
152
+
153
+ current_action = None
154
+ try:
155
+ # Process each action in sequence
156
+ for step in self._instances:
157
+ current_action = step.name
158
+ logger.debug(f"Executing step: {current_action}")
159
+
160
+ # Get current context and execute action
161
+ context = await self._context.get()
162
+ act_task = create_task(step.act(context))
163
+
164
+ # Handle task cancellation
165
+ if task.is_cancelled():
166
+ act_task.cancel(f"Cancelled by task: {task.name}")
167
+ break
168
+
169
+ # Update context with modified values
170
+ modified_ctx = await act_task
171
+ await self._context.put(modified_ctx)
172
+
173
+ logger.info(f"Finished executing workflow: {self.name}")
174
+
175
+ # Get final context and extract result
176
+ final_ctx = await self._context.get()
177
+ result = final_ctx.get(self.task_output_key)
178
+
179
+ if self.task_output_key not in final_ctx:
180
+ logger.warning(
181
+ f"Task output key: {self.task_output_key} not found in the context, None will be returned. "
182
+ f"You can check if `Action.output_key` is set the same as `WorkFlow.task_output_key`."
183
+ )
184
+
185
+ await task.finish(result)
186
+
187
+ except RuntimeError as e:
188
+ logger.error(f"Error during task: {current_action} execution: {e}")
189
+ logger.error(traceback.format_exc())
190
+ await task.fail()
191
+
192
+ async def _init_context[T](self, task: Task[T]) -> None:
193
+ """Initialize the context dictionary for workflow execution.
194
+
195
+ Args:
196
+ task: The task being served by this workflow.
197
+ """
198
+ logger.debug(f"Initializing context for workflow: {self.name}")
199
+ initial_context = {self.task_input_key: task, **dict(self.extra_init_context)}
200
+ await self._context.put(initial_context)
201
+
202
+ def steps_fallback_to_self(self) -> Self:
203
+ """Configure all steps to use this workflow's configuration as fallback.
204
+
205
+ Returns:
206
+ Self: The workflow instance for method chaining.
207
+ """
208
+ self.hold_to(self._instances)
209
+ return self
210
+
211
+ def steps_supply_tools_from_self(self) -> Self:
212
+ """Provide this workflow's tools to all steps in the workflow.
213
+
214
+ Returns:
215
+ Self: The workflow instance for method chaining.
216
+ """
217
+ self.provide_tools_to(self._instances)
218
+ return self
219
+
220
+ def update_init_context(self, **kwargs) -> Self:
221
+ """Update the initial context with additional key-value pairs.
222
+
223
+ Args:
224
+ **kwargs: Key-value pairs to add to the initial context.
225
+
226
+ Returns:
227
+ Self: The workflow instance for method chaining.
228
+ """
229
+ self.extra_init_context.update(kwargs)
230
+ return self
@@ -0,0 +1,120 @@
1
+ """The module containing the Event class."""
2
+
3
+ from typing import List, Self, Union
4
+
5
+ from fabricatio.config import configs
6
+ from fabricatio.models.utils import TaskStatus
7
+ from pydantic import BaseModel, ConfigDict, Field
8
+
9
+ type EventLike = Union[str, List[str], "Event"]
10
+
11
+
12
+ class Event(BaseModel):
13
+ """A class representing an event."""
14
+
15
+ model_config = ConfigDict(use_attribute_docstrings=True)
16
+
17
+ segments: List[str] = Field(default_factory=list, frozen=True)
18
+ """ The segments of the namespaces."""
19
+
20
+ @classmethod
21
+ def instantiate_from(cls, event: EventLike) -> "Event":
22
+ """Create an Event instance from a string or list of strings or an Event instance.
23
+
24
+ Args:
25
+ event (EventLike): The event to instantiate from.
26
+
27
+ Returns:
28
+ Event: The Event instance.
29
+ """
30
+ if isinstance(event, Event):
31
+ return event.clone()
32
+ if isinstance(event, str):
33
+ event = event.split(configs.pymitter.delimiter)
34
+
35
+ return cls(segments=event)
36
+
37
+ @classmethod
38
+ def quick_instantiate(cls, event: EventLike) -> "Event":
39
+ """Create an Event instance from a string or list of strings or an Event instance and push a wildcard and pending segment.
40
+
41
+ Args:
42
+ event (EventLike): The event to instantiate from.
43
+
44
+ Returns:
45
+ Event: The Event instance.
46
+
47
+ Notes:
48
+ This method is used to create an Event instance from a string or list of strings or an Event instance and push a wildcard and pending segment.
49
+ """
50
+ return cls.instantiate_from(event).push_wildcard().push_pending()
51
+
52
+ def derive(self, event: EventLike) -> Self:
53
+ """Derive a new event from this event and another event or a string."""
54
+ return self.clone().concat(event)
55
+
56
+ def collapse(self) -> str:
57
+ """Collapse the event into a string."""
58
+ return configs.pymitter.delimiter.join(self.segments)
59
+
60
+ def clone(self) -> Self:
61
+ """Clone the event."""
62
+ return self.__class__(segments=list(self.segments))
63
+
64
+ def push(self, segment: str) -> Self:
65
+ """Push a segment to the event."""
66
+ if not segment:
67
+ raise ValueError("The segment must not be empty.")
68
+ if configs.pymitter.delimiter in segment:
69
+ raise ValueError("The segment must not contain the delimiter.")
70
+
71
+ self.segments.append(segment)
72
+ return self
73
+
74
+ def push_wildcard(self) -> Self:
75
+ """Push a wildcard segment to the event."""
76
+ return self.push("*")
77
+
78
+ def push_pending(self) -> Self:
79
+ """Push a pending segment to the event."""
80
+ return self.push(TaskStatus.Pending.value)
81
+
82
+ def push_running(self) -> Self:
83
+ """Push a running segment to the event."""
84
+ return self.push(TaskStatus.Running.value)
85
+
86
+ def push_finished(self) -> Self:
87
+ """Push a finished segment to the event."""
88
+ return self.push(TaskStatus.Finished.value)
89
+
90
+ def push_failed(self) -> Self:
91
+ """Push a failed segment to the event."""
92
+ return self.push(TaskStatus.Failed.value)
93
+
94
+ def push_cancelled(self) -> Self:
95
+ """Push a cancelled segment to the event."""
96
+ return self.push(TaskStatus.Cancelled.value)
97
+
98
+ def pop(self) -> str:
99
+ """Pop a segment from the event."""
100
+ return self.segments.pop()
101
+
102
+ def clear(self) -> Self:
103
+ """Clear the event."""
104
+ self.segments.clear()
105
+ return self
106
+
107
+ def concat(self, event: EventLike) -> Self:
108
+ """Concatenate another event to this event."""
109
+ self.segments.extend(Event.instantiate_from(event).segments)
110
+ return self
111
+
112
+ def __hash__(self) -> int:
113
+ """Return the hash of the event, using the collapsed string."""
114
+ return hash(self.collapse())
115
+
116
+ def __eq__(self, other: object) -> bool:
117
+ """Check if the event is equal to another event or a string."""
118
+ if not isinstance(other, (str , list , Event)):
119
+ return False
120
+ return self.collapse() == Event.instantiate_from(other).collapse()