fabricatio 0.1.1__tar.gz → 0.1.2__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 (39) hide show
  1. {fabricatio-0.1.1 → fabricatio-0.1.2}/.gitignore +2 -1
  2. {fabricatio-0.1.1 → fabricatio-0.1.2}/PKG-INFO +2 -1
  3. {fabricatio-0.1.1 → fabricatio-0.1.2}/pyproject.toml +2 -1
  4. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/config.py +6 -2
  5. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/generic.py +81 -14
  6. fabricatio-0.1.2/src/fabricatio/models/role.py +50 -0
  7. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/task.py +14 -10
  8. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/parser.py +5 -2
  9. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_action.py +6 -1
  10. fabricatio-0.1.2/tests/test_parser.py +13 -0
  11. {fabricatio-0.1.1 → fabricatio-0.1.2}/uv.lock +37 -1
  12. fabricatio-0.1.1/src/fabricatio/models/role.py +0 -29
  13. fabricatio-0.1.1/tests/test_parser.py +0 -12
  14. {fabricatio-0.1.1 → fabricatio-0.1.2}/.github/workflows/build-package.yaml +0 -0
  15. {fabricatio-0.1.1 → fabricatio-0.1.2}/.github/workflows/ruff.yaml +0 -0
  16. {fabricatio-0.1.1 → fabricatio-0.1.2}/.python-version +0 -0
  17. {fabricatio-0.1.1 → fabricatio-0.1.2}/LICENSE +0 -0
  18. {fabricatio-0.1.1 → fabricatio-0.1.2}/README.md +0 -0
  19. {fabricatio-0.1.1 → fabricatio-0.1.2}/examples/minor/hello_fabricatio.py +0 -0
  20. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/__init__.py +0 -0
  21. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/core.py +0 -0
  22. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/journal.py +0 -0
  23. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/action.py +0 -0
  24. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/events.py +0 -0
  25. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/tool.py +0 -0
  26. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/models/utils.py +0 -0
  27. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/py.typed +0 -0
  28. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/toolboxes/__init__.py +0 -0
  29. {fabricatio-0.1.1 → fabricatio-0.1.2}/src/fabricatio/toolboxes/task.py +0 -0
  30. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/__init__.py +0 -0
  31. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/conftest.py +0 -0
  32. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_config.py +0 -0
  33. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_core.py +0 -0
  34. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_events.py +0 -0
  35. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_events.py +0 -0
  36. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_generic.py +0 -0
  37. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_role.py +0 -0
  38. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_tool.py +0 -0
  39. {fabricatio-0.1.1 → fabricatio-0.1.2}/tests/test_models/test_utils.py +0 -0
@@ -8,4 +8,5 @@ wheels/
8
8
 
9
9
  # Virtual environments
10
10
  .venv
11
- .idea
11
+ .idea
12
+ .env
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A LLM multi-agent framework.
5
5
  Author-email: Whth <zettainspector@foxmail.com>
6
6
  License: MIT License
@@ -40,6 +40,7 @@ Requires-Dist: asyncio>=3.4.3
40
40
  Requires-Dist: gitpython>=3.1.44
41
41
  Requires-Dist: litellm>=1.60.0
42
42
  Requires-Dist: loguru>=0.7.3
43
+ Requires-Dist: orjson>=3.10.15
43
44
  Requires-Dist: pydantic-settings>=2.7.1
44
45
  Requires-Dist: pydantic>=2.10.6
45
46
  Requires-Dist: pymitter>=1.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "gitpython>=3.1.44",
31
31
  "litellm>=1.60.0",
32
32
  "loguru>=0.7.3",
33
+ "orjson>=3.10.15",
33
34
  "pydantic>=2.10.6",
34
35
  "pydantic-settings>=2.7.1",
35
36
  "pymitter>=1.0.0",
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import List, Literal
2
2
 
3
3
  from appdirs import user_config_dir
4
4
  from pydantic import BaseModel, ConfigDict, Field, FilePath, HttpUrl, NonNegativeFloat, PositiveInt, SecretStr
@@ -29,6 +29,7 @@ class LLMConfig(BaseModel):
29
29
  stream (bool): Whether to stream the LLM model's response. Default is False.
