fabricatio 0.2.11.dev3__cp312-cp312-win_amd64.whl → 0.2.12.dev2__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,10 @@ from asyncio import gather
4
4
  from pathlib import Path
5
5
  from typing import List, Optional
6
6
 
7
+ from pydantic import Field, PositiveInt
8
+
7
9
  from fabricatio import BibManager
10
+ from fabricatio.capabilities.advanced_rag import AdvancedRAG
8
11
  from fabricatio.capabilities.censor import Censor
9
12
  from fabricatio.capabilities.extract import Extract
10
13
  from fabricatio.capabilities.rag import RAG
@@ -16,59 +19,63 @@ from fabricatio.models.extra.article_essence import ArticleEssence
16
19
  from fabricatio.models.extra.article_main import Article, ArticleChapter, ArticleSection, ArticleSubsection
17
20
  from fabricatio.models.extra.article_outline import ArticleOutline
18
21
  from fabricatio.models.extra.rule import RuleSet
22
+ from fabricatio.models.kwargs_types import ChooseKwargs, LLMKwargs
19
23
  from fabricatio.utils import ask_retain, ok
20
24
 
25
+ TYPST_CITE_USAGE = (
26
+ "citation number is REQUIRED to cite any reference!,for example in Auther Pattern: 'Doe et al.[[1]], Jack et al.[[2]]' or in Sentence Suffix Sattern: 'Global requirement is incresing[[1]].'\n"
27
+ "Everything is build upon the typst language, which is similar to latex, \n"
28
+ "Legal citing syntax examples(seperated by |): [[1]]|[[1,2]]|[[1-3]]|[[12,13-15]]|[[1-3,5-7]]\n"
29
+ "Illegal citing syntax examples(seperated by |): [[1],[2],[3]]|[[1],[1-2]]\n"
30
+ "Those reference mark shall not be omitted during the extraction\n"
31
+ "It's recommended to cite multiple references that supports your conclusion at a time.\n"
32
+ "Wrap inline expression with '\\(' and '\\)',like '\\(>5m\\)' '\\(89%\\)', and wrap block equation with '\\[' and '\\]'.\n"
33
+ "In addition to that, you can add a label outside the block equation which can be used as a cross reference identifier, the label is a string wrapped in `<` and `>` like `<energy-release-rate-equation>`.Note that the label string should be a summarizing title for the equation being labeled.\n"
34
+ "you can refer to that label by using the syntax with prefix of `@eqt:`, which indicate that this notation is citing a label from the equations. For example ' @eqt:energy-release-rate-equation ' DO remember that the notation shall have both suffixed and prefixed space char which enable the compiler to distinguish the notation from the plaintext."
35
+ "Below is two usage example:\n"
36
+ "```typst\n"
37
+ "See @eqt:mass-energy-equation , it's the foundation of physics.\n"
38
+ "\\[\n"
39
+ "E = m c^2\n"
40
+ "\\] <mass-energy-equation>\n\n\n"
41
+ "In @eqt:mass-energy-equation , \\(m\\) stands for mass, \\(c\\) stands for speed of light, and \\(E\\) stands for energy. \n"
42
+ "```\n"
43
+ )
44
+
21
45
 
22
46
  class WriteArticleContentRAG(Action, RAG, Extract):
23
47
  """Write an article based on the provided outline."""
24
48
 
49
+ search_increment_multiplier: float = 1.6
50
+ """The increment multiplier of the search increment."""
25
51
  ref_limit: int = 35
26
52
  """The limit of references to be retrieved"""
27
- threshold: float = 0.55
53
+ threshold: float = 0.62
28
54
  """The threshold of relevance"""
29
- extractor_model: str
55
+ extractor_model: LLMKwargs
30
56
  """The model to use for extracting the content from the retrieved references."""
31
- query_model: str
57
+ query_model: LLMKwargs
32
58
  """The model to use for querying the database"""
33
59
  supervisor: bool = False
34
60
  """Whether to use supervisor mode"""
35
- req: str = (
36
- "citation number is REQUIRED to cite any reference!\n"
37
- "Everything is build upon the typst language, which is similar to latex, \n"
38
- "Legal citing syntax examples(seperated by |): [[1]]|[[1,2]]|[[1-3]]|[[12,13-15]]|[[1-3,5-7]]\n"
39
- "Illegal citing syntax examples(seperated by |): [[1],[2],[3]]|[[1],[1-2]]\n"
40
- "Those reference mark shall not be omitted during the extraction\n"
41
- "It's recommended to cite multiple references that supports your conclusion at a time.\n"
42
- "Wrapp inline expression using $ $, and wrapp block equation using $$ $$."
43
- "In addition to that, you can add a label outside the block equation which can be used as a cross reference identifier, the label is a string wrapped in `<` and `>`,"
44
- "you can refer to that label by using the syntax with prefix of `@eqt:`"
45
- "Below is a usage example:\n"
46
- "```typst\n"
47
- "See @eqt:mass-energy-equation , it's the foundation of physics.\n"
48
- "$$\n"
49
- "E = m c^2"
50
- "$$\n"
51
- "<mass-energy-equation>\n\n"
52
- "In @eqt:mass-energy-equation , $m$ stands for mass, $c$ stands for speed of light, and $E$ stands for energy. \n"
53
- "```"
54
- )
61
+ result_per_query: PositiveInt = 4
62
+ """The number of results to be returned per query."""
63
+ req: str = TYPST_CITE_USAGE
64
+ """The req of the write article content."""
55
65
 
56
66
  async def _execute(
57
67
  self,
58
68
  article_outline: ArticleOutline,
59
- collection_name: str = "article_chunks",
69
+ collection_name: Optional[str] = None,
60
70
  supervisor: Optional[bool] = None,
61
71
  **cxt,
62
72
  ) -> Article:
63
73
  article = Article.from_outline(article_outline).update_ref(article_outline)
64
-
74
+ self.target_collection = collection_name or self.safe_target_collection
65
75
  if supervisor or (supervisor is None and self.supervisor):
66
- await gather(
67
- *[
68
- self._supervisor_inner(article, article_outline, chap, sec, subsec)
69
- for chap, sec, subsec in article.iter_subsections()
70
- ]
71
- )
76
+ for chap, sec, subsec in article.iter_subsections():
77
+ await self._supervisor_inner(article, article_outline, chap, sec, subsec)
78
+
72
79
  else:
73
80
  await gather(
74
81
  *[
@@ -92,22 +99,29 @@ class WriteArticleContentRAG(Action, RAG, Extract):
92
99
  from questionary import confirm, text
93
100
  from rich import print as r_print
94
101
 
95
- ret = await self.search_database(article, article_outline, chap, sec, subsec)
96
-
97
- cm = CitationManager(article_chunks=await ask_retain([r.chunk for r in ret], ret)).set_cite_number_all()
102
+ cm = CitationManager()
103
+ await self.search_database(article, article_outline, chap, sec, subsec, cm)
98
104
 
99
105
  raw = await self.write_raw(article, article_outline, chap, sec, subsec, cm)
100
106
  r_print(raw)
101
107
 
102
108
  while not await confirm("Accept this version and continue?").ask_async():
103
- if await confirm("Search for more refs?").ask_async():
104
- new_refs = await self.search_database(article, article_outline, chap, sec, subsec, supervisor=True)
105
- cm.add_chunks(await ask_retain([r.chunk for r in new_refs], new_refs))
109
+ if inst := await text("Search for more refs for additional spec.").ask_async():
110
+ await self.search_database(
111
+ article,
112
+ article_outline,
113
+ chap,
114
+ sec,
115
+ subsec,
116
+ cm,
117
+ supervisor=True,
118
+ extra_instruction=inst,
119
+ )
106
120
 
107
- instruction = await text("Enter the instructions to improve").ask_async()
108
- raw = await self.write_raw(article, article_outline, chap, sec, subsec, cm, instruction)
109
- if await confirm("Edit it?").ask_async():
110
- raw = await text("Edit", default=raw).ask_async() or raw
121
+ if instruction := await text("Enter the instructions to improve").ask_async():
122
+ raw = await self.write_raw(article, article_outline, chap, sec, subsec, cm, instruction)
123
+ if edt := await text("Edit", default=raw).ask_async():
124
+ raw = edt
111
125
 
112
126
  r_print(raw)
113
127
 
@@ -121,11 +135,14 @@ class WriteArticleContentRAG(Action, RAG, Extract):
121
135
  sec: ArticleSection,
122
136
  subsec: ArticleSubsection,
123
137
  ) -> ArticleSubsection:
124
- ret = await self.search_database(article, article_outline, chap, sec, subsec)
125
- cm = CitationManager(article_chunks=ret).set_cite_number_all()
138
+ cm = CitationManager()
139
+
140
+ await self.search_database(article, article_outline, chap, sec, subsec, cm)
126
141
 
127
142
  raw_paras = await self.write_raw(article, article_outline, chap, sec, subsec, cm)
128
143
 
144
+ raw_paras = "\n".join(p for p in raw_paras.splitlines() if p and not p.endswith("**") and not p.startswith("#"))
145
+
129
146
  return await self.extract_new_subsec(subsec, raw_paras, cm)
130
147
 
131
148
  async def extract_new_subsec(
@@ -139,11 +156,12 @@ class WriteArticleContentRAG(Action, RAG, Extract):
139
156
  f"Above is the subsection titled `{subsec.title}`.\n"
140
157
  f"I need you to extract the content to update my subsection obj provided below.\n{self.req}"
141
158
  f"{subsec.display()}\n",
159
+ **self.extractor_model,
142
160
  ),
143
161
  "Failed to propose new subsection.",
144
162
  )
