fabricatio 0.2.1.dev4__tar.gz → 0.2.3.dev0__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 (90) hide show
  1. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/PKG-INFO +1 -1
  2. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/make_a_rating/rating.py +26 -1
  3. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/task_handle/handle_task.py +43 -7
  4. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/pyproject.toml +1 -1
  5. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/capabilities/rating.py +79 -1
  6. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/config.py +3 -0
  7. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/core.py +33 -19
  8. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/action.py +6 -2
  9. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/task.py +61 -9
  10. fabricatio-0.2.3.dev0/templates/built-in/draft_rating_weights_klee.hbs +23 -0
  11. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/extract_criteria_from_reasons.hbs +1 -1
  12. fabricatio-0.2.3.dev0/templates.tar.gz +0 -0
  13. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/uv.lock +1 -1
  14. fabricatio-0.2.1.dev4/templates.tar.gz +0 -0
  15. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/.github/workflows/build-package.yaml +0 -0
  16. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/.github/workflows/ruff.yaml +0 -0
  17. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/.github/workflows/tests.yaml +0 -0
  18. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/.gitignore +0 -0
  19. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/.python-version +0 -0
  20. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/Cargo.lock +0 -0
  21. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/Cargo.toml +0 -0
  22. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/LICENSE +0 -0
  23. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/Makefile +0 -0
  24. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/README.md +0 -0
  25. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/llm_usages/llm_usage.py +0 -0
  26. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/make_diary/commits.json +0 -0
  27. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/make_diary/diary.py +0 -0
  28. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/minor/hello_fabricatio.py +0 -0
  29. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/propose_task/propose.py +0 -0
  30. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/examples/simple_chat/chat.py +0 -0
  31. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/__init__.py +0 -0
  32. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/_rust.pyi +0 -0
  33. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/_rust_instances.py +0 -0
  34. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/actions/__init__.py +0 -0
  35. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/actions/communication.py +0 -0
  36. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/actions/transmission.py +0 -0
  37. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/capabilities/task.py +0 -0
  38. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/decorators.py +0 -0
  39. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/fs/__init__.py +0 -0
  40. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/fs/curd.py +0 -0
  41. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/fs/readers.py +0 -0
  42. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/journal.py +0 -0
  43. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/events.py +0 -0
  44. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/generic.py +0 -0
  45. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/kwargs_types.py +0 -0
  46. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/role.py +0 -0
  47. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/tool.py +0 -0
  48. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/usages.py +0 -0
  49. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/models/utils.py +0 -0
  50. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/parser.py +0 -0
  51. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/py.typed +0 -0
  52. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/toolboxes/__init__.py +0 -0
  53. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/toolboxes/arithmetic.py +0 -0
  54. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/toolboxes/fs.py +0 -0
  55. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/python/fabricatio/toolboxes/task.py +0 -0
  56. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/src/hash.rs +0 -0
  57. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/src/lib.rs +0 -0
  58. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/src/templates.rs +0 -0
  59. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/binary-exploitation-ctf-solver.hbs +0 -0
  60. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/claude-xml.hbs +0 -0
  61. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/clean-up-code.hbs +0 -0
  62. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/cryptography-ctf-solver.hbs +0 -0
  63. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/dependencies.hbs +0 -0
  64. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/document-the-code.hbs +0 -0
  65. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/draft_rating_criteria.hbs +0 -0
  66. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/draft_rating_manual.hbs +0 -0
  67. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/draft_tool_usage_code.hbs +0 -0
  68. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/extract_reasons_from_examples.hbs +0 -0
  69. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/find-security-vulnerabilities.hbs +0 -0
  70. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/fix-bugs.hbs +0 -0
  71. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/improve-performance.hbs +0 -0
  72. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/make_choice.hbs +0 -0
  73. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/make_judgment.hbs +0 -0
  74. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/propose_task.hbs +0 -0
  75. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/rate_fine_grind.hbs +0 -0
  76. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/refactor.hbs +0 -0
  77. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/reverse-engineering-ctf-solver.hbs +0 -0
  78. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/task_briefing.hbs +0 -0
  79. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/web-ctf-solver.hbs +0 -0
  80. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/write-git-commit.hbs +0 -0
  81. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/write-github-pull-request.hbs +0 -0
  82. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/templates/built-in/write-github-readme.hbs +0 -0
  83. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_config.py +0 -0
  84. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_action.py +0 -0
  85. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_advanced.py +0 -0
  86. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_generic.py +0 -0
  87. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_role.py +0 -0
  88. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_task.py +0 -0
  89. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_tool.py +0 -0
  90. {fabricatio-0.2.1.dev4 → fabricatio-0.2.3.dev0}/tests/test_models/test_usages.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.1.dev4
