fabricatio 0.2.6.dev3__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 +60 -0
  2. fabricatio/_rust.cp39-win_amd64.pyd +0 -0
  3. fabricatio/_rust.pyi +116 -0
  4. fabricatio/_rust_instances.py +10 -0
  5. fabricatio/actions/article.py +81 -0
  6. fabricatio/actions/output.py +19 -0
  7. fabricatio/actions/rag.py +25 -0
  8. fabricatio/capabilities/correct.py +115 -0
  9. fabricatio/capabilities/propose.py +49 -0
  10. fabricatio/capabilities/rag.py +369 -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 +400 -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 +158 -0
  22. fabricatio/models/events.py +120 -0
  23. fabricatio/models/extra.py +171 -0
  24. fabricatio/models/generic.py +406 -0
  25. fabricatio/models/kwargs_types.py +158 -0
  26. fabricatio/models/role.py +48 -0
  27. fabricatio/models/task.py +299 -0
  28. fabricatio/models/tool.py +189 -0
  29. fabricatio/models/usages.py +682 -0
  30. fabricatio/models/utils.py +167 -0
  31. fabricatio/parser.py +149 -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 +15 -0
  37. fabricatio/workflows/rag.py +11 -0
  38. fabricatio-0.2.6.dev3.data/scripts/tdown.exe +0 -0
  39. fabricatio-0.2.6.dev3.dist-info/METADATA +432 -0
  40. fabricatio-0.2.6.dev3.dist-info/RECORD +42 -0
  41. fabricatio-0.2.6.dev3.dist-info/WHEEL +4 -0
  42. fabricatio-0.2.6.dev3.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,167 @@