145
163
  for p in new_subsec.paragraphs:
146
- p.content = cm.apply(p.content).replace("$$", "\n$$\n")
164
+ p.content = cm.apply(p.content)
147
165
  subsec.update_from(new_subsec)
148
166
  logger.debug(f"{subsec.title}:rpl\n{subsec.display()}")
149
167
  return subsec
@@ -159,23 +177,16 @@ class WriteArticleContentRAG(Action, RAG, Extract):
159
177
  extra_instruction: str = "",
160
178
  ) -> str:
161
179
  """Write the raw paragraphs of the subsec."""
162
- return (
163
- (
164
- await self.aask(
165
- f"{cm.as_prompt()}\nAbove is some related reference retrieved for you."
166
- f"{article_outline.finalized_dump()}\n\nAbove is my article outline, I m writing graduate thesis titled `{article.title}`. "
167
- f"More specifically, i m witting the Chapter `{chap.title}` >> Section `{sec.title}` >> Subsection `{subsec.title}`.\n"
168
- f"Please help me write the paragraphs of the subsec mentioned above, which is `{subsec.title}`.\n"
169
- f"{self.req}\n"
170
- f"You SHALL use `{article.language}` as writing language.\n{extra_instruction}"
171
- )
172
- )
173
- .replace(r" \( ", "$")
174
- .replace(r" \) ", "$")
175
- .replace(r"\(", "$")
176
- .replace(r"\)", "$")
177
- .replace("\\[\n", "$$\n")
178
- .replace("\n\\]", "\n$$")
180
+ return await self.aask(
181
+ f"{cm.as_prompt()}\nAbove is some related reference from other auther retrieved for you."
182
+ f"{article_outline.finalized_dump()}\n\nAbove is my article outline, I m writing graduate thesis titled `{article.title}`. "
183
+ f"More specifically, i m witting the Chapter `{chap.title}` >> Section `{sec.title}` >> Subsection `{subsec.title}`.\n"
184
+ f"Please help me write the paragraphs of the subsec mentioned above, which is `{subsec.title}`.\n"
185
+ f"{self.req}\n"
186
+ f"You SHALL use `{article.language}` as writing language.\n{extra_instruction}\n"
187
+ f"Do not use numbered list to display the outcome, you should regard you are writing the main text of the thesis.\n"
188
+ f"You should not copy others' works from the references directly on to my thesis, we can only harness the conclusion they have drawn.\n"
189
+ f"No extra explanation is allowed."
179
190
  )
180
191
 
181
192
  async def search_database(
@@ -185,28 +196,117 @@ class WriteArticleContentRAG(Action, RAG, Extract):
185
196
  chap: ArticleChapter,
186
197
  sec: ArticleSection,
187
198
  subsec: ArticleSubsection,
199
+ cm: CitationManager,
188
200
  extra_instruction: str = "",
189
201
  supervisor: bool = False,
190
- ) -> List[ArticleChunk]:
202
+ ) -> None:
191
203
  """Search database for related references."""
204
+ search_req = (
205
+ f"{article_outline.finalized_dump()}\n\nAbove is my article outline, I m writing graduate thesis titled `{article.title}`. "
206
+ f"More specifically, i m witting the Chapter `{chap.title}` >> Section `{sec.title}` >> Subsection `{subsec.title}`.\n"
207
+ f"I need to search related references to build up the content of the subsec mentioned above, which is `{subsec.title}`.\n"
208
+ f"provide 10~16 queries as possible, to get best result!\n"
209
+ f"You should provide both English version and chinese version of the refined queries!\n{extra_instruction}\n"
210
+ )
211
+
192
212
  ref_q = ok(
193
213
  await self.arefined_query(
194
- f"{article_outline.finalized_dump()}\n\nAbove is my article outline, I m writing graduate thesis titled `{article.title}`. "
195
- f"More specifically, i m witting the Chapter `{chap.title}` >> Section `{sec.title}` >> Subsection `{subsec.title}`.\n"
196
- f"I need to search related references to build up the content of the subsec mentioned above, which is `{subsec.title}`.\n"
197
- f"provide 10~16 queries as possible, to get best result!\n"
198
- f"You should provide both English version and chinese version of the refined queries!\n{extra_instruction}\n",
199
- model=self.query_model,
214
+ search_req,
215
+ **self.query_model,
200
216
  ),
201
217
  "Failed to refine query.",
202
218
  )
203
219
 
220
+ if supervisor:
221
+ ref_q = await ask_retain(ref_q)
222
+ ret = await self.aretrieve(
223
+ ref_q,
224
+ ArticleChunk,
225
+ max_accepted=self.ref_limit,
226
+ result_per_query=self.result_per_query,
227
+ similarity_threshold=self.threshold,
228
+ )
229
+
230
+ cm.add_chunks(ok(ret))
231
+ ref_q = await self.arefined_query(
232
+ f"{cm.as_prompt()}\n\nAbove is the retrieved references in the first RAG, now we need to perform the second RAG.\n\n{search_req}",
233
+ **self.query_model,
234
+ )
235
+
236
+ if ref_q is None:
237
+ logger.warning("Second refine query is None, skipping.")
238
+ return
204
239
  if supervisor:
205
240
  ref_q = await ask_retain(ref_q)
206
241
 