3
+ Version: 0.2.3.dev0
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -58,12 +58,26 @@ class MakeCriteria(Action):
58
58
  name: str = "make criteria"
59
59
  output_key: str = "criteria"
60
60
 
61
- async def _execute(self, task_input: Task, rate_topic: str, to_rate: List[str], **cxt: Unpack) -> Set[str]:
61
+ async def _execute(self, rate_topic: str, to_rate: List[str], **cxt: Unpack) -> Set[str]:
62
62
  criteria = await self.draft_rating_criteria_from_examples(rate_topic, to_rate)
63
63
  logger.info(f"Criteria: \n{criteria}")
64
64
  return set(criteria)
65
65
 
66
66
 
67
+ class MakeCompositeScore(Action):
68
+ """Make a composite score."""
69
+
70
+ name: str = "make composite score"
71
+
72
+ output_key: str = "task_output"
73
+
74
+ async def _execute(self, rate_topic: str, to_rate: List[str], **cxt: Unpack) -> List[float]:
75
+ return await self.composite_score(
76
+ rate_topic,
77
+ to_rate,
78
+ )
79
+
80
+
67
81
  async def main() -> None:
68
82
  """Main function."""
69
83
  role = Role(
@@ -85,6 +99,13 @@ async def main() -> None:
85
99
  "rate_topic": "if the food is 'good'",
86
100
  },
87
101
  ),
102
+ Event.instantiate_from("make_composite_score").push_wildcard().push("pending"): WorkFlow(
103
+ name="Make composite score",
104
+ steps=(WhatToRate, MakeCompositeScore),
105
+ extra_init_context={
106
+ "rate_topic": "if the food is 'good'",
107
+ },
108
+ ),
88
109
  },
89
110
  )
90
111
  task = await role.propose(
@@ -98,6 +119,10 @@ async def main() -> None:
98
119
 
99
120
  logger.success(f"Generated Criteria: \n{generated_criteria}")
100
121
 
122
+ composite_score = await task.move_to("make_composite_score").delegate()
123
+
124
+ logger.success(f"Composite Score: \n{composite_score}")
125
+
101
126
 
102
127
  if __name__ == "__main__":
103
128
  asyncio.run(main())
@@ -56,6 +56,30 @@ class WriteDocumentation(Action):
56
56
  return await self.aask(task_input.briefing, system_message=task_input.dependencies_prompt)
57
57
 
58
58
 
59
+ class TestCancel(Action):
60
+ """Action that says hello to the world."""
61
+
62
+ name: str = "cancel"
63
+ description: str = "cancel the task"
64
+ output_key: str = "counter"
65
+
66
+ async def _execute(self, counter: int, **_) -> int:
67
+ logger.info(f"Counter: {counter}")
68
+ await asyncio.sleep(5)
69
+ counter += 1
70
+ return counter
71
+
72
+
73
+ class WriteToOutput(Action):
74
+ """Action that says hello to the world."""
75
+
76
+ name: str = "write to output"
77
+ output_key: str = "task_output"
78
+
79
+ async def _execute(self, **_) -> str:
80
+ return "hi, this is the output"
81
+
82
+
59
83
  async def main() -> None:
60
84
  """Main function."""
61
85
  role = Role(
@@ -68,20 +92,32 @@ async def main() -> None:
68
92
  Event.instantiate_from("doc").push_wildcard().push("pending"): WorkFlow(
69
93
  name="write documentation", steps=(WriteDocumentation, DumpText)
70
94
  ),
95
+ Event.instantiate_from("cancel_test").push_wildcard().push("pending"): WorkFlow(
96
+ name="cancel_test",
97
+ steps=(TestCancel, TestCancel, TestCancel, TestCancel, TestCancel, TestCancel, WriteToOutput),
98
+ extra_init_context={"counter": 0},
99
+ ),
71
100
  },
72
101
  )
