fabricatio 0.2.7.dev0__cp312-cp312-manylinux_2_34_x86_64.whl → 0.2.7.dev2__cp312-cp312-manylinux_2_34_x86_64.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.
fabricatio/_rust.pyi CHANGED
@@ -11,7 +11,7 @@ class TemplateManager:
11
11
  """
12
12
 
13
13
  def __init__(
14
- self, template_dirs: List[Path], suffix: Optional[str] = None, active_loading: Optional[bool] = None
14
+ self, template_dirs: List[Path], suffix: Optional[str] = None, active_loading: Optional[bool] = None
15
15
  ) -> None:
16
16
  """Initialize the template manager.
17
17
 
@@ -55,6 +55,16 @@ class TemplateManager:
55
55
  RuntimeError: If template rendering fails
56
56
  """
57
57
 
58
+ def render_template_raw(self, template: str, data: Dict[str, Any]) -> str:
59
+ """Render a template with context data.
60
+
61
+ Args:
62
+ template: The template string
63
+ data: Context dictionary to provide variables to the template
64
+
65
+ Returns:
66
+ Rendered template content as string
67
+ """
58
68
 
59
69
  def blake3_hash(content: bytes) -> str:
60
70
  """Calculate the BLAKE3 cryptographic hash of data.
@@ -66,7 +76,6 @@ def blake3_hash(content: bytes) -> str:
66
76
  Hex-encoded BLAKE3 hash string
