fabricatio 0.2.0.dev12__tar.gz → 0.2.0.dev14__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 (97) hide show
  1. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/PKG-INFO +4 -1
  2. fabricatio-0.2.0.dev14/examples/propose_task/propose.py +31 -0
  3. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/pyproject.toml +5 -3
  4. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/action.py +9 -3
  5. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/generic.py +74 -4
  6. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/role.py +14 -3
  7. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/task.py +2 -2
  8. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/tool.py +21 -10
  9. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/parser.py +24 -4
  10. fabricatio-0.2.0.dev14/python/fabricatio/toolboxes/__init__.py +15 -0
  11. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/toolboxes/arithmetic.py +7 -5
  12. fabricatio-0.2.0.dev14/python/fabricatio/toolboxes/task.py +4 -0
  13. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/make_choice.hbs +2 -0
  14. fabricatio-0.2.0.dev14/templates/built-in/make_judgment.hbs +27 -0
  15. fabricatio-0.2.0.dev14/templates.tar.gz +0 -0
  16. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/uv.lock +1 -1
  17. fabricatio-0.2.0.dev12/python/fabricatio/toolboxes/__init__.py +0 -7
  18. fabricatio-0.2.0.dev12/python/fabricatio/toolboxes/task.py +0 -4
  19. fabricatio-0.2.0.dev12/templates.tar.gz +0 -0
  20. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/.github/workflows/build-package.yaml +0 -0
  21. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/.github/workflows/ruff.yaml +0 -0
  22. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/.github/workflows/tests.yaml +0 -0
  23. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/.gitignore +0 -0
  24. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/.python-version +0 -0
  25. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/Cargo.lock +0 -0
  26. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/Cargo.toml +0 -0
  27. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/LICENSE +0 -0
  28. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/Makefile +0 -0
  29. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/README.md +0 -0
  30. /fabricatio-0.2.0.dev12/examples/propose_task/propose.py → /fabricatio-0.2.0.dev14/examples/llm_usages/llm_usage.py +0 -0
  31. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/examples/minor/hello_fabricatio.py +0 -0
  32. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/examples/simple_chat/chat.py +0 -0
  33. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/__init__.py +0 -0
  34. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/_rust.pyi +0 -0
  35. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/_rust_instances.py +0 -0
  36. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/actions/__init__.py +0 -0
  37. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/actions/communication.py +0 -0
  38. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/actions/transmission.py +0 -0
  39. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/config.py +0 -0
  40. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/core.py +0 -0
  41. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/decorators.py +0 -0
  42. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/fs/__init__.py +0 -0
  43. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/fs/readers.py +0 -0
  44. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/journal.py +0 -0
  45. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/events.py +0 -0
  46. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/models/utils.py +0 -0
  47. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/python/fabricatio/py.typed +0 -0
  48. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/src/lib.rs +0 -0
  49. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/src/templates.rs +0 -0
  50. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
  51. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/claude-xml.hbs +0 -0
  52. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/clean-up-code.hbs +0 -0
  53. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
  54. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/document-the-code.hbs +0 -0
  55. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
  56. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/fix-bugs.hbs +0 -0
  57. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/improve-performance.hbs +0 -0
  58. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/propose_task.hbs +0 -0
  59. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/refactor.hbs +0 -0
  60. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
  61. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/web-ctf-solver.hbs +0 -0
  62. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/write-git-commit.hbs +0 -0
  63. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/write-github-pull-request.hbs +0 -0
  64. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/templates/built-in/write-github-readme.hbs +0 -0
  65. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/__init__.py +0 -0
  66. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_config.py +0 -0
  67. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_core.py +0 -0
  68. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_decorators.py +0 -0
  69. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_events.py +0 -0
  70. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_examples.py +0 -0
  71. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_action.py +0 -0
  72. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_communication.py +0 -0
  73. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_events.py +0 -0
  74. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_generic.py +0 -0
  75. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_llm_usage.py +0 -0
  76. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_propose_task.py +0 -0
  77. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_role.py +0 -0
  78. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_role_with_actions.py +0 -0
  79. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_task.py +0 -0
  80. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_template_manager.py +0 -0
  81. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_tool.py +0 -0
  82. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_transmission.py +0 -0
  83. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_utils.py +0 -0
  84. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_briefing.py +0 -0
  85. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_briefing_and_json_example.py +0 -0
  86. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_briefing_and_llm_usage.py +0 -0
  87. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_briefing_and_llm_usage_and_json_example.py +0 -0
  88. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency.py +0 -0
  89. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_briefing.py +0 -0
  90. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_briefing_and_json_example.py +0 -0
  91. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_briefing_and_llm_usage.py +0 -0
  92. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_json_example.py +0 -0
  93. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_llm_usage.py +0 -0
  94. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_dependency_and_llm_usage_and_json_example.py +0 -0
  95. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_with_json_example.py +0 -0
  96. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_models/test_workflow.py +0 -0
  97. {fabricatio-0.2.0.dev12 → fabricatio-0.2.0.dev14}/tests/test_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.0.dev12