30
30
  max_tokens (PositiveInt): The maximum number of tokens to generate. Set to 8192 as per request.
31
31
  """
32
+
32
33
  model_config = ConfigDict(use_attribute_docstrings=True)
33
34
  api_endpoint: HttpUrl = Field(default=HttpUrl("https://api.openai.com"))
34
35
  """
@@ -60,7 +61,7 @@ class LLMConfig(BaseModel):
60
61
  The temperature of the LLM model. Controls randomness in generation. Set to 1.0 as per request.
61
62
  """
62
63
 
63
- stop_sign: str = Field(default="")
64
+ stop_sign: str | List[str] = Field(default=("\n\n", "User:"))
64
65
  """
65
66
  The stop sign of the LLM model. No default stop sign specified.
66
67
  """
@@ -94,6 +95,7 @@ class PymitterConfig(BaseModel):
94
95
  new_listener_event (bool): If set, a newListener event is emitted when a new listener is added.
95
96
  max_listeners (int): The maximum number of listeners per event.
96
97
  """
98
+
97
99
  model_config = ConfigDict(use_attribute_docstrings=True)
98
100
  delimiter: str = Field(default=".", frozen=True)
99
101
  """
@@ -118,6 +120,7 @@ class DebugConfig(BaseModel):
118
120
  log_level (Literal["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"]): The log level of the application.
119
121
  log_file (FilePath): The log file of the application.
120
122
  """
123
+
121
124
  model_config = ConfigDict(use_attribute_docstrings=True)
122
125
 
123
126
  log_level: Literal["DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"] = Field(default="INFO")
@@ -139,6 +142,7 @@ class Settings(BaseSettings):
139
142
  debug (DebugConfig): Debug Configuration
140
143
  pymitter (PymitterConfig): Pymitter Configuration
141
144
  """
145
+
142
146
  model_config = SettingsConfigDict(
143
147
  env_prefix="FABRIK_",
144
148
  env_nested_delimiter="__",
@@ -1,7 +1,8 @@
1
1
  from asyncio import Queue
2
- from typing import Any, Dict, Iterable, List, Optional, Self
2
+ from typing import Callable, Dict, Iterable, List, Optional, Self
3
3
 
4
4
  import litellm
5
+ import orjson
5
6
  from litellm.types.utils import Choices, ModelResponse, StreamingChoices
6
7
  from pydantic import (
7
8
  BaseModel,
@@ -190,7 +191,7 @@ class LLMUsage(Base):
190
191
  The temperature of the LLM model.
191
192
  """
192
193
 
193
- llm_stop_sign: Optional[str] = None
194
+ llm_stop_sign: Optional[str | List[str]] = None
194
195
  """
195
196
  The stop sign of the LLM model.
196
197
  """
@@ -215,21 +216,12 @@ class LLMUsage(Base):
215
216
  The maximum number of tokens to generate.