67
77
  """
68
78
 
69
-
70
79
  class BibManager:
71
80
  """BibTeX bibliography manager for parsing and querying citation data."""
72
81
 
@@ -1,16 +1,14 @@
1
1
  """Actions for transmitting tasks to targets."""
2
2
 
3
- from os import PathLike
4
3
  from pathlib import Path
5
4
  from typing import Any, Callable, List, Optional
6
5
 
7
6
  from fabricatio.fs import safe_text_read
8
7
  from fabricatio.journal import logger
9
8
  from fabricatio.models.action import Action
10
- from fabricatio.models.extra import ArticleEssence, ArticleOutline, ArticleProposal
9
+ from fabricatio.models.extra import Article, ArticleEssence, ArticleOutline, ArticleProposal
11
10
  from fabricatio.models.task import Task
12
- from questionary import confirm, text
13
- from rich import print as rprint
11
+ from fabricatio.models.utils import ok
14
12
 
15
13
 
16
14
  class ExtractArticleEssence(Action):
@@ -51,18 +49,33 @@ class GenerateArticleProposal(Action):
51
49
 
52
50
  async def _execute(
53
51
  self,
54
- task_input: Task,
52
+ task_input: Optional[Task] = None,
53
+ article_briefing: Optional[str] = None,
54
+ article_briefing_path: Optional[str] = None,
55
55
  **_,
56
56
  ) -> Optional[ArticleProposal]:
57
- input_path = await self.awhich_pathstr(
58
- f"{task_input.briefing}\nExtract the path of file, which contains the article briefing that I need to read."
59
- )
60
-
61
- return await self.propose(
62
- ArticleProposal,
63
- safe_text_read(input_path),
64
- system_message=f"# your personal briefing: \n{self.briefing}",
65
- )
57
+ if article_briefing is None and article_briefing_path is None and task_input is None:
58
+ logger.error("Task not approved, since all inputs are None.")
59
+ return None
60
+
61
+ return (
62
+ await self.propose(
63
+ ArticleProposal,
64
+ briefing := (
65
+ article_briefing
66
+ or safe_text_read(
67
+ ok(
68
+ article_briefing_path
69
+ or await self.awhich_pathstr(
70
+ f"{task_input.briefing}\nExtract the path of file which contains the article briefing."
71
+ ),
72
+ "Could not find the path of file to read.",
73
+ )
74
+ )
75
+ ),
76
+ **self.prepend_sys_msg(),
77
+ )
78
+ ).update_ref(briefing)
66
79
 
67
80
 
68
81
  class GenerateOutline(Action):
@@ -76,35 +89,40 @@ class GenerateOutline(Action):
76
89
  article_proposal: ArticleProposal,
77
90
  **_,
78
91
  ) -> Optional[ArticleOutline]:
79
- return await self.propose(
92
+ out = await self.propose(
80
93
  ArticleOutline,
81
- article_proposal.display(),
82
- system_message=f"# your personal briefing: \n{self.briefing}",
94
+ article_proposal.as_prompt(),
95
+ **self.prepend_sys_msg(),
83
96
  )
84
97
 
98
+ manual = await self.draft_rating_manual(
99
+ topic=(
100
+ topic
101
+ := "Fix the internal referring error, make sure there is no more `ArticleRef` pointing to a non-existing article component."
102
+ ),
103
+ )
104
+ while err := out.resolve_ref_error():
105
+ logger.warning(f"Found error in the outline: \n{err}")
106
+ out = await self.correct_obj(
107
+ out,
108
+ reference=f"# Referring Error\n{err}",
109
+ topic=topic,
110
+ rating_manual=manual,
111
+ supervisor_check=False,
112
+ )
113
+ return out.update_ref(article_proposal)
114
+
85
115
 
86
116
  class CorrectProposal(Action):
87
117
  """Correct the proposal of the article."""
88
118
 
89
119
  output_key: str = "corrected_proposal"
90
120
 
91
- async def _execute(self, task_input: Task, article_proposal: ArticleProposal, **_) -> Any:
92
- input_path = await self.awhich_pathstr(
93
- f"{task_input.briefing}\nExtract the path of file, which contains the article briefing that I need to read."
121
+ async def _execute(self, article_proposal: ArticleProposal, **_) -> Any:
122
+ return (await self.censor_obj(article_proposal, reference=article_proposal.referenced)).update_ref(
123
+ article_proposal
94
124
  )
95
125
 
96
- ret = None
97
- while await confirm("Do you want to correct the Proposal?").ask_async():
98
- rprint(article_proposal.display())
99
- while not (topic := await text("What is the topic of the proposal reviewing?").ask_async()):
100
- ...
101
- ret = await self.correct_obj(
102
- article_proposal,
103
- safe_text_read(input_path),
104
- topic=topic,
105
- )
106
- return ret or article_proposal
107
-
108
126
 
109
127
  class CorrectOutline(Action):
110
128
  """Correct the outline of the article."""
@@ -115,17 +133,77 @@ class CorrectOutline(Action):
115
133
  async def _execute(
116
134
  self,
117
135
  article_outline: ArticleOutline,
118
- article_proposal: ArticleProposal,
119
-
120
136
  **_,
121
137
  ) -> ArticleOutline:
122
- ret = None
123
- while await confirm("Do you want to correct the outline?").ask_async():
124
- rprint(article_outline.finalized_dump())
125
- while not (topic := await text("What is the topic of the outline reviewing?").ask_async()):
126
- ...
127
- ret = await self.correct_obj(article_outline, article_proposal.display(), topic=topic)
128
- return ret or article_outline
138
+ return (await self.censor_obj(article_outline, reference=article_outline.referenced.as_prompt())).update_ref(
139
+ article_outline
140
+ )
129
141
 
130
142
 
143
+ class GenerateArticle(Action):
144
+ """Generate the article based on the outline."""
131
145
 
146
+ output_key: str = "article"
147
+ """The key of the output data."""
148
+
149
+ async def _execute(
150
+ self,
151
+ article_outline: ArticleOutline,
152
+ **_,
153
+ ) -> Optional[Article]:
154
+ article: Article = Article.from_outline(article_outline).update_ref(article_outline)
155
+
156
+ writing_manual = await self.draft_rating_manual(
157
+ topic=(
158
+ topic_1
159
+ := "improve the content of the subsection to fit the outline. SHALL never add or remove any section or subsection, you can only add or delete paragraphs within the subsection."
160
+ ),
161
+ )
162
+ err_resolve_manual = await self.draft_rating_manual(
163
+ topic=(topic_2 := "this article component has violated the constrain, please correct it.")
164
+ )
165
+ for c, deps in article.iter_dfs_with_deps(chapter=False):
166
+ logger.info(f"Updating the article component: \n{c.display()}")
167
+
168
+ out = ok(
169
+ await self.correct_obj(
170
+ c,
171
+ reference=(
172
+ ref := f"{article_outline.referenced.as_prompt()}\n" + "\n".join(d.display() for d in deps)
173
+ ),
174
+ topic=topic_1,
175
+ rating_manual=writing_manual,
176
+ supervisor_check=False,
177
+ ),
178
+ "Could not correct the article component.",
179
+ )
180
+ while err := c.resolve_update_error(out):
181
+ logger.warning(f"Found error in the article component: \n{err}")
182
+ out = ok(
183
+ await self.correct_obj(
184
+ out,
185
+ reference=f"{ref}\n\n# Violated Error\n{err}",
186
+ topic=topic_2,
187
+ rating_manual=err_resolve_manual,
188
+ supervisor_check=False,
189
+ ),
190
+ "Could not correct the article component.",
191
+ )
192
+
193
+ c.update_from(out)
194
+ return article
195
+
196
+
197
+ class CorrectArticle(Action):
198
+ """Correct the article based on the outline."""
199
+
200
+ output_key: str = "corrected_article"
201
+ """The key of the output data."""
202
+
203
+ async def _execute(
204
+ self,
205
+ article: Article,
206
+ article_outline: ArticleOutline,
207
+ **_,
208
+ ) -> Article:
209
+ return await self.censor_obj(article, reference=article_outline.referenced.as_prompt())
@@ -1,8 +1,12 @@
1
1
  """Dump the finalized output to a file."""
2
2
 
3
+ from pathlib import Path
4
+ from typing import Optional
5
+
3
6
  from fabricatio.models.action import Action
4
7
  from fabricatio.models.generic import FinalizedDumpAble
5
8
  from fabricatio.models.task import Task
9
+ from fabricatio.models.utils import ok
6
10
 
7
11
 
8
12
  class DumpFinalizedOutput(Action):
@@ -10,10 +14,21 @@ class DumpFinalizedOutput(Action):
10
14
 
11
15
  output_key: str = "dump_path"
12
16
 
13
- async def _execute(self, task_input: Task, to_dump: FinalizedDumpAble, **_) -> str:
14
- dump_path = await self.awhich_pathstr(
15
- f"{task_input.briefing}\n\nExtract a single path of the file, to which I will dump the data."
17
+ async def _execute(
18
+ self,
19
+ to_dump: FinalizedDumpAble,
20
+ task_input: Optional[Task] = None,
21
+ dump_path: Optional[str | Path] = None,
22
+ **_,
23
+ ) -> str:
24
+ dump_path = Path(
25
+ dump_path
26
+ or ok(
27
+ await self.awhich_pathstr(
28
+ f"{ok(task_input, 'Neither `task_input` and `dump_path` is provided.').briefing}\n\nExtract a single path of the file, to which I will dump the data."
29
+ ),
30
+ "Could not find the path of file to dump the data.",
31
+ )
16
32
  )
17
-
18
- to_dump.finalized_dump_to(dump_path)
19
- return dump_path
33
+ ok(to_dump, "Could not dump the data since the path is not specified.").finalized_dump_to(dump_path)
34
+ return dump_path.as_posix()
@@ -10,9 +10,11 @@ from typing import Optional, Unpack, cast
10
10
  from fabricatio._rust_instances import TEMPLATE_MANAGER
11
11
  from fabricatio.capabilities.review import Review, ReviewResult
12
12
  from fabricatio.config import configs
13
- from fabricatio.models.generic import Display, ProposedAble, WithBriefing
14
- from fabricatio.models.kwargs_types import CorrectKwargs, ReviewKwargs
13
+ from fabricatio.models.generic import CensoredAble, Display, ProposedAble, WithBriefing
14
+ from fabricatio.models.kwargs_types import CensoredCorrectKwargs, CorrectKwargs, ReviewKwargs
15
15
  from fabricatio.models.task import Task
16
+ from questionary import confirm, text
17
+ from rich import print as rprint
16
18
 
17
19
 
18
20
  class Correct(Review):
@@ -55,7 +57,7 @@ class Correct(Review):
55
57
  if supervisor_check:
56
58
  await review_res.supervisor_check()
57
59
  if "default" in kwargs:
58
- cast('ReviewKwargs[None]', kwargs)["default"] = None
60
+ cast("ReviewKwargs[None]", kwargs)["default"] = None
59
61
  return await self.propose(
60
62
  obj.__class__,
61
63
  TEMPLATE_MANAGER.render_template(
@@ -89,7 +91,7 @@ class Correct(Review):
89
91
  await review_res.supervisor_check()
90
92
 
91
93
  if "default" in kwargs:
92
- cast('ReviewKwargs[None]', kwargs)["default"] = None
94
+ cast("ReviewKwargs[None]", kwargs)["default"] = None
93
95
  return await self.ageneric_string(
94
96
  TEMPLATE_MANAGER.render_template(
95
97
  configs.templates.correct_template, {"content": input_text, "review": review_res.display()}
@@ -113,3 +115,31 @@ class Correct(Review):
113
115
  Optional[Task[T]]: The corrected task, or None if correction fails.
114
116
  """
