fabricatio 0.2.7.dev4__cp312-cp312-win_amd64.whl → 0.2.7.dev5__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.
Binary file
@@ -61,7 +61,7 @@ class GenerateArticleProposal(Action):
61
61
  logger.error("Task not approved, since all inputs are None.")
62
62
  return None
63
63
 
64
- return (
64
+ return ok(
65
65
  await self.propose(
66
66
  ArticleProposal,
67
67
  briefing := (
@@ -70,14 +70,15 @@ class GenerateArticleProposal(Action):
70
70
  ok(
71
71
  article_briefing_path
72
72
  or await self.awhich_pathstr(
73
- f"{task_input.briefing}\nExtract the path of file which contains the article briefing."
73
+ f"{ok(task_input).briefing}\nExtract the path of file which contains the article briefing."
74
74
  ),
75
75
  "Could not find the path of file to read.",
76
76
  )
77
77
  )
78
78
  ),
79
79
  **self.prepend_sys_msg(),
80
- )
80
+ ),
81
+ "Could not generate the proposal."
81
82
  ).update_ref(briefing)
82
83
 
83
84
 
@@ -92,27 +93,33 @@ class GenerateOutline(Action):
92
93
  article_proposal: ArticleProposal,
93
94
  **_,
94
95
  ) -> Optional[ArticleOutline]:
95
- out = await self.propose(
96
- ArticleOutline,
97
- article_proposal.as_prompt(),
98
- **self.prepend_sys_msg(),
96
+ out = ok(
97
+ await self.propose(
98
+ ArticleOutline,
99
+ article_proposal.as_prompt(),
100
+ **self.prepend_sys_msg(),
101
+ ),
102
+ "Could not generate the outline.",
99
103
  )
100
104
 
101
- manual = await self.draft_rating_manual(
105
+ manual = ok(await self.draft_rating_manual(
102
106
  topic=(
103
107
  topic
104
108
  := "Fix the internal referring error, make sure there is no more `ArticleRef` pointing to a non-existing article component."
105
109
  ),
106
- )
107
- while err := out.resolve_ref_error():
110
+ ),"Could not generate the rating manual.")
111
+
112
+ while pack := out.find_illegal():
113
+ component, err = ok(pack)
108
114
  logger.warning(f"Found error in the outline: \n{err}")
109
- out = await self.correct_obj(
110
- out,
111
- reference=f"# Referring Error\n{err}",
115
+ corrected = ok(await self.correct_obj(
116
+ component,
117
+ reference=f"# Original Article Outline\n{out.display()}\n# Error Need to be fixed\n{err}",
112
118
  topic=topic,
113
119
  rating_manual=manual,
114
120
  supervisor_check=False,
115
- )
121
+ ),"Could not correct the component.")
122
+ component.update_from(corrected)
116
123
  return out.update_ref(article_proposal)
117
124
 
118
125
 
@@ -156,15 +163,15 @@ class GenerateArticle(Action):
156
163
  ) -> Optional[Article]:
157
164
  article: Article = Article.from_outline(article_outline).update_ref(article_outline)
158
165
 
159
- writing_manual = await self.draft_rating_manual(
166
+ writing_manual = ok(await self.draft_rating_manual(
160
167
  topic=(
161
168
  topic_1
162
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."
163
170
  ),
164
- )
165
- err_resolve_manual = await self.draft_rating_manual(
171
+ ))
172
+ err_resolve_manual = ok(await self.draft_rating_manual(
166
173
  topic=(topic_2 := "this article component has violated the constrain, please correct it.")
167
- )
174
+ ))
168
175
  for c, deps in article.iter_dfs_with_deps(chapter=False):
169
176
  logger.info(f"Updating the article component: \n{c.display()}")
170
177
 
@@ -180,7 +187,7 @@ class GenerateArticle(Action):
180
187
  ),
181
188
  "Could not correct the article component.",
182
189
  )
183
- while err := c.resolve_update_error(out):
190
+ while err := c.resolve_update_conflict(out):
184
191
  logger.warning(f"Found error in the article component: \n{err}")