3
+ Version: 0.2.0.dev14
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -28,6 +28,9 @@ Keywords: ai,agents,multi-agent,llm,pyo3
28
28
  Author-email: Whth <zettainspector@foxmail.com>
29
29
  Requires-Python: >=3.12
30
30
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
31
+ Project-URL: Homepage, https://github.com/Whth/fabricatio
32
+ Project-URL: Repository, https://github.com/Whth/fabricatio
33
+ Project-URL: Issues, https://github.com/Whth/fabricatio/issues
31
34
 
32
35
  # Fabricatio
33
36
 
@@ -0,0 +1,31 @@
1
+ import asyncio
2
+ from typing import Any
3
+
4
+ from fabricatio import Action, Role, Task, WorkFlow, logger
5
+
6
+ task = Task(name="say hello", goal="say hello", description="say hello to the world")
7
+
8
+
9
+ class Talk(Action):
10
+ """Action that says hello to the world."""
11
+
12
+ name: str = "talk"
13
+ output_key: str = "task_output"
14
+
15
+ async def _execute(self, task_input: Task[str], **_) -> Any:
16
+ ret = "Hello fabricatio!"
17
+ logger.info("executing talk action")
18
+ return ret
19
+
20
+
21
+ async def main() -> None:
22
+ """Main function."""
23
+ role = Role(
24
+ name="talker", description="talker role", registry={task.pending_label: WorkFlow(name="talk", steps=(Talk,))}
25
+ )
26
+ logger.info(Task.json_example())
27
+ logger.info(f"proposed task: {await role.propose('write a rust clap cli that can download a html page')}")
28
+
29
+
30
+ if __name__ == "__main__":
31
+ asyncio.run(main())
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.2.0-dev.12"
3
+ version = "0.2.0-dev.14"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -25,7 +25,6 @@ keywords = [
25
25
  ]
26
26
 
27
27
 
28
-
29
28
  requires-python = ">=3.12"