115
117
  return await self.correct_obj(task, **kwargs)
118
+
119
+ async def censor_obj[M: CensoredAble](
120
+ self, obj: M, **kwargs: Unpack[CensoredCorrectKwargs[ReviewResult[str]]]
121
+ ) -> M:
122
+ """Censor and correct an object based on defined criteria and templates.
123
+
124
+ Args:
125
+ obj (M): The object to be reviewed and corrected.
126
+ **kwargs (Unpack[CensoredCorrectKwargs]): Additional keyword
127
+
128
+ Returns:
129
+ M: The censored and corrected object.
130
+ """
131
+ last_modified_obj = obj
132
+ modified_obj = None
133
+ rprint(obj.finalized_dump())
134
+ while await confirm("Begin to correct obj above with human censorship?").ask_async():
135
+ while (topic := await text("What is the topic of the obj reviewing?").ask_async()) is not None and topic:
136
+ ...
137
+ if (modified_obj := await self.correct_obj(
138
+ last_modified_obj,
139
+ topic=topic,
140
+ **kwargs,
141
+ )) is None:
142
+ break
143
+ last_modified_obj = modified_obj
144
+ rprint(last_modified_obj.finalized_dump())
145
+ return modified_obj or last_modified_obj
@@ -19,6 +19,7 @@ from fabricatio.models.kwargs_types import (
19
19
  EmbeddingKwargs,
20
20
  FetchKwargs,
21
21
  LLMKwargs,
22
+ RetrievalKwargs,
22
23
  )