207
- return await self.aretrieve(
208
- ref_q, ArticleChunk, final_limit=self.ref_limit, result_per_query=3, similarity_threshold=self.threshold
242
+ ret = await self.aretrieve(
243
+ ref_q,
244
+ ArticleChunk,
245
+ max_accepted=int(self.ref_limit * self.search_increment_multiplier),
246
+ result_per_query=int(self.result_per_query * self.search_increment_multiplier),
247
+ similarity_threshold=self.threshold,
209
248
  )
249
+ if ret is None:
250
+ logger.warning("Second retrieve is None, skipping.")
251
+ return
252
+ cm.add_chunks(ret)
253
+
254
+
255
+ class ArticleConsultRAG(Action, AdvancedRAG):
256
+ """Write an article based on the provided outline."""
257
+
258
+ output_key: str = "consult_count"
259
+ search_increment_multiplier: float = 1.6
260
+ """The multiplier to increase the limit of references to retrieve per query."""
261
+ ref_limit: int = 20
262
+ """The final limit of references."""
263
+ ref_per_q: int = 3
264
+ """The limit of references to retrieve per query."""
265
+ similarity_threshold: float = 0.62
266
+ """The similarity threshold of references to retrieve."""
267
+ ref_q_model: ChooseKwargs = Field(default_factory=ChooseKwargs)
268
+ """The model to use for refining query."""
269
+ req: str = TYPST_CITE_USAGE
270
+ """The request for the rag model."""
271
+
272
+ @precheck_package(
273
+ "questionary", "`questionary` is required for supervisor mode, please install it by `fabricatio[qa]`"
274
+ )
275
+ async def _execute(self, collection_name: Optional[str] = None, **cxt) -> int:
276
+ from questionary import confirm, text
277
+ from rich import print as r_print
278
+
279
+ from fabricatio.rust import convert_all_block_tex, convert_all_inline_tex, fix_misplaced_labels
280
+
281
+ self.target_collection = collection_name or self.safe_target_collection
282
+
283
+ cm = CitationManager()
284
+
285
+ counter = 0
286
+ while (req := await text("User: ").ask_async()) is not None:
287
+ if await confirm("Empty the cm?").ask_async():
288
+ cm.empty()
289
+ await self.clued_search(
290
+ req,
291
+ cm,
292
+ refinery_kwargs=self.ref_q_model,
293
+ expand_multiplier=self.search_increment_multiplier,
294
+ base_accepted=self.ref_limit,
295
+ result_per_query=self.ref_per_q,
296
+ similarity_threshold=self.similarity_threshold,
297
+ )
298
+
299
+ ret = await self.aask(f"{cm.as_prompt()}\n{self.req}\n{req}")
300
+
301
+ ret = fix_misplaced_labels(ret)
302
+ ret = convert_all_inline_tex(ret)
303
+ ret = convert_all_block_tex(ret)
304
+ ret = cm.apply(ret)
305
+
306
+ r_print(ret)
307
+ counter += 1
308
+ logger.info(f"{counter} rounds of conversation.")
309
+ return counter
210
310
 
211
311
 
212
312
  class TweakArticleRAG(Action, RAG, Censor):
@@ -290,7 +390,7 @@ class TweakArticleRAG(Action, RAG, Censor):
290
390
  await self.censor_obj_inplace(
291
391
  subsec,
292
392
  ruleset=ruleset,
293
- reference=f"{'\n\n'.join(d.display() for d in await self.aretrieve(refind_q, document_model=ArticleEssence, final_limit=self.ref_limit))}\n\n"
393
+ reference=f"{'\n\n'.join(d.display() for d in await self.aretrieve(refind_q, document_model=ArticleEssence, max_accepted=self.ref_limit))}\n\n"
294
394
  f"You can use Reference above to rewrite the `{subsec.__class__.__name__}`.\n"
295
395
  f"You should Always use `{subsec.language}` as written language, "
296
396
  f"which is the original language of the `{subsec.title}`. "
@@ -3,14 +3,17 @@
3
3
  from pathlib import Path
4
4
  from typing import Any, Iterable, List, Mapping, Optional, Type
5
5
 
6
+ from fabricatio import TEMPLATE_MANAGER
7
+ from fabricatio.fs import dump_text
6
8
  from fabricatio.journal import logger
7
9
  from fabricatio.models.action import Action
8
10
  from fabricatio.models.generic import FinalizedDumpAble, FromMapping, PersistentAble
9
11
  from fabricatio.models.task import Task
12
+ from fabricatio.models.usages import LLMUsage
10
13
  from fabricatio.utils import ok
11
14
 
12
15
 
13
- class DumpFinalizedOutput(Action):
16
+ class DumpFinalizedOutput(Action, LLMUsage):
14
17
  """Dump the finalized output to a file."""
15
18
 
16
19
  output_key: str = "dump_path"
@@ -38,7 +41,45 @@ class DumpFinalizedOutput(Action):
38
41
  return dump_path.as_posix()
39
42
 
40
43
 
41
- class PersistentAll(Action):
44
+ class RenderedDump(Action, LLMUsage):
45
+ """Render the data to a file."""
46
+
47
+ output_key: str = "dump_path"
48
+ dump_path: Optional[str] = None
49
+
50
+ template_name: str
51
+ """The template name to render the data."""
52
+
53
+ async def _execute(
54
+ self,
55
+ to_dump: FinalizedDumpAble,
56
+ task_input: Optional[Task] = None,
57
+ dump_path: Optional[str | Path] = None,
58
+ **_,
59
+ ) -> str:
60
+ dump_path = Path(
61
+ dump_path
62
+ or self.dump_path
63
+ or ok(
64
+ await self.awhich_pathstr(
65
+ 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."
66
+ ),
67
+ "Could not find the path of file to dump the data.",
68
+ )
69
+ )
70
+
71
+ logger.info(f"Saving output to {dump_path.as_posix()}")
72
+ dump_text(
73
+ dump_path,
74
+ TEMPLATE_MANAGER.render_template(
75
+ self.template_name, {to_dump.__class__.__name__: to_dump.finalized_dump()}
76
+ ),
77
+ )
78
+
79
+ return dump_path.as_posix()
80
+
81
+
82
+ class PersistentAll(Action, LLMUsage):
42
83
  """Persist all the data to a file."""
43
84
 
44
85
  output_key: str = "persistent_count"
@@ -0,0 +1,53 @@
1
+ """Advanced RAG (Retrieval Augmented Generation) model."""
2
+
3
+ from typing import Optional, Unpack
4
+
5
+ from fabricatio.capabilities.rag import RAG
6
+ from fabricatio.journal import logger
7
+ from fabricatio.models.adv_kwargs_types import FetchKwargs
8
+ from fabricatio.models.extra.aricle_rag import ArticleChunk, CitationManager
9
+ from fabricatio.models.kwargs_types import ChooseKwargs
10
+
11
+
12
+ class AdvancedRAG(RAG):
13
+ """A class representing the Advanced RAG (Retrieval Augmented Generation) model."""
14
+ async def clued_search(
15
+ self,
16
+ requirement: str,
17
+ cm: CitationManager,
18
+ max_capacity=40,
19
+ max_round: int = 3,
20
+ expand_multiplier: float = 1.4,
21
+ base_accepted: int = 12,
22
+ refinery_kwargs: Optional[ChooseKwargs] = None,
23
+ **kwargs: Unpack[FetchKwargs],
24
+ ) -> CitationManager:
25
+ """Asynchronously performs a clued search based on a given requirement and citation manager."""
26
+ if max_round < 2:
27
+ logger.warning(
28
+ "max_round should be greater than 1, otherwise it behaves nothing different from the `self.aretrieve`"
29
+ )
30
+
31
+ refinery_kwargs = refinery_kwargs or {}
32
+
33
+ for i in range(max_round + 1, 1):
34
+ logger.info(f"Round [{i + 1}/{max_round}] search started.")
35
+ ref_q = await self.arefined_query(
36
+ f"{cm.as_prompt()}\n\nAbove is the retrieved references in the {i - 1}th RAG, now we need to perform the {i}th RAG."
37
+ f"\n\n{requirement}",
38
+ **refinery_kwargs,
39
+ )
40
+ if ref_q is None:
41
+ logger.error(f"At round [{i + 1}/{max_round}] search, failed to refine the query, exit.")
42
+ return cm
43
+ refs = await self.aretrieve(ref_q, ArticleChunk, base_accepted, **kwargs)
44
+
45
+ if (max_capacity := max_capacity - len(refs)) < 0:
46
+ cm.add_chunks(refs[0:max_capacity])
47
+ logger.debug(f"At round [{i + 1}/{max_round}] search, the capacity is not enough, exit.")
48
+ return cm
49
+
50
+ cm.add_chunks(refs)
51
+ base_accepted = int(base_accepted * expand_multiplier)
52
+ logger.debug(f"Exceeded max_round: {max_round}, exit.")
53
+ return cm
@@ -189,7 +189,7 @@ class RAG(EmbeddingUsage):
189
189
  self,
190
190
  query: List[str] | str,
191
191
  document_model: Type[D],
192
- final_limit: int = 20,
192
+ max_accepted: int = 20,
193
193
  **kwargs: Unpack[FetchKwargs],
194
194
  ) -> List[D]:
195
195
  """Retrieve data from the collection.
@@ -197,7 +197,7 @@ class RAG(EmbeddingUsage):
197
197
  Args:
198
198
  query (List[str] | str): The query to be used for retrieval.
199
199
  document_model (Type[D]): The model class used to convert retrieved data into document objects.
200
- final_limit (int): The final limit on the number of results to return.
200
+ max_accepted (int): The final limit on the number of results to return.
201
201
  **kwargs (Unpack[FetchKwargs]): Additional keyword arguments for retrieval.
202
202
 
203
203
  Returns:
@@ -211,9 +211,9 @@ class RAG(EmbeddingUsage):
211
211
  document_model=document_model,
212
212
  **kwargs,
213
213
  )
214
- )[:final_limit]
214
+ )[:max_accepted]
215
215
 
216
- async def arefined_query(self, question: List[str] | str, **kwargs: Unpack[ChooseKwargs]) -> Optional[List[str]]:
216
+ async def arefined_query(self, question: List[str] | str, **kwargs: Unpack[ChooseKwargs[Optional[List[str]]]]) -> Optional[List[str]]:
217
217
  """Refines the given question using a template.
218
218
 
219
219
  Args:
fabricatio/config.py CHANGED
@@ -328,9 +328,9 @@ class RoutingConfig(BaseModel):
328
328
  allowed_fails: Optional[int] = 3
329
329
  """The number of allowed fails before the routing is considered failed."""
330
330
  retry_after: int = 15
331
- """The time in seconds to wait before retrying the routing after a fail."""
332
- cooldown_time: Optional[int] = 30
333
- """The time in seconds to wait before retrying the routing after a cooldown."""
331
+ """Minimum time to wait before retrying a failed request."""
332
+ cooldown_time: Optional[int] = 60
333
+ """Time to cooldown a deployment after failure in seconds."""
334
334
 
335
335
 
336
336
  class Settings(BaseSettings):
fabricatio/fs/curd.py CHANGED
@@ -20,7 +20,7 @@ def dump_text(path: Union[str, Path], text: str) -> None:
20
20
  Returns:
21
21
  None
22
22
  """
23
- Path(path).write_text(text, encoding="utf-8", errors="ignore")
23
+ Path(path).write_text(text, encoding="utf-8", errors="ignore", newline="\n")
24
24
 
25
25
 
26
26
  def copy_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
@@ -12,12 +12,12 @@ Classes:
12
12
  import traceback
13
13
  from abc import abstractmethod
14
14
  from asyncio import Queue, create_task
15
- from typing import Any, Dict, Self, Tuple, Type, Union, final
15
+ from typing import Any, Dict, Self, Sequence, Tuple, Type, Union, final
16
16
 
17
17
  from fabricatio.journal import logger
18
18
  from fabricatio.models.generic import WithBriefing
19
19
  from fabricatio.models.task import Task
20
- from fabricatio.models.usages import LLMUsage, ToolBoxUsage
20
+ from fabricatio.models.usages import ToolBoxUsage
21
21
  from fabricatio.utils import override_kwargs
22
22
  from pydantic import Field, PrivateAttr
23
23
 
@@ -26,7 +26,7 @@ OUTPUT_KEY = "task_output"
26
26
  INPUT_KEY = "task_input"
27
27
 
28
28
 
29
- class Action(WithBriefing, LLMUsage):
29
+ class Action(WithBriefing):
30
30
  """Class that represents an action to be executed in a workflow.
31
31
 
32
32
  Actions are the atomic units of work in a workflow. Each action performs
@@ -96,9 +96,9 @@ class Action(WithBriefing, LLMUsage):
96
96
  return f"## Your personality: \n{self.personality}\n# The action you are going to perform: \n{super().briefing}"
97
97
  return f"# The action you are going to perform: \n{super().briefing}"
98
98
 
99
- def to_task_output(self) -> Self:
99
+ def to_task_output(self, task_output_key: str = OUTPUT_KEY) -> Self:
100
100
  """Set the output key to OUTPUT_KEY and return the action instance."""
101
- self.output_key = OUTPUT_KEY
101
+ self.output_key = task_output_key
102
102
  return self
103
103
 
104
104
 
@@ -123,9 +123,7 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
123
123
  _instances: Tuple[Action, ...] = PrivateAttr(default_factory=tuple)
124
124
  """Instantiated action objects to be executed in this workflow."""
125
125
 
126
- steps: Tuple[Union[Type[Action], Action], ...] = Field(
127
- frozen=True,
128
- )
126
+ steps: Sequence[Union[Type[Action], Action]] = Field(frozen=True)
129
127
  """The sequence of actions to be executed, can be action classes or instances."""
130
128
 
131
129
  task_input_key: str = Field(default=INPUT_KEY)
@@ -9,7 +9,7 @@ from fabricatio.journal import logger
9
9
  from fabricatio.models.extra.rag import MilvusDataBase
10
10
  from fabricatio.models.generic import AsPrompt
11
11
  from fabricatio.models.kwargs_types import ChunkKwargs
12
- from fabricatio.rust import BibManager, is_chinese, split_into_chunks
12
+ from fabricatio.rust import BibManager, blake3_hash, split_into_chunks
13
13
  from fabricatio.utils import ok
14
14
  from more_itertools.recipes import flatten, unique
15
15
  from pydantic import Field
@@ -53,7 +53,7 @@ class ArticleChunk(MilvusDataBase, AsPrompt):
53
53
 
54
54
  def _as_prompt_inner(self) -> Dict[str, str]:
55
55
  return {
56
- f"[[{ok(self._cite_number, 'You need to update cite number first.')}]] reference `{self.article_title}`": self.chunk
56
+ f"[[{ok(self._cite_number, 'You need to update cite number first.')}]] reference `{self.article_title}` from {self.as_auther_seq()}": self.chunk
57
57
  }
58
58
 
59
59
  @property
@@ -139,15 +139,9 @@ class ArticleChunk(MilvusDataBase, AsPrompt):
139
139
  return re.sub(r"\[[\d\s,\\~–-]+]", "", string)
140
140
 
141
141
  @property
142
- def auther_firstnames(self) -> List[str]:
143
- """Get the first name of the authors."""
144
- ret = []
145
- for n in self.authors:
146
- if is_chinese(n):
147
- ret.append(n[0])
148
- else:
149
- ret.append(n.split()[-1])
150
- return ret
142
+ def auther_lastnames(self) -> List[str]:
143
+ """Get the last name of the authors."""
144
+ return [n.split()[-1] for n in self.authors]
151
145
 
152
146
  def as_auther_seq(self) -> str:
153
147
  """Get the auther sequence."""
@@ -155,13 +149,13 @@ class ArticleChunk(MilvusDataBase, AsPrompt):
155
149
  case 0:
156
150
  raise ValueError("No authors found")
157
151
  case 1:
158
- return f"({self.auther_firstnames[0]},{self.year}){self.as_typst_cite()}"
152
+ return f"({self.auther_lastnames[0]},{self.year}){self.as_typst_cite()}"
159
153
  case 2:
160
- return f"({self.auther_firstnames[0]}{self.and_word}{self.auther_firstnames[1]},{self.year}){self.as_typst_cite()}"
154
+ return f"({self.auther_lastnames[0]}{self.and_word}{self.auther_lastnames[1]},{self.year}){self.as_typst_cite()}"
161
155
  case 3:
162
- return f"({self.auther_firstnames[0]},{self.auther_firstnames[1]}{self.and_word}{self.auther_firstnames[2]},{self.year}){self.as_typst_cite()}"
156
+ return f"({self.auther_lastnames[0]},{self.auther_lastnames[1]}{self.and_word}{self.auther_lastnames[2]},{self.year}){self.as_typst_cite()}"
163
157
  case _:
164
- return f"({self.auther_firstnames[0]},{self.auther_firstnames[1]}{self.and_word}{self.auther_firstnames[2]}{self.etc_word},{self.year}){self.as_typst_cite()}"
158
+ return f"({self.auther_lastnames[0]},{self.auther_lastnames[1]}{self.and_word}{self.auther_lastnames[2]}{self.etc_word},{self.year}){self.as_typst_cite()}"
165
159
 
166
160
  def update_cite_number(self, cite_number: int) -> Self:
167
161
  """Update the cite number."""
@@ -182,20 +176,32 @@ class CitationManager(AsPrompt):
182
176
  abbr_sep: str = "-"
183
177
  """Separator for abbreviated citation numbers."""
184
178
 
185
- def update_chunks(self, article_chunks: List[ArticleChunk], set_cite_number: bool = True) -> Self:
179
+ def update_chunks(
180
+ self, article_chunks: List[ArticleChunk], set_cite_number: bool = True, dedup: bool = True
181
+ ) -> Self:
186
182
  """Update article chunks."""
187
183
  self.article_chunks.clear()
188
184
  self.article_chunks.extend(article_chunks)
185
+ if dedup:
186
+ self.article_chunks = list(unique(self.article_chunks, lambda c: blake3_hash(c.chunk.encode())))
189
187
  if set_cite_number:
190
188
  self.set_cite_number_all()
191
189
  return self
192
190
 
193
- def add_chunks(self, article_chunks: List[ArticleChunk], set_cite_number: bool = True)-> Self:
191
+ def empty(self) -> Self:
192
+ """Empty the article chunks."""
193
+ self.article_chunks.clear()
194
+ return self
195
+
196
+ def add_chunks(self, article_chunks: List[ArticleChunk], set_cite_number: bool = True, dedup: bool = True) -> Self:
194
197
  """Add article chunks."""
195
198
  self.article_chunks.extend(article_chunks)
199
+ if dedup:
200
+ self.article_chunks = list(unique(self.article_chunks, lambda c: blake3_hash(c.chunk.encode())))
196
201
  if set_cite_number:
197
202
  self.set_cite_number_all()
198
203
  return self
204
+
199
205
  def set_cite_number_all(self) -> Self:
200
206
  """Set citation numbers for all article chunks."""
201
207
  for i, a in enumerate(self.article_chunks, 1):
@@ -208,7 +214,7 @@ class CitationManager(AsPrompt):
208
214
 
209
215
  def apply(self, string: str) -> str:
210
216
  """Apply citation replacements to the input string."""
211
- for origin,m in re.findall(self.pat, string):
217
+ for origin, m in re.findall(self.pat, string):
212
218
  logger.info(f"Matching citation: {m}")
213
219
  notations = self.convert_to_numeric_notations(m)
214
220
  logger.info(f"Citing Notations: {notations}")
@@ -216,9 +222,26 @@ class CitationManager(AsPrompt):
216
222
  logger.info(f"Citation Number Sequence: {citation_number_seq}")
217
223
  dedup = self.deduplicate_citation(citation_number_seq)
218
224
  logger.info(f"Deduplicated Citation Number Sequence: {dedup}")
219
- string=string.replace(origin, self.unpack_cite_seq(dedup))
225
+ string = string.replace(origin, self.unpack_cite_seq(dedup))
220
226
  return string
221
227
 
228
+ def citation_count(self, string: str) -> int:
229
+ """Get the citation count in the string."""
230
+ count = 0
231
+ for _, m in re.findall(self.pat, string):
232
+ logger.info(f"Matching citation: {m}")
233
+ notations = self.convert_to_numeric_notations(m)
234
+ logger.info(f"Citing Notations: {notations}")
235
+ citation_number_seq = list(flatten(self.decode_expr(n) for n in notations))
236
+ logger.info(f"Citation Number Sequence: {citation_number_seq}")
237
+ count += len(dedup := self.deduplicate_citation(citation_number_seq))
238
+ logger.info(f"Deduplicated Citation Number Sequence: {dedup}")
239
+ return count
240
+
241
+ def citation_coverage(self, string: str) -> float:
242
+ """Get the citation coverage in the string."""
243
+ return self.citation_count(string) / len(self.article_chunks)
244
+
222
245
  def decode_expr(self, string: str) -> List[int]:
223
246
  """Decode citation expression into a list of integers."""
224
247
  if self.abbr_sep in string:
@@ -46,6 +46,16 @@ class ArticleMetaData(SketchedAble, Described, WordCount, Titled, Language):
46
46
  aims: List[str]
47
47
  """List of writing aims of the research component in academic style."""
48
48
 
49
+ @property
50
+ def typst_metadata_comment(self) -> str:
51
+ """Generates a comment for the metadata of the article component."""
52
+ return comment(
53
+ (f"Desc:\n {self.description}\n" if self.description else "")
54
+ + (f"Aims:\n {'\n '.join(self.aims)}\n" if self.aims else "")
55
+ + (f"Expected Word Count:{self.expected_word_count}" if self.expected_word_count else "")
56
+
57
+ )
58
+
49
59
 
50
60
  class ArticleOutlineBase(
51
61
  ArticleMetaData,
@@ -89,13 +99,7 @@ class SubSectionBase(ArticleOutlineBase):
89
99
 
90
100
  def to_typst_code(self) -> str:
91
101
  """Converts the component into a Typst code snippet for rendering."""
92
- return (
93
- f"=== {self.title}\n"
94
- f"{comment(f'Desc:\n{self.description}\nAims:\n{"\n".join(self.aims)}')}\n"
95
- + f"Expected Word Count:{self.expected_word_count}"
96
- if self.expected_word_count
97
- else ""
98
- )
102
+ return f"=== {self.title}\n{self.typst_metadata_comment}\n"
99
103
 
100
104
  def introspect(self) -> str:
101
105
  """Introspects the article subsection outline."""
@@ -120,13 +124,9 @@ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
120
124
  Returns:
121
125
  str: The formatted Typst code snippet.
122
126
  """
123
- return (
124
- f"== {self.title}\n"
125
- f"{comment(f'Desc:\n{self.description}\nAims:\n{"\n".join(self.aims)}')}\n"
126
- + f"Expected Word Count:{self.expected_word_count}"
127
- if self.expected_word_count
128
- else ""
129
- ) + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
127
+ return f"== {self.title}\n{self.typst_metadata_comment}\n" + "\n\n".join(
128
+ subsec.to_typst_code() for subsec in self.subsections
129
+ )
130
130
 
131
131
  def resolve_update_conflict(self, other: Self) -> str:
132
132
  """Resolve update errors in the article outline."""
@@ -169,13 +169,9 @@ class ChapterBase[T: SectionBase](ArticleOutlineBase):
169
169
 
170
170
  def to_typst_code(self) -> str:
171
171
  """Converts the chapter into a Typst formatted code snippet for rendering."""
172
- return (
173
- f"= {self.title}\n"
174
- f"{comment(f'Desc:\n{self.description}\nAims:\n{"\n".join(self.aims)}')}\n"
175
- + f"Expected Word Count:{self.expected_word_count}"
176
- if self.expected_word_count
177
- else ""
178
- ) + "\n\n".join(sec.to_typst_code() for sec in self.sections)
172
+ return f"= {self.title}\n{self.typst_metadata_comment}\n" + "\n\n".join(
173
+ sec.to_typst_code() for sec in self.sections
174
+ )
179
175
 
180
176
  def resolve_update_conflict(self, other: Self) -> str:
181
177
  """Resolve update errors in the article outline."""
@@ -317,8 +313,41 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, WordCount, Descri
317
313
  === Implementation Details
318
314
  == Evaluation Protocol
319
315
  """
320
- return comment(
321
- f"Title:{self.title}\nDesc:\n{self.description}\n" + f"Word Count:{self.expected_word_count}"
322
- if self.expected_word_count
323
- else ""
324
- ) + "\n\n".join(a.to_typst_code() for a in self.chapters)
316
+ return (
317
+ comment(
318
+ f"Title:{self.title}\n"
319
+ + (f"Desc:\n{self.description}\n" if self.description else "")
320
+ + f"Word Count:{self.expected_word_count}"
321
+ if self.expected_word_count
322
+ else ""
323
+ )
324
+ + "\n\n"
325
+ + "\n\n".join(a.to_typst_code() for a in self.chapters)
326
+ )
327
+
328
+ def avg_chap_wordcount[S](self: S) -> S:
329
+ """Set all chap have same word count sum up to be `self.expected_word_count`."""
330
+ avg = int(self.expected_word_count / len(self.chapters))
331
+ for c in self.chapters:
332
+ c.expected_word_count = avg
333
+ return self
334
+
335
+ def avg_sec_wordcount[S](self: S) -> S:
336
+ """Set all sec have same word count sum up to be `self.expected_word_count`."""
337
+ for c in self.chapters:
338
+ avg = int(c.expected_word_count / len(c.sections))
339
+ for s in c.sections:
340
+ s.expected_word_count = avg
341
+ return self
342
+
343
+ def avg_subsec_wordcount[S](self: S) -> S:
344
+ """Set all subsec have same word count sum up to be `self.expected_word_count`."""
345
+ for _, s in self.iter_sections():
346
+ avg = int(s.expected_word_count / len(s.subsections))
347
+ for ss in s.subsections:
348
+ ss.expected_word_count = avg
349
+ return self
350
+
351
+ def avg_wordcount_recursive[S](self:S) -> S:
352
+ """Set all chap, sec, subsec have same word count sum up to be `self.expected_word_count`."""
353
+ return self.avg_chap_wordcount().avg_sec_wordcount().avg_subsec_wordcount()
@@ -12,10 +12,14 @@ from fabricatio.models.extra.article_base import (
12
12
  SubSectionBase,
13
13
  )
14
14
  from fabricatio.models.extra.article_outline import (
15
+ ArticleChapterOutline,
15
16
  ArticleOutline,
17
+ ArticleSectionOutline,
18
+ ArticleSubsectionOutline,
16
19
  )
17
20
  from fabricatio.models.generic import Described, PersistentAble, SequencePatch, SketchedAble, WithRef, WordCount
18
- from fabricatio.rust import convert_all_block_tex, convert_all_inline_tex, word_count
21
+ from fabricatio.rust import convert_all_block_tex, convert_all_inline_tex, fix_misplaced_labels, word_count
22
+ from fabricatio.utils import fallback_kwargs
19
23
  from pydantic import Field, NonNegativeInt
20
24
 
21
25
  PARAGRAPH_SEP = "// - - -"
@@ -89,7 +93,7 @@ class ArticleSubsection(SubSectionBase):
89
93
  Returns:
90
94
  str: Typst code snippet for rendering.
91
95
  """
92
- return f"=== {self.title}\n" + f"\n{PARAGRAPH_SEP}\n".join(p.content for p in self.paragraphs)
96
+ return super().to_typst_code() + f"\n\n{PARAGRAPH_SEP}\n\n".join(p.content for p in self.paragraphs)
93
97
 
94
98
  @classmethod
95
99
  def from_typst_code(cls, title: str, body: str) -> Self:
@@ -161,6 +165,7 @@ class Article(
161
165
  """Convert tex to typst code."""
162
166
  for _, _, subsec in self.iter_subsections():
163
167
  for p in subsec.paragraphs:
168
+ p.content = fix_misplaced_labels(p.content)
164
169
  p.content = convert_all_inline_tex(p.content)
165
170
  p.content = convert_all_block_tex(p.content)
166
171
  return self
@@ -181,6 +186,50 @@ class Article(
181
186
  def iter_subsections(self) -> Generator[Tuple[ArticleChapter, ArticleSection, ArticleSubsection], None, None]:
182
187
  return super().iter_subsections() # pyright: ignore [reportReturnType]
183
188
 
189
+ def extrac_outline(self) -> ArticleOutline:
190
+ """Extract outline from article."""
191
+ # Create an empty list to hold chapter outlines
192
+ chapters = []
193
+
194
+ # Iterate through each chapter in the article
195
+ for chapter in self.chapters:
196
+ # Create an empty list to hold section outlines
197
+ sections = []
198
+
199
+ # Iterate through each section in the chapter
200
+ for section in chapter.sections:
201
+ # Create an empty list to hold subsection outlines
202
+ subsections = []
203
+
204
+ # Iterate through each subsection in the section
205
+ for subsection in section.subsections:
206
+ # Create a subsection outline and add it to the list
207
+ subsections.append(
208
+ ArticleSubsectionOutline(**subsection.model_dump(exclude={"paragraphs"}, by_alias=True))
209
+ )
210
+
211
+ # Create a section outline and add it to the list
212
+ sections.append(
213
+ ArticleSectionOutline(
214
+ **section.model_dump(exclude={"subsections"}, by_alias=True),
215
+ subsections=subsections,
216
+ )
217
+ )
218
+
219
+ # Create a chapter outline and add it to the list
220
+ chapters.append(
221
+ ArticleChapterOutline(
222
+ **chapter.model_dump(exclude={"sections"}, by_alias=True),
223
+ sections=sections,
224
+ )
225
+ )
226
+
227
+ # Create and return the article outline
228
+ return ArticleOutline(
229
+ **self.model_dump(exclude={"chapters"}, by_alias=True),
230
+ chapters=chapters,
231
+ )
232
+
184
233
  @classmethod
185
234
  def from_outline(cls, outline: ArticleOutline) -> "Article":
186
235
  """Generates an article from the given outline.
@@ -218,15 +267,18 @@ class Article(
218
267
  return article
219
268
 
220
269
  @classmethod
221
- def from_typst_code(cls, title: str, body: str) -> Self:
270
+ def from_typst_code(cls, title: str, body: str, **kwargs) -> Self:
222
271
  """Generates an article from the given Typst code."""
223
272
  return cls(
224
273
  chapters=[
225
274
  ArticleChapter.from_typst_code(*pack) for pack in extract_sections(body, level=1, section_char="=")
226
275
  ],
227
276
  heading=title,
228
- expected_word_count=word_count(body),
229
- abstract="",
277
+ **fallback_kwargs(
278
+ kwargs,
279
+ expected_word_count=word_count(body),
280
+ abstract="",
281
+ ),
230
282
  )
231
283
 
232
284
  @classmethod
@@ -248,3 +300,4 @@ class Article(
248
300
 
249
301
  for a in self.iter_dfs():
250
302
  a.title = await text(f"Edit `{a.title}`.", default=a.title).ask_async() or a.title
303
+ return self
@@ -122,7 +122,7 @@ class FromMapping(Base):
122
122
 
123
123
  @classmethod
124
124
  @abstractmethod
125
- def from_mapping(cls, mapping: Mapping[str, Any], **kwargs: Any) -> List[Self]:
125
+ def from_mapping[S](cls: S, mapping: Mapping[str, Any], **kwargs: Any) -> List[S]:
126
126
  """Generate a list of objects from a mapping."""
127
127
 
128
128
 
@@ -186,7 +186,7 @@ class WithRef[T](Base):
186
186
  @overload
187
187
  def update_ref[S: WithRef](self: S, reference: None = None) -> S: ...
188
188
 
189
- def update_ref[S: WithRef](self: S, reference: Union[T, "WithRef[T]", None] = None) -> S: # noqa: PYI019
189
+ def update_ref[S: WithRef](self: S, reference: Union[T, "WithRef[T]", None] = None) -> S:
190
190
  """Update the reference of the object.
191
191
 
192
192
  Args:
@@ -201,7 +201,7 @@ class WithRef[T](Base):
201
201
  self._reference = reference # pyright: ignore [reportAttributeAccessIssue]
202
202
  return self
203
203
 
204
- def derive[S: WithRef](self: S, reference: Any) -> S: # noqa: PYI019
204
+ def derive[S: WithRef](self: S, reference: Any) -> S:
205
205
  """Derive a new object from the current object.
206
206
 
207
207
  Args:
@@ -789,7 +789,7 @@ class ScopedConfig(Base):
789
789
  """The dimensions of the Milvus server."""
790
790
 
791
791
  @final
792
- def fallback_to(self, other: "ScopedConfig") -> Self:
792
+ def fallback_to(self, other: Union["ScopedConfig", Any]) -> Self:
793
793
  """Merge configuration values with fallback priority.
794
794
 
795
795
  Copies non-null values from 'other' to self where current values are None.
@@ -800,6 +800,9 @@ class ScopedConfig(Base):
800
800
  Returns:
801
801
  Self: Current instance with merged values
802
802
  """
803
+ if not isinstance(other, ScopedConfig):
804
+ return self
805
+
803
806
  # Iterate over the attribute names and copy values from 'other' to 'self' where applicable
804
807
  # noinspection PydanticTypeChecker,PyTypeChecker
805
808
  for attr_name in ScopedConfig.model_fields:
@@ -811,7 +814,7 @@ class ScopedConfig(Base):
811
814
  return self
812
815
 
813
816
  @final
814
- def hold_to(self, others: Union["ScopedConfig", Iterable["ScopedConfig"]]) -> Self:
817
+ def hold_to(self, others: Union[Union["ScopedConfig", Any], Iterable[Union["ScopedConfig", Any]]]) -> Self:
815
818
  """Propagate non-null values to other configurations.
816
819
 
817
820
  Copies current non-null values to target configurations where they are None.
@@ -824,7 +827,8 @@ class ScopedConfig(Base):
824
827
  """
825
828
  if not isinstance(others, Iterable):
826
829
  others = [others]
827
- for other in others:
830
+
831
+ for other in (o for o in others if isinstance(o, ScopedConfig)):
828
832
  # noinspection PyTypeChecker,PydanticTypeChecker
829
833
  for attr_name in ScopedConfig.model_fields:
830
834
  if (attr := getattr(self, attr_name)) is not None and getattr(other, attr_name) is None:
@@ -33,7 +33,7 @@ class LLMKwargs(TypedDict, total=False):
33
33
  including generation parameters and caching options.
34
34
  """
35
35
 
36
- model: str
36
+ model: Optional[str]
37
37
  temperature: float
38
38
  stop: str | list[str]
39
39
  top_p: float
fabricatio/models/role.py CHANGED
@@ -2,18 +2,18 @@
2
2
 
3
3
  from typing import Any, Self, Set
4
4
 
5
- from fabricatio.capabilities.correct import Correct
6
- from fabricatio.capabilities.task import HandleTask, ProposeTask
5
+ from fabricatio.capabilities.propose import Propose
7
6
  from fabricatio.core import env
8
7
  from fabricatio.journal import logger
9
8
  from fabricatio.models.action import WorkFlow
10
9
  from fabricatio.models.events import Event
11
10
  from fabricatio.models.generic import WithBriefing
12
11
  from fabricatio.models.tool import ToolBox
12
+ from fabricatio.models.usages import ToolBoxUsage
13
13
  from pydantic import Field
14
14
 
15
15
 
16
- class Role(WithBriefing, ProposeTask, HandleTask, Correct):
16
+ class Role(WithBriefing, Propose, ToolBoxUsage):
17
17
  """Class that represents a role with a registry of events and workflows.
18
18
 
19
19
  A Role serves as a container for workflows, managing their registration to events
@@ -23,7 +23,8 @@ class Role(WithBriefing, ProposeTask, HandleTask, Correct):
23
23
  registry: Mapping of events to workflows that handle them
24
24
  toolboxes: Set of toolboxes available to this role and its workflows
25
25
  """
26
- description:str =""
26
+
27
+ description: str = ""
27
28
  """A brief description of the role's responsibilities and capabilities."""
28
29
 
29
30
  registry: dict[Event | str, WorkFlow] = Field(default_factory=dict)
Binary file
fabricatio/rust.pyi CHANGED
@@ -325,6 +325,16 @@ def convert_all_block_tex(string: str) -> str:
325
325
  The converted string with block TeX code replaced.
326
326
  """
327
327
 
328
+ def fix_misplaced_labels(input: str) -> str:
329
+ """A func to fix labels in a string.
330
+
331
+ Args:
332
+ input: The input string containing misplaced labels.
333
+
334
+ Returns:
335
+ The fixed string with labels properly placed.
336
+ """
337
+
328
338
  def comment(string: str) -> str:
329
339
  """Add comment to the string.
330
340
 
fabricatio/utils.py CHANGED
@@ -47,7 +47,7 @@ async def ask_retain[V](candidates: List[str], value_mapping: Optional[List[V]]
47
47
  "Please choose those that should be retained.",
48
48
  choices=[Choice(p, value=p, checked=True) for p in candidates]
49
49
  if value_mapping is None
50
- else [Choice(p, value=v) for p, v in zip(candidates, value_mapping, strict=True)],
50
+ else [Choice(p, value=v, checked=True) for p, v in zip(candidates, value_mapping, strict=True)],
51
51
  ).ask_async()
52
52
 
53
53
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.11.dev3
3
+ Version: 0.2.12.dev2
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -1,40 +1,41 @@
1
- fabricatio-0.2.11.dev3.dist-info/METADATA,sha256=alou2beIqeg3_FeS2dp-VQx15_Ep-0bf-tbGrUSxGmw,5263
2
- fabricatio-0.2.11.dev3.dist-info/WHEEL,sha256=jABKVkLC9kJr8mi_er5jOqpiQUjARSLXDUIIxDqsS50,96
3
- fabricatio-0.2.11.dev3.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
1
+ fabricatio-0.2.12.dev2.dist-info/METADATA,sha256=jqf6D_gMBaD9ClwWRHwr9-vI0WAFYc_u2XdLjpb4dq0,5263
2
+ fabricatio-0.2.12.dev2.dist-info/WHEEL,sha256=jABKVkLC9kJr8mi_er5jOqpiQUjARSLXDUIIxDqsS50,96
3
+ fabricatio-0.2.12.dev2.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
4
4
  fabricatio/actions/article.py,sha256=SFl1zc0hz9vW2sW4VJm9-w8E7kLEty-2LzXi9wgWMmE,10905
5
- fabricatio/actions/article_rag.py,sha256=P1Bgvk6w8HogEMOw15-_cn2lzKpIcmfe_Ku2mlkdkYk,14318
5
+ fabricatio/actions/article_rag.py,sha256=dJJfPkfwnx8P4BebESOpPaeTfvER-1U68aPXpwMdDCQ,18723
6
6
  fabricatio/actions/fs.py,sha256=gJR14U4ln35nt8Z7OWLVAZpqGaLnED-r1Yi-lX22tkI,959
7
- fabricatio/actions/output.py,sha256=ttXLC2wZmtVN9Ik8zsA7g45rwBO656LyRjOGRdVSyJA,6977
7
+ fabricatio/actions/output.py,sha256=lX0HkDse3ypCzZgeF-8Dr-EnNFdBiE-WQ1iLPFlGM1g,8302
8
8
  fabricatio/actions/rag.py,sha256=KN-OWgcQjGmNgSZ-s5B8m4LpYKSGFJR8eq72mo2CP9k,3592
9
9
  fabricatio/actions/rules.py,sha256=dkvCgNDjt2KSO1VgPRsxT4YBmIIMeetZb5tiz-slYkU,3640
10
10
  fabricatio/actions/__init__.py,sha256=wVENCFtpVb1rLFxoOFJt9-8smLWXuJV7IwA8P3EfFz4,48
11
11
  fabricatio/capabilities/advanced_judge.py,sha256=selB0Gwf1F4gGJlwBiRo6gI4KOUROgh3WnzO3mZFEls,706
12
+ fabricatio/capabilities/advanced_rag.py,sha256=0VbVqx2WGhQfF9LNkHM6tdeodNxF4S0Gh7_vmH3X0UA,2282
12
13
  fabricatio/capabilities/censor.py,sha256=bBT5qy-kp7fh8g4Lz3labSwxwJ60gGd_vrkc6k1cZ1U,4719
13
14
  fabricatio/capabilities/check.py,sha256=kYqzohhv2bZfl1aKSUt7a8snT8YEl2zgha_ZdAdMMfQ,8622
14
15
  fabricatio/capabilities/correct.py,sha256=W_cInqlciNEhyMK0YI53jk4EvW9uAdge90IO9OElUmA,10420
15
16
  fabricatio/capabilities/extract.py,sha256=PMjkWvbsv57IYT7zzd_xbIu4eQqQjpcmBtJzqlWZhHY,2495
16
17
  fabricatio/capabilities/propose.py,sha256=hkBeSlmcTdfYWT-ph6nlbtHXBozi_JXqXlWcnBy3W78,2007
17
- fabricatio/capabilities/rag.py,sha256=kqcunWBC6oA4P1rzIG2Xu9zqSg73H3uKPF41JJQ1HVI,9595
18
+ fabricatio/capabilities/rag.py,sha256=a48dEaWE-sniwLhNtGz4xlSmRi_-PdOgDzNaDwnam_g,9619
18
19
  fabricatio/capabilities/rating.py,sha256=iMtQs3H6vCjuEjiuuz4SRKMVaX7yff7MHWz-slYvi5g,17835
19
20
  fabricatio/capabilities/review.py,sha256=-EMZe0ADFPT6fPGmra16UPjJC1M3rAs6dPFdTZ88Fgg,5060
20
21
  fabricatio/capabilities/task.py,sha256=uks1U-4LNCUdwdRxAbJJjMc31hOw6jlrcYriuQQfb04,4475
21
22
  fabricatio/capabilities/__init__.py,sha256=v1cHRHIJ2gxyqMLNCs6ERVcCakSasZNYzmMI4lqAcls,57
22
- fabricatio/config.py,sha256=okqrVoLhvmAjmfQXlLY3js4nC_qW4v7mxoYaGO2dMQ8,17984
23
+ fabricatio/config.py,sha256=ckm55Oq0IhG1ztwHbKOG-7Mg7uWE1fb_-DxeKZyc8DQ,17951
23
24
  fabricatio/constants.py,sha256=thfDuF6JEtJ5CHOnAJLfqvn5834n8ep6DH2jc6XGzQM,577
24
25
  fabricatio/core.py,sha256=VQ_JKgUGIy2gZ8xsTBZCdr_IP7wC5aPg0_bsOmjQ588,6458
25
26
  fabricatio/decorators.py,sha256=RFMYUlQPf561-BIHetpMd7fPig5bZ2brzWiQTgoLOlY,8966
26
- fabricatio/fs/curd.py,sha256=p8y0LGKgVDk-CWOlm37E6wg7RK6RCD6denKo-VsW28c,4763
27
+ fabricatio/fs/curd.py,sha256=652nHulbJ3gwt0Z3nywtPMmjhEyglDvEfc3p7ieJNNA,4777
27
28
  fabricatio/fs/readers.py,sha256=UXvcJO3UCsxHu9PPkg34Yh55Zi-miv61jD_wZQJgKRs,1751
28
29
  fabricatio/fs/__init__.py,sha256=FydmlEY_3QY74r1BpGDc5lFLhE6g6gkwOAtE30Fo-aI,786
29
30
  fabricatio/journal.py,sha256=stnEP88aUBA_GmU9gfTF2EZI8FS2OyMLGaMSTgK4QgA,476
30
- fabricatio/models/action.py,sha256=SnL8FAZsUCi9xM9BZT-CSjSRuI-4YBC5By8-ZHEBrW8,10109
31
+ fabricatio/models/action.py,sha256=X7xbun7SyIEpC3-kbJgAagquJGkOBlxhBxbC_sHZX2w,10120
31
32
  fabricatio/models/adv_kwargs_types.py,sha256=kUO-SiZtFuz5cZCmMLnJJ9tjQ4-Zd_foo6R8HQMlM5A,1950
32
33
  fabricatio/models/events.py,sha256=wiirk_ASg3iXDOZU_gIimci1VZVzWE1nDmxy-hQVJ9M,4150
33
34
  fabricatio/models/extra/advanced_judge.py,sha256=INUl_41C8jkausDekkjnEmTwNfLCJ23TwFjq2cM23Cw,1092
34
- fabricatio/models/extra/aricle_rag.py,sha256=bJ9qNa9DkTVvja8GVue5wMnJCwnr6TEO7_fQbQK7fv4,9780
35
- fabricatio/models/extra/article_base.py,sha256=CeYs0D6XghxgpSnQ-rhtWuuFhcouy_vc6E5oUCPck_w,12840
35
+ fabricatio/models/extra/aricle_rag.py,sha256=p91JI8FmFSrHVg5KbhJq4w8vQdx4VUW75SrtQUi9ju4,10987
36
+ fabricatio/models/extra/article_base.py,sha256=eyawpikQEp0-PR32Z0atpRvA77Ht3KjDwL5RD---mf8,14066
36
37
  fabricatio/models/extra/article_essence.py,sha256=mlIkkRMR3I1RtqiiOnmIE3Vy623L4eECumkRzryE1pw,2749
37
- fabricatio/models/extra/article_main.py,sha256=4rjev0wpI2jf52NLNatRbqFQmN6rtKaMB9iy30hSEXM,9818
38
+ fabricatio/models/extra/article_main.py,sha256=19ZXnVRk4oZA7t8wTJzSTCGiDUS8NTuy2uqcKCkqQqg,11907
38
39
  fabricatio/models/extra/article_outline.py,sha256=w7O0SHgC7exbptWVbR62FMHAueMgBpyWKVYMGGl_oj8,1427
39
40
  fabricatio/models/extra/article_proposal.py,sha256=NbyjW-7UiFPtnVD9nte75re4xL2pD4qL29PpNV4Cg_M,1870
40
41
  fabricatio/models/extra/patches.py,sha256=_WNCxtYzzsVfUxI16vu4IqsLahLYRHdbQN9er9tqhC0,997
@@ -42,24 +43,24 @@ fabricatio/models/extra/problem.py,sha256=8tTU-3giFHOi5j7NJsvH__JJyYcaGrcfsRnkzQ
42
43
  fabricatio/models/extra/rag.py,sha256=RMi8vhEPB0I5mVmjRLRLxYHUnm9pFhvVwysaIwmW2s0,3955
43
44
  fabricatio/models/extra/rule.py,sha256=KQQELVhCLUXhEZ35jU3WGYqKHuCYEAkn0p6pxAE-hOU,2625
44
45
  fabricatio/models/extra/__init__.py,sha256=XlYnS_2B9nhLhtQkjE7rvvfPmAAtXVdNi9bSDAR-Ge8,54
45
- fabricatio/models/generic.py,sha256=7wcG01DN9g4q1DJGZsTUxSTMixQgwXX0xlX4HbbLc6U,31185
46
- fabricatio/models/kwargs_types.py,sha256=GEw75ZiiDEFx_ImhCBENnPF7K0BcdTQ1ocH5jSPwMRs,4774
47
- fabricatio/models/role.py,sha256=-CRcj5_M3_ciLPzwiNn92grBmwoSLQ-n4koVZiCNTBM,2953
46
+ fabricatio/models/generic.py,sha256=yXaVyUd3MK0lFUgsT0diMWtgD9s5rM9yKTH44eE58jM,31314
47
+ fabricatio/models/kwargs_types.py,sha256=BPqZUgxz4WJaB7hmvrhNxHXp-O7O4SiAdn6UguBRij8,4784
48
+ fabricatio/models/role.py,sha256=b8FDRF4VjMMt93Uh5yiAufFbsoH7RcUaaFJAjVmq2l0,2931
48
49
  fabricatio/models/task.py,sha256=bLYSKjlRAlb4jMYyF12RTnm_8pVXysSmX8CYLrEmbQ8,11096
49
50
  fabricatio/models/tool.py,sha256=jQ51g4lwTPfsMF1nbreDJtBczbxIHoXcPuLSOqHliq8,12506
50
51
  fabricatio/models/usages.py,sha256=0bzITf0vug9ZaN6qnjNfFB7T8BAvpXE0bvx0otFYLLA,33356
51
52
  fabricatio/parser.py,sha256=-RbW2yzfJiu2ARq-lZw4tfgsjY2rIZWtJpoUmaE6gJQ,6637
52
53
  fabricatio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- fabricatio/rust.pyi,sha256=GJRLeeQ1UIaf5kOgJWX2GkwIacoTBk3yBKuD5cKW8i4,10489
54
+ fabricatio/rust.pyi,sha256=Tdyid-kS402bxiIdX218nh2ekkGBD66K-6PhMN-xU0k,10734
54
55
  fabricatio/rust_instances.py,sha256=Byeo8KHW_dJiXujJq7YPGDLBX5bHNDYbBc4sY3uubVY,313
55
56
  fabricatio/toolboxes/arithmetic.py,sha256=WLqhY-Pikv11Y_0SGajwZx3WhsLNpHKf9drzAqOf_nY,1369
56
57
  fabricatio/toolboxes/fs.py,sha256=l4L1CVxJmjw9Ld2XUpIlWfV0_Fu_2Og6d3E13I-S4aE,736
57
58
  fabricatio/toolboxes/__init__.py,sha256=KBJi5OG_pExscdlM7Bnt_UF43j4I3Lv6G71kPVu4KQU,395
58
- fabricatio/utils.py,sha256=sLD6L_BHXusJjCu3gsUpxOFbmEOnT577xU1kaZQ04SE,3084
59
+ fabricatio/utils.py,sha256=4aK6bNHCCGEbSkLRKrDBzabuVAow9PrJ6SVGUX1Rt-U,3098
59
60
  fabricatio/workflows/articles.py,sha256=ObYTFUqLUk_CzdmmnX6S7APfxcGmPFqnFr9pdjU7Z4Y,969
60
61
  fabricatio/workflows/rag.py,sha256=-YYp2tlE9Vtfgpg6ROpu6QVO8j8yVSPa6yDzlN3qVxs,520
61
62
  fabricatio/workflows/__init__.py,sha256=5ScFSTA-bvhCesj3U9Mnmi6Law6N1fmh5UKyh58L3u8,51
62
63
  fabricatio/__init__.py,sha256=Rmvq2VgdS2u68vnOi2i5RbeWbAwrJDbk8D8D883PJWE,1022
63
- fabricatio/rust.cp312-win_amd64.pyd,sha256=Z3wuzfa_m81nq1W6MVF66ne-gQ0-gkNFdknay_wVskY,4150272
64
- fabricatio-0.2.11.dev3.data/scripts/tdown.exe,sha256=nHWlrICGMzmDpo1bZA-l1tqyCZhSreyrc5S1J3E7KgU,3350016
65
- fabricatio-0.2.11.dev3.dist-info/RECORD,,
64
+ fabricatio/rust.cp312-win_amd64.pyd,sha256=ym9nuMyb-fdV1XDY4sHtPfzU8o7ee0r5U1LhAnTtLOA,4161536
65
+ fabricatio-0.2.12.dev2.data/scripts/tdown.exe,sha256=PUdB1PvJP6qyimU9c8qNzl9PcQtR1eBJxTtnLiY4wuk,3349504
66
+ fabricatio-0.2.12.dev2.dist-info/RECORD,,