30
29
  dependencies = [
31
30
  "appdirs>=1.4.4",
@@ -44,7 +43,10 @@ dependencies = [
44
43
  "rich>=13.9.4",
45
44
  ]
46
45
 
47
-
46
+ [project.urls]
47
+ Homepage = "https://github.com/Whth/fabricatio"
48
+ Repository = "https://github.com/Whth/fabricatio"
49
+ Issues = "https://github.com/Whth/fabricatio/issues"
48
50
  [build-system]
49
51
  requires = ["maturin>=1.0,<2.0"]
50
52
  build-backend = "maturin"
@@ -8,10 +8,11 @@ from typing import Any, Dict, Self, Tuple, Type, Unpack
8
8
  from fabricatio.journal import logger
9
9
  from fabricatio.models.generic import LLMUsage, WithBriefing
10
10
  from fabricatio.models.task import ProposeTask, Task
11
+ from fabricatio.models.tool import ToolBoxUsage
11
12
  from pydantic import Field, PrivateAttr
12
13
 
13
14
 
14
- class Action(ProposeTask):
15
+ class Action(ProposeTask, ToolBoxUsage):
15
16
  """Class that represents an action to be executed in a workflow."""
16
17
 
17
18
  personality: str = Field(default="")
@@ -50,7 +51,7 @@ class Action(ProposeTask):
50
51
  return f"# The action you are going to perform: \n{super().briefing}"
51
52
 
52
53
 
53
- class WorkFlow[A: Type[Action] | Action](WithBriefing, LLMUsage):
54
+ class WorkFlow[A: Type[Action] | Action](WithBriefing, LLMUsage, ToolBoxUsage):
54
55
  """Class that represents a workflow to be executed in a task."""
55
56
 
56
57
  _context: Queue[Dict[str, Any]] = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
@@ -121,7 +122,12 @@ class WorkFlow[A: Type[Action] | Action](WithBriefing, LLMUsage):
121
122
  logger.debug(f"Initializing context for workflow: {self.name}")
122
123
  await self._context.put({self.task_input_key: None, **dict(self.extra_init_context)})
123
124
 
124
- def fallback_to_self(self) -> Self:
125
+ def steps_fallback_to_self(self) -> Self:
125
126
  """Set the fallback for each step to the workflow itself."""
126
127
  self.hold_to(self._instances)
127
128
  return self
129
+
130
+ def steps_supply_tools_from_self(self) -> Self:
131
+ """Supply the tools from the workflow to each step."""
132
+ self.provide_tools_to(self._instances)
133
+ return self
@@ -17,6 +17,7 @@ from pydantic import (
17
17
  Field,
18
18
  HttpUrl,
19
19
  NonNegativeFloat,
20
+ NonNegativeInt,
20
21
  PositiveInt,
21
22
  SecretStr,
22
23
  )
@@ -295,6 +296,7 @@ class LLMUsage(Base):
295
296
  self,
296
297
  instruction: str,
297
298
  choices: List[T],
299
+ k: NonNegativeInt = 0,
298
300
  max_validations: PositiveInt = 2,
299
301
  system_message: str = "",
300
302
  model: str | None = None,
@@ -311,6 +313,7 @@ class LLMUsage(Base):
311
313
  Args:
312
314
  instruction: The user-provided instruction/question description.
313
315
  choices: A list of candidate options, requiring elements to have `name` and `briefing` fields.
316
+ k: The number of choices to select, 0 means infinite.
314
317
  max_validations: Maximum number of validation failures, default is 2.
315
318
  system_message: Custom system-level prompt, defaults to an empty string.
316
319
  model: The name of the LLM model to use.
@@ -332,14 +335,17 @@ class LLMUsage(Base):
332
335
  """
333
336
  prompt = template_manager.render_template(
334
337
  "make_choice",
335
- {"instruction": instruction, "options": [m.model_dump(include={"name", "briefing"}) for m in choices]},
338
+ {
339
+ "instruction": instruction,
340
+ "options": [m.model_dump(include={"name", "briefing"}) for m in choices],
341
+ "k": k,
342
+ },
336
343
  )
337
344
  names = [c.name for c in choices]
338
345
 
339
346
  def _validate(response: str) -> List[T] | None:
340
- cap = JsonCapture.capture(response)
341
- ret = orjson.loads(cap)
342
- if not isinstance(ret, List):
347
+ ret = JsonCapture.convert_with(response, orjson.loads)
348
+ if not isinstance(ret, List) or len(ret) != k:
343
349
  return None
344
350
  if any(n not in names for n in ret):
345
351
  return None
@@ -360,6 +366,70 @@ class LLMUsage(Base):
360
366
  max_retries=max_retries,
361
367
  )
362
368
 
369
+ async def ajudge(
370
+ self,
371
+ prompt: str,
372
+ affirm_case: str = "",
373
+ deny_case: str = "",
374
+ max_validations: PositiveInt = 2,
375
+ system_message: str = "",
376
+ model: str | None = None,
377
+ temperature: NonNegativeFloat | None = None,
378
+ stop: str | List[str] | None = None,
379
+ top_p: NonNegativeFloat | None = None,
380
+ max_tokens: PositiveInt | None = None,
381
+ stream: bool | None = None,
382
+ timeout: PositiveInt | None = None,
383
+ max_retries: PositiveInt | None = None,
384
+ ) -> bool:
385
+ """Asynchronously judges a prompt using AI validation.
386
+
387
+ Args:
388
+ prompt (str): The input prompt to be judged.
389
+ affirm_case (str, optional): The affirmative case for the AI model. Defaults to "".
390
+ deny_case (str, optional): The negative case for the AI model. Defaults to "".
391
+ max_validations (PositiveInt, optional): Maximum number of validation attempts. Defaults to 2.
392
+ system_message (str, optional): System message for the AI model. Defaults to "".
393
+ model (str | None, optional): AI model to use. Defaults to None.
394
+ temperature (NonNegativeFloat | None, optional): Sampling temperature. Defaults to None.
395
+ stop (str | List[str] | None, optional): Stop sequences. Defaults to None.
396
+ top_p (NonNegativeFloat | None, optional): Nucleus sampling parameter. Defaults to None.
397
+ max_tokens (PositiveInt | None, optional): Maximum number of tokens to generate. Defaults to None.
398
+ stream (bool | None, optional): Whether to stream the response. Defaults to None.
399
+ timeout (PositiveInt | None, optional): Timeout in seconds. Defaults to None.
400
+ max_retries (PositiveInt | None, optional): Maximum number of retries. Defaults to None.
401
+
402
+ Returns:
403
+ bool: The judgment result (True or False) based on the AI's response.
404
+
405
+ Notes:
406
+ The method uses an internal validator to ensure the response is a boolean value.
407
+ If the response cannot be converted to a boolean, it will return None.
408
+ """
409
+
410
+ def _validate(response: str) -> bool | None:
411
+ ret = JsonCapture.convert_with(response, orjson.loads)
412
+ if not isinstance(ret, bool):
413
+ return None
414
+ return ret
415
+
416
+ return await self.aask_validate(
417
+ question=template_manager.render_template(
418
+ "make_judgment", {"prompt": prompt, "affirm_case": affirm_case, "deny_case": deny_case}
419
+ ),
420
+ validator=_validate,
421
+ max_validations=max_validations,
422
+ system_message=system_message,
423
+ model=model,
424
+ temperature=temperature,
425
+ stop=stop,
426
+ top_p=top_p,
427
+ max_tokens=max_tokens,
428
+ stream=stream,
429
+ timeout=timeout,
430
+ max_retries=max_retries,
431
+ )
432
+
363
433
  def fallback_to(self, other: "LLMUsage") -> Self:
364
434
  """Fallback to another instance's attribute values if the current instance's attributes are None.
365
435
 
@@ -1,25 +1,36 @@
1
1
  """Module that contains the Role class."""
2
2
 
3
- from typing import Any
3
+ from typing import Any, Set
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
9
  from fabricatio.models.task import ProposeTask
10
+ from fabricatio.models.tool import ToolBox, ToolBoxUsage
11
+ from fabricatio.toolboxes import basic_toolboxes
10
12
  from pydantic import Field
11
13
 
12
14
 
13
- class Role(ProposeTask):
15
+ class Role(ProposeTask, ToolBoxUsage):
14
16
  """Class that represents a role with a registry of events and workflows."""
15
17
 
16
18
  registry: dict[Event | str, WorkFlow] = Field(...)
17
19
  """ The registry of events and workflows."""
18
20
 
21
+ toolboxes: Set[ToolBox] = Field(default=basic_toolboxes)
22
+
19
23
  def model_post_init(self, __context: Any) -> None:
20
24
  """Register the workflows in the role to the event bus."""
21
25
  for event, workflow in self.registry.items():
22
- workflow.fallback_to(self).fallback_to_self().inject_personality(self.briefing)
26
+ (
27
+ workflow.fallback_to(self)
28
+ .steps_fallback_to_self()
29
+ .inject_personality(self.briefing)
30
+ .supply_tools_from(self)
31
+ .steps_supply_tools_from_self()
32
+ )
33
+
23
34
  logger.debug(
24
35
  f"Registering workflow: {workflow.name} for event: {event.collapse() if isinstance(event, Event) else event}"
25
36
  )
@@ -268,8 +268,8 @@ class ProposeTask(LLMUsage, WithBriefing):
268
268
  try:
269
269
  cap = JsonCapture.capture(response)
270
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)
271
+ logger.info(f"Captured JSON: \n{cap}")
272
+ return Task.model_validate_json(cap)
273
273
  except ValidationError as e:
274
274
  logger.error(f"Failed to parse task from JSON: {e}")
275
275
  return None
@@ -1,8 +1,9 @@
1
1
  """A module for defining tools and toolboxes."""
2
2
 
3
3
  from inspect import iscoroutinefunction, signature
4
- from typing import Any, Callable, Iterable, List, Optional, Self, Union
4
+ from typing import Any, Callable, Iterable, List, Self, Set, Union
5
5
 
6
+ from fabricatio.journal import logger
6
7
  from fabricatio.models.generic import Base, WithBriefing
7
8
  from pydantic import Field
8
9
 
@@ -28,6 +29,7 @@ class Tool[**P, R](WithBriefing):
28
29
 
29
30
  def invoke(self, *args: P.args, **kwargs: P.kwargs) -> R:
30
31
  """Invoke the tool's source function with the provided arguments."""
32
+ logger.info(f"Invoking tool: {self.name} with args: {args} and kwargs: {kwargs}")
31
33
  return self.source(*args, **kwargs)
32
34
 
33
35
  @property
@@ -105,14 +107,23 @@ class ToolBox(WithBriefing):
105
107
  assert tool, f"No tool named {name} found."
106
108
  return tool
107
109
 
110
+ def __hash__(self) -> int:
111
+ """Return a hash of the toolbox based on its briefing."""
112
+ return hash(self.briefing)
108
113
 