216
217
  """
217
218
 
218
- def model_post_init(self, __context: Any) -> None:
219
- """Initialize the LLM model with API key and endpoint.
220
-
221
- Args:
222
- __context (Any): The context passed during model initialization.
223
- """
224
- litellm.api_key = self.llm_api_key.get_secret_value() if self.llm_api_key else configs.llm.api_key
225
- litellm.api_base = self.llm_api_endpoint.unicode_string() if self.llm_api_endpoint else configs.llm.api_endpoint
226
-
227
219
  async def aquery(
228
220
  self,
229
221
  messages: List[Dict[str, str]],
230
222
  model: str | None = None,
231
223
  temperature: NonNegativeFloat | None = None,
232
- stop: str | None = None,
224
+ stop: str | List[str] | None = None,
233
225
  top_p: NonNegativeFloat | None = None,
234
226
  max_tokens: PositiveInt | None = None,
235
227
  n: PositiveInt | None = None,
@@ -266,6 +258,10 @@ class LLMUsage(Base):
266
258
  stream=stream or self.llm_stream or configs.llm.stream,
267
259
  timeout=timeout or self.llm_timeout or configs.llm.timeout,
268
260
  max_retries=max_retries or self.llm_max_retries or configs.llm.max_retries,
261
+ api_key=self.llm_api_key.get_secret_value() if self.llm_api_key else configs.llm.api_key.get_secret_value(),
262
+ base_url=self.llm_api_endpoint.unicode_string()
263
+ if self.llm_api_endpoint
264
+ else configs.llm.api_endpoint.unicode_string(),
269
265
  )
270
266
 
271
267
  async def ainvoke(
@@ -274,7 +270,7 @@ class LLMUsage(Base):
274
270
  system_message: str = "",
275
271
  model: str | None = None,
276
272
  temperature: NonNegativeFloat | None = None,
277
- stop: str | None = None,
273
+ stop: str | List[str] | None = None,
278
274
  top_p: NonNegativeFloat | None = None,
279
275
  max_tokens: PositiveInt | None = None,
280
276
  n: PositiveInt | None = None,
@@ -321,7 +317,7 @@ class LLMUsage(Base):
321
317
  system_message: str = "",
322
318
  model: str | None = None,
323
319
  temperature: NonNegativeFloat | None = None,
324
- stop: str | None = None,
320
+ stop: str | List[str] | None = None,
325
321
  top_p: NonNegativeFloat | None = None,
326
322
  max_tokens: PositiveInt | None = None,
327
323
  stream: bool | None = None,
@@ -365,6 +361,61 @@ class LLMUsage(Base):
365
361
  .message.content
366
362
  )
367
363
 
364
+ async def aask_validate[T](
365
+ self,
366
+ question: str,
367
+ validator: Callable[[str], T | None],
368
+ max_validations: PositiveInt = 2,
369
+ system_message: str = "",
370
+ model: str | None = None,
371
+ temperature: NonNegativeFloat | None = None,
372
+ stop: str | List[str] | None = None,
373
+ top_p: NonNegativeFloat | None = None,
374
+ max_tokens: PositiveInt | None = None,
375
+ stream: bool | None = None,
376
+ timeout: PositiveInt | None = None,
377
+ max_retries: PositiveInt | None = None,
378
+ ) -> T:
379
+ """Asynchronously ask a question and validate the response using a given validator.
380
+
381
+ Args:
382
+ question (str): The question to ask.
383
+ validator (Callable[[str], T | None]): A function to validate the response.
384
+ max_validations (PositiveInt): Maximum number of validation attempts.
385
+ system_message (str): System message to include in the request.
386
+ model (str | None): The model to use for the request.
387
+ temperature (NonNegativeFloat | None): Temperature setting for the request.
388
+ stop (str | None): Stop sequence for the request.
389
+ top_p (NonNegativeFloat | None): Top-p sampling parameter.
390
+ max_tokens (PositiveInt | None): Maximum number of tokens in the response.
391
+ stream (bool | None): Whether to stream the response.
392
+ timeout (PositiveInt | None): Timeout for the request.
393
+ max_retries (PositiveInt | None): Maximum number of retries for the request.
394
+
395
+ Returns:
396
+ T: The validated response.
397
+
398
+ Raises:
399
+ ValueError: If the response fails to validate after the maximum number of attempts.
400
+ """
401
+ for _ in range(max_validations):
402
+ if (
403
+ response := await self.aask(
404
+ question,
405
+ system_message,
406
+ model,
407
+ temperature,
408
+ stop,
409
+ top_p,
410
+ max_tokens,
411
+ stream,
412
+ timeout,
413
+ max_retries,
414
+ )
415
+ ) and (validated := validator(response)):
416
+ return validated
417
+ raise ValueError("Failed to validate the response.")
418
+
368
419
  def fallback_to(self, other: "LLMUsage") -> Self:
369
420
  """Fallback to another instance's attribute values if the current instance's attributes are None.
370
421
 
@@ -397,3 +448,19 @@ class LLMUsage(Base):
397
448
 
398
449
  # Return the current instance to allow for method chaining
399
450
  return self