73
102
 
74
- prompt = "i want you to write a cli app implemented with python , which can calculate the sum to a given n, all write to a single file names `cli.py`, put it in `output` folder."
75
-
76
- proposed_task = await role.propose(prompt)
77
- path = await proposed_task.move_to("coding").delegate()
103
+ proposed_task = await role.propose(
104
+ "i want you to write a cli app implemented with python , which can calculate the sum to a given n, all write to a single file names `cli.py`, put it in `output` folder."
105
+ )
106
+ path = await proposed_task.delegate("coding")
78
107
  logger.success(f"Code Path: {path}")
79
108
 
80
- proposed_task = await role.propose(f"write Readme.md file for the code, source file {path},save it in `README.md`")
81
- proposed_task.override_dependencies([path])
82
- doc = await proposed_task.move_to("doc").delegate()
109
+ proposed_task = await role.propose(
110
+ f"write Readme.md file for the code, source file {path},save it in `README.md`,which is in the `output` folder, too."
111
+ )
112
+ proposed_task.override_dependencies(path)
113
+ doc = await proposed_task.delegate("doc")
83
114
  logger.success(f"Documentation: \n{doc}")
84
115
 
116
+ proposed_task.publish("cancel_test")
117
+ await proposed_task.cancel()
118
+ out = await proposed_task.get_output()
119
+ logger.info(f"Canceled Task Output: {out}")
120
+
85
121
 
86
122
  if __name__ == "__main__":
87
123
  asyncio.run(main())
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fabricatio"
3
- version = "0.2.1-dev4"
3
+ version = "0.2.3-dev0"
4
4
  description = "A LLM multi-agent framework."
5
5
  readme = "README.md"
6
6
  license = { file = "LICENSE" }
@@ -12,7 +12,7 @@ from fabricatio.models.generic import WithBriefing
12
12
  from fabricatio.models.kwargs_types import GenerateKwargs, ValidateKwargs
13
13
  from fabricatio.models.usages import LLMUsage
14
14
  from fabricatio.parser import JsonCapture
15
- from more_itertools import flatten
15
+ from more_itertools import flatten, windowed
16
16
  from pydantic import NonNegativeInt, PositiveInt
17
17
 
18
18
 
@@ -275,3 +275,81 @@ class GiveRating(WithBriefing, LLMUsage):
275
275
  validator=_criteria_validator,
276
276
  **kwargs,
277
277
  )
278
+
279
+ async def drafting_rating_weights_klee(
280
+ self,
281
+ topic: str,
282
+ criteria: Set[str],
283
+ **kwargs: Unpack[ValidateKwargs],
284
+ ) -> Dict[str, float]:
285
+ """Drafts rating weights for a given topic and criteria using the Klee method.
286
+
287
+ Args:
288
+ topic (str): The topic for the rating weights.
289
+ criteria (Set[str]): A set of criteria for the rating weights.
290
+ **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
291
+
292
+ Returns:
293
+ Dict[str, float]: A dictionary representing the drafted rating weights for each criterion.
294
+ """
295
+ if len(criteria) < 2: # noqa: PLR2004
296
+ raise ValueError("At least two criteria are required to draft rating weights")
297
+
298
+ def _validator(resp: str) -> float | None:
299
+ if (cap := JsonCapture.convert_with(resp, orjson.loads)) is not None and isinstance(cap, float):
300
+ return cap
301
+ return None
302
+
303
+ criteria = list(criteria) # freeze the order
304
+ windows = windowed(criteria, 2)
305
+
306
+ # get the importance multiplier indicating how important is second criterion compared to the first one
307
+ relative_weights = await self.aask_validate_batch(
308
+ questions=[
309
+ template_manager.render_template(
310
+ configs.templates.draft_rating_weights_klee_template,
311
+ {
312
+ "topic": topic,
313
+ "first": pair[0],
314
+ "second": pair[1],
315
+ },
316
+ )
317
+ for pair in windows
318
+ ],
319
+ validator=_validator,
320
+ **GenerateKwargs(system_message=f"# your personal briefing: \n{self.briefing}", **kwargs),
321
+ )
322
+ weights = [1]
323
+ for rw in relative_weights:
324
+ weights.append(weights[-1] * rw)
325
+ total = sum(weights)
326
+ return dict(zip(criteria, [w / total for w in weights], strict=True))
327
+
328
+ async def composite_score(
329
+ self,
330
+ topic: str,
331
+ to_rate: List[str],
332
+ reasons_count: PositiveInt = 2,
333
+ criteria_count: PositiveInt = 5,
334
+ **kwargs: Unpack[ValidateKwargs],
335
+ ) -> List[float]:
336
+ """Calculates the composite scores for a list of items based on a given topic and criteria.
337
+
338
+ Args:
339
+ topic (str): The topic for the rating.
340
+ to_rate (List[str]): A list of strings to be rated.
341
+ reasons_count (PositiveInt, optional): The number of reasons to extract from each pair of examples. Defaults to 2.
342
+ criteria_count (PositiveInt, optional): The number of criteria to draft. Defaults to 5.
343
+ **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
344
+
345
+ Returns:
346
+ List[float]: A list of composite scores for the items.
347
+ """
348
+ criteria = await self.draft_rating_criteria_from_examples(
349
+ topic, to_rate, reasons_count, criteria_count, **kwargs
350
+ )
351
+ weights = await self.drafting_rating_weights_klee(topic, criteria, **kwargs)
352
+ logger.info(f"Criteria: {criteria}\nWeights: {weights}")
353
+ ratings_seq = await self.rate(to_rate, topic, criteria, **kwargs)
354
+
355
+ return [sum(ratings[c] * weights[c] for c in criteria) for ratings in ratings_seq]
@@ -172,6 +172,9 @@ class TemplateConfig(BaseModel):
172
172
  extract_criteria_from_reasons_template: str = Field(default="extract_criteria_from_reasons")
