fabricatio 0.2.8.dev3__cp312-cp312-manylinux_2_34_x86_64.whl → 0.2.9__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/__init__.py +4 -11
- fabricatio/actions/__init__.py +1 -0
- fabricatio/actions/article.py +128 -165
- fabricatio/actions/article_rag.py +62 -46
- fabricatio/actions/output.py +60 -4
- fabricatio/actions/rag.py +2 -1
- fabricatio/actions/rules.py +72 -0
- fabricatio/capabilities/__init__.py +1 -0
- fabricatio/capabilities/censor.py +104 -0
- fabricatio/capabilities/check.py +148 -32
- fabricatio/capabilities/correct.py +162 -100
- fabricatio/capabilities/rag.py +5 -4
- fabricatio/capabilities/rating.py +109 -54
- fabricatio/capabilities/review.py +1 -1
- fabricatio/capabilities/task.py +2 -1
- fabricatio/config.py +14 -6
- fabricatio/fs/readers.py +20 -1
- fabricatio/models/action.py +63 -41
- fabricatio/models/adv_kwargs_types.py +25 -0
- fabricatio/models/extra/__init__.py +1 -0
- fabricatio/models/extra/advanced_judge.py +7 -4
- fabricatio/models/extra/article_base.py +125 -79
- fabricatio/models/extra/article_main.py +101 -19
- fabricatio/models/extra/article_outline.py +2 -3
- fabricatio/models/extra/article_proposal.py +15 -14
- fabricatio/models/extra/patches.py +20 -0
- fabricatio/models/extra/problem.py +64 -23
- fabricatio/models/extra/rule.py +39 -10
- fabricatio/models/generic.py +405 -75
- fabricatio/models/kwargs_types.py +23 -17
- fabricatio/models/task.py +1 -1
- fabricatio/models/tool.py +149 -14
- fabricatio/models/usages.py +55 -56
- fabricatio/parser.py +12 -13
- fabricatio/rust.cpython-312-x86_64-linux-gnu.so +0 -0
- fabricatio/{_rust.pyi → rust.pyi} +42 -4
- fabricatio/{_rust_instances.py → rust_instances.py} +1 -1
- fabricatio/utils.py +5 -5
- fabricatio/workflows/__init__.py +1 -0
- fabricatio/workflows/articles.py +3 -5
- fabricatio-0.2.9.data/scripts/tdown +0 -0
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/METADATA +1 -1
- fabricatio-0.2.9.dist-info/RECORD +61 -0
- fabricatio/_rust.cpython-312-x86_64-linux-gnu.so +0 -0
- fabricatio-0.2.8.dev3.data/scripts/tdown +0 -0
- fabricatio-0.2.8.dev3.dist-info/RECORD +0 -53
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/licenses/LICENSE +0 -0
fabricatio/__init__.py
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
"""Fabricatio is a Python library for building llm app using event-based agent structure."""
|
2
2
|
|
3
|
-
from
|
4
|
-
|
5
|
-
from fabricatio import actions, toolboxes, workflows
|
6
|
-
from fabricatio._rust import BibManager
|
7
|
-
from fabricatio._rust_instances import TEMPLATE_MANAGER
|
3
|
+
from fabricatio import actions, capabilities, toolboxes, workflows
|
8
4
|
from fabricatio.core import env
|
9
5
|
from fabricatio.journal import logger
|
10
6
|
from fabricatio.models import extra
|
@@ -14,6 +10,8 @@ from fabricatio.models.role import Role
|
|
14
10
|
from fabricatio.models.task import Task
|
15
11
|
from fabricatio.models.tool import ToolBox
|
16
12
|
from fabricatio.parser import Capture, GenericCapture, JsonCapture, PythonCapture
|
13
|
+
from fabricatio.rust import BibManager
|
14
|
+
from fabricatio.rust_instances import TEMPLATE_MANAGER
|
17
15
|
|
18
16
|
__all__ = [
|
19
17
|
"TEMPLATE_MANAGER",
|
@@ -29,15 +27,10 @@ __all__ = [
|
|
29
27
|
"ToolBox",
|
30
28
|
"WorkFlow",
|
31
29
|
"actions",
|
30
|
+
"capabilities",
|
32
31
|
"env",
|
33
32
|
"extra",
|
34
33
|
"logger",
|
35
34
|
"toolboxes",
|
36
35
|
"workflows",
|
37
36
|
]
|
38
|
-
|
39
|
-
|
40
|
-
if find_spec("pymilvus"):
|
41
|
-
from fabricatio.capabilities.rag import RAG
|
42
|
-
|
43
|
-
__all__ += ["RAG"]
|
@@ -0,0 +1 @@
|
|
1
|
+
"""A module containing some builtin actins."""
|
fabricatio/actions/article.py
CHANGED
@@ -2,24 +2,27 @@
|
|
2
2
|
|
3
3
|
from asyncio import gather
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import
|
5
|
+
from typing import Callable, List, Optional
|
6
6
|
|
7
|
-
from
|
8
|
-
|
7
|
+
from more_itertools import filter_map
|
8
|
+
|
9
|
+
from fabricatio.capabilities.censor import Censor
|
10
|
+
from fabricatio.capabilities.propose import Propose
|
9
11
|
from fabricatio.fs import safe_text_read
|
10
12
|
from fabricatio.journal import logger
|
11
13
|
from fabricatio.models.action import Action
|
12
|
-
from fabricatio.models.extra.article_base import
|
14
|
+
from fabricatio.models.extra.article_base import ArticleRef, SubSectionBase
|
13
15
|
from fabricatio.models.extra.article_essence import ArticleEssence
|
14
16
|
from fabricatio.models.extra.article_main import Article
|
15
17
|
from fabricatio.models.extra.article_outline import ArticleOutline
|
16
18
|
from fabricatio.models.extra.article_proposal import ArticleProposal
|
19
|
+
from fabricatio.models.extra.rule import RuleSet
|
17
20
|
from fabricatio.models.task import Task
|
21
|
+
from fabricatio.rust import BibManager, detect_language
|
18
22
|
from fabricatio.utils import ok
|
19
|
-
from more_itertools import filter_map
|
20
23
|
|
21
24
|
|
22
|
-
class ExtractArticleEssence(Action):
|
25
|
+
class ExtractArticleEssence(Action, Propose):
|
23
26
|
"""Extract the essence of article(s) in text format from the paths specified in the task dependencies.
|
24
27
|
|
25
28
|
Notes:
|
@@ -47,11 +50,11 @@ class ExtractArticleEssence(Action):
|
|
47
50
|
out = []
|
48
51
|
|
49
52
|
for ess in await self.propose(
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
ArticleEssence,
|
54
|
+
[
|
55
|
+
f"{c}\n\n\nBased the provided academic article above, you need to extract the essence from it.\n\nWrite the value string using `{detect_language(c)}`"
|
56
|
+
for c in contents
|
57
|
+
],
|
55
58
|
):
|
56
59
|
if ess is None:
|
57
60
|
logger.warning("Could not extract article essence")
|
@@ -91,7 +94,7 @@ class FixArticleEssence(Action):
|
|
91
94
|
return out
|
92
95
|
|
93
96
|
|
94
|
-
class GenerateArticleProposal(Action):
|
97
|
+
class GenerateArticleProposal(Action, Propose):
|
95
98
|
"""Generate an outline for the article based on the extracted essence."""
|
96
99
|
|
97
100
|
output_key: str = "article_proposal"
|
@@ -102,38 +105,33 @@ class GenerateArticleProposal(Action):
|
|
102
105
|
task_input: Optional[Task] = None,
|
103
106
|
article_briefing: Optional[str] = None,
|
104
107
|
article_briefing_path: Optional[str] = None,
|
105
|
-
langauge: Optional[str] = None,
|
106
108
|
**_,
|
107
109
|
) -> Optional[ArticleProposal]:
|
108
110
|
if article_briefing is None and article_briefing_path is None and task_input is None:
|
109
111
|
logger.error("Task not approved, since all inputs are None.")
|
110
112
|
return None
|
111
113
|
|
112
|
-
|
114
|
+
briefing = article_briefing or safe_text_read(
|
115
|
+
ok(
|
116
|
+
article_briefing_path
|
117
|
+
or await self.awhich_pathstr(
|
118
|
+
f"{ok(task_input).briefing}\nExtract the path of file which contains the article briefing."
|
119
|
+
),
|
120
|
+
"Could not find the path of file to read.",
|
121
|
+
)
|
122
|
+
)
|
123
|
+
|
124
|
+
logger.info("Start generating the proposal.")
|
125
|
+
return ok(
|
113
126
|
await self.propose(
|
114
127
|
ArticleProposal,
|
115
|
-
briefing
|
116
|
-
article_briefing
|
117
|
-
or safe_text_read(
|
118
|
-
ok(
|
119
|
-
article_briefing_path
|
120
|
-
or await self.awhich_pathstr(
|
121
|
-
f"{ok(task_input).briefing}\nExtract the path of file which contains the article briefing."
|
122
|
-
),
|
123
|
-
"Could not find the path of file to read.",
|
124
|
-
)
|
125
|
-
)
|
126
|
-
),
|
128
|
+
f"{briefing}\n\nWrite the value string using `{detect_language(briefing)}` as written language.",
|
127
129
|
),
|
128
130
|
"Could not generate the proposal.",
|
129
131
|
).update_ref(briefing)
|
130
|
-
if langauge:
|
131
|
-
proposal.language = langauge
|
132
|
-
|
133
|
-
return proposal
|
134
132
|
|
135
133
|
|
136
|
-
class GenerateInitialOutline(Action):
|
134
|
+
class GenerateInitialOutline(Action, Propose):
|
137
135
|
"""Generate the initial article outline based on the article proposal."""
|
138
136
|
|
139
137
|
output_key: str = "initial_article_outline"
|
@@ -144,135 +142,146 @@ class GenerateInitialOutline(Action):
|
|
144
142
|
article_proposal: ArticleProposal,
|
145
143
|
**_,
|
146
144
|
) -> Optional[ArticleOutline]:
|
145
|
+
raw_outline = await self.aask(
|
146
|
+
f"{(article_proposal.as_prompt())}\n\nNote that you should use `{article_proposal.language}` to write the `ArticleOutline`\n"
|
147
|
+
f"Design each chapter of a proper and academic and ready for release manner.\n"
|
148
|
+
f"You Must make sure every chapter have sections, and every section have subsections.\n"
|
149
|
+
f"Make the chapter and sections and subsections bing divided into a specific enough article component.",
|
150
|
+
)
|
151
|
+
|
147
152
|
return ok(
|
148
153
|
await self.propose(
|
149
154
|
ArticleOutline,
|
150
|
-
|
155
|
+
f'{raw_outline}\n\n\n\noutline provided above is the outline i need to extract to a JSON,'
|
151
156
|
),
|
152
157
|
"Could not generate the initial outline.",
|
153
158
|
).update_ref(article_proposal)
|
154
159
|
|
155
160
|
|
156
|
-
class FixIntrospectedErrors(Action):
|
161
|
+
class FixIntrospectedErrors(Action, Censor):
|
157
162
|
"""Fix introspected errors in the article outline."""
|
158
163
|
|
159
164
|
output_key: str = "introspected_errors_fixed_outline"
|
160
165
|
"""The key of the output data."""
|
161
166
|
|
167
|
+
ruleset: Optional[RuleSet] = None
|
168
|
+
"""The ruleset to use to fix the introspected errors."""
|
169
|
+
max_error_count: Optional[int] = None
|
170
|
+
"""The maximum number of errors to fix."""
|
171
|
+
|
162
172
|
async def _execute(
|
163
173
|
self,
|
164
174
|
article_outline: ArticleOutline,
|
165
|
-
|
175
|
+
intro_fix_ruleset: Optional[RuleSet] = None,
|
166
176
|
**_,
|
167
177
|
) -> Optional[ArticleOutline]:
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
while pack := article_outline.find_introspected():
|
179
|
-
component, err = ok(pack)
|
180
|
-
logger.warning(f"Found introspected error: {err}")
|
181
|
-
corrected = ok(
|
182
|
-
await self.correct_obj(
|
183
|
-
component,
|
184
|
-
reference=f"# Original Article Outline\n{article_outline.display()}\n# Error Need to be fixed\n{err}",
|
185
|
-
topic=intro_topic,
|
186
|
-
rating_manual=introspect_manual,
|
187
|
-
supervisor_check=supervisor_check,
|
178
|
+
counter = 0
|
179
|
+
origin = article_outline
|
180
|
+
while pack := article_outline.gather_introspected():
|
181
|
+
logger.info(f"Found {counter}th introspected errors")
|
182
|
+
logger.warning(f"Found introspected error: {pack}")
|
183
|
+
article_outline = ok(
|
184
|
+
await self.censor_obj(
|
185
|
+
article_outline,
|
186
|
+
ruleset=ok(intro_fix_ruleset or self.ruleset, "No ruleset provided"),
|
187
|
+
reference=f"{article_outline.display()}\n # Fatal Error of the Original Article Outline\n{pack}",
|
188
188
|
),
|
189
189
|
"Could not correct the component.",
|
190
|
-
)
|
191
|
-
|
190
|
+
).update_ref(origin)
|
191
|
+
|
192
|
+
if self.max_error_count and counter > self.max_error_count:
|
193
|
+
logger.warning("Max error count reached, stopping.")
|
194
|
+
break
|
195
|
+
counter += 1
|
192
196
|
|
193
197
|
return article_outline
|
194
198
|
|
195
199
|
|
196
|
-
class FixIllegalReferences(Action):
|
200
|
+
class FixIllegalReferences(Action, Censor):
|
197
201
|
"""Fix illegal references in the article outline."""
|
198
202
|
|
199
203
|
output_key: str = "illegal_references_fixed_outline"
|
200
204
|
"""The key of the output data."""
|
201
205
|
|
206
|
+
ruleset: Optional[RuleSet] = None
|
207
|
+
"""Ruleset to use to fix the illegal references."""
|
208
|
+
max_error_count: Optional[int] = None
|
209
|
+
"""The maximum number of errors to fix."""
|
210
|
+
|
202
211
|
async def _execute(
|
203
212
|
self,
|
204
213
|
article_outline: ArticleOutline,
|
205
|
-
|
214
|
+
ref_fix_ruleset: Optional[RuleSet] = None,
|
206
215
|
**_,
|
207
216
|
) -> Optional[ArticleOutline]:
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
217
|
+
counter = 0
|
218
|
+
while pack := article_outline.find_illegal_ref():
|
219
|
+
logger.info(f"Found {counter}th illegal references")
|
220
|
+
cmp, ref, err = ok(pack)
|
221
|
+
logger.warning(f"Found illegal referring error: {err}\n\n{cmp.display()}")
|
222
|
+
new = ok(
|
223
|
+
await self.censor_obj_inplace(
|
224
|
+
cmp,
|
225
|
+
ruleset=ok(ref_fix_ruleset or self.ruleset, "No ruleset provided"),
|
226
|
+
reference=f"{article_outline.as_prompt()}\n# `ArticleRef` usage doc\n{ArticleRef.__doc__}\n# Invalid `ArticleRef`\n```json\n{ref}```\n\n# Some Ref errors found that need to be fixed for the `ArticleRef`\n{err}",
|
213
227
|
),
|
214
|
-
|
215
|
-
"Could not generate the rating manual.",
|
216
|
-
)
|
217
|
-
|
218
|
-
while pack := article_outline.find_illegal_ref(gather_identical=True):
|
219
|
-
refs, err = ok(pack)
|
220
|
-
logger.warning(f"Found illegal referring error: {err}")
|
221
|
-
corrected_ref = ok(
|
222
|
-
await self.correct_obj(
|
223
|
-
refs[0], # pyright: ignore [reportIndexIssue]
|
224
|
-
reference=f"# Original Article Outline\n{article_outline.display()}\n# Error Need to be fixed\n{err}",
|
225
|
-
topic=ref_topic,
|
226
|
-
rating_manual=ref_manual,
|
227
|
-
supervisor_check=supervisor_check,
|
228
|
-
)
|
228
|
+
"Could not correct the component",
|
229
229
|
)
|
230
|
-
for ref in refs:
|
231
|
-
ref.update_from(corrected_ref) # pyright: ignore [reportAttributeAccessIssue]
|
232
230
|
|
233
|
-
|
231
|
+
logger.info(f"Applying correction:\n{new.display()}")
|
232
|
+
if self.max_error_count and counter > self.max_error_count:
|
233
|
+
logger.warning("Max error count reached, stopping.")
|
234
|
+
break
|
235
|
+
counter += 1
|
236
|
+
|
237
|
+
return article_outline
|
234
238
|
|
235
239
|
|
236
|
-
class TweakOutlineForwardRef(Action,
|
240
|
+
class TweakOutlineForwardRef(Action, Censor):
|
237
241
|
"""Tweak the forward references in the article outline.
|
238
242
|
|
239
243
|
Ensures that the conclusions of the current chapter effectively support the analysis of subsequent chapters.
|
240
244
|
"""
|
241
245
|
|
242
246
|
output_key: str = "article_outline_fw_ref_checked"
|
247
|
+
ruleset: Optional[RuleSet] = None
|
248
|
+
"""Ruleset to use to fix the illegal references."""
|
243
249
|
|
244
|
-
async def _execute(
|
250
|
+
async def _execute(
|
251
|
+
self, article_outline: ArticleOutline, ref_twk_ruleset: Optional[RuleSet] = None, **cxt
|
252
|
+
) -> ArticleOutline:
|
245
253
|
return await self._inner(
|
246
254
|
article_outline,
|
247
|
-
|
248
|
-
topic="Ensure conclusions support the analysis of subsequent chapters, sections or subsections.",
|
255
|
+
ruleset=ok(ref_twk_ruleset or self.ruleset, "No ruleset provided"),
|
249
256
|
field_name="support_to",
|
250
257
|
)
|
251
258
|
|
252
|
-
async def _inner(
|
253
|
-
|
254
|
-
|
255
|
-
tweak_support_to_manual = ok(
|
256
|
-
await self.draft_rating_manual(topic),
|
257
|
-
"Could not generate the rating manual.",
|
259
|
+
async def _inner(self, article_outline: ArticleOutline, ruleset: RuleSet, field_name: str) -> ArticleOutline:
|
260
|
+
await gather(
|
261
|
+
*[self._loop(a[-1], article_outline, field_name, ruleset) for a in article_outline.iter_subsections()],
|
258
262
|
)
|
259
|
-
|
260
|
-
if await self.evidently_judge(
|
261
|
-
f"{article_outline.as_prompt()}\n\n{a.display()}\n"
|
262
|
-
f"Does the `{a.__class__.__name__}`'s `{field_name}` field need to be extended or tweaked?"
|
263
|
-
):
|
264
|
-
patch = ArticleRefPatch.default()
|
265
|
-
patch.tweaked = getattr(a, field_name)
|
266
|
-
|
267
|
-
await self.correct_obj_inplace(
|
268
|
-
patch,
|
269
|
-
topic=topic,
|
270
|
-
reference=f"{article_outline.as_prompt()}\nThe Article component whose `{field_name}` field needs to be extended or tweaked",
|
271
|
-
rating_manual=tweak_support_to_manual,
|
272
|
-
supervisor_check=supervisor_check,
|
273
|
-
)
|
263
|
+
|
274
264
|
return article_outline
|
275
265
|
|
266
|
+
async def _loop(
|
267
|
+
self, a: SubSectionBase, article_outline: ArticleOutline, field_name: str, ruleset: RuleSet
|
268
|
+
) -> None:
|
269
|
+
if judge := await self.evidently_judge(
|
270
|
+
f"{article_outline.as_prompt()}\n\n{a.display()}\n"
|
271
|
+
f"# `ArticleRef` usage doc\n{ArticleRef.__doc__}\n"
|
272
|
+
f"# Ruleset\n{ruleset.display()}\n"
|
273
|
+
f"Does the `{a.__class__.__name__}`'s `{field_name}` field contains inappropriate ref that obviously violated the ruleset provided?\n"
|
274
|
+
f"true for it does violated, false for it does not violated."
|
275
|
+
):
|
276
|
+
await self.censor_obj_inplace(
|
277
|
+
a,
|
278
|
+
ruleset=ruleset,
|
279
|
+
reference=f"{article_outline.as_prompt()}\n"
|
280
|
+
f"The Article component titled `{a.title}` whose `{field_name}` field needs to be tweaked.\n"
|
281
|
+
f"# `ArticleRef` usage doc\n{ArticleRef.__doc__}\n"
|
282
|
+
f"# Judgement\n{judge.display()}",
|
283
|
+
)
|
284
|
+
|
276
285
|
|
277
286
|
class TweakOutlineBackwardRef(TweakOutlineForwardRef):
|
278
287
|
"""Tweak the backward references in the article outline.
|
@@ -281,91 +290,45 @@ class TweakOutlineBackwardRef(TweakOutlineForwardRef):
|
|
281
290
|
"""
|
282
291
|
|
283
292
|
output_key: str = "article_outline_bw_ref_checked"
|
293
|
+
ruleset: Optional[RuleSet] = None
|
284
294
|
|
285
|
-
async def _execute(
|
295
|
+
async def _execute(
|
296
|
+
self, article_outline: ArticleOutline, ref_twk_ruleset: Optional[RuleSet] = None, **cxt
|
297
|
+
) -> ArticleOutline:
|
286
298
|
return await self._inner(
|
287
299
|
article_outline,
|
288
|
-
|
289
|
-
topic="Ensure the dependencies of the current chapter are neither abused nor missing.",
|
300
|
+
ruleset=ok(ref_twk_ruleset or self.ruleset, "No ruleset provided"),
|
290
301
|
field_name="depend_on",
|
291
302
|
)
|
292
303
|
|
293
304
|
|
294
|
-
class GenerateArticle(Action):
|
305
|
+
class GenerateArticle(Action, Censor):
|
295
306
|
"""Generate the article based on the outline."""
|
296
307
|
|
297
308
|
output_key: str = "article"
|
298
309
|
"""The key of the output data."""
|
310
|
+
ruleset: Optional[RuleSet] = None
|
299
311
|
|
300
312
|
async def _execute(
|
301
313
|
self,
|
302
314
|
article_outline: ArticleOutline,
|
303
|
-
|
315
|
+
article_gen_ruleset: Optional[RuleSet] = None,
|
304
316
|
**_,
|
305
317
|
) -> Optional[Article]:
|
306
318
|
article: Article = Article.from_outline(ok(article_outline, "Article outline not specified.")).update_ref(
|
307
319
|
article_outline
|
308
320
|
)
|
309
321
|
|
310
|
-
write_para_manual = ok(
|
311
|
-
await self.draft_rating_manual(w_topic := "write the following paragraph in the subsection.")
|
312
|
-
)
|
313
|
-
|
314
322
|
await gather(
|
315
323
|
*[
|
316
|
-
self.
|
324
|
+
self.censor_obj_inplace(
|
317
325
|
subsec,
|
318
|
-
|
319
|
-
|
320
|
-
rating_manual=write_para_manual,
|
321
|
-
supervisor_check=supervisor_check,
|
326
|
+
ruleset=ok(article_gen_ruleset or self.ruleset, "No ruleset provided"),
|
327
|
+
reference=f"{article_outline.as_prompt()}\n# `ArticleRef` usage doc\n{ArticleRef.__doc__}# Error Need to be fixed\n{err}\nYou should use `{subsec.language}` to write the new `Subsection`.",
|
322
328
|
)
|
323
|
-
for _,
|
324
|
-
if (err := subsec.introspect())
|
329
|
+
for _, _, subsec in article.iter_subsections()
|
330
|
+
if (err := subsec.introspect()) and logger.warning(f"Found Introspection Error:\n{err}") is None
|
325
331
|
],
|
326
|
-
return_exceptions=True,
|
327
332
|
)
|
328
333
|
|
329
334
|
return article
|
330
|
-
|
331
|
-
|
332
|
-
class CorrectProposal(Action):
|
333
|
-
"""Correct the proposal of the article."""
|
334
|
-
|
335
|
-
output_key: str = "corrected_proposal"
|
336
|
-
|
337
|
-
async def _execute(self, article_proposal: ArticleProposal, **_) -> Any:
|
338
|
-
return (await self.censor_obj(article_proposal, reference=article_proposal.referenced)).update_ref(
|
339
|
-
article_proposal
|
340
|
-
)
|
341
|
-
|
342
|
-
|
343
|
-
class CorrectOutline(Action):
|
344
|
-
"""Correct the outline of the article."""
|
345
|
-
|
346
|
-
output_key: str = "corrected_outline"
|
347
|
-
"""The key of the output data."""
|
348
|
-
|
349
|
-
async def _execute(
|
350
|
-
self,
|
351
|
-
article_outline: ArticleOutline,
|
352
|
-
**_,
|
353
|
-
) -> ArticleOutline:
|
354
|
-
return (await self.censor_obj(article_outline, reference=article_outline.referenced.as_prompt())).update_ref(
|
355
|
-
article_outline
|
356
|
-
)
|
357
|
-
|
358
|
-
|
359
|
-
class CorrectArticle(Action):
|
360
|
-
"""Correct the article based on the outline."""
|
361
|
-
|
362
|
-
output_key: str = "corrected_article"
|
363
|
-
"""The key of the output data."""
|
364
|
-
|
365
|
-
async def _execute(
|
366
|
-
self,
|
367
|
-
article: Article,
|
368
|
-
article_outline: ArticleOutline,
|
369
|
-
**_,
|
370
|
-
) -> Article:
|
371
|
-
return await self.censor_obj(article, reference=article_outline.referenced.as_prompt())
|
@@ -1,73 +1,89 @@
|
|
1
1
|
"""A module for writing articles using RAG (Retrieval-Augmented Generation) capabilities."""
|
2
2
|
|
3
3
|
from asyncio import gather
|
4
|
-
from typing import
|
4
|
+
from typing import Optional
|
5
5
|
|
6
|
+
from fabricatio.capabilities.censor import Censor
|
6
7
|
from fabricatio.capabilities.rag import RAG
|
7
8
|
from fabricatio.models.action import Action
|
8
|
-
from fabricatio.models.extra.article_main import Article,
|
9
|
+
from fabricatio.models.extra.article_main import Article, ArticleSubsection
|
10
|
+
from fabricatio.models.extra.rule import RuleSet
|
9
11
|
from fabricatio.utils import ok
|
10
12
|
|
11
13
|
|
12
|
-
class TweakArticleRAG(Action, RAG):
|
13
|
-
"""Write an article based on the provided outline.
|
14
|
+
class TweakArticleRAG(Action, RAG, Censor):
|
15
|
+
"""Write an article based on the provided outline.
|
16
|
+
|
17
|
+
This class inherits from `Action`, `RAG`, and `Censor` to provide capabilities for writing and refining articles
|
18
|
+
using Retrieval-Augmented Generation (RAG) techniques. It processes an article outline, enhances subsections by
|
19
|
+
searching for related references, and applies censoring rules to ensure compliance with the provided ruleset.
|
20
|
+
|
21
|
+
Attributes:
|
22
|
+
output_key (str): The key used to store the output of the action.
|
23
|
+
ruleset (Optional[RuleSet]): The ruleset to be used for censoring the article.
|
24
|
+
"""
|
14
25
|
|
15
26
|
output_key: str = "rag_tweaked_article"
|
27
|
+
"""The key used to store the output of the action."""
|
28
|
+
|
29
|
+
ruleset: Optional[RuleSet] = None
|
30
|
+
"""The ruleset to be used for censoring the article."""
|
31
|
+
|
32
|
+
ref_limit: int = 30
|
33
|
+
"""The limit of references to be retrieved"""
|
16
34
|
|
17
35
|
async def _execute(
|
18
36
|
self,
|
19
37
|
article: Article,
|
20
38
|
collection_name: str = "article_essence",
|
21
|
-
|
22
|
-
"Use correct citation format based on author count. Cite using author surnames and year:"
|
23
|
-
"For 3+ authors: 'Author1, Author2 et al. (YYYY)'"
|
24
|
-
"For 2 authors: 'Author1 & Author2 (YYYY)'"
|
25
|
-
"Single author: 'Author1 (YYYY)'"
|
26
|
-
"Multiple citations: 'Author1 (YYYY), Author2 (YYYY)'"
|
27
|
-
"Prioritize formulas from reference highlights."
|
28
|
-
"Specify authors/years only."
|
29
|
-
"You can create numeric citation numbers for article whose `bibtex_cite_key` is 'wangWind2024' by using notation like `#cite(<wangWind2024>)`."
|
30
|
-
"Paragraphs must exceed 2-3 sentences",
|
31
|
-
supervisor_check: bool = False,
|
39
|
+
twk_rag_ruleset: Optional[RuleSet] = None,
|
32
40
|
parallel: bool = False,
|
33
41
|
**cxt,
|
34
42
|
) -> Optional[Article]:
|
35
|
-
"""Write an article based on the provided outline.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
"""Write an article based on the provided outline.
|
44
|
+
|
45
|
+
This method processes the article outline, either in parallel or sequentially, by enhancing each subsection
|
46
|
+
with relevant references and applying censoring rules.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
article (Article): The article to be processed.
|
50
|
+
collection_name (str): The name of the collection to view for processing.
|
51
|
+
twk_rag_ruleset (Optional[RuleSet]): The ruleset to apply for censoring. If not provided, the class's ruleset is used.
|
52
|
+
parallel (bool): If True, process subsections in parallel. Otherwise, process them sequentially.
|
53
|
+
**cxt: Additional context parameters.
|
44
54
|
|
45
|
-
|
55
|
+
Returns:
|
56
|
+
Optional[Article]: The processed article with enhanced subsections and applied censoring rules.
|
57
|
+
"""
|
46
58
|
self.view(collection_name)
|
47
59
|
|
48
60
|
if parallel:
|
49
61
|
await gather(
|
50
62
|
*[
|
51
|
-
self._inner(article, subsec,
|
63
|
+
self._inner(article, subsec, ok(twk_rag_ruleset or self.ruleset, "No ruleset provided!"))
|
52
64
|
for _, __, subsec in article.iter_subsections()
|
53
65
|
],
|
54
66
|
return_exceptions=True,
|
55
67
|
)
|
56
68
|
else:
|
57
69
|
for _, __, subsec in article.iter_subsections():
|
58
|
-
await self._inner(article, subsec,
|
59
|
-
|
70
|
+
await self._inner(article, subsec, ok(twk_rag_ruleset or self.ruleset, "No ruleset provided!"))
|
60
71
|
return article
|
61
72
|
|
62
|
-
async def _inner(
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
async def _inner(self, article: Article, subsec: ArticleSubsection, ruleset: RuleSet) -> None:
|
74
|
+
"""Enhance a subsection of the article with references and apply censoring rules.
|
75
|
+
|
76
|
+
This method refines the query for the subsection, retrieves related references, and applies censoring rules
|
77
|
+
to the subsection's paragraphs.
|
78
|
+
|
79
|
+
Args:
|
80
|
+
article (Article): The article containing the subsection.
|
81
|
+
subsec (ArticleSubsection): The subsection to be enhanced.
|
82
|
+
ruleset (RuleSet): The ruleset to apply for censoring.
|
83
|
+
|
84
|
+
Returns:
|
85
|
+
None
|
86
|
+
"""
|
71
87
|
refind_q = ok(
|
72
88
|
await self.arefined_query(
|
73
89
|
f"{article.referenced.as_prompt()}\n"
|
@@ -75,15 +91,15 @@ class TweakArticleRAG(Action, RAG):
|
|
75
91
|
f"{subsec.display()}\n"
|
76
92
|
f"# Requirement\n"
|
77
93
|
f"Search related articles in the base to find reference candidates, "
|
78
|
-
f"
|
94
|
+
f"provide queries in both `English` and `{subsec.language}` can get more accurate results.",
|
79
95
|
)
|
80
96
|
)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
97
|
+
await self.censor_obj_inplace(
|
98
|
+
subsec,
|
99
|
+
ruleset=ruleset,
|
100
|
+
reference=f"{await self.aretrieve_compact(refind_q, final_limit=self.ref_limit)}\n\n"
|
101
|
+
f"You can use Reference above to rewrite the `{subsec.__class__.__name__}`.\n"
|
102
|
+
f"You should Always use `{subsec.language}` as written language, "
|
103
|
+
f"which is the original language of the `{subsec.title}`. "
|
104
|
+
f"since rewrite a `{subsec.__class__.__name__}` in a different language is usually a bad choice",
|
89
105
|
)
|