23
24
  from fabricatio.models.usages import EmbeddingUsage
24
25
  from fabricatio.models.utils import MilvusData, ok
@@ -213,6 +214,25 @@ class RAG(EmbeddingUsage):
213
214
  self.add_document(await self.pack(text), collection_name or self.safe_target_collection, flush=True)
214
215
  return self
215
216
 
217
+ @overload
218
+ async def afetch_document[V: (int, str, float, bytes)](
219
+ self,
220
+ vecs: List[List[float]],
221
+ desired_fields: List[str],
222
+ collection_name: Optional[str] = None,
223
+ similarity_threshold: float = 0.37,
224
+ result_per_query: int = 10,
225
+ ) -> List[Dict[str, V]]: ...
226
+
227
+ @overload
228
+ async def afetch_document[V: (int, str, float, bytes)](
229
+ self,
230
+ vecs: List[List[float]],
231
+ desired_fields: str,
232
+ collection_name: Optional[str] = None,
233
+ similarity_threshold: float = 0.37,
234
+ result_per_query: int = 10,
235
+ ) -> List[V]: ...
216
236
  async def afetch_document[V: (int, str, float, bytes)](
217
237
  self,
218
238
  vecs: List[List[float]],
@@ -275,7 +295,7 @@ class RAG(EmbeddingUsage):
275
295
  if isinstance(query, str):
276
296
  query = [query]
277
297
  return cast(
278
- 'List[str]',
298
+ "List[str]",
279
299
  await self.afetch_document(
280
300
  vecs=(await self.vectorize(query)),
281
301
  desired_fields="text",
@@ -283,6 +303,24 @@ class RAG(EmbeddingUsage):
283
303
  ),
284
304
  )[:final_limit]
285
305
 
306
+ async def aretrieve_compact(
307
+ self,
308
+ query: List[str] | str,
309
+ **kwargs: Unpack[RetrievalKwargs],
310
+ ) -> str:
311
+ """Retrieve data from the collection and format it for display.
312
+
313
+ Args:
314
+ query (List[str] | str): The query to be used for retrieval.
315
+ **kwargs (Unpack[RetrievalKwargs]): Additional keyword arguments for retrieval.
316
+
317
+ Returns:
318
+ str: A formatted string containing the retrieved data.
319
+ """
320
+ return TEMPLATE_MANAGER.render_template(
321
+ configs.templates.retrieved_display_template, {"docs": (await self.aretrieve(query, **kwargs))}
322
+ )
323
+
286
324
  async def aask_retrieved(
287
325
  self,
288
326
  question: str,
@@ -313,16 +351,14 @@ class RAG(EmbeddingUsage):
313
351
  Returns:
314
352
  str: A string response generated after asking with the context of retrieved documents.
315
353
  """
316
- docs = await self.aretrieve(
354
+ rendered = await self.aretrieve_compact(
317
355
  query or question,
318
- final_limit,
356
+ final_limit=final_limit,
319
357
  collection_name=collection_name,
320
358
  result_per_query=result_per_query,
321
359
  similarity_threshold=similarity_threshold,
322
360
  )
323
361
 
324
- rendered = TEMPLATE_MANAGER.render_template(configs.templates.retrieved_display_template, {"docs": docs[::-1]})
325
-
326
362
  logger.debug(f"Retrieved Documents: \n{rendered}")
327
363
  return await self.aask(
328
364
  question,
@@ -10,6 +10,7 @@ from fabricatio.journal import logger
10
10
  from fabricatio.models.generic import WithBriefing
11
11
  from fabricatio.models.kwargs_types import ValidateKwargs
12
12
  from fabricatio.models.usages import LLMUsage
13
+ from fabricatio.models.utils import override_kwargs
13
14
  from fabricatio.parser import JsonCapture
14
15
  from more_itertools import flatten, windowed
15
16
  from pydantic import NonNegativeInt, PositiveInt
@@ -126,13 +127,13 @@ class GiveRating(WithBriefing, LLMUsage):
126
127
  return await self.rate_fine_grind(to_rate, manual, score_range, **kwargs)
127
128
 
128
129
  async def draft_rating_manual(
129
- self, topic: str, criteria: Set[str], **kwargs: Unpack[ValidateKwargs[Dict[str, str]]]
130
+ self, topic: str, criteria: Optional[Set[str]] = None, **kwargs: Unpack[ValidateKwargs[Dict[str, str]]]
130
131
  ) -> Optional[Dict[str, str]]:
131
132
  """Drafts a rating manual based on a topic and dimensions.
132
133
 
133
134
  Args:
134
135
  topic (str): The topic for the rating manual.
135
- criteria (Set[str]): A set of dimensions for the rating manual.
136
+ criteria (Optional[Set[str]], optional): A set of criteria for the rating manual. If not specified, then this method will draft the criteria automatically.
136
137
  **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
137
138
 
138
139
  Returns:
@@ -148,6 +149,14 @@ class GiveRating(WithBriefing, LLMUsage):
148
149
  return json_data
149
150
  return None
150
151
 
152
+ criteria = criteria or await self.draft_rating_criteria(
153
+ topic, **self.prepend_sys_msg(override_kwargs(dict(kwargs), default=None))
154
+ )
155
+
156
+ if criteria is None:
157
+ logger.error(f"Failed to draft rating criteria for topic {topic}")
158
+ return None
159
+
151
160
  return await self.aask_validate(
152
161
  question=(
153
162
  TEMPLATE_MANAGER.render_template(
@@ -159,7 +168,7 @@ class GiveRating(WithBriefing, LLMUsage):
159
168
  )
160
169
  ),
161
170
  validator=_validator,
162
- **self.prepend(kwargs),
171
+ **self.prepend_sys_msg(kwargs),
163
172
  )
164
173
 
165
174
  async def draft_rating_criteria(
@@ -191,7 +200,7 @@ class GiveRating(WithBriefing, LLMUsage):
191
200
  validator=lambda resp: set(out)
192
201
  if (out := JsonCapture.validate_with(resp, list, str, criteria_count)) is not None
193
202
  else out,
194
- **self.prepend(kwargs),
203
+ **self.prepend_sys_msg(kwargs),
195
204
  )
196
205
 
197
206
  async def draft_rating_criteria_from_examples(
@@ -244,7 +253,7 @@ class GiveRating(WithBriefing, LLMUsage):
244
253
  validator=lambda resp: JsonCapture.validate_with(
245
254
  resp, target_type=list, elements_type=str, length=reasons_count
246
255
  ),
247
- **self.prepend(kwargs),
256
+ **self.prepend_sys_msg(kwargs),
248
257
  )
249
258
  )
250
259
  # extract certain mount of criteria from reasons according to their importance and frequency
@@ -301,7 +310,7 @@ class GiveRating(WithBriefing, LLMUsage):
301
310
  for pair in windows
302
311
  ],