1
+ """A module containing utility classes for the models."""
2
+
3
+ from enum import Enum
4
+ from typing import Any, Dict, List, Literal, Optional, Self
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+ from questionary import text
8
+
9
+
10
+ class Message(BaseModel):
11
+ """A class representing a message."""
12
+
13
+ model_config = ConfigDict(use_attribute_docstrings=True)
14
+ role: Literal["user", "system", "assistant"] = Field(default="user")
15
+ """
16
+ Who is sending the message.
17
+ """
18
+ content: str = Field(default="")
19
+ """
20
+ The content of the message.
21
+ """
22
+
23
+
24
+ class Messages(list):
25
+ """A list of messages."""
26
+
27
+ def add_message(self, role: Literal["user", "system", "assistant"], content: str) -> Self:
28
+ """Adds a message to the list with the specified role and content.
29
+
30
+ Args:
31
+ role (Literal["user", "system", "assistant"]): The role of the message sender.
32
+ content (str): The content of the message.
33
+
34
+ Returns:
35
+ Self: The current instance of Messages to allow method chaining.
36
+ """
37
+ if content:
38
+ self.append(Message(role=role, content=content))
39
+ return self
40
+
41
+ def add_user_message(self, content: str) -> Self:
42
+ """Adds a user message to the list with the specified content.
43
+
44
+ Args:
45
+ content (str): The content of the user message.
46
+
47
+ Returns:
48
+ Self: The current instance of Messages to allow method chaining.
49
+ """
50
+ return self.add_message("user", content)
51
+
52
+ def add_system_message(self, content: str) -> Self:
53
+ """Adds a system message to the list with the specified content.
54
+
55
+ Args:
56
+ content (str): The content of the system message.
57
+
58
+ Returns:
59
+ Self: The current instance of Messages to allow method chaining.
60
+ """
61
+ return self.add_message("system", content)
62
+
63
+ def add_assistant_message(self, content: str) -> Self:
64
+ """Adds an assistant message to the list with the specified content.
65
+
66
+ Args:
67
+ content (str): The content of the assistant message.
68
+
69
+ Returns:
70
+ Self: The current instance of Messages to allow method chaining.
71
+ """
72
+ return self.add_message("assistant", content)
73
+
74
+ def as_list(self) -> List[Dict[str, str]]:
75
+ """Converts the messages to a list of dictionaries.
76
+
77
+ Returns:
78
+ list[dict]: A list of dictionaries representing the messages.
79
+ """
80
+ return [message.model_dump() for message in self]
81
+
82
+
83
+ class MilvusData(BaseModel):
84
+ """A class representing data stored in Milvus."""
85
+
86
+ model_config = ConfigDict(use_attribute_docstrings=True)
87
+ id: Optional[int] = Field(default=None)
88
+ """The identifier of the data."""
89
+
90
+ vector: List[float]
91
+ """The vector representation of the data."""
92
+
93
+ text: str
94
+ """The text representation of the data."""
95
+
96
+ subject: Optional[str] = Field(default=None)
97
+ """A subject label that we use to demo metadata filtering later."""
98
+
99
+ def prepare_insertion(self) -> Dict[str, Any]:
100
+ """Prepares the data for insertion into Milvus.
101
+
102
+ Returns:
103
+ dict: A dictionary containing the data to be inserted into Milvus.
104
+ """
105
+ return self.model_dump(exclude_none=True)
106
+
107
+ def update_subject(self, new_subject: str) -> Self:
108
+ """Updates the subject label of the data.
109
+
110
+ Args:
111
+ new_subject (str): The new subject label.
112
+
113
+ Returns:
114
+ Self: The updated instance of MilvusData.
115
+ """
116
+ self.subject = new_subject
117
+ return self
118
+
119
+ def update_id(self, new_id: int) -> Self:
120
+ """Updates the identifier of the data.
121
+
122
+ Args:
123
+ new_id (int): The new identifier.
124
+
125
+ Returns:
126
+ Self: The updated instance of MilvusData.
127
+ """
128
+ self.id = new_id
129
+ return self
130
+
131
+
132
+ class TaskStatus(Enum):
133
+ """An enumeration representing the status of a task.
134
+
135
+ Attributes:
136
+ Pending: The task is pending.
137
+ Running: The task is currently running.
138
+ Finished: The task has been successfully completed.
139
+ Failed: The task has failed.
140
+ Cancelled: The task has been cancelled.
141
+ """
142
+
143
+ Pending = "pending"
144
+ Running = "running"
145
+ Finished = "finished"
146
+ Failed = "failed"
147
+ Cancelled = "cancelled"
148
+
149
+
150
+ async def ask_edit(
151
+ text_seq: List[str],
152
+ ) -> List[str]:
153
+ """Asks the user to edit a list of texts.
154
+
155
+ Args:
156
+ text_seq (List[str]): A list of texts to be edited.
157
+
158
+ Returns:
159
+ List[str]: A list of edited texts.
160
+ If the user does not edit a text, it will not be included in the returned list.
161
+ """
162
+ res = []
163
+ for i, t in enumerate(text_seq):
164
+ edited = await text(f"[{i}] ", default=t).ask_async()
165
+ if edited:
166
+ res.append(edited)
167
+ return res
fabricatio/parser.py ADDED
@@ -0,0 +1,149 @@
1
+ """A module to parse text using regular expressions."""
2
+
3
+ from typing import Any, Callable, Iterable, List, Optional, Self, Tuple, Type
4
+
5
+ import orjson
6
+ import regex
7
+ from json_repair import repair_json
8
+ from pydantic import BaseModel, ConfigDict, Field, PositiveInt, PrivateAttr, ValidationError
9
+ from regex import Pattern, compile
10
+
11
+ from fabricatio.config import configs
12
+ from fabricatio.journal import logger
13
+
14
+
15
+ class Capture(BaseModel):
16
+ """A class to capture patterns in text using regular expressions.
17
+
18
+ Attributes:
19
+ pattern (str): The regular expression pattern to search for.
20
+ _compiled (Pattern): The compiled regular expression pattern.
21
+ """
22
+
23
+ model_config = ConfigDict(use_attribute_docstrings=True)
24
+ target_groups: Tuple[int, ...] = Field(default_factory=tuple)
25
+ """The target groups to capture from the pattern."""
26
+ pattern: str = Field(frozen=True)
27
+ """The regular expression pattern to search for."""
28
+ flags: PositiveInt = Field(default=regex.DOTALL | regex.MULTILINE | regex.IGNORECASE, frozen=True)
29
+ """The flags to use when compiling the regular expression pattern."""
30
+ capture_type: Optional[str] = None
31
+ """The type of capture to perform, e.g., 'json', which is used to dispatch the fixer accordingly."""
32
+ _compiled: Pattern = PrivateAttr()
33
+
34
+ def model_post_init(self, __context: Any) -> None:
35
+ """Initialize the compiled pattern."""
36
+ self._compiled = compile(self.pattern, self.flags)
37
+
38
+ def fix[T](self, text: str | Iterable[str] | T) -> str | List[str] | T:
39
+ """Fix the text using the pattern.
40
+
41
+ Args:
42
+ text (str | List[str]): The text to fix.
43
+
44
+ Returns:
45
+ str | List[str]: The fixed text with the same type as input.
46
+ """
47
+ match self.capture_type:
48
+ case "json":
49
+ if isinstance(text, str):
50
+ return repair_json(text, ensure_ascii=False)
51
+ return [repair_json(item, ensure_ascii=False) for item in text]
52
+ case _:
53
+ return text
54
+
55
+ def capture(self, text: str) -> Tuple[str, ...] | str | None:
56
+ """Capture the first occurrence of the pattern in the given text.
57
+
58
+ Args:
59
+ text (str): The text to search the pattern in.
60
+
61
+ Returns:
62
+ str | None: The captured text if the pattern is found, otherwise None.
63
+
64
+ """
65
+ match = self._compiled.search(text)
66
+ if match is None:
67
+ return None
68
+ groups = self.fix(match.groups()) if configs.general.use_json_repair else match.groups()
69
+ if self.target_groups:
70
+ cap = tuple(groups[g - 1] for g in self.target_groups)
71
+ logger.debug(f"Captured text: {'\n\n'.join(cap)}")
72
+ return cap
73
+ cap = groups[0]
74
+ logger.debug(f"Captured text: \n{cap}")
75
+ return cap
76
+
77
+ def convert_with[T](self, text: str, convertor: Callable[[Tuple[str, ...]], T] | Callable[[str], T]) -> T | None:
78
+ """Convert the given text using the pattern.
79
+
80
+ Args:
81
+ text (str): The text to search the pattern in.
82
+ convertor (Callable[[Tuple[str, ...]], T] | Callable[[str], T]): The function to convert the captured text.
83
+
84
+ Returns:
85
+ str | None: The converted text if the pattern is found, otherwise None.
86
+ """
87
+ if (cap := self.capture(text)) is None:
88
+ return None
89
+ try:
90
+ return convertor(cap)
91
+ except (ValueError, SyntaxError, ValidationError) as e:
92
+ logger.error(f"Failed to convert text using {convertor.__name__} to convert.\nerror: {e}\n {cap}")
93
+ return None
94
+
95
+ def validate_with[K, T, E](
96
+ self,
97
+ text: str,
98
+ target_type: Type[T],
99
+ elements_type: Optional[Type[E]] = None,
100
+ length: Optional[int] = None,
101
+ deserializer: Callable[[Tuple[str, ...]], K] | Callable[[str], K] = orjson.loads,
102
+ ) -> T | None:
103
+ """Validate the given text using the pattern.
104
+
105
+ Args:
106
+ text (str): The text to search the pattern in.
107
+ target_type (Type[T]): The expected type of the output, dict or list.
108
+ elements_type (Optional[Type[E]]): The expected type of the elements in the output dict keys or list elements.
109
+ length (Optional[int]): The expected length of the output, bool(length)==False means no length validation.
110
+ deserializer (Callable[[Tuple[str, ...]], K] | Callable[[str], K]): The function to deserialize the captured text.
111
+
112
+ Returns:
113
+ T | None: The validated text if the pattern is found and the output is of the expected type, otherwise None.
114
+ """
115
+ judges = [lambda output_obj: isinstance(output_obj, target_type)]
116
+ if elements_type:
117
+ judges.append(lambda output_obj: all(isinstance(e, elements_type) for e in output_obj))
118
+ if length:
119
+ judges.append(lambda output_obj: len(output_obj) == length)
120
+
121
+ if (out := self.convert_with(text, deserializer)) and all(j(out) for j in judges):
122
+ return out
123
+ return None
124
+
125
+ @classmethod
126
+ def capture_code_block(cls, language: str) -> Self:
127
+ """Capture the first occurrence of a code block in the given text.
128
+
129
+ Args:
130
+ language (str): The text containing the code block.
131
+
132
+ Returns:
133
+ Self: The instance of the class with the captured code block.
134
+ """
135
+ return cls(pattern=f"```{language}\n(.*?)\n```", capture_type=language)
136
+
137
+ @classmethod
138
+ def capture_generic_block(cls, language: str) -> Self:
139
+ """Capture the first occurrence of a generic code block in the given text.
140
+
141
+ Returns:
142
+ Self: The instance of the class with the captured code block.
143
+ """
144
+ return cls(pattern=f"--- Start of {language} ---\n(.*?)\n--- end of {language} ---", capture_type=language)
145
+
146
+
147
+ JsonCapture = Capture.capture_code_block("json")
148
+ PythonCapture = Capture.capture_code_block("python")
149
+ GenericCapture = Capture.capture_generic_block("String")
fabricatio/py.typed ADDED
File without changes
@@ -0,0 +1,15 @@
1
+ """Contains the built-in toolboxes for the Fabricatio package."""
2
+
3
+ from typing import Set
4
+
5
+ from fabricatio.models.tool import ToolBox
6
+ from fabricatio.toolboxes.arithmetic import arithmetic_toolbox
7
+ from fabricatio.toolboxes.fs import fs_toolbox
8
+
9
+ basic_toolboxes: Set[ToolBox] = {arithmetic_toolbox}
10
+
11
+ __all__ = [
12
+ "arithmetic_toolbox",
13
+ "basic_toolboxes",
14
+ "fs_toolbox",
15
+ ]
@@ -0,0 +1,62 @@
1
+ """Arithmetic tools for Fabricatio."""
2
+
3
+ from fabricatio.models.tool import ToolBox
4
+
5
+ arithmetic_toolbox = ToolBox(name="ArithmeticToolBox", description="A toolbox for arithmetic operations.")
6
+
7
+
8
+ @arithmetic_toolbox.collect_tool
9
+ def add(a: float, b: float) -> float:
10
+ """Add two numbers.
11
+
12
+ Args:
13
+ a (float): The first number.
14
+ b (float): The second number.
15
+
16
+ Returns:
17
+ float: The sum of the two numbers.
18
+ """
19
+ return a + b
20
+
21
+
22
+ @arithmetic_toolbox.collect_tool
23
+ def subtract(a: float, b: float) -> float:
24
+ """Subtract two numbers.
25
+
26
+ Args:
27
+ a (float): The first number.
28
+ b (float): The second number.
29
+
30
+ Returns:
31
+ float: The result of subtracting b from a.
32
+ """
33
+ return a - b
34
+
35
+
36
+ @arithmetic_toolbox.collect_tool
37
+ def multiply(a: float, b: float) -> float:
38
+ """Multiply two numbers.
39
+
40
+ Args:
41
+ a (float): The first number.
42
+ b (float): The second number.
43
+
44
+ Returns:
45
+ float: The product of the two numbers.
46
+ """
47
+ return a * b
48
+
49
+
50
+ @arithmetic_toolbox.collect_tool
51
+ def divide(a: float, b: float) -> float:
52
+ """Divide two numbers.
53
+
54
+ Args:
55
+ a (float): The numerator.
56
+ b (float): The denominator (must not be zero).
57
+
58
+ Returns:
59
+ float: The result of dividing a by b.
60
+
61
+ """
62
+ return a / b
@@ -0,0 +1,31 @@
1
+ """File system tool box."""
2
+
3
+ from fabricatio.fs import (
4
+ absolute_path,
5
+ copy_file,
6
+ create_directory,
7
+ delete_directory,
8
+ delete_file,
9
+ dump_text,
10
+ gather_files,
11
+ move_file,
12
+ safe_json_read,
13
+ safe_text_read,
14
+ tree,
15
+ )
16
+ from fabricatio.models.tool import ToolBox
17
+
18
+ fs_toolbox = (
19
+ ToolBox(name="FsToolBox", description="A toolbox for basic file system operations.")
20
+ .add_tool(dump_text)
21
+ .add_tool(copy_file)
22
+ .add_tool(move_file)
23
+ .add_tool(delete_file)
24
+ .add_tool(tree)
25
+ .add_tool(delete_directory)
26
+ .add_tool(create_directory)
27
+ .add_tool(absolute_path)
28
+ .add_tool(safe_text_read)
29
+ .add_tool(safe_json_read)
30
+ .add_tool(gather_files)
31
+ )
@@ -0,0 +1,15 @@
1
+ """Store article essence in the database."""
2
+
3
+ from fabricatio.actions.article import GenerateArticleProposal, GenerateOutline
4
+ from fabricatio.actions.output import DumpFinalizedOutput
5
+ from fabricatio.models.action import WorkFlow
6
+
7
+ WriteOutlineWorkFlow = WorkFlow(
8
+ name="Generate Article Outline",
9
+ description="Generate an outline for an article. dump the outline to the given path. in typst format.",
10
+ steps=(
11
+ GenerateArticleProposal,
12
+ GenerateOutline(output_key="to_dump"),
13
+ DumpFinalizedOutput(output_key="task_output"),
14
+ ),
15
+ )
@@ -0,0 +1,11 @@
1
+ """The workflow for extracting the essence of an article and storing it in the database."""
2
+
3
+ from fabricatio.actions.article import ExtractArticleEssence
4
+ from fabricatio.actions.rag import InjectToDB
5
+ from fabricatio.models.action import WorkFlow
6
+
7
+ StoreArticle = WorkFlow(
8
+ name="Extract Article Essence",
9
+ description="Extract the essence of an article in the given path, and store it in the database.",
10
+ steps=(ExtractArticleEssence(output_key="to_inject"), InjectToDB(output_key="task_output")),
11
+ )