109
- class ToolUsage(Base):
114
+
115
+ class ToolBoxUsage(Base):
110
116
  """A class representing the usage of tools in a task."""
111
117
 
112
- toolboxes: Optional[List[ToolBox]]
113
- """The tools used by the task, a list of ToolBox instances."""
118
+ toolboxes: Set[ToolBox] = Field(default_factory=set)
119
+ """A set of toolboxes used by the instance."""
120
+
121
+ @property
122
+ def available_toolbox_names(self) -> List[str]:
123
+ """Return a list of available toolbox names."""
124
+ return [toolbox.name for toolbox in self.toolboxes]
114
125
 
115
- def supply_tools_from(self, others: Union["ToolUsage", Iterable["ToolUsage"]]) -> Self:
126
+ def supply_tools_from(self, others: Union["ToolBoxUsage", Iterable["ToolBoxUsage"]]) -> Self:
116
127
  """Supplies tools from other ToolUsage instances to this instance.
117
128
 
118
129
  Args:
@@ -122,13 +133,13 @@ class ToolUsage(Base):
122
133
  Returns:
123
134
  Self: The current ToolUsage instance with updated tools.
124
135
  """
125
- if isinstance(others, ToolUsage):
136
+ if isinstance(others, ToolBoxUsage):
126
137
  others = [others]
127
138
  for other in others:
128
- self.toolboxes.extend(other.toolboxes)
139
+ self.toolboxes.update(other.toolboxes)
129
140
  return self
130
141
 
