fabricatio 0.2.3.dev2__cp312-cp312-win_amd64.whl → 0.2.4__cp312-cp312-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 (38) hide show
  1. fabricatio/__init__.py +24 -3
  2. fabricatio/_rust.cp312-win_amd64.pyd +0 -0
  3. fabricatio/actions/article.py +81 -0
  4. fabricatio/actions/output.py +21 -0
  5. fabricatio/actions/rag.py +25 -0
  6. fabricatio/capabilities/propose.py +55 -0
  7. fabricatio/capabilities/rag.py +241 -54
  8. fabricatio/capabilities/rating.py +12 -36
  9. fabricatio/capabilities/task.py +6 -23
  10. fabricatio/config.py +46 -2
  11. fabricatio/fs/__init__.py +24 -2
  12. fabricatio/fs/curd.py +14 -8
  13. fabricatio/fs/readers.py +5 -2
  14. fabricatio/models/action.py +19 -4
  15. fabricatio/models/events.py +36 -0
  16. fabricatio/models/extra.py +168 -0
  17. fabricatio/models/generic.py +218 -7
  18. fabricatio/models/kwargs_types.py +15 -0
  19. fabricatio/models/task.py +11 -43
  20. fabricatio/models/tool.py +3 -2
  21. fabricatio/models/usages.py +153 -184
  22. fabricatio/models/utils.py +19 -0
  23. fabricatio/parser.py +35 -8
  24. fabricatio/toolboxes/__init__.py +1 -3
  25. fabricatio/toolboxes/fs.py +15 -1
  26. fabricatio/workflows/articles.py +15 -0
  27. fabricatio/workflows/rag.py +11 -0
  28. fabricatio-0.2.4.data/scripts/tdown.exe +0 -0
  29. {fabricatio-0.2.3.dev2.dist-info → fabricatio-0.2.4.dist-info}/METADATA +40 -148
  30. fabricatio-0.2.4.dist-info/RECORD +40 -0
  31. fabricatio/actions/__init__.py +0 -5
  32. fabricatio/actions/communication.py +0 -15
  33. fabricatio/actions/transmission.py +0 -23
  34. fabricatio/toolboxes/task.py +0 -6
  35. fabricatio-0.2.3.dev2.data/scripts/tdown.exe +0 -0
  36. fabricatio-0.2.3.dev2.dist-info/RECORD +0 -37
  37. {fabricatio-0.2.3.dev2.dist-info → fabricatio-0.2.4.dist-info}/WHEEL +0 -0
  38. {fabricatio-0.2.3.dev2.dist-info → fabricatio-0.2.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,17 +1,25 @@
1
1
  """This module defines generic classes for models in the Fabricatio library."""
2
2
 
3
+ from abc import abstractmethod
3
4
  from pathlib import Path
4
- from typing import Callable, List, Self
5
+ from typing import Callable, Iterable, List, Optional, Self, Union, final
5
6
 
6
7
  import orjson
7
8
  from fabricatio._rust import blake3_hash
8
9
  from fabricatio._rust_instances import template_manager
9
10
  from fabricatio.config import configs
10
11
  from fabricatio.fs.readers import magika, safe_text_read
12
+ from fabricatio.journal import logger
13
+ from fabricatio.parser import JsonCapture
11
14
  from pydantic import (
12
15
  BaseModel,
13
16
  ConfigDict,
14
17
  Field,
18
+ HttpUrl,
19
+ NonNegativeFloat,
20
+ PositiveFloat,
21
+ PositiveInt,
22
+ SecretStr,
15
23
  )
16
24
 
17
25
 
@@ -21,6 +29,18 @@ class Base(BaseModel):
21
29
  model_config = ConfigDict(use_attribute_docstrings=True)
22
30
 
23
31
 
32
+ class Display(Base):
33
+ """Class that provides a method to display the model in a formatted JSON string."""
34
+
35
+ def display(self) -> str:
36
+ """Display the model in a formatted JSON string.
37
+
38
+ Returns:
39
+ str: The formatted JSON string of the model.
40
+ """
41
+ return self.model_dump_json(indent=1)
42
+
43
+
24
44
  class Named(Base):
25
45
  """Class that includes a name attribute."""
26
46
 
@@ -48,22 +68,87 @@ class WithBriefing(Named, Described):
48
68
  return f"{self.name}: {self.description}" if self.description else self.name
49
69
 
50
70
 
51
- class WithJsonExample(Base):
52
- """Class that provides a JSON schema for the model."""
71
+ class WithFormatedJsonSchema(Base):
72
+ """Class that provides a formatted JSON schema of the model."""
53
73
 
54
74
  @classmethod
55
- def json_example(cls) -> str:
56
- """Return a JSON example for the model.
75
+ def formated_json_schema(cls) -> str:
76
+ """Get the JSON schema of the model in a formatted string.
57
77
 
58
78
  Returns:
59
- str: A JSON example for the model.
79
+ str: The JSON schema of the model in a formatted string.
60
80
  """
61
81
  return orjson.dumps(
62
- {field_name: field_info.description for field_name, field_info in cls.model_fields.items()},
82
+ cls.model_json_schema(),
63
83
  option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS,
64
84
  ).decode()
65
85
 
66
86
 
87
+ class CreateJsonObjPrompt(WithFormatedJsonSchema):
88
+ """Class that provides a prompt for creating a JSON object."""
89
+
90
+ @classmethod
91
+ def create_json_prompt(cls, requirement: str) -> str:
92
+ """Create the prompt for creating a JSON object with given requirement.
93
+
94
+ Args:
95
+ requirement (str): The requirement for the JSON object.
96
+
97
+ Returns:
98
+ str: The prompt for creating a JSON object with given requirement.
99
+ """
100
+ return template_manager.render_template(
101
+ configs.templates.create_json_obj_template,
102
+ {"requirement": requirement, "json_schema": cls.formated_json_schema()},
103
+ )
104
+
105
+
106
+ class InstantiateFromString(Base):
107
+ """Class that provides a method to instantiate the class from a string."""
108
+
109
+ @classmethod
110
+ def instantiate_from_string(cls, string: str) -> Self | None:
111
+ """Instantiate the class from a string.
112
+
113
+ Args:
114
+ string (str): The string to instantiate the class from.
115
+
116
+ Returns:
117
+ Self | None: The instance of the class or None if the string is not valid.
118
+ """
119
+ return JsonCapture.convert_with(string, cls.model_validate_json)
120
+
121
+
122
+ class ProposedAble(CreateJsonObjPrompt, InstantiateFromString):
123
+ """Class that provides methods for proposing a task."""
124
+
125
+ pass
126
+
127
+
128
+ class FinalizedDumpAble(Base):
129
+ """Class that provides a method to finalize the dump of the object."""
130
+
131
+ @abstractmethod
132
+ def finalized_dump(self) -> str:
133
+ """Finalize the dump of the object.
134
+
135
+ Returns:
136
+ str: The finalized dump of the object.
137
+ """
138
+
139
+ def finalized_dump_to(self, path: str | Path) -> Self:
140
+ """Finalize the dump of the object to a file.
141
+
142
+ Args:
143
+ path (str | Path): The path to save the finalized dump.
144
+
145
+ Returns:
146
+ Self: The current instance of the object.
147
+ """
148
+ Path(path).write_text(self.finalized_dump(), encoding="utf-8")
149
+ return self
150
+
151
+
67
152
  class WithDependency(Base):
68
153
  """Class that manages file dependencies."""
69
154
 
@@ -150,3 +235,129 @@ class WithDependency(Base):
150
235
  for p in self.dependencies
151
236
  },
152
237
  )
238
+
239
+
240
+ class PrepareVectorization(Base):
241
+ """Class that prepares the vectorization of the model."""
242
+
243
+ @abstractmethod
244
+ def _prepare_vectorization_inner(self) -> str:
245
+ """Prepare the vectorization of the model."""
246
+
247
+ def prepare_vectorization(self, max_length: Optional[int] = None) -> str:
248
+ """Prepare the vectorization of the model.
249
+
250
+ Returns:
251
+ str: The prepared vectorization of the model.
252
+ """
253
+ max_length = max_length or configs.embedding.max_sequence_length
254
+ chunk = self._prepare_vectorization_inner()
255
+ if len(chunk) > max_length:
256
+ logger.error(err := f"Chunk exceeds maximum sequence length {max_length}.")
257
+ raise ValueError(err)
258
+
259
+ return chunk
260
+
261
+
262
+ class ScopedConfig(Base):
263
+ """Class that manages a scoped configuration."""
264
+
265
+ llm_api_endpoint: Optional[HttpUrl] = None
266
+ """The OpenAI API endpoint."""
267
+
268
+ llm_api_key: Optional[SecretStr] = None
269
+ """The OpenAI API key."""
270
+
271
+ llm_timeout: Optional[PositiveInt] = None
272
+ """The timeout of the LLM model."""
273
+
274
+ llm_max_retries: Optional[PositiveInt] = None
275
+ """The maximum number of retries."""
276
+
277
+ llm_model: Optional[str] = None
278
+ """The LLM model name."""
279
+
280
+ llm_temperature: Optional[NonNegativeFloat] = None
281
+ """The temperature of the LLM model."""
282
+
283
+ llm_stop_sign: Optional[str | List[str]] = None
284
+ """The stop sign of the LLM model."""
285
+
286
+ llm_top_p: Optional[NonNegativeFloat] = None
287
+ """The top p of the LLM model."""
288
+
289
+ llm_generation_count: Optional[PositiveInt] = None
290
+ """The number of generations to generate."""
291
+
292
+ llm_stream: Optional[bool] = None
293
+ """Whether to stream the LLM model's response."""
294
+
295
+ llm_max_tokens: Optional[PositiveInt] = None
296
+ """The maximum number of tokens to generate."""
297
+
298
+ embedding_api_endpoint: Optional[HttpUrl] = None
299
+ """The OpenAI API endpoint."""
300
+
301
+ embedding_api_key: Optional[SecretStr] = None
302
+ """The OpenAI API key."""
303
+
304
+ embedding_timeout: Optional[PositiveInt] = None
305
+ """The timeout of the LLM model."""
306
+
307
+ embedding_model: Optional[str] = None
308
+ """The LLM model name."""
309
+
310
+ embedding_max_sequence_length: Optional[PositiveInt] = None
311
+ """The maximum sequence length."""
312
+
313
+ embedding_dimensions: Optional[PositiveInt] = None
314
+ """The dimensions of the embedding."""
315
+ embedding_caching: Optional[bool] = False
316
+ """Whether to cache the embedding result."""
317
+
318
+ milvus_uri: Optional[HttpUrl] = Field(default=None)
319
+ """The URI of the Milvus server."""
320
+ milvus_token: Optional[SecretStr] = Field(default=None)
321
+ """The token for the Milvus server."""
322
+ milvus_timeout: Optional[PositiveFloat] = Field(default=None)
323
+ """The timeout for the Milvus server."""
324
+ milvus_dimensions: Optional[PositiveInt] = Field(default=None)
325
+ """The dimensions of the Milvus server."""
326
+
327
+ @final
328
+ def fallback_to(self, other: "ScopedConfig") -> Self:
329
+ """Fallback to another instance's attribute values if the current instance's attributes are None.
330
+
331
+ Args:
332
+ other (LLMUsage): Another instance from which to copy attribute values.
333
+
334
+ Returns:
335
+ Self: The current instance, allowing for method chaining.
336
+ """
337
+ # Iterate over the attribute names and copy values from 'other' to 'self' where applicable
338
+ # noinspection PydanticTypeChecker,PyTypeChecker
339
+ for attr_name in ScopedConfig.model_fields:
340
+ # Copy the attribute value from 'other' to 'self' only if 'self' has None and 'other' has a non-None value
341
+ if getattr(self, attr_name) is None and (attr := getattr(other, attr_name)) is not None:
342
+ setattr(self, attr_name, attr)
343
+
344
+ # Return the current instance to allow for method chaining
345
+ return self
346
+
347
+ @final
348
+ def hold_to(self, others: Union["ScopedConfig", Iterable["ScopedConfig"]]) -> Self:
349
+ """Hold to another instance's attribute values if the current instance's attributes are None.
350
+
351
+ Args:
352
+ others (LLMUsage | Iterable[LLMUsage]): Another instance or iterable of instances from which to copy attribute values.
353
+
354
+ Returns:
355
+ Self: The current instance, allowing for method chaining.
356
+ """
357
+ if not isinstance(others, Iterable):
358
+ others = [others]
359
+ for other in others:
360
+ # noinspection PyTypeChecker,PydanticTypeChecker
361
+ for attr_name in ScopedConfig.model_fields:
362
+ if (attr := getattr(self, attr_name)) is not None and getattr(other, attr_name) is None:
363
+ setattr(other, attr_name, attr)
@@ -5,6 +5,21 @@ from typing import List, NotRequired, TypedDict
5
5
  from pydantic import NonNegativeFloat, NonNegativeInt, PositiveInt
6
6
 
7
7
 
8
+ class CollectionSimpleConfigKwargs(TypedDict):
9
+ """A type representing the configuration for a collection."""
10
+
11
+ dimension: NotRequired[int]
12
+ timeout: NotRequired[float]
13
+
14
+
15
+ class FetchKwargs(TypedDict):
16
+ """A type representing the keyword arguments for the fetch method."""
17
+
18
+ collection_name: NotRequired[str]
19
+ similarity_threshold: NotRequired[float]
20
+ result_per_query: NotRequired[int]
21
+
22
+
8
23
  class EmbeddingKwargs(TypedDict):
9
24
  """A type representing the keyword arguments for the embedding method."""
10
25
 
fabricatio/models/task.py CHANGED
@@ -4,7 +4,6 @@ It includes methods to manage the task's lifecycle, such as starting, finishing,
4
4
  """
5
5
 
6
6
  from asyncio import Queue
7
- from enum import Enum
8
7
  from typing import Any, List, Optional, Self
9
8
 
10
9
  from fabricatio._rust_instances import template_manager
@@ -12,53 +11,36 @@ from fabricatio.config import configs
12
11
  from fabricatio.core import env
13
12
  from fabricatio.journal import logger
14
13
  from fabricatio.models.events import Event, EventLike
15
- from fabricatio.models.generic import WithBriefing, WithDependency, WithJsonExample
14
+ from fabricatio.models.generic import ProposedAble, WithBriefing, WithDependency
15
+ from fabricatio.models.utils import TaskStatus
16
16
  from pydantic import Field, PrivateAttr
17
17
 
18
18
 
19
- class TaskStatus(Enum):
20
- """An enumeration representing the status of a task.
21
-
22
- Attributes:
23
- Pending: The task is pending.
24
- Running: The task is currently running.
25
- Finished: The task has been successfully completed.
26
- Failed: The task has failed.
27
- Cancelled: The task has been cancelled.
28
- """
29
-
30
- Pending = "pending"
31
- Running = "running"
32
- Finished = "finished"
33
- Failed = "failed"
34
- Cancelled = "cancelled"
35
-
36
-
37
- class Task[T](WithBriefing, WithJsonExample, WithDependency):
19
+ class Task[T](WithBriefing, ProposedAble, WithDependency):
38
20
  """A class representing a task with a status and output.
39
21
 
40
22
  Attributes:
41
23
  name (str): The name of the task.
42
24
  description (str): The description of the task.
43
- goal (str): The goal of the task.
25
+ goals (str): The goal of the task.
44
26
  dependencies (List[str]): The file dependencies of the task, a list of file paths.
45
27
  namespace (List[str]): The namespace of the task, a list of namespace segment, as string.
46
28
  """
47
29
 
48
30
  name: str = Field(...)
49
- """The name of the task, which should be a concise and descriptive name."""
31
+ """The name of the task, which should be concise and descriptive."""
50
32
 
51
33
  description: str = Field(default="")
52
- """The description of the task, which should provide every details and noting about the task if provided, obeying the CEFR level rule and 5W1H rule."""
34
+ """A detailed explanation of the task that includes all necessary information. Should be clear and answer what, why, when, where, who, and how questions."""
53
35
 
54
- goal: List[str] = Field(default=[])
55
- """The goal of the task, a list of strings. The goal should be a concise and clear statement of what the task is intended to achieve, goal SHALL NOT be too broad or too narrow."""
36
+ goals: List[str] = Field(default=[])
37
+ """A list of objectives that the task aims to accomplish. Each goal should be clear and specific. Complex tasks should be broken into multiple smaller goals."""
56
38
 
57
39
  namespace: List[str] = Field(default_factory=list)
58
- """The namespace of the task, a list of namespace segment, as string, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
40
+ """A list of string segments that identify the task's location in the system. If not specified, defaults to an empty list."""
59
41
 
60
42
  dependencies: List[str] = Field(default_factory=list)
61
- """A list of file paths, These file are needed to read or write to meet a specific requirement of this task, if it is not directly given out, it SHALL just be a empty list meaning `NOT ASSIGNED`"""
43
+ """A list of file paths that are needed or mentioned in the task's description (either reading or writing) to complete this task. If not specified, defaults to an empty list."""
62
44
 
63
45
  _output: Queue[T | None] = PrivateAttr(default_factory=Queue)
64
46
  """The output queue of the task."""
@@ -101,20 +83,6 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
101
83
  self.namespace = self._namespace.segments
102
84
  return self
103
85
 
104
- @classmethod
105
- def simple_task(cls, name: str, goal: List[str], description: str) -> Self:
106
- """Create a simple task with a name, goal, and description.
107
-
108
- Args:
109
- name (str): The name of the task.
110
- goal (List[str]): The goal of the task.
111
- description (str): The description of the task.
112
-
113
- Returns:
114
- Task: A new instance of the `Task` class.
115
- """
116
- return cls(name=name, goal=goal, description=description)
117
-
118
86
  def update_task(self, goal: Optional[List[str] | str] = None, description: Optional[str] = None) -> Self:
119
87
  """Update the goal and description of the task.
120
88
 
@@ -126,7 +94,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
126
94
  Task: The updated instance of the `Task` class.
127
95
  """
128
96
  if goal:
129
- self.goal = goal if isinstance(goal, list) else [goal]
97
+ self.goals = goal if isinstance(goal, list) else [goal]
130
98
  if description:
131
99
  self.description = description
132
100
  return self
fabricatio/models/tool.py CHANGED
@@ -7,7 +7,7 @@ from types import CodeType, ModuleType
7
7
  from typing import Any, Callable, Dict, List, Optional, Self, overload
8
8
 
9
9
  from fabricatio.config import configs
10
- from fabricatio.decorators import use_temp_module
10
+ from fabricatio.decorators import logging_execution_info, use_temp_module
11
11
  from fabricatio.journal import logger
12
12
  from fabricatio.models.generic import WithBriefing
13
13
  from pydantic import BaseModel, ConfigDict, Field
@@ -31,6 +31,7 @@ class Tool[**P, R](WithBriefing):
31
31
 
32
32
  if not self.name:
33
33
  raise RuntimeError("The tool must have a source function.")
34
+
34
35
  self.description = self.description or self.source.__doc__ or ""
35
36
  self.description = self.description.strip()
36
37
 
@@ -84,7 +85,7 @@ class ToolBox(WithBriefing):
84
85
  Returns:
85
86
  Self: The current instance of the toolbox.
86
87
  """
87
- self.tools.append(Tool(source=func))
88
+ self.collect_tool(logging_execution_info(func))
88
89
  return self
89
90
 
90
91
  @property