303
312
  validator=lambda resp: JsonCapture.validate_with(resp, target_type=float),
304
- **self.prepend(kwargs),
313
+ **self.prepend_sys_msg(kwargs),
305
314
  )
306
315
  weights = [1]
307
316
  for rw in relative_weights:
@@ -1,6 +1,6 @@
1
1
  """A module that provides functionality to rate tasks based on a rating manual and score range."""
2
2
 
3
- from typing import List, Optional, Self, Set, Unpack, cast
3
+ from typing import Dict, List, Optional, Self, Set, Unpack, cast
4
4
 
5
5
  from fabricatio._rust_instances import TEMPLATE_MANAGER
6
6
  from fabricatio.capabilities.propose import Propose
@@ -200,13 +200,14 @@ class Review(GiveRating, Propose):
200
200
  ReviewResult[Task[T]]: A review result containing identified problems and proposed solutions,
201
201
  with a reference to the original task.
202
202
  """
203
- return cast('ReviewResult[Task[T]]', await self.review_obj(task, **kwargs))
203
+ return cast("ReviewResult[Task[T]]", await self.review_obj(task, **kwargs))
204
204
 
205
205
  async def review_string(
206
206
  self,
207
207
  input_text: str,
208
208
  topic: str,
209
209
  criteria: Optional[Set[str]] = None,
210
+ rating_manual: Optional[Dict[str, str]] = None,
210
211
  **kwargs: Unpack[ValidateKwargs[ReviewResult[str]]],
211
212
  ) -> ReviewResult[str]:
212
213
  """Review a string based on specified topic and criteria.