131
- def provide_tools_to(self, others: Union["ToolUsage", Iterable["ToolUsage"]]) -> Self:
142
+ def provide_tools_to(self, others: Union["ToolBoxUsage", Iterable["ToolBoxUsage"]]) -> Self:
132
143
  """Provides tools from this instance to other ToolUsage instances.
133
144
 
134
145
  Args:
@@ -138,8 +149,8 @@ class ToolUsage(Base):
138
149
  Returns:
139
150
  Self: The current ToolUsage instance.
140
151
  """
141
- if isinstance(others, ToolUsage):
152
+ if isinstance(others, ToolBoxUsage):
142
153
  others = [others]
143
154
  for other in others:
144
- other.toolboxes.extend(self.toolboxes)
155
+ other.toolboxes.update(self.toolboxes)
145
156
  return self
@@ -1,11 +1,13 @@
1
1
  """A module to parse text using regular expressions."""
2
2
 
3
- from typing import Any, Self, Tuple
3
+ from typing import Any, Callable, Self, Tuple
4
4
 
5
5
  import regex
6
6
  from pydantic import BaseModel, ConfigDict, Field, PositiveInt, PrivateAttr
7
7
  from regex import Pattern, compile
8
8
 
9
+ from fabricatio.journal import logger
10
+
9
11
 
10
12
  class Capture(BaseModel):
11
13
  """A class to capture patterns in text using regular expressions.
@@ -32,7 +34,7 @@ class Capture(BaseModel):
32
34
  """
33
35
  self._compiled = compile(self.pattern, self.flags)
34
36
 
35
- def capture(self, text: str) -> Tuple[str, ...] | None:
37
+ def capture(self, text: str) -> Tuple[str, ...] | str | None:
36
38
  """Capture the first occurrence of the pattern in the given text.
37
39
 
38
40
  Args:
@@ -48,7 +50,25 @@ class Capture(BaseModel):
48
50
 
49
51
  if self.target_groups:
50
52
  return tuple(match.group(g) for g in self.target_groups)
51
- return (match.group(),)
53
+ return match.group(1)
54
+
55
+ def convert_with[T](self, text: str, convertor: Callable[[Tuple[str, ...]], T] | Callable[[str], T]) -> T | None:
56
+ """Convert the given text using the pattern.
57
+
58
+ Args:
59
+ text (str): The text to search the pattern in.
60
+ convertor (Callable[[Tuple[str, ...]], T] | Callable[[str], T]): The function to convert the captured text.
61
+
62
+ Returns:
63
+ str | None: The converted text if the pattern is found, otherwise None.
64
+ """
65
+ if cap := self.capture(text) is None:
66
+ return None
67
+ try:
68
+ return convertor(cap)
69
+ except ValueError as e:
70
+ logger.error(f"Failed to convert text using convertor: {convertor.__name__}, error: \n{e}")
71
+ return None
52
72
 
53
73
  @classmethod
54
74
  def capture_code_block(cls, language: str) -> Self:
@@ -60,7 +80,7 @@ class Capture(BaseModel):
60
80
  Returns:
61
81
  Self: The instance of the class with the captured code block.
62
82
  """
63
- return cls(pattern=f"```{language}\n(.*?)\n```", target_groups=(1,))
83
+ return cls(pattern=f"```{language}\n(.*?)\n```")
64
84
 
65
85
 
66
86
  JsonCapture = Capture.capture_code_block("json")
@@ -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.task import task_toolbox
8
+
9
+ basic_toolboxes: Set[ToolBox] = {task_toolbox, arithmetic_toolbox}
10
+
11
+ __all__ = [
12
+ "arithmetic_toolbox",
13
+ "basic_toolboxes",
14
+ "task_toolbox",
15
+ ]
@@ -1,9 +1,11 @@
1
+ """Arithmetic tools for Fabricatio."""
2
+
1
3
  from fabricatio.models.tool import ToolBox
2
4
 
