fabricatio 0.2.7.dev4__cp312-cp312-win_amd64.whl → 0.2.8.dev0__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.
- fabricatio/_rust.cp312-win_amd64.pyd +0 -0
- fabricatio/actions/article.py +97 -73
- fabricatio/actions/output.py +36 -1
- fabricatio/capabilities/correct.py +26 -6
- fabricatio/models/action.py +2 -0
- fabricatio/models/extra/article_base.py +279 -89
- fabricatio/models/extra/article_main.py +35 -126
- fabricatio/models/extra/article_outline.py +6 -155
- fabricatio/models/extra/article_proposal.py +12 -14
- fabricatio/models/generic.py +71 -20
- fabricatio/models/usages.py +5 -1
- fabricatio-0.2.8.dev0.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dev0.dist-info}/METADATA +1 -1
- {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dev0.dist-info}/RECORD +16 -16
- fabricatio-0.2.7.dev4.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dev0.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,12 @@
|
|
1
1
|
"""ArticleBase and ArticleSubsection classes for managing hierarchical document components."""
|
2
2
|
|
3
3
|
from itertools import chain
|
4
|
-
from typing import Generator, List, Self, Tuple
|
4
|
+
from typing import Generator, List, Self, Tuple, override
|
5
5
|
|
6
6
|
from fabricatio.journal import logger
|
7
7
|
from fabricatio.models.extra.article_base import (
|
8
8
|
ArticleBase,
|
9
|
-
|
10
|
-
ArticleRef,
|
9
|
+
ArticleOutlineBase,
|
11
10
|
ChapterBase,
|
12
11
|
SectionBase,
|
13
12
|
SubSectionBase,
|
@@ -28,26 +27,28 @@ 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
|
|
41
|
-
def
|
42
|
-
"""
|
43
|
-
if self.
|
44
|
-
return f"
|
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}`."
|
45
44
|
return ""
|
46
45
|
|
47
|
-
def
|
46
|
+
def update_from_inner(self, other: Self) -> Self:
|
48
47
|
"""Updates the current instance with the attributes of another instance."""
|
49
48
|
logger.debug(f"Updating SubSection {self.title}")
|
50
|
-
|
49
|
+
super().update_from_inner(other)
|
50
|
+
self.paragraphs.clear()
|
51
|
+
self.paragraphs.extend(other.paragraphs)
|
51
52
|
return self
|
52
53
|
|
53
54
|
def to_typst_code(self) -> str:
|
@@ -56,102 +57,33 @@ class ArticleSubsection(ArticleMainBase, SubSectionBase):
|
|
56
57
|
Returns:
|
57
58
|
str: Typst code snippet for rendering.
|
58
59
|
"""
|
59
|
-
return f"=== {self.title}\n" + "\n\n".join(
|
60
|
+
return f"=== {self.title}\n" + "\n\n".join(p.content for p in self.paragraphs)
|
60
61
|
|
61
62
|
|
62
|
-
class ArticleSection(
|
63
|
+
class ArticleSection(SectionBase[ArticleSubsection]):
|
63
64
|
"""Atomic argumentative unit with high-level specificity."""
|
64
65
|
|
65
|
-
def resolve_update_error(self, other: Self) -> str:
|
66
|
-
"""Resolve update errors in the article outline."""
|
67
|
-
if (s_len := len(self.subsections)) == 0:
|
68
|
-
return ""
|
69
66
|
|
70
|
-
|
71
|
-
return f"Subsections length mismatched, expected {len(self.subsections)}, got {len(other.subsections)}"
|
72
|
-
|
73
|
-
sub_sec_err_seq = [
|
74
|
-
out for s, o in zip(self.subsections, other.subsections, strict=True) if (out := s.resolve_update_error(o))
|
75
|
-
]
|
76
|
-
|
77
|
-
if sub_sec_err_seq:
|
78
|
-
return "\n".join(sub_sec_err_seq)
|
79
|
-
return ""
|
80
|
-
|
81
|
-
def _update_from_inner(self, other: Self) -> Self:
|
82
|
-
"""Updates the current instance with the attributes of another instance."""
|
83
|
-
if len(self.subsections) == 0:
|
84
|
-
self.subsections = other.subsections
|
85
|
-
return self
|
86
|
-
|
87
|
-
for self_subsec, other_subsec in zip(self.subsections, other.subsections, strict=True):
|
88
|
-
self_subsec.update_from(other_subsec)
|
89
|
-
return self
|
90
|
-
|
91
|
-
def to_typst_code(self) -> str:
|
92
|
-
"""Converts the section into a Typst formatted code snippet.
|
93
|
-
|
94
|
-
Returns:
|
95
|
-
str: The formatted Typst code snippet.
|
96
|
-
"""
|
97
|
-
return f"== {self.title}\n" + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
|
98
|
-
|
99
|
-
|
100
|
-
class ArticleChapter(ArticleMainBase, ChapterBase[ArticleSection]):
|
67
|
+
class ArticleChapter(ChapterBase[ArticleSection]):
|
101
68
|
"""Thematic progression implementing research function."""
|
102
69
|
|
103
|
-
def resolve_update_error(self, other: Self) -> str:
|
104
|
-
"""Resolve update errors in the article outline."""
|
105
|
-
if (s_len := len(self.sections)) == 0:
|
106
|
-
return ""
|
107
|
-
|
108
|
-
if s_len != len(other.sections):
|
109
|
-
return f"Sections length mismatched, expected {len(self.sections)}, got {len(other.sections)}"
|
110
|
-
sec_err_seq = [
|
111
|
-
out for s, o in zip(self.sections, other.sections, strict=True) if (out := s.resolve_update_error(o))
|
112
|
-
]
|
113
|
-
if sec_err_seq:
|
114
|
-
return "\n".join(sec_err_seq)
|
115
|
-
return ""
|
116
|
-
|
117
|
-
def _update_from_inner(self, other: Self) -> Self:
|
118
|
-
"""Updates the current instance with the attributes of another instance."""
|
119
|
-
if len(self.sections) == 0:
|
120
|
-
self.sections = other.sections
|
121
|
-
return self
|
122
|
-
|
123
|
-
for self_sec, other_sec in zip(self.sections, other.sections, strict=True):
|
124
|
-
self_sec.update_from(other_sec)
|
125
|
-
return self
|
126
|
-
|
127
|
-
def to_typst_code(self) -> str:
|
128
|
-
"""Converts the chapter into a Typst formatted code snippet for rendering."""
|
129
|
-
return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
|
130
|
-
|
131
70
|
|
132
|
-
class Article(
|
71
|
+
class Article(
|
72
|
+
Display,
|
73
|
+
CensoredAble,
|
74
|
+
WithRef[ArticleOutline],
|
75
|
+
PersistentAble,
|
76
|
+
ArticleBase[ArticleChapter],
|
77
|
+
):
|
133
78
|
"""Represents a complete academic paper specification, incorporating validation constraints.
|
134
79
|
|
135
80
|
This class integrates display, censorship processing, article structure referencing, and persistence capabilities,
|
136
81
|
aiming to provide a comprehensive model for academic papers.
|
137
82
|
"""
|
138
83
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
title: str
|
143
|
-
"""Represents the title of the academic paper."""
|
144
|
-
|
145
|
-
language: str
|
146
|
-
"""Written language of the article. SHALL be aligned to the language of the article outline provided."""
|
147
|
-
|
148
|
-
def finalized_dump(self) -> str:
|
149
|
-
"""Exports the article in `typst` format.
|
150
|
-
|
151
|
-
Returns:
|
152
|
-
str: Strictly formatted outline with typst formatting.
|
153
|
-
"""
|
154
|
-
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()
|
155
87
|
|
156
88
|
@classmethod
|
157
89
|
def from_outline(cls, outline: ArticleOutline) -> "Article":
|
@@ -164,7 +96,7 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
|
|
164
96
|
Article: The generated article.
|
165
97
|
"""
|
166
98
|
# Set the title from the outline
|
167
|
-
article = Article(**outline.model_dump(
|
99
|
+
article = Article(**outline.model_dump(exclude={"chapters"}), chapters=[])
|
168
100
|
|
169
101
|
for chapter in outline.chapters:
|
170
102
|
# Create a new chapter
|
@@ -189,48 +121,25 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
|
|
189
121
|
article.chapters.append(article_chapter)
|
190
122
|
return article
|
191
123
|
|
192
|
-
def
|
193
|
-
"""Performs a depth-first search (DFS) through the article structure.
|
194
|
-
|
195
|
-
Returns:
|
196
|
-
Generator[ArticleMainBase]: Each component in the article structure.
|
197
|
-
"""
|
198
|
-
for chap in self.chapters:
|
199
|
-
for sec in chap.sections:
|
200
|
-
yield from sec.subsections
|
201
|
-
yield sec
|
202
|
-
yield chap
|
203
|
-
|
204
|
-
def deref(self, ref: ArticleRef) -> ArticleMainBase:
|
205
|
-
"""Resolves a reference to the corresponding section or subsection in the article.
|
206
|
-
|
207
|
-
Args:
|
208
|
-
ref (ArticleRef): The reference to resolve.
|
209
|
-
|
210
|
-
Returns:
|
211
|
-
ArticleMainBase: The corresponding section or subsection.
|
212
|
-
"""
|
213
|
-
return ok(ref.deref(self), f"{ref} not found in {self.title}")
|
214
|
-
|
215
|
-
def gather_dependencies(self, article: ArticleMainBase) -> List[ArticleMainBase]:
|
124
|
+
def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
|
216
125
|
"""Gathers dependencies for all sections and subsections in the article.
|
217
126
|
|
218
127
|
This method should be called after the article is fully constructed.
|
219
128
|
"""
|
220
|
-
depends = [
|
129
|
+
depends = [ok(a.deref(self)) for a in article.depend_on]
|
221
130
|
|
222
131
|
supports = []
|
223
|
-
for a in self.
|
224
|
-
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}:
|
225
134
|
supports.append(a)
|
226
135
|
|
227
136
|
return list(set(depends + supports))
|
228
137
|
|
229
|
-
def gather_dependencies_recursive(self, article:
|
138
|
+
def gather_dependencies_recursive(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
|
230
139
|
"""Gathers all dependencies recursively for the given article.
|
231
140
|
|
232
141
|
Args:
|
233
|
-
article (
|
142
|
+
article (ArticleOutlineBase): The article to gather dependencies for.
|
234
143
|
|
235
144
|
Returns:
|
236
145
|
List[ArticleBase]: A list of all dependencies for the given article.
|
@@ -269,7 +178,7 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
|
|
269
178
|
|
270
179
|
def iter_dfs_with_deps(
|
271
180
|
self, chapter: bool = True, section: bool = True, subsection: bool = True
|
272
|
-
) -> Generator[Tuple[
|
181
|
+
) -> Generator[Tuple[ArticleOutlineBase, List[ArticleOutlineBase]], None, None]:
|
273
182
|
"""Iterates through the article in a depth-first manner, yielding each component and its dependencies.
|
274
183
|
|
275
184
|
Args:
|
@@ -283,7 +192,7 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
|
|
283
192
|
if all((not chapter, not section, not subsection)):
|
284
193
|
raise ValueError("At least one of chapter, section, or subsection must be True.")
|
285
194
|
|
286
|
-
for component in self.
|
195
|
+
for component in self.iter_dfs_rev():
|
287
196
|
if not chapter and isinstance(component, ArticleChapter):
|
288
197
|
continue
|
289
198
|
if not section and isinstance(component, ArticleSection):
|
@@ -1,30 +1,25 @@
|
|
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, Tuple, Union
|
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
|
-
class ArticleSectionOutline(
|
23
|
-
"""A slightly more detailed research component specification for academic paper generation."""
|
17
|
+
class ArticleSectionOutline(SectionBase[ArticleSubsectionOutline]):
|
18
|
+
"""A slightly more detailed research component specification for academic paper generation, Must contain subsections."""
|
24
19
|
|
25
20
|
|
26
|
-
class ArticleChapterOutline(
|
27
|
-
"""Macro-structural unit implementing standard academic paper organization."""
|
21
|
+
class ArticleChapterOutline(ChapterBase[ArticleSectionOutline]):
|
22
|
+
"""Macro-structural unit implementing standard academic paper organization. Must contain sections."""
|
28
23
|
|
29
24
|
|
30
25
|
class ArticleOutline(
|
@@ -34,148 +29,4 @@ class ArticleOutline(
|
|
34
29
|
PersistentAble,
|
35
30
|
ArticleBase[ArticleChapterOutline],
|
36
31
|
):
|
37
|
-
"""
|
38
|
-
|
39
|
-
abstract: str
|
40
|
-
"""The abstract is a concise summary of the academic paper's main findings."""
|
41
|
-
|
42
|
-
prospect: str
|
43
|
-
"""Consolidated research statement with four pillars:
|
44
|
-
1. Problem Identification: Current limitations
|
45
|
-
2. Methodological Response: Technical approach
|
46
|
-
3. Empirical Validation: Evaluation strategy
|
47
|
-
4. Scholarly Impact: Field contributions
|
48
|
-
"""
|
49
|
-
|
50
|
-
title: str
|
51
|
-
"""Title of the academic paper."""
|
52
|
-
|
53
|
-
language: str
|
54
|
-
"""Written language of the article. SHALL be aligned to the language of the article proposal provided."""
|
55
|
-
|
56
|
-
def finalized_dump(self) -> str:
|
57
|
-
"""Generates standardized hierarchical markup for academic publishing systems.
|
58
|
-
|
59
|
-
Implements ACL 2024 outline conventions with four-level structure:
|
60
|
-
= Chapter Title (Level 1)
|
61
|
-
== Section Title (Level 2)
|
62
|
-
=== Subsection Title (Level 3)
|
63
|
-
==== Subsubsection Title (Level 4)
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
str: Strictly formatted outline with academic sectioning
|
67
|
-
|
68
|
-
Example:
|
69
|
-
= Methodology
|
70
|
-
== Neural Architecture Search Framework
|
71
|
-
=== Differentiable Search Space
|
72
|
-
==== Constrained Optimization Parameters
|
73
|
-
=== Implementation Details
|
74
|
-
== Evaluation Protocol
|
75
|
-
"""
|
76
|
-
lines: List[str] = []
|
77
|
-
for i, chapter in enumerate(self.chapters, 1):
|
78
|
-
lines.append(f"= Chapter {i}: {chapter.title}")
|
79
|
-
for j, section in enumerate(chapter.sections, 1):
|
80
|
-
lines.append(f"== {i}.{j} {section.title}")
|
81
|
-
for k, subsection in enumerate(section.subsections, 1):
|
82
|
-
lines.append(f"=== {i}.{j}.{k} {subsection.title}")
|
83
|
-
return "\n".join(lines)
|
84
|
-
|
85
|
-
def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
|
86
|
-
"""Iterates through the article outline in a depth-first manner.
|
87
|
-
|
88
|
-
Returns:
|
89
|
-
ArticleOutlineBase: Each component in the article outline.
|
90
|
-
"""
|
91
|
-
for chapter in self.chapters:
|
92
|
-
for section in chapter.sections:
|
93
|
-
yield from section.subsections
|
94
|
-
yield section
|
95
|
-
yield chapter
|
96
|
-
|
97
|
-
def resolve_ref_error(self) -> str:
|
98
|
-
"""Resolve reference errors in the article outline.
|
99
|
-
|
100
|
-
Returns:
|
101
|
-
str: Error message indicating reference errors in the article outline.
|
102
|
-
|
103
|
-
Notes:
|
104
|
-
This function is designed to find all invalid `ArticleRef` objs in `depend_on` and `support_to` fields, which will be added to the final error summary.
|
105
|
-
"""
|
106
|
-
summary = ""
|
107
|
-
for component in self.iter_dfs():
|
108
|
-
for ref in component.depend_on:
|
109
|
-
if not ref.deref(self):
|
110
|
-
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"
|
111
|
-
for ref in component.support_to:
|
112
|
-
if not ref.deref(self):
|
113
|
-
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"
|
114
|
-
|
115
|
-
return summary
|
116
|
-
|
117
|
-
@classmethod
|
118
|
-
def from_typst_code(
|
119
|
-
cls, typst_code: str, title: str = "", article_language: str = "en", prospect: str = "", abstract: str = ""
|
120
|
-
) -> "ArticleOutline":
|
121
|
-
"""Parses a Typst code string and creates an ArticleOutline instance."""
|
122
|
-
self = cls(language=article_language, prospect=prospect, abstract=abstract, chapters=[], title=title)
|
123
|
-
stack = [self] # 根节点为ArticleOutline实例
|
124
|
-
|
125
|
-
for line in typst_code.splitlines():
|
126
|
-
parsed = cls._parse_line(line)
|
127
|
-
if not parsed:
|
128
|
-
continue
|
129
|
-
level, title = parsed
|
130
|
-
cls._adjust_stack(stack, level)
|
131
|
-
parent = stack[-1]
|
132
|
-
component = cls._create_component(level, title)
|
133
|
-
cls._add_to_parent(parent, component, level)
|
134
|
-
stack.append(component)
|
135
|
-
|
136
|
-
return self
|
137
|
-
|
138
|
-
@classmethod
|
139
|
-
def _parse_line(cls, line: str) -> Optional[Tuple[int, str]]:
|
140
|
-
stripped = line.strip()
|
141
|
-
if not stripped.startswith("="):
|
142
|
-
return None
|
143
|
-
match = regex.match(r"^(\=+)(.*)", stripped)
|
144
|
-
if not match:
|
145
|
-
return None
|
146
|
-
eqs, title_part = match.groups()
|
147
|
-
return len(eqs), title_part.strip()
|
148
|
-
|
149
|
-
@classmethod
|
150
|
-
def _adjust_stack(cls, stack: List[object], target_level: int) -> None:
|
151
|
-
while len(stack) > target_level:
|
152
|
-
stack.pop()
|
153
|
-
|
154
|
-
@classmethod
|
155
|
-
def _create_component(cls, level: int, title: str) -> ArticleOutlineBase:
|
156
|
-
default_kwargs = {
|
157
|
-
"writing_aim": [],
|
158
|
-
"depend_on": [],
|
159
|
-
"support_to": [],
|
160
|
-
"description": [],
|
161
|
-
}
|
162
|
-
component_map = {
|
163
|
-
1: lambda: ArticleChapterOutline(title=title, sections=[], **default_kwargs),
|
164
|
-
2: lambda: ArticleSectionOutline(title=title, subsections=[], **default_kwargs),
|
165
|
-
3: lambda: ArticleSubsectionOutline(title=title, **default_kwargs),
|
166
|
-
}
|
167
|
-
return ok(component_map.get(level, lambda: None)(), "Invalid level")
|
168
|
-
|
169
|
-
@classmethod
|
170
|
-
def _add_to_parent(
|
171
|
-
cls,
|
172
|
-
parent: Union["ArticleOutline", ArticleChapterOutline, ArticleSectionOutline],
|
173
|
-
component: ArticleOutlineBase,
|
174
|
-
level: int,
|
175
|
-
) -> None:
|
176
|
-
if level == 1 and isinstance(component, ArticleChapterOutline):
|
177
|
-
parent.chapters.append(component)
|
178
|
-
elif level == 2 and isinstance(component, ArticleSectionOutline): # noqa: PLR2004
|
179
|
-
parent.sections.append(component)
|
180
|
-
elif level == 3 and isinstance(component, ArticleSubsectionOutline): # noqa: PLR2004
|
181
|
-
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
|
|
@@ -95,7 +95,7 @@ class WithRef[T](Base):
|
|
95
95
|
"""Get the referenced object."""
|
96
96
|
return ok(self._reference, "_reference is None")
|
97
97
|
|
98
|
-
def update_ref[S](self: S, reference: T | S) -> S: # noqa: PYI019
|
98
|
+
def update_ref[S: "WithRef"](self: S, reference: T | S) -> S: # noqa: PYI019
|
99
99
|
"""Update the reference of the object."""
|
100
100
|
if isinstance(reference, self.__class__):
|
101
101
|
self._reference = reference.referenced
|
@@ -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:
|
@@ -139,6 +140,61 @@ class PersistentAble(Base):
|
|
139
140
|
return self.model_validate_json(Path(path).read_text(encoding="utf-8"))
|
140
141
|
|
141
142
|
|
143
|
+
class ModelHash(Base):
|
144
|
+
"""Class that provides a hash value for the object."""
|
145
|
+
|
146
|
+
def __hash__(self) -> int:
|
147
|
+
"""Calculates a hash value for the ArticleBase object based on its model_dump_json representation."""
|
148
|
+
return hash(self.model_dump_json())
|
149
|
+
|
150
|
+
|
151
|
+
class UpdateFrom(Base):
|
152
|
+
"""Class that provides a method to update the object from another object."""
|
153
|
+
|
154
|
+
def update_pre_check(self, other: Self) -> Self:
|
155
|
+
"""Pre-check for updating the object from another object."""
|
156
|
+
if not isinstance(other, self.__class__):
|
157
|
+
raise TypeError(f"Cannot update from a non-{self.__class__.__name__} instance.")
|
158
|
+
|
159
|
+
return self
|
160
|
+
|
161
|
+
@abstractmethod
|
162
|
+
def update_from_inner(self, other: Self) -> Self:
|
163
|
+
"""Updates the current instance with the attributes of another instance."""
|
164
|
+
|
165
|
+
@final
|
166
|
+
def update_from(self, other: Self) -> Self:
|
167
|
+
"""Updates the current instance with the attributes of another instance."""
|
168
|
+
return self.update_pre_check(other).update_from_inner(other)
|
169
|
+
|
170
|
+
|
171
|
+
class ResolveUpdateConflict(Base):
|
172
|
+
"""Class that provides a method to update the object from another object."""
|
173
|
+
|
174
|
+
@abstractmethod
|
175
|
+
def resolve_update_conflict(self, other: Self) -> str:
|
176
|
+
"""Resolve the update conflict between two objects.
|
177
|
+
|
178
|
+
Args:
|
179
|
+
other (Self): The other object to resolve the update conflict with.
|
180
|
+
|
181
|
+
Returns:
|
182
|
+
str: The resolved update conflict.
|
183
|
+
"""
|
184
|
+
|
185
|
+
|
186
|
+
class Introspect(Base):
|
187
|
+
"""Class that provides a method to introspect the object."""
|
188
|
+
|
189
|
+
@abstractmethod
|
190
|
+
def introspect(self) -> str:
|
191
|
+
"""Internal introspection of the object.
|
192
|
+
|
193
|
+
Returns:
|
194
|
+
str: The internal introspection of the object.
|
195
|
+
"""
|
196
|
+
|
197
|
+
|
142
198
|
class WithBriefing(Named, Described):
|
143
199
|
"""Class that provides a briefing based on the name and description."""
|
144
200
|
|
@@ -173,20 +229,11 @@ class WithBriefing(Named, Described):
|
|
173
229
|
raise TypeError(f"{system_msg_like} is not a dict or str")
|
174
230
|
|
175
231
|
|
176
|
-
class
|
232
|
+
class UnsortGenerate(GenerateJsonSchema):
|
177
233
|
"""Class that provides a reverse JSON schema of the model."""
|
178
234
|
|
179
|
-
def
|
180
|
-
|
181
|
-
sorted_dict: dict[str, JsonSchemaValue] = {}
|
182
|
-
# Reverse all keys regardless of parent_key
|
183
|
-
keys = reversed(value.keys())
|
184
|
-
for key in keys:
|
185
|
-
sorted_dict[key] = self._sort_recursive(value[key], parent_key=key)
|
186
|
-
return sorted_dict
|
187
|
-
if isinstance(value, list):
|
188
|
-
# Reverse list order and process each item
|
189
|
-
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."""
|
190
237
|
return value
|
191
238
|
|
192
239
|
|
@@ -201,7 +248,7 @@ class WithFormatedJsonSchema(Base):
|
|
201
248
|
str: The JSON schema of the model in a formatted string.
|
202
249
|
"""
|
203
250
|
return orjson.dumps(
|
204
|
-
cls.model_json_schema(schema_generator=
|
251
|
+
cls.model_json_schema(schema_generator=UnsortGenerate),
|
205
252
|
option=orjson.OPT_INDENT_2,
|
206
253
|
).decode()
|
207
254
|
|
@@ -261,6 +308,10 @@ class ProposedAble(CreateJsonObjPrompt, InstantiateFromString):
|
|
261
308
|
"""Class that provides a method to propose a JSON object based on the requirement."""
|
262
309
|
|
263
310
|
|
311
|
+
class ProposedUpdateAble(PersistentAble, UpdateFrom, ABC):
|
312
|
+
"""Make the obj can be updated from the proposed obj in place."""
|
313
|
+
|
314
|
+
|
264
315
|
class FinalizedDumpAble(Base):
|
265
316
|
"""Class that provides a method to finalize the dump of the object."""
|
266
317
|
|
@@ -392,7 +443,7 @@ class PrepareVectorization(Base):
|
|
392
443
|
"""
|
393
444
|
max_length = max_length or configs.embedding.max_sequence_length
|
394
445
|
chunk = self._prepare_vectorization_inner()
|
395
|
-
if len(chunk) > max_length:
|
446
|
+
if max_length and len(chunk) > max_length:
|
396
447
|
logger.error(err := f"Chunk exceeds maximum sequence length {max_length}.")
|
397
448
|
raise ValueError(err)
|
398
449
|
|
@@ -475,7 +526,7 @@ class ScopedConfig(Base):
|
|
475
526
|
"""Fallback to another instance's attribute values if the current instance's attributes are None.
|
476
527
|
|
477
528
|
Args:
|
478
|
-
other (
|
529
|
+
other (ScopedConfig): Another instance from which to copy attribute values.
|
479
530
|
|
480
531
|
Returns:
|
481
532
|
Self: The current instance, allowing for method chaining.
|
@@ -495,7 +546,7 @@ class ScopedConfig(Base):
|
|
495
546
|
"""Hold to another instance's attribute values if the current instance's attributes are None.
|
496
547
|
|
497
548
|
Args:
|
498
|
-
others (
|
549
|
+
others (Union[ScopedConfig, Iterable[ScopedConfig]]): Another instance or iterable of instances from which to copy attribute values.
|
499
550
|
|
500
551
|
Returns:
|
501
552
|
Self: The current instance, allowing for method chaining.
|