173
173
  """The name of the extract criteria from reasons template which will be used to extract criteria from reasons."""
174
174
 
175
+ draft_rating_weights_klee_template: str = Field(default="draft_rating_weights_klee")
176
+ """The name of the draft rating weights klee template which will be used to draft rating weights with Klee method."""
177
+
175
178
 
176
179
  class MagikaConfig(BaseModel):
177
180
  """Magika configuration class."""
@@ -38,11 +38,11 @@ class Env(BaseModel):
38
38
 
39
39
  @overload
40
40
  def on[**P, R](
41
- self,
42
- event: str | Event,
43
- func: Optional[Callable[P, R]] = None,
44
- /,
45
- ttl: int = -1,
41
+ self,
42
+ event: str | Event,
43
+ func: Optional[Callable[P, R]] = None,
44
+ /,
45
+ ttl: int = -1,
46
46
  ) -> Callable[[Callable[P, R]], Callable[P, R]]:
47
47
  """
48
48
  Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
@@ -58,11 +58,11 @@ class Env(BaseModel):
58
58
  ...
59
59
 
60
60
  def on[**P, R](
61
- self,
62
- event: str | Event,
63
- func: Optional[Callable[P, R]] = None,
64
- /,
65
- ttl=-1,
61
+ self,
62
+ event: str | Event,
63
+ func: Optional[Callable[P, R]] = None,
64
+ /,
65
+ ttl=-1,
66
66
  ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
67
67
  """Registers an event listener with a specific function that listens indefinitely or for a specified number of times.
68
68
 
@@ -78,14 +78,13 @@ class Env(BaseModel):
78
78
  event = event.collapse()
79
79
  if func is None:
80
80
  return self._ee.on(event, ttl=ttl)
81
-
82
81
  self._ee.on(event, func, ttl=ttl)
83
82
  return self
84
83
 
85
84
  @overload
86
85
  def once[**P, R](
87
- self,
88
- event: str | Event,
86
+ self,
87
+ event: str | Event,
89
88
  ) -> Callable[[Callable[P, R]], Callable[P, R]]:
90
89
  """
91
90
  Registers an event listener that listens only once.
@@ -100,9 +99,9 @@ class Env(BaseModel):
100
99
 
101
100
  @overload
102
101
  def once[**P, R](
103
- self,
104
- event: str | Event,
105
- func: Callable[[Callable[P, R]], Callable[P, R]],
102
+ self,
103
+ event: str | Event,
104
+ func: Callable[[Callable[P, R]], Callable[P, R]],
106
105
  ) -> Self:
107
106
  """