451
+
452
+
453
+ class WithJsonExample(Base):
454
+ """Class that provides a JSON schema for the model."""
455
+
456
+ @classmethod
457
+ def json_example(cls) -> str:
458
+ """Return a JSON example for the model.
459
+
460
+ Returns:
461
+ str: A JSON example for the model.
462
+ """
463
+ return orjson.dumps(
464
+ {field_name: field_info.description for field_name, field_info in cls.model_fields.items()},
465
+ option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS,
466
+ ).decode()
@@ -0,0 +1,50 @@
1
+ from typing import Any
2
+
3
+ from pydantic import Field, ValidationError
4
+
5
+ from fabricatio.core import env
6
+ from fabricatio.journal import logger
7
+ from fabricatio.models.action import WorkFlow
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
12
+
13
+
14
+ class Role(Memorable, WithBriefing, WithToDo, LLMUsage):
15
+ """Class that represents a role with a registry of events and workflows."""
16
+
17
+ registry: dict[Event | str, WorkFlow] = Field(...)
18
+ """ The registry of events and workflows."""
19
+
20
+ def model_post_init(self, __context: Any) -> None:
21
+ """Register the workflows in the role to the event bus."""
22
+ for event, workflow in self.registry.items():
23
+ workflow.fallback_to(self)
24
+ logger.debug(
25
+ f"Registering workflow: {workflow.name} for event: {event.collapse() if isinstance(event, Event) else event}"
26
+ )
27
+ 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
+ )
@@ -12,7 +12,7 @@ from pydantic import Field, PrivateAttr
12
12
  from fabricatio.config import configs
13
13
  from fabricatio.core import env
14
14
  from fabricatio.journal import logger
15
- from fabricatio.models.generic import WithBriefing
15
+ from fabricatio.models.generic import WithBriefing, WithJsonExample
16
16
 
17
17
 
18
18
  class TaskStatus(Enum):
@@ -25,27 +25,31 @@ class TaskStatus(Enum):
25
25
  Cancelled = "cancelled"
26
26
 
27
27
 
28
- class Task[T](WithBriefing):
28
+ class Task[T](WithBriefing, WithJsonExample):
29
29
  """Class that represents a task with a status and output.
30
30
 
31
31
  Attributes:
32
32
  name (str): The name of the task.
33
33
  description (str): The description of the task.
34
34
  _output (Queue): The output queue of the task.
35
- status (TaskStatus): The status of the task.
35
+ _status (TaskStatus): The status of the task.
36
36
  goal (str): The goal of the task.
37
37
  """
38
38
 
39
39
  name: str = Field(...)
40
40
  """The name of the task."""
41
+
41
42
  description: str = Field(default="")
42
43
  """The description of the task."""
43
- _output: Queue = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
44
- status: TaskStatus = Field(default=TaskStatus.Pending)
45
- """The status of the task."""
44
+
46
45
  goal: str = Field(default="")
47
46
  """The goal of the task."""
48
47
 
48
+ _output: Queue = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
49
+ """The output queue of the task."""
50
+ _status: TaskStatus = PrivateAttr(default=TaskStatus.Pending)
51
+ """The status of the task."""
52
+
49
53
  @classmethod
50
54
  def simple_task(cls, name: str, goal: str, description: str) -> Self:
51
55
  """Create a simple task with a name, goal, and description.
@@ -151,7 +155,7 @@ class Task[T](WithBriefing):
151
155
  Self: The finished instance of the Task class.
152
156
  """
153
157
  logger.info(f"Finishing task {self.name}")
154
- self.status = TaskStatus.Finished
158
+ self._status = TaskStatus.Finished
155
159
  await self._output.put(output)
156
160
  logger.debug(f"Output set for task {self.name}")
157
161
  await env.emit_async(self.finished_label, self)
@@ -165,7 +169,7 @@ class Task[T](WithBriefing):
165
169
  Self: The running instance of the Task class.
166
170
  """
167
171
  logger.info(f"Starting task {self.name}")
168
- self.status = TaskStatus.Running
172
+ self._status = TaskStatus.Running
169
173
  await env.emit_async(self.running_label, self)
170
174
  return self
171
175
 
@@ -175,7 +179,7 @@ class Task[T](WithBriefing):
175
179
  Returns:
176
180
  Self: The cancelled instance of the Task class.
177
181
  """
