fabricatio 0.2.7.dev5__cp312-cp312-manylinux_2_34_x86_64.whl → 0.2.8.dev0__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.cpython-312-x86_64-linux-gnu.so +0 -0
- fabricatio/actions/article.py +87 -70
- fabricatio/actions/output.py +36 -1
- fabricatio/capabilities/correct.py +26 -6
- fabricatio/models/action.py +2 -0
- fabricatio/models/extra/article_base.py +177 -86
- fabricatio/models/extra/article_main.py +21 -49
- fabricatio/models/extra/article_outline.py +4 -159
- fabricatio/models/extra/article_proposal.py +12 -14
- fabricatio/models/generic.py +12 -16
- fabricatio/models/usages.py +5 -1
- {fabricatio-0.2.7.dev5.data → fabricatio-0.2.8.dev0.data}/scripts/tdown +0 -0
- {fabricatio-0.2.7.dev5.dist-info → fabricatio-0.2.8.dev0.dist-info}/METADATA +1 -1
- {fabricatio-0.2.7.dev5.dist-info → fabricatio-0.2.8.dev0.dist-info}/RECORD +16 -16
- {fabricatio-0.2.7.dev5.dist-info → fabricatio-0.2.8.dev0.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.7.dev5.dist-info → fabricatio-0.2.8.dev0.dist-info}/licenses/LICENSE +0 -0
Binary file
|
fabricatio/actions/article.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Actions for transmitting tasks to targets."""
|
2
2
|
|
3
|
+
from asyncio import gather
|
3
4
|
from pathlib import Path
|
4
5
|
from typing import Any, Callable, List, Optional
|
5
6
|
|
@@ -55,13 +56,14 @@ class GenerateArticleProposal(Action):
|
|
55
56
|
task_input: Optional[Task] = None,
|
56
57
|
article_briefing: Optional[str] = None,
|
57
58
|
article_briefing_path: Optional[str] = None,
|
59
|
+
langauge: Optional[str] = None,
|
58
60
|
**_,
|
59
61
|
) -> Optional[ArticleProposal]:
|
60
62
|
if article_briefing is None and article_briefing_path is None and task_input is None:
|
61
63
|
logger.error("Task not approved, since all inputs are None.")
|
62
64
|
return None
|
63
65
|
|
64
|
-
|
66
|
+
proposal = ok(
|
65
67
|
await self.propose(
|
66
68
|
ArticleProposal,
|
67
69
|
briefing := (
|
@@ -78,8 +80,12 @@ class GenerateArticleProposal(Action):
|
|
78
80
|
),
|
79
81
|
**self.prepend_sys_msg(),
|
80
82
|
),
|
81
|
-
"Could not generate the proposal."
|
83
|
+
"Could not generate the proposal.",
|
82
84
|
).update_ref(briefing)
|
85
|
+
if langauge:
|
86
|
+
proposal.language = langauge
|
87
|
+
|
88
|
+
return proposal
|
83
89
|
|
84
90
|
|
85
91
|
class GenerateOutline(Action):
|
@@ -102,27 +108,92 @@ class GenerateOutline(Action):
|
|
102
108
|
"Could not generate the outline.",
|
103
109
|
)
|
104
110
|
|
105
|
-
|
106
|
-
|
107
|
-
topic
|
108
|
-
|
111
|
+
introspect_manual = ok(
|
112
|
+
await self.draft_rating_manual(
|
113
|
+
topic=(
|
114
|
+
intro_topic
|
115
|
+
:= "Fix the error in the article outline, make sure there is no more error in the article outline."
|
116
|
+
),
|
109
117
|
),
|
110
|
-
|
118
|
+
"Could not generate the rating manual.",
|
119
|
+
)
|
111
120
|
|
112
|
-
while pack := out.
|
121
|
+
while pack := out.find_introspected():
|
113
122
|
component, err = ok(pack)
|
114
|
-
logger.warning(f"Found error
|
115
|
-
corrected = ok(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
123
|
+
logger.warning(f"Found introspected error: {err}")
|
124
|
+
corrected = ok(
|
125
|
+
await self.correct_obj(
|
126
|
+
component,
|
127
|
+
reference=f"# Original Article Outline\n{out.display()}\n# Error Need to be fixed\n{err}",
|
128
|
+
topic=intro_topic,
|
129
|
+
rating_manual=introspect_manual,
|
130
|
+
supervisor_check=False,
|
131
|
+
),
|
132
|
+
"Could not correct the component.",
|
133
|
+
)
|
122
134
|
component.update_from(corrected)
|
135
|
+
|
136
|
+
ref_manual = ok(
|
137
|
+
await self.draft_rating_manual(
|
138
|
+
topic=(
|
139
|
+
ref_topic
|
140
|
+
:= "Fix the internal referring error, make sure there is no more `ArticleRef` pointing to a non-existing article component."
|
141
|
+
),
|
142
|
+
),
|
143
|
+
"Could not generate the rating manual.",
|
144
|
+
)
|
145
|
+
|
146
|
+
while pack := out.find_illegal_ref():
|
147
|
+
ref, err = ok(pack)
|
148
|
+
logger.warning(f"Found illegal referring error: {err}")
|
149
|
+
ok(
|
150
|
+
await self.correct_obj_inplace(
|
151
|
+
ref,
|
152
|
+
reference=f"# Original Article Outline\n{out.display()}\n# Error Need to be fixed\n{err}\n\n",
|
153
|
+
topic=ref_topic,
|
154
|
+
rating_manual=ref_manual,
|
155
|
+
supervisor_check=False,
|
156
|
+
)
|
157
|
+
)
|
123
158
|
return out.update_ref(article_proposal)
|
124
159
|
|
125
160
|
|
161
|
+
class GenerateArticle(Action):
|
162
|
+
"""Generate the article based on the outline."""
|
163
|
+
|
164
|
+
output_key: str = "article"
|
165
|
+
"""The key of the output data."""
|
166
|
+
|
167
|
+
async def _execute(
|
168
|
+
self,
|
169
|
+
article_outline: ArticleOutline,
|
170
|
+
**_,
|
171
|
+
) -> Optional[Article]:
|
172
|
+
article: Article = Article.from_outline(ok(article_outline, "Article outline not specified.")).update_ref(
|
173
|
+
article_outline
|
174
|
+
)
|
175
|
+
|
176
|
+
write_para_manual = ok(
|
177
|
+
await self.draft_rating_manual(w_topic := "write the following paragraph in the subsection.")
|
178
|
+
)
|
179
|
+
|
180
|
+
await gather(
|
181
|
+
*[
|
182
|
+
self.correct_obj_inplace(
|
183
|
+
subsec,
|
184
|
+
reference=f"# Original Article Outline\n{article_outline.display()}\n# Error Need to be fixed\n{err}",
|
185
|
+
topic=w_topic,
|
186
|
+
rating_manual=write_para_manual,
|
187
|
+
supervisor_check=False,
|
188
|
+
)
|
189
|
+
for _, __, subsec in article.iter_subsections()
|
190
|
+
if (err := subsec.introspect())
|
191
|
+
],
|
192
|
+
return_exceptions=True,
|
193
|
+
)
|
194
|
+
return article
|
195
|
+
|
196
|
+
|
126
197
|
class CorrectProposal(Action):
|
127
198
|
"""Correct the proposal of the article."""
|
128
199
|
|
@@ -150,60 +221,6 @@ class CorrectOutline(Action):
|
|
150
221
|
)
|
151
222
|
|
152
223
|
|
153
|
-
class GenerateArticle(Action):
|
154
|
-
"""Generate the article based on the outline."""
|
155
|
-
|
156
|
-
output_key: str = "article"
|
157
|
-
"""The key of the output data."""
|
158
|
-
|
159
|
-
async def _execute(
|
160
|
-
self,
|
161
|
-
article_outline: ArticleOutline,
|
162
|
-
**_,
|
163
|
-
) -> Optional[Article]:
|
164
|
-
article: Article = Article.from_outline(article_outline).update_ref(article_outline)
|
165
|
-
|
166
|
-
writing_manual = ok(await self.draft_rating_manual(
|
167
|
-
topic=(
|
168
|
-
topic_1
|
169
|
-
:= "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."
|
170
|
-
),
|
171
|
-
))
|
172
|
-
err_resolve_manual = ok(await self.draft_rating_manual(
|
173
|
-
topic=(topic_2 := "this article component has violated the constrain, please correct it.")
|
174
|
-
))
|
175
|
-
for c, deps in article.iter_dfs_with_deps(chapter=False):
|
176
|
-
logger.info(f"Updating the article component: \n{c.display()}")
|
177
|
-
|
178
|
-
out = ok(
|
179
|
-
await self.correct_obj(
|
180
|
-
c,
|
181
|
-
reference=(
|
182
|
-
ref := f"{article_outline.referenced.as_prompt()}\n" + "\n".join(d.display() for d in deps)
|
183
|
-
),
|
184
|
-
topic=topic_1,
|
185
|
-
rating_manual=writing_manual,
|
186
|
-
supervisor_check=False,
|
187
|
-
),
|
188
|
-
"Could not correct the article component.",
|
189
|
-
)
|
190
|
-
while err := c.resolve_update_conflict(out):
|
191
|
-
logger.warning(f"Found error in the article component: \n{err}")
|
192
|
-
out = ok(
|
193
|
-
await self.correct_obj(
|
194
|
-
out,
|
195
|
-
reference=f"{ref}\n\n# Violated Error\n{err}",
|
196
|
-
topic=topic_2,
|
197
|
-
rating_manual=err_resolve_manual,
|
198
|
-
supervisor_check=False,
|
199
|
-
),
|
200
|
-
"Could not correct the article component.",
|
201
|
-
)
|
202
|
-
|
203
|
-
c.update_from(out)
|
204
|
-
return article
|
205
|
-
|
206
|
-
|
207
224
|
class CorrectArticle(Action):
|
208
225
|
"""Correct the article based on the outline."""
|
209
226
|
|
fabricatio/actions/output.py
CHANGED
@@ -3,8 +3,9 @@
|
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import Optional
|
5
5
|
|
6
|
+
from fabricatio.journal import logger
|
6
7
|
from fabricatio.models.action import Action
|
7
|
-
from fabricatio.models.generic import FinalizedDumpAble
|
8
|
+
from fabricatio.models.generic import FinalizedDumpAble, PersistentAble
|
8
9
|
from fabricatio.models.task import Task
|
9
10
|
from fabricatio.models.utils import ok
|
10
11
|
|
@@ -32,3 +33,37 @@ class DumpFinalizedOutput(Action):
|
|
32
33
|
)
|
33
34
|
ok(to_dump, "Could not dump the data since the path is not specified.").finalized_dump_to(dump_path)
|
34
35
|
return dump_path.as_posix()
|
36
|
+
|
37
|
+
|
38
|
+
class PersistentAll(Action):
|
39
|
+
"""Persist all the data to a file."""
|
40
|
+
|
41
|
+
output_key: str = "persistent_count"
|
42
|
+
|
43
|
+
async def _execute(
|
44
|
+
self,
|
45
|
+
task_input: Optional[Task] = None,
|
46
|
+
persist_dir: Optional[str | Path] = None,
|
47
|
+
**cxt,
|
48
|
+
) -> int:
|
49
|
+
persist_dir = Path(
|
50
|
+
persist_dir
|
51
|
+
or ok(
|
52
|
+
await self.awhich_pathstr(
|
53
|
+
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 persist the data."
|
54
|
+
),
|
55
|
+
"Can not find the path of file to persist the data.",
|
56
|
+
)
|
57
|
+
)
|
58
|
+
|
59
|
+
count = 0
|
60
|
+
if persist_dir.is_file():
|
61
|
+
logger.warning("Dump should be a directory, but it is a file. Skip dumping.")
|
62
|
+
return count
|
63
|
+
persist_dir.mkdir(parents=True, exist_ok=True)
|
64
|
+
for v in cxt.values():
|
65
|
+
if isinstance(v, PersistentAble):
|
66
|
+
v.persist(persist_dir)
|
67
|
+
count += 1
|
68
|
+
|
69
|
+
return count
|
@@ -10,7 +10,7 @@ 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 CensoredAble, Display, ProposedAble, WithBriefing
|
13
|
+
from fabricatio.models.generic import CensoredAble, Display, ProposedAble, ProposedUpdateAble, WithBriefing
|
14
14
|
from fabricatio.models.kwargs_types import CensoredCorrectKwargs, CorrectKwargs, ReviewKwargs
|
15
15
|
from fabricatio.models.task import Task
|
16
16
|
from questionary import confirm, text
|
@@ -134,12 +134,32 @@ class Correct(Review):
|
|
134
134
|
while await confirm("Begin to correct obj above with human censorship?").ask_async():
|
135
135
|
while (topic := await text("What is the topic of the obj reviewing?").ask_async()) is not None and topic:
|
136
136
|
...
|
137
|
-
if (
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
if (
|
138
|
+
modified_obj := await self.correct_obj(
|
139
|
+
last_modified_obj,
|
140
|
+
topic=topic,
|
141
|
+
**kwargs,
|
142
|
+
)
|
143
|
+
) is None:
|
142
144
|
break
|
143
145
|
last_modified_obj = modified_obj
|
144
146
|
rprint(last_modified_obj.finalized_dump())
|
145
147
|
return modified_obj or last_modified_obj
|
148
|
+
|
149
|
+
async def correct_obj_inplace[M: ProposedUpdateAble](
|
150
|
+
self, obj: M, **kwargs: Unpack[CorrectKwargs[ReviewResult[str]]]
|
151
|
+
) -> Optional[M]:
|
152
|
+
"""Correct an object in place based on defined criteria and templates.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
obj (M): The object to be corrected.
|
156
|
+
**kwargs (Unpack[CensoredCorrectKwargs]): Additional keyword arguments for the correction process.
|
157
|
+
|
158
|
+
Returns:
|
159
|
+
Optional[M]: The corrected object, or None if correction fails.
|
160
|
+
"""
|
161
|
+
corrected_obj = await self.correct_obj(obj, **kwargs)
|
162
|
+
if corrected_obj is None:
|
163
|
+
return corrected_obj
|
164
|
+
obj.update_from(corrected_obj)
|
165
|
+
return obj
|
fabricatio/models/action.py
CHANGED
@@ -170,6 +170,8 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
|
|
170
170
|
# Update context with modified values
|
171
171
|
modified_ctx = await act_task
|
172
172
|
logger.success(f"Step execution finished: {current_action}")
|
173
|
+
if step.output_key:
|
174
|
+
logger.success(f"Setting output: {step.output_key}")
|
173
175
|
await self._context.put(modified_ctx)
|
174
176
|
|
175
177
|
logger.success(f"Workflow execution finished: {self.name}")
|
@@ -1,24 +1,22 @@
|
|
1
1
|
"""A foundation for hierarchical document components with dependency tracking."""
|
2
|
-
|
2
|
+
|
3
|
+
from abc import abstractmethod
|
3
4
|
from enum import StrEnum
|
4
|
-
from
|
5
|
+
from functools import cache
|
6
|
+
from itertools import chain
|
7
|
+
from typing import Generator, List, Optional, Self, Tuple
|
5
8
|
|
6
9
|
from fabricatio.models.generic import (
|
7
|
-
Base,
|
8
10
|
CensoredAble,
|
9
11
|
Display,
|
12
|
+
FinalizedDumpAble,
|
10
13
|
Introspect,
|
11
14
|
ModelHash,
|
12
15
|
PersistentAble,
|
13
|
-
|
16
|
+
ProposedUpdateAble,
|
14
17
|
ResolveUpdateConflict,
|
15
|
-
UpdateFrom,
|
16
18
|
)
|
17
19
|
|
18
|
-
if TYPE_CHECKING:
|
19
|
-
from fabricatio.models.extra.article_main import Article
|
20
|
-
from fabricatio.models.extra.article_outline import ArticleOutline
|
21
|
-
|
22
20
|
|
23
21
|
class ReferringType(StrEnum):
|
24
22
|
"""Enumeration of different types of references that can be made in an article."""
|
@@ -28,7 +26,11 @@ class ReferringType(StrEnum):
|
|
28
26
|
SUBSECTION = "subsection"
|
29
27
|
|
30
28
|
|
31
|
-
|
29
|
+
type RefKey = Tuple[str, Optional[str], Optional[str]]
|
30
|
+
|
31
|
+
|
32
|
+
@cache
|
33
|
+
class ArticleRef(CensoredAble, Display, ProposedUpdateAble):
|
32
34
|
"""Reference to a specific chapter, section or subsection within the article. You SHALL not refer to an article component that is external and not present within our own article.
|
33
35
|
|
34
36
|
Examples:
|
@@ -61,30 +63,21 @@ class ArticleRef(CensoredAble):
|
|
61
63
|
```
|
62
64
|
"""
|
63
65
|
|
64
|
-
referred_subsection_title: Optional[str] = None
|
65
|
-
"""`title` Field of the referenced subsection."""
|
66
|
-
|
67
|
-
referred_section_title: Optional[str] = None
|
68
|
-
"""`title` Field of the referenced section."""
|
69
|
-
|
70
66
|
referred_chapter_title: str
|
71
67
|
"""`title` Field of the referenced chapter"""
|
68
|
+
referred_section_title: Optional[str] = None
|
69
|
+
"""`title` Field of the referenced section."""
|
70
|
+
referred_subsection_title: Optional[str] = None
|
71
|
+
"""`title` Field of the referenced subsection."""
|
72
72
|
|
73
|
-
def
|
74
|
-
"""
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
"""Dereference the reference to the actual section or subsection within the provided article."""
|
80
|
-
|
81
|
-
@overload
|
82
|
-
def deref(self, article: "ArticleOutline") -> Optional["ArticleOutlineBase"]:
|
83
|
-
"""Dereference the reference to the actual section or subsection within the provided article."""
|
73
|
+
def update_from_inner(self, other: Self) -> Self:
|
74
|
+
"""Updates the current instance with the attributes of another instance."""
|
75
|
+
self.referred_chapter_title = other.referred_chapter_title
|
76
|
+
self.referred_section_title = other.referred_section_title
|
77
|
+
self.referred_subsection_title = other.referred_subsection_title
|
78
|
+
return self
|
84
79
|
|
85
|
-
def deref(
|
86
|
-
self, article: Union["ArticleOutline", "Article"]
|
87
|
-
) -> Union["ArticleOutlineBase", "ArticleMainBase", None]:
|
80
|
+
def deref(self, article: "ArticleBase") -> Optional["ArticleOutlineBase"]:
|
88
81
|
"""Dereference the reference to the actual section or subsection within the provided article.
|
89
82
|
|
90
83
|
Args:
|
@@ -111,21 +104,70 @@ class ArticleRef(CensoredAble):
|
|
111
104
|
return ReferringType.CHAPTER
|
112
105
|
|
113
106
|
|
114
|
-
class
|
115
|
-
|
107
|
+
class ArticleMetaData(CensoredAble, Display):
|
108
|
+
"""Metadata for an article component."""
|
109
|
+
|
110
|
+
description: str
|
111
|
+
"""Description of the research component in academic style."""
|
112
|
+
|
113
|
+
support_to: List[ArticleRef]
|
114
|
+
"""List of references to other component of this articles that this component supports."""
|
115
|
+
depend_on: List[ArticleRef]
|
116
|
+
"""List of references to other component of this articles that this component depends on."""
|
117
|
+
|
118
|
+
writing_aim: List[str]
|
119
|
+
"""List of writing aims of the research component in academic style."""
|
120
|
+
title: str
|
121
|
+
"""Do not add any prefix or suffix to the title. should not contain special characters."""
|
122
|
+
|
123
|
+
|
124
|
+
class ArticleOutlineBase(
|
125
|
+
ArticleMetaData,
|
126
|
+
ResolveUpdateConflict,
|
127
|
+
ProposedUpdateAble,
|
128
|
+
PersistentAble,
|
129
|
+
ModelHash,
|
116
130
|
Introspect,
|
117
131
|
):
|
118
|
-
"""Base class for article
|
132
|
+
"""Base class for article outlines."""
|
119
133
|
|
120
|
-
|
121
|
-
|
134
|
+
@property
|
135
|
+
def metadata(self) -> ArticleMetaData:
|
136
|
+
"""Returns the metadata of the article component."""
|
137
|
+
return ArticleMetaData.model_validate(self, from_attributes=True)
|
122
138
|
|
139
|
+
def update_metadata(self, other: ArticleMetaData) -> Self:
|
140
|
+
"""Updates the metadata of the current instance with the attributes of another instance."""
|
141
|
+
self.support_to.clear()
|
142
|
+
self.support_to.extend(other.support_to)
|
143
|
+
self.depend_on.clear()
|
144
|
+
self.depend_on.extend(other.depend_on)
|
145
|
+
self.writing_aim.clear()
|
146
|
+
self.writing_aim.extend(other.writing_aim)
|
147
|
+
self.description = other.description
|
148
|
+
return self
|
149
|
+
|
150
|
+
def display_metadata(self) -> str:
|
151
|
+
"""Displays the metadata of the current instance."""
|
152
|
+
return self.model_dump_json(
|
153
|
+
indent=1, include={"title", "writing_aim", "description", "support_to", "depend_on"}
|
154
|
+
)
|
155
|
+
|
156
|
+
def update_from_inner(self, other: Self) -> Self:
|
157
|
+
"""Updates the current instance with the attributes of another instance."""
|
158
|
+
return self.update_metadata(other)
|
159
|
+
|
160
|
+
@abstractmethod
|
123
161
|
def to_typst_code(self) -> str:
|
124
162
|
"""Converts the component into a Typst code snippet for rendering."""
|
125
|
-
return f"=== {self.title}\n"
|
126
163
|
|
127
|
-
|
128
|
-
|
164
|
+
|
165
|
+
class SubSectionBase(ArticleOutlineBase):
|
166
|
+
"""Base class for article sections and subsections."""
|
167
|
+
|
168
|
+
def to_typst_code(self) -> str:
|
169
|
+
"""Converts the component into a Typst code snippet for rendering."""
|
170
|
+
return f"=== {self.title}\n"
|
129
171
|
|
130
172
|
def introspect(self) -> str:
|
131
173
|
"""Introspects the article subsection outline."""
|
@@ -138,16 +180,11 @@ class SubSectionBase(
|
|
138
180
|
return ""
|
139
181
|
|
140
182
|
|
141
|
-
class SectionBase[T: SubSectionBase](
|
142
|
-
UpdateFrom,
|
143
|
-
Introspect,
|
144
|
-
):
|
183
|
+
class SectionBase[T: SubSectionBase](ArticleOutlineBase):
|
145
184
|
"""Base class for article sections and subsections."""
|
146
185
|
|
147
186
|
subsections: List[T]
|
148
187
|
"""Subsections of the section. Contains at least one subsection. You can also add more as needed."""
|
149
|
-
title: str
|
150
|
-
"""Title of the section, do not add any prefix or suffix to the title. should not contain special characters."""
|
151
188
|
|
152
189
|
def to_typst_code(self) -> str:
|
153
190
|
"""Converts the section into a Typst formatted code snippet.
|
@@ -174,6 +211,7 @@ class SectionBase[T: SubSectionBase](
|
|
174
211
|
|
175
212
|
def update_from_inner(self, other: Self) -> Self:
|
176
213
|
"""Updates the current instance with the attributes of another instance."""
|
214
|
+
super().update_from_inner(other)
|
177
215
|
if len(self.subsections) == 0:
|
178
216
|
self.subsections = other.subsections
|
179
217
|
return self
|
@@ -189,16 +227,12 @@ class SectionBase[T: SubSectionBase](
|
|
189
227
|
return ""
|
190
228
|
|
191
229
|
|
192
|
-
class ChapterBase[T: SectionBase](
|
193
|
-
UpdateFrom,
|
194
|
-
Introspect,
|
195
|
-
):
|
230
|
+
class ChapterBase[T: SectionBase](ArticleOutlineBase):
|
196
231
|
"""Base class for article chapters."""
|
197
232
|
|
198
233
|
sections: List[T]
|
199
234
|
"""Sections of the chapter. Contains at least one section. You can also add more as needed."""
|
200
|
-
|
201
|
-
"""Title of the chapter, do not add any prefix or suffix to the title. should not contain special characters."""
|
235
|
+
|
202
236
|
def to_typst_code(self) -> str:
|
203
237
|
"""Converts the chapter into a Typst formatted code snippet for rendering."""
|
204
238
|
return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
|
@@ -233,21 +267,35 @@ class ChapterBase[T: SectionBase](
|
|
233
267
|
return ""
|
234
268
|
|
235
269
|
|
236
|
-
class ArticleBase[T: ChapterBase](
|
270
|
+
class ArticleBase[T: ChapterBase](FinalizedDumpAble):
|
237
271
|
"""Base class for article outlines."""
|
238
272
|
|
273
|
+
language: str
|
274
|
+
"""Written language of the article. SHALL be aligned to the language of the article proposal provided."""
|
275
|
+
|
276
|
+
title: str
|
277
|
+
"""Title of the academic paper."""
|
278
|
+
|
279
|
+
prospect: str
|
280
|
+
"""Consolidated research statement with four pillars:
|
281
|
+
1. Problem Identification: Current limitations
|
282
|
+
2. Methodological Response: Technical approach
|
283
|
+
3. Empirical Validation: Evaluation strategy
|
284
|
+
4. Scholarly Impact: Field contributions
|
285
|
+
"""
|
286
|
+
|
287
|
+
abstract: str
|
288
|
+
"""The abstract is a concise summary of the academic paper's main findings."""
|
239
289
|
chapters: List[T]
|
240
290
|
"""Chapters of the article. Contains at least one chapter. You can also add more as needed."""
|
241
|
-
title: str
|
242
|
-
"""Title of the article outline, do not add any prefix or suffix to the title. should not contain special characters."""
|
243
291
|
|
244
|
-
def
|
292
|
+
def iter_dfs_rev(
|
245
293
|
self,
|
246
|
-
) -> Generator[
|
247
|
-
"""Performs a depth-first search (DFS) through the article structure.
|
294
|
+
) -> Generator[ArticleOutlineBase, None, None]:
|
295
|
+
"""Performs a depth-first search (DFS) through the article structure in reverse order.
|
248
296
|
|
249
297
|
Returns:
|
250
|
-
Generator[ArticleMainBase]: Each component in the article structure.
|
298
|
+
Generator[ArticleMainBase]: Each component in the article structure in reverse order.
|
251
299
|
"""
|
252
300
|
for chap in self.chapters:
|
253
301
|
for sec in chap.sections:
|
@@ -255,37 +303,80 @@ class ArticleBase[T: ChapterBase](Base):
|
|
255
303
|
yield sec
|
256
304
|
yield chap
|
257
305
|
|
306
|
+
def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
|
307
|
+
"""Performs a depth-first search (DFS) through the article structure.
|
258
308
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
ABC,
|
268
|
-
):
|
269
|
-
"""Base class for article outlines."""
|
309
|
+
Returns:
|
310
|
+
Generator[ArticleMainBase]: Each component in the article structure.
|
311
|
+
"""
|
312
|
+
for chap in self.chapters:
|
313
|
+
yield chap
|
314
|
+
for sec in chap.sections:
|
315
|
+
yield sec
|
316
|
+
yield from sec.subsections
|
270
317
|
|
271
|
-
|
272
|
-
|
318
|
+
def iter_sections(self) -> Generator[Tuple[ChapterBase, SectionBase], None, None]:
|
319
|
+
"""Iterates through all sections in the article.
|
273
320
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
321
|
+
Returns:
|
322
|
+
Generator[ArticleOutlineBase]: Each section in the article.
|
323
|
+
"""
|
324
|
+
for chap in self.chapters:
|
325
|
+
for sec in chap.sections:
|
326
|
+
yield chap, sec
|
278
327
|
|
279
|
-
|
280
|
-
|
328
|
+
def iter_subsections(self) -> Generator[Tuple[ChapterBase, SectionBase, SubSectionBase], None, None]:
|
329
|
+
"""Iterates through all subsections in the article.
|
281
330
|
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
self.
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
331
|
+
Returns:
|
332
|
+
Generator[ArticleOutlineBase]: Each subsection in the article.
|
333
|
+
"""
|
334
|
+
for chap, sec in self.iter_sections():
|
335
|
+
for subsec in sec.subsections:
|
336
|
+
yield chap, sec, subsec
|
337
|
+
|
338
|
+
def find_introspected(self) -> Optional[Tuple[ArticleOutlineBase, str]]:
|
339
|
+
"""Finds the first introspected component in the article structure."""
|
340
|
+
summary = ""
|
341
|
+
for component in self.iter_dfs_rev():
|
342
|
+
summary += component.introspect()
|
343
|
+
if summary:
|
344
|
+
return component, summary
|
345
|
+
return None
|
346
|
+
|
347
|
+
def find_illegal_ref(self) -> Optional[Tuple[ArticleRef, str]]:
|
348
|
+
"""Finds the first illegal component in the outline.
|
349
|
+
|
350
|
+
Returns:
|
351
|
+
Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
|
352
|
+
"""
|
353
|
+
summary = ""
|
354
|
+
for component in self.iter_dfs_rev():
|
355
|
+
for ref in chain(component.depend_on, component.support_to):
|
356
|
+
if not ref.deref(self):
|
357
|
+
summary += f"Invalid internal reference in `{component.__class__.__name__}` titled `{component.title}`, because the referred {ref.referring_type} is not exists within the article, see the original obj dump: {ref.model_dump()}\n"
|
358
|
+
if summary:
|
359
|
+
return ref, summary
|
360
|
+
return None
|
361
|
+
|
362
|
+
def finalized_dump(self) -> str:
|
363
|
+
"""Generates standardized hierarchical markup for academic publishing systems.
|
364
|
+
|
365
|
+
Implements ACL 2024 outline conventions with four-level structure:
|
366
|
+
= Chapter Title (Level 1)
|
367
|
+
== Section Title (Level 2)
|
368
|
+
=== Subsection Title (Level 3)
|
369
|
+
==== Subsubsection Title (Level 4)
|
370
|
+
|
371
|
+
Returns:
|
372
|
+
str: Strictly formatted outline with academic sectioning
|
373
|
+
|
374
|
+
Example:
|
375
|
+
= Methodology
|
376
|
+
== Neural Architecture Search Framework
|
377
|
+
=== Differentiable Search Space
|
378
|
+
==== Constrained Optimization Parameters
|
379
|
+
=== Implementation Details
|
380
|
+
== Evaluation Protocol
|
381
|
+
"""
|
382
|
+
return "\n\n".join(a.to_typst_code() for a in self.chapters)
|
@@ -7,7 +7,6 @@ from fabricatio.journal import logger
|
|
7
7
|
from fabricatio.models.extra.article_base import (
|
8
8
|
ArticleBase,
|
9
9
|
ArticleOutlineBase,
|
10
|
-
ArticleRef,
|
11
10
|
ChapterBase,
|
12
11
|
SectionBase,
|
13
12
|
SubSectionBase,
|
@@ -28,21 +27,26 @@ class Paragraph(CensoredAble):
|
|
28
27
|
writing_aim: List[str]
|
29
28
|
"""Specific communicative objectives for this paragraph's content."""
|
30
29
|
|
31
|
-
|
32
|
-
"""
|
30
|
+
content: str
|
31
|
+
"""The actual content of the paragraph, represented as a string."""
|
33
32
|
|
34
33
|
|
35
|
-
class ArticleSubsection(
|
34
|
+
class ArticleSubsection(SubSectionBase):
|
36
35
|
"""Atomic argumentative unit with technical specificity."""
|
37
36
|
|
38
37
|
paragraphs: List[Paragraph]
|
39
38
|
"""List of Paragraph objects containing the content of the subsection."""
|
40
39
|
|
40
|
+
def introspect(self) -> str:
|
41
|
+
"""Introspects the subsection and returns a message if it has no paragraphs."""
|
42
|
+
if len(self.paragraphs) == 0:
|
43
|
+
return f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs, to achieve the goal of `{self.writing_aim}`."
|
44
|
+
return ""
|
45
|
+
|
41
46
|
def update_from_inner(self, other: Self) -> Self:
|
42
47
|
"""Updates the current instance with the attributes of another instance."""
|
43
48
|
logger.debug(f"Updating SubSection {self.title}")
|
44
|
-
|
45
|
-
ArticleOutlineBase.update_from(self, other)
|
49
|
+
super().update_from_inner(other)
|
46
50
|
self.paragraphs.clear()
|
47
51
|
self.paragraphs.extend(other.paragraphs)
|
48
52
|
return self
|
@@ -53,19 +57,17 @@ class ArticleSubsection(ArticleOutlineBase, SubSectionBase):
|
|
53
57
|
Returns:
|
54
58
|
str: Typst code snippet for rendering.
|
55
59
|
"""
|
56
|
-
return f"=== {self.title}\n" + "\n\n".join(
|
60
|
+
return f"=== {self.title}\n" + "\n\n".join(p.content for p in self.paragraphs)
|
57
61
|
|
58
62
|
|
59
|
-
class ArticleSection(
|
63
|
+
class ArticleSection(SectionBase[ArticleSubsection]):
|
60
64
|
"""Atomic argumentative unit with high-level specificity."""
|
61
65
|
|
62
66
|
|
63
|
-
|
64
|
-
class ArticleChapter(ArticleOutlineBase, ChapterBase[ArticleSection]):
|
67
|
+
class ArticleChapter(ChapterBase[ArticleSection]):
|
65
68
|
"""Thematic progression implementing research function."""
|
66
69
|
|
67
70
|
|
68
|
-
|
69
71
|
class Article(
|
70
72
|
Display,
|
71
73
|
CensoredAble,
|
@@ -79,22 +81,9 @@ class Article(
|
|
79
81
|
aiming to provide a comprehensive model for academic papers.
|
80
82
|
"""
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
title: str
|
86
|
-
"""Represents the title of the academic paper."""
|
87
|
-
|
88
|
-
language: str
|
89
|
-
"""Written language of the article. SHALL be aligned to the language of the article outline provided."""
|
90
|
-
|
91
|
-
def finalized_dump(self) -> str:
|
92
|
-
"""Exports the article in `typst` format.
|
93
|
-
|
94
|
-
Returns:
|
95
|
-
str: Strictly formatted outline with typst formatting.
|
96
|
-
"""
|
97
|
-
return "\n\n".join(c.to_typst_code() for c in self.chapters)
|
84
|
+
@override
|
85
|
+
def iter_subsections(self) -> Generator[Tuple[ArticleChapter, ArticleSection, ArticleSubsection], None, None]:
|
86
|
+
return super().iter_subsections()
|
98
87
|
|
99
88
|
@classmethod
|
100
89
|
def from_outline(cls, outline: ArticleOutline) -> "Article":
|
@@ -107,7 +96,7 @@ class Article(
|
|
107
96
|
Article: The generated article.
|
108
97
|
"""
|
109
98
|
# Set the title from the outline
|
110
|
-
article = Article(**outline.model_dump(
|
99
|
+
article = Article(**outline.model_dump(exclude={"chapters"}), chapters=[])
|
111
100
|
|
112
101
|
for chapter in outline.chapters:
|
113
102
|
# Create a new chapter
|
@@ -132,33 +121,16 @@ class Article(
|
|
132
121
|
article.chapters.append(article_chapter)
|
133
122
|
return article
|
134
123
|
|
135
|
-
@override
|
136
|
-
def iter_dfs(
|
137
|
-
self,
|
138
|
-
) -> Generator[ArticleChapter | ArticleSection | ArticleSubsection, None, None]:
|
139
|
-
return super().iter_dfs()
|
140
|
-
|
141
|
-
def deref(self, ref: ArticleRef) -> ArticleOutlineBase:
|
142
|
-
"""Resolves a reference to the corresponding section or subsection in the article.
|
143
|
-
|
144
|
-
Args:
|
145
|
-
ref (ArticleRef): The reference to resolve.
|
146
|
-
|
147
|
-
Returns:
|
148
|
-
ArticleOutlineBase: The corresponding section or subsection.
|
149
|
-
"""
|
150
|
-
return ok(ref.deref(self), f"{ref} not found in {self.title}")
|
151
|
-
|
152
124
|
def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
|
153
125
|
"""Gathers dependencies for all sections and subsections in the article.
|
154
126
|
|
155
127
|
This method should be called after the article is fully constructed.
|
156
128
|
"""
|
157
|
-
depends = [
|
129
|
+
depends = [ok(a.deref(self)) for a in article.depend_on]
|
158
130
|
|
159
131
|
supports = []
|
160
|
-
for a in self.
|
161
|
-
if article in {
|
132
|
+
for a in self.iter_dfs_rev():
|
133
|
+
if article in {ok(b.deref(self)) for b in a.support_to}:
|
162
134
|
supports.append(a)
|
163
135
|
|
164
136
|
return list(set(depends + supports))
|
@@ -220,7 +192,7 @@ class Article(
|
|
220
192
|
if all((not chapter, not section, not subsection)):
|
221
193
|
raise ValueError("At least one of chapter, section, or subsection must be True.")
|
222
194
|
|
223
|
-
for component in self.
|
195
|
+
for component in self.iter_dfs_rev():
|
224
196
|
if not chapter and isinstance(component, ArticleChapter):
|
225
197
|
continue
|
226
198
|
if not section and isinstance(component, ArticleSection):
|
@@ -1,45 +1,26 @@
|
|
1
1
|
"""A module containing the ArticleOutline class, which represents the outline of an academic paper."""
|
2
2
|
|
3
|
-
from typing import Generator, List, Optional, Self, Tuple, Union, override
|
4
|
-
|
5
|
-
import regex
|
6
3
|
from fabricatio.models.extra.article_base import (
|
7
4
|
ArticleBase,
|
8
|
-
ArticleOutlineBase,
|
9
5
|
ChapterBase,
|
10
6
|
SectionBase,
|
11
7
|
SubSectionBase,
|
12
8
|
)
|
13
9
|
from fabricatio.models.extra.article_proposal import ArticleProposal
|
14
10
|
from fabricatio.models.generic import CensoredAble, Display, PersistentAble, WithRef
|
15
|
-
from fabricatio.models.utils import ok
|
16
11
|
|
17
12
|
|
18
|
-
class ArticleSubsectionOutline(
|
13
|
+
class ArticleSubsectionOutline(SubSectionBase):
|
19
14
|
"""Atomic research component specification for academic paper generation."""
|
20
15
|
|
21
16
|
|
22
|
-
|
23
|
-
class ArticleSectionOutline(ArticleOutlineBase, SectionBase[ArticleSubsectionOutline]):
|
17
|
+
class ArticleSectionOutline(SectionBase[ArticleSubsectionOutline]):
|
24
18
|
"""A slightly more detailed research component specification for academic paper generation, Must contain subsections."""
|
25
19
|
|
26
20
|
|
27
|
-
|
28
|
-
"""Updates the current instance with the attributes of another instance."""
|
29
|
-
super().update_from_inner(other)
|
30
|
-
super(ArticleOutlineBase, self).update_from_inner(other)
|
31
|
-
return self
|
32
|
-
|
33
|
-
|
34
|
-
class ArticleChapterOutline(ArticleOutlineBase, ChapterBase[ArticleSectionOutline]):
|
21
|
+
class ArticleChapterOutline(ChapterBase[ArticleSectionOutline]):
|
35
22
|
"""Macro-structural unit implementing standard academic paper organization. Must contain sections."""
|
36
23
|
|
37
|
-
def update_from_inner(self, other: Self) -> Self:
|
38
|
-
"""Updates the current instance with the attributes of another instance."""
|
39
|
-
super().update_from_inner(other)
|
40
|
-
super(ArticleOutlineBase, self).update_from_inner(other)
|
41
|
-
return self
|
42
|
-
|
43
24
|
|
44
25
|
class ArticleOutline(
|
45
26
|
Display,
|
@@ -48,140 +29,4 @@ class ArticleOutline(
|
|
48
29
|
PersistentAble,
|
49
30
|
ArticleBase[ArticleChapterOutline],
|
50
31
|
):
|
51
|
-
"""
|
52
|
-
|
53
|
-
abstract: str
|
54
|
-
"""The abstract is a concise summary of the academic paper's main findings."""
|
55
|
-
|
56
|
-
prospect: str
|
57
|
-
"""Consolidated research statement with four pillars:
|
58
|
-
1. Problem Identification: Current limitations
|
59
|
-
2. Methodological Response: Technical approach
|
60
|
-
3. Empirical Validation: Evaluation strategy
|
61
|
-
4. Scholarly Impact: Field contributions
|
62
|
-
"""
|
63
|
-
|
64
|
-
title: str
|
65
|
-
"""Title of the academic paper."""
|
66
|
-
|
67
|
-
language: str
|
68
|
-
"""Written language of the article. SHALL be aligned to the language of the article proposal provided."""
|
69
|
-
|
70
|
-
def finalized_dump(self) -> str:
|
71
|
-
"""Generates standardized hierarchical markup for academic publishing systems.
|
72
|
-
|
73
|
-
Implements ACL 2024 outline conventions with four-level structure:
|
74
|
-
= Chapter Title (Level 1)
|
75
|
-
== Section Title (Level 2)
|
76
|
-
=== Subsection Title (Level 3)
|
77
|
-
==== Subsubsection Title (Level 4)
|
78
|
-
|
79
|
-
Returns:
|
80
|
-
str: Strictly formatted outline with academic sectioning
|
81
|
-
|
82
|
-
Example:
|
83
|
-
= Methodology
|
84
|
-
== Neural Architecture Search Framework
|
85
|
-
=== Differentiable Search Space
|
86
|
-
==== Constrained Optimization Parameters
|
87
|
-
=== Implementation Details
|
88
|
-
== Evaluation Protocol
|
89
|
-
"""
|
90
|
-
lines: List[str] = []
|
91
|
-
for i, chapter in enumerate(self.chapters, 1):
|
92
|
-
lines.append(f"= Chapter {i}: {chapter.title}")
|
93
|
-
for j, section in enumerate(chapter.sections, 1):
|
94
|
-
lines.append(f"== {i}.{j} {section.title}")
|
95
|
-
for k, subsection in enumerate(section.subsections, 1):
|
96
|
-
lines.append(f"=== {i}.{j}.{k} {subsection.title}")
|
97
|
-
return "\n".join(lines)
|
98
|
-
|
99
|
-
@override
|
100
|
-
def iter_dfs(
|
101
|
-
self,
|
102
|
-
) -> Generator[ArticleChapterOutline | ArticleSectionOutline | ArticleSubsectionOutline, None, None]:
|
103
|
-
return super().iter_dfs()
|
104
|
-
def find_illegal(self) -> Optional[Tuple[ArticleOutlineBase, str]]:
|
105
|
-
"""Finds the first illegal component in the outline.
|
106
|
-
|
107
|
-
Returns:
|
108
|
-
Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
|
109
|
-
"""
|
110
|
-
summary = ""
|
111
|
-
for component in self.iter_dfs():
|
112
|
-
for ref in component.depend_on:
|
113
|
-
if not ref.deref(self):
|
114
|
-
summary += f"Invalid internal reference in {component.__class__.__name__} titled `{component.title}` at `depend_on` field, because the referred {ref.referring_type} is not exists within the article, see the original obj dump: {ref.model_dump()}\n"
|
115
|
-
for ref in component.support_to:
|
116
|
-
if not ref.deref(self):
|
117
|
-
summary += f"Invalid internal reference in {component.__class__.__name__} titled `{component.title}` at `support_to` field, because the referred {ref.referring_type} is not exists within the article, see the original obj dump: {ref.model_dump()}\n"
|
118
|
-
summary += component.introspect()
|
119
|
-
if summary:
|
120
|
-
return component, summary
|
121
|
-
return None
|
122
|
-
|
123
|
-
@classmethod
|
124
|
-
def from_typst_code(
|
125
|
-
cls, typst_code: str, title: str = "", article_language: str = "en", prospect: str = "", abstract: str = ""
|
126
|
-
) -> "ArticleOutline":
|
127
|
-
"""Parses a Typst code string and creates an ArticleOutline instance."""
|
128
|
-
self = cls(language=article_language, prospect=prospect, abstract=abstract, chapters=[], title=title)
|
129
|
-
stack = [self] # 根节点为ArticleOutline实例
|
130
|
-
|
131
|
-
for line in typst_code.splitlines():
|
132
|
-
parsed = cls._parse_line(line)
|
133
|
-
if not parsed:
|
134
|
-
continue
|
135
|
-
level, title = parsed
|
136
|
-
cls._adjust_stack(stack, level)
|
137
|
-
parent = stack[-1]
|
138
|
-
component = cls._create_component(level, title)
|
139
|
-
cls._add_to_parent(parent, component, level)
|
140
|
-
stack.append(component)
|
141
|
-
|
142
|
-
return self
|
143
|
-
|
144
|
-
@classmethod
|
145
|
-
def _parse_line(cls, line: str) -> Optional[Tuple[int, str]]:
|
146
|
-
stripped = line.strip()
|
147
|
-
if not stripped.startswith("="):
|
148
|
-
return None
|
149
|
-
match = regex.match(r"^(\=+)(.*)", stripped)
|
150
|
-
if not match:
|
151
|
-
return None
|
152
|
-
eqs, title_part = match.groups()
|
153
|
-
return len(eqs), title_part.strip()
|
154
|
-
|
155
|
-
@classmethod
|
156
|
-
def _adjust_stack(cls, stack: List[object], target_level: int) -> None:
|
157
|
-
while len(stack) > target_level:
|
158
|
-
stack.pop()
|
159
|
-
|
160
|
-
@classmethod
|
161
|
-
def _create_component(cls, level: int, title: str) -> ArticleOutlineBase:
|
162
|
-
default_kwargs = {
|
163
|
-
"writing_aim": [],
|
164
|
-
"depend_on": [],
|
165
|
-
"support_to": [],
|
166
|
-
"description": [],
|
167
|
-
}
|
168
|
-
component_map = {
|
169
|
-
1: lambda: ArticleChapterOutline(title=title, sections=[], **default_kwargs),
|
170
|
-
2: lambda: ArticleSectionOutline(title=title, subsections=[], **default_kwargs),
|
171
|
-
3: lambda: ArticleSubsectionOutline(title=title, **default_kwargs),
|
172
|
-
}
|
173
|
-
return ok(component_map.get(level, lambda: None)(), "Invalid level")
|
174
|
-
|
175
|
-
@classmethod
|
176
|
-
def _add_to_parent(
|
177
|
-
cls,
|
178
|
-
parent: Union["ArticleOutline", ArticleChapterOutline, ArticleSectionOutline],
|
179
|
-
component: ArticleOutlineBase,
|
180
|
-
level: int,
|
181
|
-
) -> None:
|
182
|
-
if level == 1 and isinstance(component, ArticleChapterOutline):
|
183
|
-
parent.chapters.append(component)
|
184
|
-
elif level == 2 and isinstance(component, ArticleSectionOutline): # noqa: PLR2004
|
185
|
-
parent.sections.append(component)
|
186
|
-
elif level == 3 and isinstance(component, ArticleSubsectionOutline): # noqa: PLR2004
|
187
|
-
parent.subsections.append(component)
|
32
|
+
"""Outline of an academic paper, containing chapters, sections, subsections."""
|
@@ -11,25 +11,23 @@ class ArticleProposal(CensoredAble, Display, WithRef[str], AsPrompt, PersistentA
|
|
11
11
|
Guides LLM in generating comprehensive research proposals with clearly defined components.
|
12
12
|
"""
|
13
13
|
|
14
|
+
language: str
|
15
|
+
"""The language in which the article is written. This should align with the language specified in the article briefing."""
|
16
|
+
|
17
|
+
title: str
|
18
|
+
"""The title of the academic paper, formatted in Title Case."""
|
19
|
+
|
20
|
+
focused_problem: List[str]
|
21
|
+
"""A list of specific research problems or questions that the paper aims to address."""
|
22
|
+
|
14
23
|
technical_approaches: List[str]
|
15
|
-
"""
|
24
|
+
"""A list of technical approaches or methodologies used to solve the research problems."""
|
16
25
|
|
17
26
|
research_methods: List[str]
|
18
|
-
"""
|
19
|
-
Example: ['Differentiable architecture search', 'Transformer-based search space', 'Multi-lingual perplexity evaluation']"""
|
27
|
+
"""A list of methodological components, including techniques and tools utilized in the research."""
|
20
28
|
|
21
29
|
research_aim: List[str]
|
22
|
-
"""
|
23
|
-
Example: ['Develop parameter-efficient NAS framework', 'Establish cross-lingual architecture transfer metrics']"""
|
24
|
-
|
25
|
-
focused_problem: List[str]
|
26
|
-
"""Specific research problem(s) or question(s) addressed (list of 1-3 concise statements).
|
27
|
-
Example: ['NAS computational overhead in low-resource settings', 'Architecture transferability across language pairs']"""
|
28
|
-
|
29
|
-
title: str
|
30
|
-
"""Paper title in academic style (Title Case, 8-15 words). Example: 'Exploring Neural Architecture Search for Low-Resource Machine Translation'"""
|
31
|
-
language: str
|
32
|
-
"""Written language of the article. SHALL be aligned to the language of the article briefing provided."""
|
30
|
+
"""A list of primary research objectives that the paper seeks to achieve."""
|
33
31
|
|
34
32
|
def _as_prompt_inner(self) -> Dict[str, str]:
|
35
33
|
return {"ArticleBriefing": self.referenced, "ArticleProposal": self.display()}
|
fabricatio/models/generic.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""This module defines generic classes for models in the Fabricatio library."""
|
2
2
|
|
3
|
-
from abc import abstractmethod
|
3
|
+
from abc import ABC, abstractmethod
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Self, Union, final, overload
|
6
6
|
|
@@ -108,7 +108,7 @@ class PersistentAble(Base):
|
|
108
108
|
"""Class that provides a method to persist the object."""
|
109
109
|
|
110
110
|
def persist(self, path: str | Path) -> Self:
|
111
|
-
"""Persist the object to a file.
|
111
|
+
"""Persist the object to a file or directory.
|
112
112
|
|
113
113
|
Args:
|
114
114
|
path (str | Path): The path to save the object.
|
@@ -117,7 +117,7 @@ class PersistentAble(Base):
|
|
117
117
|
Self: The current instance of the object.
|
118
118
|
"""
|
119
119
|
p = Path(path)
|
120
|
-
out = self.model_dump_json()
|
120
|
+
out = self.model_dump_json(indent=1)
|
121
121
|
if p.is_dir():
|
122
122
|
p.joinpath(f"{self.__class__.__name__}_{blake3_hash(out.encode())[:6]}.json").write_text(
|
123
123
|
out, encoding="utf-8"
|
@@ -125,6 +125,7 @@ class PersistentAble(Base):
|
|
125
125
|
return self
|
126
126
|
p.mkdir(exist_ok=True, parents=True)
|
127
127
|
p.write_text(out, encoding="utf-8")
|
128
|
+
logger.info(f"Persisted {self} to {p.as_posix()}")
|
128
129
|
return self
|
129
130
|
|
130
131
|
def from_persistent(self, path: str | Path) -> Self:
|
@@ -228,20 +229,11 @@ class WithBriefing(Named, Described):
|
|
228
229
|
raise TypeError(f"{system_msg_like} is not a dict or str")
|
229
230
|
|
230
231
|
|
231
|
-
class
|
232
|
+
class UnsortGenerate(GenerateJsonSchema):
|
232
233
|
"""Class that provides a reverse JSON schema of the model."""
|
233
234
|
|
234
|
-
def
|
235
|
-
|
236
|
-
sorted_dict: dict[str, JsonSchemaValue] = {}
|
237
|
-
# Reverse all keys regardless of parent_key
|
238
|
-
keys = reversed(value.keys())
|
239
|
-
for key in keys:
|
240
|
-
sorted_dict[key] = self._sort_recursive(value[key], parent_key=key)
|
241
|
-
return sorted_dict
|
242
|
-
if isinstance(value, list):
|
243
|
-
# Reverse list order and process each item
|
244
|
-
return [self._sort_recursive(item, parent_key) for item in reversed(value)]
|
235
|
+
def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue:
|
236
|
+
"""Not sort."""
|
245
237
|
return value
|
246
238
|
|
247
239
|
|
@@ -256,7 +248,7 @@ class WithFormatedJsonSchema(Base):
|
|
256
248
|
str: The JSON schema of the model in a formatted string.
|
257
249
|
"""
|
258
250
|
return orjson.dumps(
|
259
|
-
cls.model_json_schema(schema_generator=
|
251
|
+
cls.model_json_schema(schema_generator=UnsortGenerate),
|
260
252
|
option=orjson.OPT_INDENT_2,
|
261
253
|
).decode()
|
262
254
|
|
@@ -316,6 +308,10 @@ class ProposedAble(CreateJsonObjPrompt, InstantiateFromString):
|
|
316
308
|
"""Class that provides a method to propose a JSON object based on the requirement."""
|
317
309
|
|
318
310
|
|
311
|
+
class ProposedUpdateAble(PersistentAble, UpdateFrom, ABC):
|
312
|
+
"""Make the obj can be updated from the proposed obj in place."""
|
313
|
+
|
314
|
+
|
319
315
|
class FinalizedDumpAble(Base):
|
320
316
|
"""Class that provides a method to finalize the dump of the object."""
|
321
317
|
|
fabricatio/models/usages.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""This module contains classes that manage the usage of language models and tools in tasks."""
|
2
2
|
|
3
|
+
import traceback
|
3
4
|
from asyncio import gather
|
4
5
|
from typing import Callable, Dict, Iterable, List, Optional, Self, Sequence, Set, Type, Union, Unpack, overload
|
5
6
|
|
@@ -38,6 +39,8 @@ ROUTER = Router(
|
|
38
39
|
allowed_fails=configs.routing.allowed_fails,
|
39
40
|
retry_after=configs.routing.retry_after,
|
40
41
|
cooldown_time=configs.routing.cooldown_time,
|
42
|
+
cache_responses=configs.cache.enabled,
|
43
|
+
cache_kwargs=configs.cache.params,
|
41
44
|
)
|
42
45
|
|
43
46
|
|
@@ -135,7 +138,7 @@ class LLMUsage(ScopedConfig):
|
|
135
138
|
List[Choices | StreamingChoices]: A list of choices or streaming choices from the model response.
|
136
139
|
"""
|
137
140
|
resp = await self.aquery(
|
138
|
-
messages=Messages().add_system_message(system_message).add_user_message(question),
|
141
|
+
messages=Messages().add_system_message(system_message).add_user_message(question).as_list(),
|
139
142
|
n=n,
|
140
143
|
**kwargs,
|
141
144
|
)
|
@@ -324,6 +327,7 @@ class LLMUsage(ScopedConfig):
|
|
324
327
|
continue
|
325
328
|
except Exception as e: # noqa: BLE001
|
326
329
|
logger.error(f"Error during validation: \n{e}")
|
330
|
+
logger.debug(traceback.format_exc())
|
327
331
|
break
|
328
332
|
if not kwargs.get("no_cache"):
|
329
333
|
kwargs["no_cache"] = True
|
Binary file
|
@@ -1,22 +1,22 @@
|
|
1
|
-
fabricatio-0.2.
|
2
|
-
fabricatio-0.2.
|
3
|
-
fabricatio-0.2.
|
1
|
+
fabricatio-0.2.8.dev0.dist-info/METADATA,sha256=jgTrZJZiqFyLRjwYSr96zd6uT0LKFrsao5BS5GTw49Q,5122
|
2
|
+
fabricatio-0.2.8.dev0.dist-info/WHEEL,sha256=7FgAcpQES0h1xhfN9Ugve9FTUilU6sRAr1WJ5ph2cuw,108
|
3
|
+
fabricatio-0.2.8.dev0.dist-info/licenses/LICENSE,sha256=yDZaTLnOi03bi3Dk6f5IjhLUc5old2yOsihHWU0z-i0,1067
|
4
4
|
fabricatio/decorators.py,sha256=GrkclNTGT2bk7cjTyuca7mqSVlKwTcujcj3uBuZpT8s,7343
|
5
5
|
fabricatio/core.py,sha256=MaEKZ6DDmbdScAY-7F1gwGA6fr7ADX6Mz5rNVi2msFA,6277
|
6
|
-
fabricatio/models/generic.py,sha256=
|
6
|
+
fabricatio/models/generic.py,sha256=gbm1BnJqhdw42rx2Dcq1f13EnLVUKqFOJB_LxQhQg3M,18664
|
7
7
|
fabricatio/models/tool.py,sha256=ifivEnYiEUtjeRxQkX8vjfyzn1m1acgfrsABbQqCsGs,6912
|
8
8
|
fabricatio/models/role.py,sha256=UgIfGdfIBu4cOug8Nm1a04JCEwjXR_MDZUQhumwMptk,2710
|
9
9
|
fabricatio/models/kwargs_types.py,sha256=2tiApAy8JvpkDWKk_26_X1_g08mcHHKQz3c8pCd73x0,5286
|
10
10
|
fabricatio/models/utils.py,sha256=MYwUmvr2P2F4Q3b73IUft2D8WG5FN3hMHLBXKtru0kU,5678
|
11
|
-
fabricatio/models/extra/article_proposal.py,sha256=
|
12
|
-
fabricatio/models/extra/article_main.py,sha256=
|
11
|
+
fabricatio/models/extra/article_proposal.py,sha256=UGH55J6T4oR6tPiOxP4nmUIXavBHUOURKlibT2PiQn0,1365
|
12
|
+
fabricatio/models/extra/article_main.py,sha256=ssh8FWo5HzOdm77LArsQzv61yg3CBKMSxtctjYbmxM4,7519
|
13
13
|
fabricatio/models/extra/article_essence.py,sha256=q80VKO11QONkounDL98IpfG6DSC8ymfm7iQiWkzp3rY,9003
|
14
|
-
fabricatio/models/extra/article_outline.py,sha256=
|
15
|
-
fabricatio/models/extra/article_base.py,sha256=
|
16
|
-
fabricatio/models/usages.py,sha256=
|
14
|
+
fabricatio/models/extra/article_outline.py,sha256=aiqPCB4j-m3dl5n3R7gn_Idqx5kST1hbnGZhondz2DA,1113
|
15
|
+
fabricatio/models/extra/article_base.py,sha256=hekBRwPGg1fyGwCqW2n-EU1OjVc3QXsWElaBymo7F_k,14888
|
16
|
+
fabricatio/models/usages.py,sha256=1TJ-wzMtPKIH1hYhWliv-IfjxUVvbJIdj5BO0_4nRy0,31003
|
17
17
|
fabricatio/models/events.py,sha256=UvOc6V3vfjKuvh7irDezJ8EGpsNo5yzLdq4xQexVonw,4063
|
18
18
|
fabricatio/models/task.py,sha256=-EnzpEyM6Z687gF1lPcmA2szEUw6dFpu3lOtseaz95o,10193
|
19
|
-
fabricatio/models/action.py,sha256=
|
19
|
+
fabricatio/models/action.py,sha256=yEYayK_gNtggpVgWlQ4K4wCHPqlhkouhUKW1SsJoR20,8646
|
20
20
|
fabricatio/toolboxes/fs.py,sha256=OQMdeokYxSNVrCZJAweJ0cYiK4k2QuEiNdIbS5IHIV8,705
|
21
21
|
fabricatio/toolboxes/__init__.py,sha256=dYm_Gd8XolSU_h4wnkA09dlaLDK146eeFz0CUgPZ8_c,380
|
22
22
|
fabricatio/toolboxes/arithmetic.py,sha256=sSTPkKI6-mb278DwQKFO9jKyzc9kCx45xNH7V6bGBpE,1307
|
@@ -27,21 +27,21 @@ fabricatio/fs/__init__.py,sha256=hTuYtzmvIGtbg7PTdoqLEQJ0E63hOzZltCIrLlDKaSE,559
|
|
27
27
|
fabricatio/config.py,sha256=o_lbhLJgkdxIkonLsAw2-zc2pntTui1CZmSvXMGASpc,16507
|
28
28
|
fabricatio/journal.py,sha256=Op0wC-JlZumnAc_aDmYM4ljnSNLoKEEMfcIRbCF69ow,455
|
29
29
|
fabricatio/__init__.py,sha256=6EjK4SxbnvFxdO9ftkXD9rxSuoPEIITNzUkuMO9s3yU,1092
|
30
|
-
fabricatio/actions/output.py,sha256=
|
30
|
+
fabricatio/actions/output.py,sha256=22IwSPukxFT_Yf0rdEBuUFKTfUipOLx_ZieW0PHAlkI,2246
|
31
31
|
fabricatio/actions/article_rag.py,sha256=jAoFU75vZ-jFROGh4bZKmvY10K9zJPLqzeEa7wSiwu8,1421
|
32
32
|
fabricatio/actions/rag.py,sha256=Tsjn9IkO8OlKlhBBnk7J6qh9st61jzD6SUYClGhYs7I,2686
|
33
|
-
fabricatio/actions/article.py,sha256=
|
33
|
+
fabricatio/actions/article.py,sha256=Oi67XUw_lE4Fdb6RzrgdbEdFBG4WutPRQQWBk5HbOms,8077
|
34
34
|
fabricatio/_rust_instances.py,sha256=bQmlhUCcxTmRgvw1SfzYzNNpgW_UCjmkYw5f-VPAyg8,304
|
35
35
|
fabricatio/workflows/articles.py,sha256=oHNV5kNKEcOKP55FA7I1SlxQRlk6N26cpem_QYu05g0,1021
|
36
36
|
fabricatio/workflows/rag.py,sha256=uOZXprD479fUhLA6sYvEM8RWcVcUZXXtP0xRbTMPdHE,509
|
37
37
|
fabricatio/parser.py,sha256=OV6bIAfLJ-GfaKoTeIOqS3X3GqCgyvzSJsgYMO3ogj4,6100
|
38
|
-
fabricatio/capabilities/correct.py,sha256=
|
38
|
+
fabricatio/capabilities/correct.py,sha256=t9H-5FmbK78ahh0LhS4FAuOTw5qTkgkwIuBolkzYeBI,7104
|
39
39
|
fabricatio/capabilities/rag.py,sha256=FsjaMKeFgl-kBmUGDfkWn2Sa9Gp1AfOWpOZRZXZlNFY,17270
|
40
40
|
fabricatio/capabilities/rating.py,sha256=K6dIaMDMWNUhCenuP_lFriaofj6gBPjfNXEILp5yzjU,14556
|
41
41
|
fabricatio/capabilities/review.py,sha256=_m7uGNfhW7iDhcCJrLiSBmEvMq56fpPIzNGh1X20YZM,11103
|
42
42
|
fabricatio/capabilities/propose.py,sha256=4QvONVVUp1rs34Te2Rjams6NioEt6FhEAxDWiveQnSg,1544
|
43
43
|
fabricatio/capabilities/task.py,sha256=5XUxYNkPIHKm-Q0_oCeEqS-i3kfq9twHqcDiZL0rKVo,4526
|
44
44
|
fabricatio/_rust.pyi,sha256=n6mFYqLQlyfumJZQ_E3SesR_yLrjfRLjf6N1VdlF6U8,3707
|
45
|
-
fabricatio/_rust.cpython-312-x86_64-linux-gnu.so,sha256=
|
46
|
-
fabricatio-0.2.
|
47
|
-
fabricatio-0.2.
|
45
|
+
fabricatio/_rust.cpython-312-x86_64-linux-gnu.so,sha256=wQr2FLieZwAKIXasejND5CX7Y4-vhRHwqI8ds00bK9Y,1916040
|
46
|
+
fabricatio-0.2.8.dev0.data/scripts/tdown,sha256=zC3ne1G7HqaXSJ2QJlv0ILXfKgT758L15JvGfbiC1gE,4588888
|
47
|
+
fabricatio-0.2.8.dev0.dist-info/RECORD,,
|
File without changes
|
File without changes
|