108
107
  Registers an event listener with a specific function that listens only once.
@@ -117,9 +116,9 @@ class Env(BaseModel):
117
116
  ...
118
117
 
119
118
  def once[**P, R](
120
- self,
121
- event: str | Event,
122
- func: Optional[Callable[P, R]] = None,
119
+ self,
120
+ event: str | Event,
121
+ func: Optional[Callable[P, R]] = None,
123
122
  ) -> Callable[[Callable[P, R]], Callable[P, R]] | Self:
124
123
  """Registers an event listener with a specific function that listens only once.
125
124
 
@@ -163,5 +162,20 @@ class Env(BaseModel):
163
162
  event = event.collapse()
164
163
  return await self._ee.emit_async(event, *args, **kwargs)
165
164
 
165
+ def emit_future[**P](self, event: str | Event, *args: P.args, **kwargs: P.kwargs) -> None:
166
+ """Emits an event to all registered listeners and returns a future object.
167
+
168
+ Args:
169
+ event (str | Event): The event to emit.
170
+ *args: Positional arguments to pass to the listeners.
171
+ **kwargs: Keyword arguments to pass to the listeners.
172
+
173
+ Returns:
174
+ None: The future object.
175
+ """
176
+ if isinstance(event, Event):
177
+ event = event.collapse()
178
+ return self._ee.emit_future(event, *args, **kwargs)
179
+
166
180
 
167
181
  env = Env()
@@ -2,7 +2,7 @@
2
2
 
3
3
  import traceback
4
4
  from abc import abstractmethod
5
- from asyncio import Queue
5
+ from asyncio import Queue, create_task
6
6
  from typing import Any, Dict, Self, Tuple, Type, Union, Unpack
7
7
 
8
8
  from fabricatio.capabilities.rating import GiveRating
@@ -108,7 +108,11 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
108
108
  try:
109
109
  for step in self._instances:
110
110
  logger.debug(f"Executing step: {step.name}")
111
- modified_ctx = await step.act(await self._context.get())
111
+ act_task = create_task(step.act(await self._context.get()))
112
+ if task.is_cancelled():
113
+ act_task.cancel(f"Cancelled by task: {task.name}")
114
+ break
115
+ modified_ctx = await act_task
112
116
  await self._context.put(modified_ctx)
113
117
  current_action = step.name
114
118
  logger.info(f"Finished executing workflow: {self.name}")
@@ -60,7 +60,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
60
60
  dependencies: List[str] = Field(default_factory=list)
61
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`"""
62
62
 
63
- _output: Queue = PrivateAttr(default_factory=lambda: Queue(maxsize=1))
63
+ _output: Queue[T | None] = PrivateAttr(default_factory=Queue)
64
64
  """The output queue of the task."""
65
65
 
66
66
  _status: TaskStatus = PrivateAttr(default=TaskStatus.Pending)
@@ -131,7 +131,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
131
131
  self.description = description
132
132
  return self
133
133
 
134
- async def get_output(self) -> T:
134
+ async def get_output(self) -> T | None:
135
135
  """Get the output of the task.
136
136
 
137
137
  Returns:
@@ -232,6 +232,7 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
232
232
  """
233
233
  logger.info(f"Cancelling task `{self.name}`")
234
234
  self._status = TaskStatus.Cancelled
235
+ await self._output.put(None)
235
236
  await env.emit_async(self.cancelled_label, self)
236
237
  return self
237
238
 
@@ -243,27 +244,38 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
243
244
  """
244
245
  logger.info(f"Failing task `{self.name}`")
245
246
  self._status = TaskStatus.Failed
247
+ await self._output.put(None)
246
248
  await env.emit_async(self.failed_label, self)
247
249
  return self
248
250
 
249
- async def publish(self) -> Self:
251
+ def publish(self, new_namespace: Optional[EventLike] = None) -> Self:
250
252
  """Publish the task to the event bus.
251
253
 
254
+ Args:
255
+ new_namespace(EventLike, optional): The new namespace to move the task to.
256
+
252
257
  Returns:
253
- Task: The published instance of the `Task` class
258
+ Task: The published instance of the `Task` class.
254
259
  """
260
+ if new_namespace:
261
+ self.move_to(new_namespace)
255
262
  logger.info(f"Publishing task `{(label := self.pending_label)}`")
256
- await env.emit_async(label, self)
263
+ env.emit_future(label, self)
257
264
  return self
258
265
 
259
- async def delegate(self) -> T:
260
- """Delegate the task to the event bus and wait for the output.
266
+ async def delegate(self, new_namespace: Optional[EventLike] = None) -> T | None:
267
+ """Delegate the task to the event.
268
+
269
+ Args:
270
+ new_namespace(EventLike, optional): The new namespace to move the task to.
261
271
 
262
272
  Returns:
263
- T: The output of the task
273
+ T|None: The output of the task.
264
274
  """
275
+ if new_namespace:
276
+ self.move_to(new_namespace)
265
277
  logger.info(f"Delegating task `{(label := self.pending_label)}`")
266
- await env.emit_async(label, self)
278
+ env.emit_future(label, self)
267
279
  return await self.get_output()
268
280
 
269
281
  @property
@@ -277,3 +289,43 @@ class Task[T](WithBriefing, WithJsonExample, WithDependency):
277
289
  configs.templates.task_briefing_template,
278
290
  self.model_dump(),
279
291
  )
292
+
293
+ def is_running(self) -> bool:
294
+ """Check if the task is running.
295
+
296
+ Returns:
297
+ bool: True if the task is running, False otherwise.
298
+ """
299
+ return self._status == TaskStatus.Running
300
+
301
+ def is_finished(self) -> bool:
302
+ """Check if the task is finished.
303
+
304
+ Returns:
305
+ bool: True if the task is finished, False otherwise.
306
+ """
307
+ return self._status == TaskStatus.Finished
308
+
309
+ def is_failed(self) -> bool:
310
+ """Check if the task is failed.
311
+
312
+ Returns:
313
+ bool: True if the task is failed, False otherwise.
314
+ """
315
+ return self._status == TaskStatus.Failed
316
+
317
+ def is_cancelled(self) -> bool:
318
+ """Check if the task is cancelled.
319
+
320
+ Returns:
321
+ bool: True if the task is cancelled, False otherwise.
322
+ """
323
+ return self._status == TaskStatus.Cancelled
324
+
325
+ def is_pending(self) -> bool:
326
+ """Check if the task is pending.
327
+
328
+ Returns:
329
+ bool: True if the task is pending, False otherwise.
330
+ """
331
+ return self._status == TaskStatus.Pending
@@ -0,0 +1,23 @@
1
+ # Task:
2
+ Please compare the following criteria for the topic "{{topic}}":
3
+
4
+ First Criterion: {{first}}
5
+ Second Criterion: {{second}}
6
+
7
+ How much more important is the `{{second}}` compared to the `{{first}}` within the topic of `{{topic}}`? Provide a Positive floating-point number where 1.0 means they are equally important, values greater than 1.0 mean the `{{second}}` is more important, and values less than 1.0 mean the `{{first}}` is more important.
8
+ Your response SHALL be a Positive floating-point number within a JSON codeblock.
9
+ # Examples:
10
+
11
+ Topic: "Best icecream"
12
+ First Criterion: "Taste"
13
+ Second Criterion: "Texture"
14
+
15
+ ----- Start of response example -----
16
+ ```json
17
+ 1.5
18
+ ```
19
+ ----- End of response example -----
20
+
21
+ # Warning:
22
+ - Your response must be a POSITIVE floating-point number within a JSON codeblock.
23
+ - No explanation or additional information is needed, only the POSITIVE floating-point number.
@@ -21,4 +21,4 @@ Extract 4 criteria from the given reasons list
21
21
 
22
22
  # Warning:
23
23
  - The output must be formatted as a JSON array with exactly {{criteria_count}} strings, each representing a distinct criterion. Follow the structure shown in the response example closely. Responses not matching this format will fail validation.
24
-
24
+ - The criteria extracted should be relevant and distinct, ranking DESCENDING in terms of importance or frequency in the given reasons list.
Binary file
@@ -216,7 +216,7 @@ wheels = [
216
216
 
217
217
  [[package]]
218
218
  name = "fabricatio"
219
- version = "0.2.1.dev4"
219
+ version = "0.2.3.dev0"
220
220
  source = { editable = "." }
221
221
  dependencies = [
222
222
  { name = "appdirs" },
Binary file
File without changes