@@ -219,6 +220,7 @@ class Review(GiveRating, Propose):
219
220
  topic (str): The subject topic for the review criteria.
220
221
  criteria (Optional[Set[str]], optional): A set of criteria for the review.
221
222
  If not provided, criteria will be drafted automatically. Defaults to None.
223
+ rating_manual (Optional[Dict[str,str]], optional): A dictionary of rating criteria and their corresponding scores.
222
224
  **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
223
225
 
224
226
  Returns:
@@ -227,12 +229,13 @@ class Review(GiveRating, Propose):
227
229
  """
228
230
  default = None
229
231
  if "default" in kwargs:
232
+ # this `default` is the default for the `propose` method
230
233
  default = kwargs.pop("default")
231
234
 
232
235
  criteria = criteria or (await self.draft_rating_criteria(topic, **kwargs))
233
236
  if not criteria:
234
237
  raise ValueError("No criteria provided for review.")
235
- manual = await self.draft_rating_manual(topic, criteria, **kwargs)
238
+ manual = rating_manual or await self.draft_rating_manual(topic, criteria, **kwargs)
236
239
 
237
240
  if default is not None:
238
241
  kwargs["default"] = default
@@ -37,7 +37,7 @@ class ProposeTask(WithBriefing, Propose):
37
37
  logger.error(err := f"{self.name}: Prompt must be provided.")
38
38
  raise ValueError(err)
39
39
 
40
- return await self.propose(Task, prompt, **self.prepend(cast('Dict[str, Any]', kwargs)))
40
+ return await self.propose(Task, prompt, **self.prepend_sys_msg(cast("Dict[str, Any]", kwargs)))
41
41
 
42
42
 
43
43
  class HandleTask(WithBriefing, ToolBoxUsage):
@@ -81,7 +81,7 @@ class HandleTask(WithBriefing, ToolBoxUsage):
81
81
  return await self.aask_validate(
82
82
  question=q,
83
83
  validator=_validator,
84
- **self.prepend(cast('Dict[str, Any]', kwargs)),
84
+ **self.prepend_sys_msg(cast("Dict[str, Any]", kwargs)),
85
85
  )
86
86
 
87
87
  async def handle_fine_grind(
fabricatio/config.py CHANGED
@@ -235,6 +235,9 @@ class TemplateConfig(BaseModel):
235
235
  co_validation_template: str = Field(default="co_validation")
236
236
  """The name of the co-validation template which will be used to co-validate a string."""
237
237
 
238
+ as_prompt_template: str = Field(default="as_prompt")
239
+ """The name of the as prompt template which will be used to convert a string to a prompt."""
240
+
238
241
 
239
242
  class MagikaConfig(BaseModel):
240
243
  """Magika configuration class."""
fabricatio/decorators.py CHANGED
@@ -177,3 +177,35 @@ def use_temp_module[**P, R](modules: ModuleType | List[ModuleType]) -> Callable[
177
177
  return _wrapper
178
178
 
179
179
  return _decorator
180
+
181
+
182
+ def logging_exec_time[**P, R](func: Callable[P, R]) -> Callable[P, R]:
183
+ """Decorator to log the execution time of a function.
184
+
185
+ Args:
186
+ func (Callable): The function to be executed
187
+
188
+ Returns:
189
+ Callable: A decorator that wraps the function to log the execution time.
190
+ """
191
+ from time import time
192
+
193
+ if iscoroutinefunction(func):
194
+
195
+ @wraps(func)
196
+ async def _async_wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
197
+ start_time = time()
198
+ result = await func(*args, **kwargs)
199
+ logger.debug(f"Execution time of `{func.__name__}`: {time() - start_time:.2f} s")
200
+ return result
201
+
202
+ return _async_wrapper
203
+
204
+ @wraps(func)
205
+ def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
206
+ start_time = time()
207
+ result = func(*args, **kwargs)
208
+ logger.debug(f"Execution time of {func.__name__}: {(time() - start_time) * 1000:.2f} ms")
209
+ return result
210
+
211
+ return _wrapper
@@ -48,7 +48,7 @@ class Action(HandleTask, ProposeTask, Correct):
48
48
  self.description = self.description or self.__class__.__doc__ or ""
49
49
 
50
50
  @abstractmethod
51
- async def _execute(self,*_, **cxt) -> Any: # noqa: ANN002
51
+ async def _execute(self, *_, **cxt) -> Any: # noqa: ANN002
52
52
  """Execute the action logic with the provided context arguments.