178
- self.status = TaskStatus.Cancelled
182
+ self._status = TaskStatus.Cancelled
179
183
  await env.emit_async(self.cancelled_label, self)
180
184
  return self
181
185
 
@@ -186,7 +190,7 @@ class Task[T](WithBriefing):
186
190
  Self: The failed instance of the Task class.
187
191
  """
188
192
  logger.error(f"Task {self.name} failed")
189
- self.status = TaskStatus.Failed
193
+ self._status = TaskStatus.Failed
190
194
  await env.emit_async(self.failed_label, self)
191
195
  return self
192
196
 
@@ -1,6 +1,7 @@
1
1
  from typing import Any, Self, Tuple
2
2
 
3
- from pydantic import Field, PrivateAttr
3
+ import regex
4
+ from pydantic import Field, PositiveInt, PrivateAttr
4
5
  from regex import Pattern, compile
5
6
 
6
7
  from fabricatio.models.generic import Base
@@ -18,6 +19,8 @@ class Capture(Base):
18
19
  """The target groups to capture from the pattern."""
19
20
  pattern: str = Field(frozen=True)
20
21
  """The regular expression pattern to search for."""
22
+ flags: PositiveInt = Field(default=regex.DOTALL | regex.MULTILINE | regex.IGNORECASE, frozen=True)
23
+ """The flags to use when compiling the regular expression pattern."""
21
24
  _compiled: Pattern = PrivateAttr()
22
25
 
23
26
  def model_post_init(self, __context: Any) -> None:
@@ -26,7 +29,7 @@ class Capture(Base):
26
29
  Args:
27
30
  __context (Any): The context in which the model is initialized.
28
31
  """
29
- self._compiled = compile(self.pattern)
32
+ self._compiled = compile(self.pattern, self.flags)
30
33
 
31
34
  def capture(self, text: str) -> Tuple[str, ...] | None:
32
35
  """Capture the first occurrence of the pattern in the given text.
@@ -8,12 +8,14 @@ class TestAction(Action):
8
8
  async def _execute(self, *args, **kwargs):
9
9
  return "executed"
10
10
 
11
+
11
12
  @pytest.mark.asyncio
12
13
  async def test_action_execute():
13
14
  action = TestAction(name="test_action")
14
15
  result = await action._execute()
15
16
  assert result == "executed"
16
17
 
18
+
17
19
  @pytest.mark.asyncio
18
20
  async def test_action_act():
19
21
  action = TestAction(name="test_action", output_key="result")
@@ -21,6 +23,7 @@ async def test_action_act():
21
23
  await action.act(context)
22
24
  assert context["result"] == "executed"
23
25
 
26
+
24
27
  @pytest.mark.asyncio
25
28
  async def test_workflow_execute():
26
29
  class TestWorkflowAction(Action):
@@ -30,6 +33,7 @@ async def test_workflow_execute():
30
33
  workflow = WorkFlow(steps=(TestWorkflowAction(name="test_workflow_action"),), name="test_workflow")
31
34
  await workflow.execute()
32
35
 
36
+
33
37
  @pytest.mark.asyncio
34
38
  async def test_workflow_model_post_init():
35
39
  class TestWorkflowAction(Action):
@@ -40,6 +44,7 @@ async def test_workflow_model_post_init():
40
44
  workflow.model_post_init(None)
41
45
  assert workflow.steps[0].llm_api_endpoint == workflow.llm_api_endpoint
42
46
 
47
+
43
48
  @pytest.mark.asyncio
44
49
  async def test_workflow_serve():
45
50
  class TestWorkflowAction(Action):
@@ -49,5 +54,5 @@ async def test_workflow_serve():
49
54
  workflow = WorkFlow(steps=(TestWorkflowAction(name="test_workflow_action"),), name="test_workflow")
50
55
  task = Task(input="data")
51
56
  await workflow.serve(task)
52
- assert task.status == "finished"
57
+ assert task._status == "finished"
53
58
  assert task.output == "executed"
@@ -0,0 +1,13 @@
1
+ from fabricatio import Capture
2
+
3
+
4
+ def test_capture() -> None:
5
+ """Test the Capture class."""
6
+ capture = Capture(pattern=r"(\d+)", target_groups=(1,))
7
+ assert capture.capture("123") == ("123",)
8
+ assert capture.capture("abc") is None
9
+ assert Capture.capture_code_block("python").capture("""
10
+ ```python
11
+ print('Hello, World!')
12
+ ```
13
+ """) == ("print('Hello, World!')",)
@@ -251,7 +251,7 @@ wheels = [
251
251
 
252
252
  [[package]]
253
253
  name = "fabricatio"
254
- version = "0.1.0"
254
+ version = "0.1.1"
255
255
  source = { editable = "." }
256
256
  dependencies = [
257
257
  { name = "aiohttp" },
@@ -261,6 +261,7 @@ dependencies = [
261
261
  { name = "gitpython" },
262
262
  { name = "litellm" },
263
263
  { name = "loguru" },
264
+ { name = "orjson" },
264
265
  { name = "pydantic" },
265
266
  { name = "pydantic-settings" },
266
267
  { name = "pymitter" },
@@ -299,6 +300,7 @@ requires-dist = [
299
300
  { name = "gitpython", specifier = ">=3.1.44" },
300
301
  { name = "litellm", specifier = ">=1.60.0" },
301
302
  { name = "loguru", specifier = ">=0.7.3" },
303
+ { name = "orjson", specifier = ">=3.10.15" },
302
304
  { name = "pydantic", specifier = ">=2.10.6" },
303
305
  { name = "pydantic-settings", specifier = ">=2.7.1" },
304
306
  { name = "pymitter", specifier = ">=1.0.0" },
@@ -842,6 +844,40 @@ wheels = [
842
844
  { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/93/76/70c5ad6612b3e4c89fa520266bbf2430a89cae8bd87c1e2284698af5927e/openai-1.61.0-py3-none-any.whl", hash = "sha256:e8c512c0743accbdbe77f3429a1490d862f8352045de8dc81969301eb4a4f666", size = 460623 },
843
845
  ]
844
846
 
847
+ [[package]]
848
+ name = "orjson"
849
+ version = "3.10.15"
850
+ source = { registry = "https://mirrors.bfsu.edu.cn/pypi/web/simple" }
851
+ sdist = { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/ae/f9/5dea21763eeff8c1590076918a446ea3d6140743e0e36f58f369928ed0f4/orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e", size = 5282482 }
852
+ wheels = [
853
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/66/85/22fe737188905a71afcc4bf7cc4c79cd7f5bbe9ed1fe0aac4ce4c33edc30/orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a", size = 249504 },
854
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/48/b7/2622b29f3afebe938a0a9037e184660379797d5fd5234e5998345d7a5b43/orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d", size = 125080 },
855
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/ce/8f/0b72a48f4403d0b88b2a41450c535b3e8989e8a2d7800659a967efc7c115/orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0", size = 150121 },
856
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/06/ec/acb1a20cd49edb2000be5a0404cd43e3c8aad219f376ac8c60b870518c03/orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4", size = 139796 },
857
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/33/e1/f7840a2ea852114b23a52a1c0b2bea0a1ea22236efbcdb876402d799c423/orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767", size = 154636 },
858
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/fa/da/31543337febd043b8fa80a3b67de627669b88c7b128d9ad4cc2ece005b7a/orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41", size = 130621 },
859
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/ed/78/66115dc9afbc22496530d2139f2f4455698be444c7c2475cb48f657cefc9/orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514", size = 138516 },
860
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/22/84/cd4f5fb5427ffcf823140957a47503076184cb1ce15bcc1165125c26c46c/orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17", size = 130762 },
861
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/93/1f/67596b711ba9f56dd75d73b60089c5c92057f1130bb3a25a0f53fb9a583b/orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b", size = 414700 },
862
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/7c/0c/6a3b3271b46443d90efb713c3e4fe83fa8cd71cda0d11a0f69a03f437c6e/orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7", size = 141077 },
863
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/3b/9b/33c58e0bfc788995eccd0d525ecd6b84b40d7ed182dd0751cd4c1322ac62/orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a", size = 129898 },
864
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/01/c1/d577ecd2e9fa393366a1ea0a9267f6510d86e6c4bb1cdfb9877104cac44c/orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665", size = 142566 },
865
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/ed/eb/a85317ee1732d1034b92d56f89f1de4d7bf7904f5c8fb9dcdd5b1c83917f/orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa", size = 133732 },
866
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/06/10/fe7d60b8da538e8d3d3721f08c1b7bff0491e8fa4dd3bf11a17e34f4730e/orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6", size = 249399 },
867
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/6b/83/52c356fd3a61abd829ae7e4366a6fe8e8863c825a60d7ac5156067516edf/orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a", size = 125044 },
868
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/55/b2/d06d5901408e7ded1a74c7c20d70e3a127057a6d21355f50c90c0f337913/orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9", size = 150066 },
869
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/75/8c/60c3106e08dc593a861755781c7c675a566445cc39558677d505878d879f/orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0", size = 139737 },
870
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/6a/8c/ae00d7d0ab8a4490b1efeb01ad4ab2f1982e69cc82490bf8093407718ff5/orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307", size = 154804 },
871
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/22/86/65dc69bd88b6dd254535310e97bc518aa50a39ef9c5a2a5d518e7a223710/orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e", size = 130583 },
872
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/bb/00/6fe01ededb05d52be42fabb13d93a36e51f1fd9be173bd95707d11a8a860/orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7", size = 138465 },
873
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/db/2f/4cc151c4b471b0cdc8cb29d3eadbce5007eb0475d26fa26ed123dca93b33/orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8", size = 130742 },
874
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/9f/13/8a6109e4b477c518498ca37963d9c0eb1508b259725553fb53d53b20e2ea/orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca", size = 414669 },
875
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/22/7b/1d229d6d24644ed4d0a803de1b0e2df832032d5beda7346831c78191b5b2/orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561", size = 141043 },
876
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/cc/d3/6dc91156cf12ed86bed383bcb942d84d23304a1e57b7ab030bf60ea130d6/orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825", size = 129826 },
877
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/b3/38/c47c25b86f6996f1343be721b6ea4367bc1c8bc0fc3f6bbcd995d18cb19d/orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890", size = 142542 },
878
+ { url = "https://mirrors.bfsu.edu.cn/pypi/web/packages/27/f1/1d7ec15b20f8ce9300bc850de1e059132b88990e46cd0ccac29cbf11e4f9/orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf", size = 133444 },
879
+ ]
880
+
845
881
  [[package]]
846
882
  name = "packaging"
847
883
  version = "24.2"
@@ -1,29 +0,0 @@
1
- from typing import Any
2
-
3
- from pydantic import Field
4
-
5
- from fabricatio.core import env
6
- from fabricatio.journal import logger
7
- from fabricatio.models.action import WorkFlow
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
-
12
-
13
- class Role(Memorable, WithBriefing, WithToDo, LLMUsage):
14
- """Class that represents a role with a registry of events and workflows."""
15
-
16
- registry: dict[Event | str, WorkFlow] = Field(...)
17
- """ The registry of events and workflows."""
18
-
19
- def model_post_init(self, __context: Any) -> None:
20
- """Register the workflows in the role to the event bus."""
21
- for event, workflow in self.registry.items():
22
- workflow.fallback_to(self)
23
- logger.debug(
24
- f"Registering workflow: {workflow.name} for event: {event.collapse() if isinstance(event, Event) else event}"
25
- )
26
- env.on(event, workflow.serve)
27
-
28
- async def propose(self, prompt: str) -> Task:
29
- """Propose a task to the role."""
@@ -1,12 +0,0 @@
1
- from fabricatio import Capture
2
-
3
-
4
- def test_capture():
5
- """Test the Capture class.
6
- """
7
- capture = Capture(pattern=r"(\d+)", target_groups=(1,))
8
- assert capture.capture("123") == ("123",)
9
- assert capture.capture("abc") is None
10
- assert Capture.capture_code_block("python").capture("```python\nprint('Hello, World!')\n```") == (
11
- "print('Hello, World!')",
12
- )
File without changes
File without changes
File without changes
File without changes
File without changes