3
- arithmetic_tools = ToolBox(name="ArithmeticToolBox", description="A toolbox for arithmetic operations.")
5
+ arithmetic_toolbox = ToolBox(name="ArithmeticToolBox", description="A toolbox for arithmetic operations.")
4
6
 
5
7
 
6
- @arithmetic_tools.collect_tool
8
+ @arithmetic_toolbox.collect_tool
7
9
  def add(a: float, b: float) -> float:
8
10
  """Add two numbers.
9
11
 
@@ -17,7 +19,7 @@ def add(a: float, b: float) -> float:
17
19
  return a + b
18
20
 
19
21
 
20
- @arithmetic_tools.collect_tool
22
+ @arithmetic_toolbox.collect_tool
21
23
  def subtract(a: float, b: float) -> float:
22
24
  """Subtract two numbers.
23
25
 
@@ -31,7 +33,7 @@ def subtract(a: float, b: float) -> float:
31
33
  return a - b
32
34
 
33
35
 
34
- @arithmetic_tools.collect_tool
36
+ @arithmetic_toolbox.collect_tool
35
37
  def multiply(a: float, b: float) -> float:
36
38
  """Multiply two numbers.
37
39
 
@@ -45,7 +47,7 @@ def multiply(a: float, b: float) -> float:
45
47
  return a * b
46
48
 
47
49
 
48
- @arithmetic_tools.collect_tool
50
+ @arithmetic_toolbox.collect_tool
49
51
  def divide(a: float, b: float) -> float:
50
52
  """Divide two numbers.
51
53
 
@@ -0,0 +1,4 @@
1
+ from fabricatio.models.task import Task
2
+ from fabricatio.models.tool import ToolBox
3
+
4
+ task_toolbox = ToolBox(name="TaskToolBox", description="A toolbox for tasks management.").add_tool(Task.simple_task)
@@ -18,7 +18,9 @@ Example:
18
18
  ```json
19
19
  ["option1", "option2"]
20
20
  ```
21
+
21
22
  ### Warnings:
22
23
 
23
24
  Note that All items in list you returned **MUST** be contained in [{{#each options}}"{{name}}", {{/each}}], No extra items are allowed.
24
25
  Your response shall not have any explanation, only the JSON array of selected option names.
26
+ {{#if k}}The array returned MUST have a length of **{{k}}**.{{/if}}
@@ -0,0 +1,27 @@
1
+ # Prompt:
2
+
3
+ {{ prompt }}
4
+
5
+ # Task:
6
+
7
+ Please provide a boolean response (true or false) based on the prompt given above.:
8
+
9
+ # Example:
10
+
11
+ e.g. 1
12
+ ```json
13
+ true
14
+ ```
15
+
16
+ e.g. 2
17
+ ```json
18
+ false
19
+ ```
20
+
21
+ # Warnings:
22
+
23
+ - Your response shall not have any explanation, only the boolean response.
24
+ - Your response shall be either `true` or `false`.
25
+ {{#if affirm_case}}- If {{affirm_case}}, respond `true`.{{/if}}
26
+ {{#if deny_case}}- If {{deny_case}}, respond `false`.{{/if}}
27
+
Binary file
@@ -260,7 +260,7 @@ wheels = [
260
260
 
261
261
  [[package]]
262
262
  name = "fabricatio"
263
- version = "0.2.0.dev12"
263
+ version = "0.2.0.dev14"
264
264
  source = { editable = "." }
265
265
  dependencies = [
266
266
  { name = "appdirs" },
@@ -1,7 +0,0 @@
1
- """Contains the built-in toolboxes for the Fabricatio package."""
2
-
3
- from fabricatio.toolboxes.task import TaskToolBox
4
-
5
- __all__ = [
6
- "TaskToolBox",
7
- ]
@@ -1,4 +0,0 @@
1
- from fabricatio.models.task import Task
2
- from fabricatio.models.tool import ToolBox
3
-
4
- TaskToolBox = ToolBox(name="TaskToolBox", description="A toolbox for tasks management.").add_tool(Task.simple_task)
Binary file