185
192
  out = ok(
186
193
  await self.correct_obj(
@@ -1,11 +1,19 @@
1
1
  """A foundation for hierarchical document components with dependency tracking."""
2
-
3
- from abc import abstractmethod
2
+ from abc import ABC
4
3
  from enum import StrEnum
5
- from typing import TYPE_CHECKING, List, Optional, Self, Union, final, overload
6
-
7
- from fabricatio.models.generic import Base, CensoredAble, Display, PersistentAble
8
- from pydantic import Field
4
+ from typing import TYPE_CHECKING, Generator, List, Optional, Self, Union, overload
5
+
6
+ from fabricatio.models.generic import (
7
+ Base,
8
+ CensoredAble,
9
+ Display,
10
+ Introspect,
11
+ ModelHash,
12
+ PersistentAble,
13
+ ProposedAble,
14
+ ResolveUpdateConflict,
15
+ UpdateFrom,
16
+ )
9
17
 
10
18
  if TYPE_CHECKING:
11
19
  from fabricatio.models.extra.article_main import Article
@@ -15,9 +23,9 @@ if TYPE_CHECKING:
15
23
  class ReferringType(StrEnum):
16
24
  """Enumeration of different types of references that can be made in an article."""
17
25
 
18
- CHAPTER: str = "chapter"
19
- SECTION: str = "section"
20
- SUBSECTION: str = "subsection"
26
+ CHAPTER = "chapter"
27
+ SECTION = "section"
28
+ SUBSECTION = "subsection"
21
29
 
22
30
 
23
31
  class ArticleRef(CensoredAble):
@@ -103,90 +111,181 @@ class ArticleRef(CensoredAble):
103
111
  return ReferringType.CHAPTER
104
112
 
105
113
 
106
- class SubSectionBase(Base):
114
+ class SubSectionBase(
115
+ UpdateFrom,
116
+ Introspect,
117
+ ):
107
118
  """Base class for article sections and subsections."""
108
119
 
120
+ title: str
121
+ """Title of the subsection, do not add any prefix or suffix to the title. should not contain special characters."""
109
122
 
110
- class SectionBase[T: SubSectionBase](Base):
111
- """Base class for article sections and subsections."""
112
-
113
- subsections: List[T] = Field(min_length=1)
114
- """List of subsections, each containing a specific research component. Must contains at least 1 subsection, But do remember you should always add more subsection as required."""
123
+ def to_typst_code(self) -> str:
124
+ """Converts the component into a Typst code snippet for rendering."""
125
+ return f"=== {self.title}\n"
115
126
 
127
+ def update_from_inner(self, other: Self) -> Self:
128
+ return self
116
129
 
117
- class ChapterBase[T: SectionBase](Base):
118
- """Base class for article chapters."""
130
+ def introspect(self) -> str:
131
+ """Introspects the article subsection outline."""
132
+ return ""
119
133
 
120
- sections: List[T] = Field(min_length=1)
121
- """List of sections, each containing a specific research component. Must contains at least 1 section, But do remember you should always add more section as required."""
134
+ def resolve_update_conflict(self, other: Self) -> str:
135
+ """Resolve update errors in the article outline."""
136
+ if self.title != other.title:
137
+ return f"Title mismatched, expected `{self.title}`, got `{other.title}`"
138
+ return ""
122
139
 
123
140
 
124
- class ArticleBase[T: ChapterBase](Base):
125
- """Base class for article outlines."""
141
+ class SectionBase[T: SubSectionBase](
142
+ UpdateFrom,
143
+ Introspect,
144
+ ):
145
+ """Base class for article sections and subsections."""
126
146
 
127
- chapters: List[T] = Field(min_length=5)
128
- """List of chapters, each containing a specific research component. Must contains at least 5 chapters, But do remember you should always add more chapter as required."""
147
+ subsections: List[T]
148
+ """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."""
129
151
 
152
+ def to_typst_code(self) -> str:
153
+ """Converts the section into a Typst formatted code snippet.
130
154
 
131
- class ArticleOutlineBase(Base):
132
- """Base class for article outlines."""
155
+ Returns:
156
+ str: The formatted Typst code snippet.
157
+ """
158
+ return f"== {self.title}\n" + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
133
159
 
134
- title: str
135
- """Title of the research component in academic style."""
136
- description: str
137
- """Description of the research component in academic style."""
160
+ def resolve_update_conflict(self, other: Self) -> str:
161
+ """Resolve update errors in the article outline."""
162
+ out = ""
163
+ if self.title != other.title:
164
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
165
+ if len(self.subsections) != len(other.subsections):
166
+ out += f"Section count mismatched, expected `{len(self.subsections)}`, got `{len(other.subsections)}`"
167
+ return out or "\n".join(
168
+ [
169
+ conf
170
+ for s, o in zip(self.subsections, other.subsections, strict=True)
171
+ if (conf := s.resolve_update_conflict(o))
172
+ ]
173
+ )
174
+
175
+ def update_from_inner(self, other: Self) -> Self:
176
+ """Updates the current instance with the attributes of another instance."""
177
+ if len(self.subsections) == 0:
178
+ self.subsections = other.subsections
179
+ return self
138
180
 
139
- support_to: List[ArticleRef]
140
- """Required: List of all essential ArticleRef objects identifying components this section provides evidence for.
141
- Format: Each reference must point to a specific chapter, section, or subsection.
142
- Note: References form a directed acyclic graph in the document structure."""
143
- depend_on: List[ArticleRef]
144
- """Required: List of all essential ArticleRef objects identifying components this section builds upon.
145
- Format: Each reference must point to a previously defined chapter, section, or subsection.
146
- Note: Circular dependencies are not permitted."""
181
+ for self_subsec, other_subsec in zip(self.subsections, other.subsections, strict=True):
182
+ self_subsec.update_from(other_subsec)
183
+ return self
147
184
 
148
- writing_aim: List[str]
149
- """Required: List of specific rhetorical objectives (3-5 items).
150
- Format: Each item must be an actionable phrase starting with a verb.
151
- Example: ['Establish metric validity', 'Compare with baseline approaches',
152
- 'Justify threshold selection']"""
185
+ def introspect(self) -> str:
186
+ """Introspects the article section outline."""
187
+ if len(self.subsections) == 0:
188
+ return f"Section `{self.title}` contains no subsections, expected at least one, but got 0, you can add one or more as needed."
189
+ return ""
153
190
 
154
191
 
155
- class ArticleMainBase(CensoredAble, Display, ArticleOutlineBase, PersistentAble):
156
- """Foundation for hierarchical document components with dependency tracking."""
192
+ class ChapterBase[T: SectionBase](
193
+ UpdateFrom,
194
+ Introspect,
195
+ ):
196
+ """Base class for article chapters."""
157
197
 
158
- @abstractmethod
198
+ sections: List[T]
199
+ """Sections of the chapter. Contains at least one section. You can also add more as needed."""
200
+ title: str
201
+ """Title of the chapter, do not add any prefix or suffix to the title. should not contain special characters."""
159
202
  def to_typst_code(self) -> str:
160
- """Converts the component into a Typst code snippet for rendering."""
203
+ """Converts the chapter into a Typst formatted code snippet for rendering."""
204
+ return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
205
+
206
+ def resolve_update_conflict(self, other: Self) -> str:
207
+ """Resolve update errors in the article outline."""
208
+ out = ""
161
209
 
162
- def _update_pre_check(self, other: Self) -> Self:
163
- if not isinstance(other, self.__class__):
164
- raise TypeError(f"Cannot update from a non-{self.__class__} instance.")
165
210
  if self.title != other.title:
166
- raise ValueError("Cannot update from a different title.")
211
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
212
+ if len(self.sections) == len(other.sections):
213
+ out += f"Chapter count mismatched, expected `{len(self.sections)}`, got `{len(other.sections)}`"
214
+
215
+ return out or "\n".join(
216
+ [conf for s, o in zip(self.sections, other.sections, strict=True) if (conf := s.resolve_update_conflict(o))]
217
+ )
218
+
219
+ def update_from_inner(self, other: Self) -> Self:
220
+ """Updates the current instance with the attributes of another instance."""
221
+ if len(self.sections) == 0:
222
+ self.sections = other.sections
223
+ return self
224
+
225
+ for self_sec, other_sec in zip(self.sections, other.sections, strict=True):
226
+ self_sec.update_from(other_sec)
167
227
  return self
168
228
 
169
- @abstractmethod
170
- def resolve_update_error(self, other: Self) -> str:
171
- """Resolve update errors in the article outline.
229
+ def introspect(self) -> str:
230
+ """Introspects the article chapter outline."""
231
+ if len(self.sections) == 0:
232
+ return f"Chapter `{self.title}` contains no sections, expected at least one, but got 0, you can add one or more as needed."
233
+ return ""
234
+
235
+
236
+ class ArticleBase[T: ChapterBase](Base):
237
+ """Base class for article outlines."""
238
+
239
+ chapters: List[T]
240
+ """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
+
244
+ def iter_dfs(
245
+ self,
246
+ ) -> Generator[ChapterBase | SectionBase | SubSectionBase, None, None]:
247
+ """Performs a depth-first search (DFS) through the article structure.
172
248
 
173
249
  Returns:
174
- str: Error message indicating update errors in the article outline.
250
+ Generator[ArticleMainBase]: Each component in the article structure.
175
251
  """
252
+ for chap in self.chapters:
253
+ for sec in chap.sections:
254
+ yield from sec.subsections
255
+ yield sec
256
+ yield chap
257
+
258
+
259
+ class ArticleOutlineBase(
260
+ CensoredAble,
261
+ UpdateFrom,
262
+ ResolveUpdateConflict,
263
+ ProposedAble,
264
+ PersistentAble,
265
+ Display,
266
+ ModelHash,
267
+ ABC,
268
+ ):
269
+ """Base class for article outlines."""
176
270
 
177
- @abstractmethod
178
- def _update_from_inner(self, other: Self) -> Self:
179
- """Updates the current instance with the attributes of another instance."""
271
+ description: str
272
+ """Description of the research component in academic style."""
180
273
 
181
- @final
182
- def update_from(self, other: Self) -> Self:
183
- """Updates the current instance with the attributes of another instance."""
184
- return self._update_pre_check(other)._update_from_inner(other)
274
+ support_to: List[ArticleRef]
275
+ """List of references to other component of this articles that this component supports."""
276
+ depend_on: List[ArticleRef]
277
+ """List of references to other component of this articles that this component depends on."""
185
278
 
186
- def __eq__(self, other: "ArticleMainBase") -> bool:
187
- """Compares two ArticleBase objects based on their model_dump_json representation."""
188
- return self.model_dump_json() == other.model_dump_json()
279
+ writing_aim: List[str]
280
+ """List of writing aims of the research component in academic style."""
189
281
 
190
- def __hash__(self) -> int:
191
- """Calculates a hash value for the ArticleBase object based on its model_dump_json representation."""
192
- return hash(self.model_dump_json())
282
+ def update_from_inner(self, other: Self) -> Self:
283
+ """Updates the current instance with the attributes of another instance."""
284
+ self.support_to.clear()
285
+ self.support_to.extend(other.support_to)
286
+ self.depend_on.clear()
287
+ self.depend_on.extend(other.depend_on)
288
+ self.writing_aim.clear()
289
+ self.writing_aim.extend(other.writing_aim)
290
+ self.description = other.description
291
+ return self
@@ -1,12 +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
- ArticleMainBase,
9
+ ArticleOutlineBase,
10
10
  ArticleRef,
11
11
  ChapterBase,
12
12
  SectionBase,
@@ -32,22 +32,19 @@ class Paragraph(CensoredAble):
32
32
  """List of sentences forming the paragraph's content."""
33
33
 
34
34
 
35
- class ArticleSubsection(ArticleMainBase, SubSectionBase):
35
+ class ArticleSubsection(ArticleOutlineBase, SubSectionBase):
36
36
  """Atomic argumentative unit with technical specificity."""
37
37
 
38
38
  paragraphs: List[Paragraph]
39
39
  """List of Paragraph objects containing the content of the subsection."""
40
40
 
41
- def resolve_update_error(self, other: Self) -> str:
42
- """Resolve update errors in the article outline."""
43
- if self.title != other.title:
44
- return f"Title `{other.title}` mismatched, expected `{self.title}`. "
45
- return ""
46
-
47
- def _update_from_inner(self, other: Self) -> Self:
41
+ def update_from_inner(self, other: Self) -> Self:
48
42
  """Updates the current instance with the attributes of another instance."""
49
43
  logger.debug(f"Updating SubSection {self.title}")
50
- self.paragraphs = other.paragraphs
44
+ SubSectionBase.update_from(self, other)
45
+ ArticleOutlineBase.update_from(self, other)
46
+ self.paragraphs.clear()
47
+ self.paragraphs.extend(other.paragraphs)
51
48
  return self
52
49
 
53
50
  def to_typst_code(self) -> str:
@@ -59,77 +56,23 @@ class ArticleSubsection(ArticleMainBase, SubSectionBase):
59
56
  return f"=== {self.title}\n" + "\n\n".join("".join(p.sentences) for p in self.paragraphs)
60
57
 
61
58
 
62
- class ArticleSection(ArticleMainBase, SectionBase[ArticleSubsection]):
59
+ class ArticleSection(ArticleOutlineBase, SectionBase[ArticleSubsection]):
63
60
  """Atomic argumentative unit with high-level specificity."""
64
61
 
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
-
70
- if s_len != len(other.subsections):
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
62
 
99
63
 
100
- class ArticleChapter(ArticleMainBase, ChapterBase[ArticleSection]):
64
+ class ArticleChapter(ArticleOutlineBase, ChapterBase[ArticleSection]):
101
65
  """Thematic progression implementing research function."""
102
66
 
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
67
 
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
68
 
131
-
132
- class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, ArticleBase[ArticleChapter]):
69
+ class Article(
70
+ Display,
71
+ CensoredAble,
72
+ WithRef[ArticleOutline],
73
+ PersistentAble,
74
+ ArticleBase[ArticleChapter],
75
+ ):
133
76
  """Represents a complete academic paper specification, incorporating validation constraints.
134
77
 
135
78
  This class integrates display, censorship processing, article structure referencing, and persistence capabilities,
@@ -189,30 +132,24 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
189
132
  article.chapters.append(article_chapter)
190
133
  return article
191
134
 
192
- def iter_dfs(self) -> Generator[ArticleMainBase, None, None]:
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:
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:
205
142
  """Resolves a reference to the corresponding section or subsection in the article.
206
143
 
207
144
  Args:
208
145
  ref (ArticleRef): The reference to resolve.
209
146
 
210
147
  Returns:
211
- ArticleMainBase: The corresponding section or subsection.
148
+ ArticleOutlineBase: The corresponding section or subsection.
212
149
  """
213
150
  return ok(ref.deref(self), f"{ref} not found in {self.title}")
214
151
 
215
- def gather_dependencies(self, article: ArticleMainBase) -> List[ArticleMainBase]:
152
+ def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
216
153
  """Gathers dependencies for all sections and subsections in the article.
217
154
 
218
155
  This method should be called after the article is fully constructed.
@@ -226,11 +163,11 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
226
163
 
227
164
  return list(set(depends + supports))
228
165
 
229
- def gather_dependencies_recursive(self, article: ArticleMainBase) -> List[ArticleMainBase]:
166
+ def gather_dependencies_recursive(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
230
167
  """Gathers all dependencies recursively for the given article.
231
168
 
232
169
  Args:
233
- article (ArticleMainBase): The article to gather dependencies for.
170
+ article (ArticleOutlineBase): The article to gather dependencies for.
234
171
 
235
172
  Returns:
236
173
  List[ArticleBase]: A list of all dependencies for the given article.
@@ -269,7 +206,7 @@ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble, Ar
269
206
 
270
207
  def iter_dfs_with_deps(
271
208
  self, chapter: bool = True, section: bool = True, subsection: bool = True
272
- ) -> Generator[Tuple[ArticleMainBase, List[ArticleMainBase]], None, None]:
209
+ ) -> Generator[Tuple[ArticleOutlineBase, List[ArticleOutlineBase]], None, None]:
273
210
  """Iterates through the article in a depth-first manner, yielding each component and its dependencies.
274
211
 
275
212
  Args:
@@ -1,6 +1,6 @@
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
3
+ from typing import Generator, List, Optional, Self, Tuple, Union, override
4
4
 
5
5
  import regex
6
6
  from fabricatio.models.extra.article_base import (
@@ -19,12 +19,26 @@ class ArticleSubsectionOutline(ArticleOutlineBase, SubSectionBase):
19
19
  """Atomic research component specification for academic paper generation."""
20
20
 
21
21
 
22
+
22
23
  class ArticleSectionOutline(ArticleOutlineBase, SectionBase[ArticleSubsectionOutline]):
23
- """A slightly more detailed research component specification for academic paper generation."""
24
+ """A slightly more detailed research component specification for academic paper generation, Must contain subsections."""
25
+
26
+
27
+ def update_from_inner(self, other: Self) -> Self:
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
24
32
 
25
33
 
26
34
  class ArticleChapterOutline(ArticleOutlineBase, ChapterBase[ArticleSectionOutline]):
27
- """Macro-structural unit implementing standard academic paper organization."""
35
+ """Macro-structural unit implementing standard academic paper organization. Must contain sections."""
36
+
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
28
42
 
29
43
 
30
44
  class ArticleOutline(
@@ -34,7 +48,7 @@ class ArticleOutline(
34
48
  PersistentAble,
35
49
  ArticleBase[ArticleChapterOutline],
36
50
  ):
37
- """Complete academic paper blueprint with hierarchical validation."""
51
+ """A class representing the outline of an academic paper."""
38
52
 
39
53
  abstract: str
40
54
  """The abstract is a concise summary of the academic paper's main findings."""
@@ -82,26 +96,16 @@ class ArticleOutline(
82
96
  lines.append(f"=== {i}.{j}.{k} {subsection.title}")
83
97
  return "\n".join(lines)
84
98
 
85
- def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
86
- """Iterates through the article outline in a depth-first manner.
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.
87
106
 
88
107
  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.
108
+ Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
105
109
  """
106
110
  summary = ""
107
111
  for component in self.iter_dfs():
@@ -111,8 +115,10 @@ class ArticleOutline(
111
115
  for ref in component.support_to:
112
116
  if not ref.deref(self):
113
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"
114
-
115
- return summary
118
+ summary += component.introspect()
119
+ if summary:
120
+ return component, summary
121
+ return None
116
122
 
117
123
  @classmethod
118
124
  def from_typst_code(
@@ -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
@@ -139,6 +139,61 @@ class PersistentAble(Base):
139
139
  return self.model_validate_json(Path(path).read_text(encoding="utf-8"))
140
140
 
141
141
 
142
+ class ModelHash(Base):
143
+ """Class that provides a hash value for the object."""
144
+
145
+ def __hash__(self) -> int:
146
+ """Calculates a hash value for the ArticleBase object based on its model_dump_json representation."""
147
+ return hash(self.model_dump_json())
148
+
149
+
150
+ class UpdateFrom(Base):
151
+ """Class that provides a method to update the object from another object."""
152
+
153
+ def update_pre_check(self, other: Self) -> Self:
154
+ """Pre-check for updating the object from another object."""
155
+ if not isinstance(other, self.__class__):
156
+ raise TypeError(f"Cannot update from a non-{self.__class__.__name__} instance.")
157
+
158
+ return self
159
+
160
+ @abstractmethod
161
+ def update_from_inner(self, other: Self) -> Self:
162
+ """Updates the current instance with the attributes of another instance."""
163
+
164
+ @final
165
+ def update_from(self, other: Self) -> Self:
166
+ """Updates the current instance with the attributes of another instance."""
167
+ return self.update_pre_check(other).update_from_inner(other)
168
+
169
+
170
+ class ResolveUpdateConflict(Base):
171
+ """Class that provides a method to update the object from another object."""
172
+
173
+ @abstractmethod
174
+ def resolve_update_conflict(self, other: Self) -> str:
175
+ """Resolve the update conflict between two objects.
176
+
177
+ Args:
178
+ other (Self): The other object to resolve the update conflict with.
179
+
180
+ Returns:
181
+ str: The resolved update conflict.
182
+ """
183
+
184
+
185
+ class Introspect(Base):
186
+ """Class that provides a method to introspect the object."""
187
+
188
+ @abstractmethod
189
+ def introspect(self) -> str:
190
+ """Internal introspection of the object.
191
+
192
+ Returns:
193
+ str: The internal introspection of the object.
194
+ """
195
+
196
+
142
197
  class WithBriefing(Named, Described):
143
198
  """Class that provides a briefing based on the name and description."""
144
199
 
@@ -392,7 +447,7 @@ class PrepareVectorization(Base):
392
447
  """
393
448
  max_length = max_length or configs.embedding.max_sequence_length
394
449
  chunk = self._prepare_vectorization_inner()
395
- if len(chunk) > max_length:
450
+ if max_length and len(chunk) > max_length:
396
451
  logger.error(err := f"Chunk exceeds maximum sequence length {max_length}.")
397
452
  raise ValueError(err)
398
453
 
@@ -475,7 +530,7 @@ class ScopedConfig(Base):
475
530
  """Fallback to another instance's attribute values if the current instance's attributes are None.
476
531
 
477
532
  Args:
478
- other (LLMUsage): Another instance from which to copy attribute values.
533
+ other (ScopedConfig): Another instance from which to copy attribute values.
479
534
 
480
535
  Returns:
481
536
  Self: The current instance, allowing for method chaining.
@@ -495,7 +550,7 @@ class ScopedConfig(Base):
495
550
  """Hold to another instance's attribute values if the current instance's attributes are None.
496
551
 
497
552
  Args:
498
- others (LLMUsage | Iterable[LLMUsage]): Another instance or iterable of instances from which to copy attribute values.
553
+ others (Union[ScopedConfig, Iterable[ScopedConfig]]): Another instance or iterable of instances from which to copy attribute values.
499
554
 
500
555
  Returns:
501
556
  Self: The current instance, allowing for method chaining.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fabricatio
3
- Version: 0.2.7.dev4
3
+ Version: 0.2.7.dev5
4
4
  Classifier: License :: OSI Approved :: MIT License
5
5
  Classifier: Programming Language :: Rust
6
6
  Classifier: Programming Language :: Python :: 3.12
@@ -1,7 +1,7 @@
1
- fabricatio-0.2.7.dev4.dist-info/METADATA,sha256=DshHoi8eegFUNjltQNJgm0SkKSGCLPYJj-oaBBlth9o,5259
2
- fabricatio-0.2.7.dev4.dist-info/WHEEL,sha256=jABKVkLC9kJr8mi_er5jOqpiQUjARSLXDUIIxDqsS50,96
3
- fabricatio-0.2.7.dev4.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
4
- fabricatio/actions/article.py,sha256=AgxNKIRLXF9T-TdrhLPE8NWmT8QZXz1QvFnouvuoRBc,7684
1
+ fabricatio-0.2.7.dev5.dist-info/METADATA,sha256=ANxpgCb6nTOneCJ-RdQ7ejNoefGzRdKyfQIvwVfCJiU,5259
2
+ fabricatio-0.2.7.dev5.dist-info/WHEEL,sha256=jABKVkLC9kJr8mi_er5jOqpiQUjARSLXDUIIxDqsS50,96
3
+ fabricatio-0.2.7.dev5.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
4
+ fabricatio/actions/article.py,sha256=cv1pyGOvgMVMaHJqVDKY6h5_u1XtVw3lzJCDFqx4aoc,8073
5
5
  fabricatio/actions/article_rag.py,sha256=PiOFxI6VTmLXm3BK-01g_KH1mTE9uOtnA-CwUjt16AU,1456
6
6
  fabricatio/actions/output.py,sha256=K7xsBH8MjXRH6JOy3ZO94KCQzX2jNrwPPK_rRXVkS0E,1161
7
7
  fabricatio/actions/rag.py,sha256=QBdzEM8MloM_ahx5pTBZAETm9_631lTe_0ih_he_Iuo,2759
@@ -20,12 +20,12 @@ fabricatio/fs/__init__.py,sha256=PCf0s_9KDjVfNw7AfPoJzGt3jMq4gJOfbcT4pb0D0ZY,588
20
20
  fabricatio/journal.py,sha256=stnEP88aUBA_GmU9gfTF2EZI8FS2OyMLGaMSTgK4QgA,476
21
21
  fabricatio/models/action.py,sha256=UlflniS__MMrUXglu_U3PDFAtKEjVsKEix17AT9oP3M,8769
22
22
  fabricatio/models/events.py,sha256=QvlnS8FEELg6KNabcJMeh2GV_y0ZBzKOPphcteKYWYU,4183
23
- fabricatio/models/extra/article_base.py,sha256=9zkS89G3L9moc0iYNq9D7DCcv0K52jRQWjM1MUK__dg,8449
23
+ fabricatio/models/extra/article_base.py,sha256=Y4ZdC1yabs7jCNKwc-P8GpAJShGJx7EcLyhXuJ194Fo,11469
24
24
  fabricatio/models/extra/article_essence.py,sha256=DUESuK4CGgkRvIMoJCv4l8MNp5MawRYoNOtLCrFRPXY,9229
25
- fabricatio/models/extra/article_main.py,sha256=yhgZvHWAgL-BJ-eolaXTqBzyWq340GfJucoTY_2CPzs,11342
26
- fabricatio/models/extra/article_outline.py,sha256=83hFbCaUDuXn9K_0qcCXQOQ1NBmK-7305ChfKvhNXns,7404
25
+ fabricatio/models/extra/article_main.py,sha256=l6HO3PajaesEET6Evd2A7OOpHRelGYr4y88JyoPmP3Y,8498
26
+ fabricatio/models/extra/article_outline.py,sha256=VV6RUY7VzKwqCXoykDi-vL2nqHouOuo1Q3hLdCdNDpQ,7740
27
27
  fabricatio/models/extra/article_proposal.py,sha256=p0NPzqg9x6t65DZqdF52Z1P0JwP6kwo2_eP-NsXgifU,1720
28
- fabricatio/models/generic.py,sha256=GVjcDnzwjKElCZoEVciS7X8SSPjqEp_6M2fLiyAkwNo,17644
28
+ fabricatio/models/generic.py,sha256=yEpP4cvvmB0sDbs36pIl2KYFsnNRKq7V0m3HqoSQc7o,19492
29
29
  fabricatio/models/kwargs_types.py,sha256=chJ-rHaeBVRUPuORHuGR3DdNxxTUrotz0eflPEh4l4w,5474
30
30
  fabricatio/models/role.py,sha256=mmQbJ6GKr2Gx3wtjEz8d-vYoXs09ffcEkT_eCXaDd3E,2782
31
31
  fabricatio/models/task.py,sha256=8NaR7ojQWyM740EDTqt9stwHKdrD6axCRpLKo0QzS-I,10492
@@ -42,6 +42,6 @@ fabricatio/workflows/rag.py,sha256=-YYp2tlE9Vtfgpg6ROpu6QVO8j8yVSPa6yDzlN3qVxs,5
42
42
  fabricatio/_rust.pyi,sha256=dGTGV7viu3YAGl1cRKIWrdHPc1hlwk3_hbaDaswxdVo,3831
43
43
  fabricatio/_rust_instances.py,sha256=2GwF8aVfYNemRI2feBzH1CZfBGno-XJJE5imJokGEYw,314
44
44
  fabricatio/__init__.py,sha256=SzBYsRhZeL77jLtfJEjmoHOSwHwUGyvMATX6xfndLDM,1135
45
- fabricatio/_rust.cp312-win_amd64.pyd,sha256=opsv_4PpUw06LsJqoKuuTr8aQ3NdAMxB1Ep2RrQqaLc,1835520
46
- fabricatio-0.2.7.dev4.data/scripts/tdown.exe,sha256=eEr8wBSNDWdntRBBly7YQr1DnA0ru8NAgAmfMPw7SdU,3402240
47
- fabricatio-0.2.7.dev4.dist-info/RECORD,,
45
+ fabricatio/_rust.cp312-win_amd64.pyd,sha256=db4DHfetEQMNh7hqmL6All-asJooQxFIIXZkoWKAx4c,1835008
46
+ fabricatio-0.2.7.dev5.data/scripts/tdown.exe,sha256=xZcDsLIn9rJZcM8iDNgS-eNK4ckH5rufC6mU6I2vmOo,3399680
47
+ fabricatio-0.2.7.dev5.dist-info/RECORD,,