53
53
 
54
54
  This method must be implemented by subclasses to define the actual behavior.
@@ -147,6 +147,8 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
147
147
  Args:
148
148
  task: The task to be processed.
149
149
  """
150
+ logger.info(f"Start execute workflow: {self.name}")
151
+
150
152
  await task.start()
151
153
  await self._init_context(task)
152
154
 
@@ -155,12 +157,11 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
155
157
  # Process each action in sequence
156
158
  for step in self._instances:
157
159
  current_action = step.name
158
- logger.debug(f"Executing step: {current_action}")
160
+ logger.info(f"Executing step: {current_action}")
159
161
 
160
162
  # Get current context and execute action
161
163
  context = await self._context.get()
162
164
  act_task = create_task(step.act(context))
163
-
164
165
  # Handle task cancellation
165
166
  if task.is_cancelled():
166
167
  act_task.cancel(f"Cancelled by task: {task.name}")
@@ -168,9 +169,10 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
168
169
 
169
170
  # Update context with modified values
170
171
  modified_ctx = await act_task
172
+ logger.success(f"Step execution finished: {current_action}")
171
173
  await self._context.put(modified_ctx)
172
174
 
173
- logger.info(f"Finished executing workflow: {self.name}")
175
+ logger.success(f"Workflow execution finished: {self.name}")
174
176
 
175
177
  # Get final context and extract result
176
178
  final_ctx = await self._context.get()
@@ -184,9 +186,9 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
184
186
 
185
187
  await task.finish(result)
186
188
 
187
- except RuntimeError as e:
188
- logger.error(f"Error during task: {current_action} execution: {e}")
189
- logger.error(traceback.format_exc())
189
+ except Exception as e: # noqa: BLE001
190
+ logger.critical(f"Error during task: {current_action} execution: {e}")
191
+ logger.critical(traceback.format_exc())
190
192
  await task.fail()
191
193
 
192
194
  async def _init_context[T](self, task: Task[T]) -> None: