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.
@@ -2,25 +2,35 @@
2
2
 
3
3
  from abc import abstractmethod
4
4
  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
9
-
10
- if TYPE_CHECKING:
11
- from fabricatio.models.extra.article_main import Article
12
- from fabricatio.models.extra.article_outline import ArticleOutline
5
+ from functools import cache
6
+ from itertools import chain
7
+ from typing import Generator, List, Optional, Self, Tuple
8
+
9
+ from fabricatio.models.generic import (
10
+ CensoredAble,
11
+ Display,
12
+ FinalizedDumpAble,
13
+ Introspect,
14
+ ModelHash,
15
+ PersistentAble,
16
+ ProposedUpdateAble,
17
+ ResolveUpdateConflict,
18
+ )
13
19
 
14
20
 
15
21
  class ReferringType(StrEnum):
16
22
  """Enumeration of different types of references that can be made in an article."""
17
23
 
18
- CHAPTER: str = "chapter"
19
- SECTION: str = "section"
20
- SUBSECTION: str = "subsection"
24
+ CHAPTER = "chapter"
25
+ SECTION = "section"
26
+ SUBSECTION = "subsection"
27
+
21
28
 
29
+ type RefKey = Tuple[str, Optional[str], Optional[str]]
22
30
 
23
- class ArticleRef(CensoredAble):
31
+
32
+ @cache
33
+ class ArticleRef(CensoredAble, Display, ProposedUpdateAble):
24
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.
25
35
 
26
36
  Examples:
@@ -53,30 +63,21 @@ class ArticleRef(CensoredAble):
53
63
  ```
54
64
  """
55
65
 
56
- referred_subsection_title: Optional[str] = None
57
- """`title` Field of the referenced subsection."""
58
-
59
- referred_section_title: Optional[str] = None
60
- """`title` Field of the referenced section."""
61
-
62
66
  referred_chapter_title: str
63
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."""
64
72
 
65
- def __hash__(self) -> int:
66
- """Overrides the default hash function to ensure consistent hashing across instances."""
67
- return hash((self.referred_chapter_title, self.referred_section_title, self.referred_subsection_title))
68
-
69
- @overload
70
- def deref(self, article: "Article") -> Optional["ArticleMainBase"]:
71
- """Dereference the reference to the actual section or subsection within the provided article."""
72
-
73
- @overload
74
- def deref(self, article: "ArticleOutline") -> Optional["ArticleOutlineBase"]:
75
- """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
76
79
 
77
- def deref(
78
- self, article: Union["ArticleOutline", "Article"]
79
- ) -> Union["ArticleOutlineBase", "ArticleMainBase", None]:
80
+ def deref(self, article: "ArticleBase") -> Optional["ArticleOutlineBase"]:
80
81
  """Dereference the reference to the actual section or subsection within the provided article.
81
82
 
82
83
  Args:
@@ -103,90 +104,279 @@ class ArticleRef(CensoredAble):
103
104
  return ReferringType.CHAPTER
104
105
 
105
106
 
106
- class SubSectionBase(Base):
107
- """Base class for article sections and subsections."""
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,
130
+ Introspect,
131
+ ):
132
+ """Base class for article outlines."""
133
+
134
+ @property
135
+ def metadata(self) -> ArticleMetaData:
136
+ """Returns the metadata of the article component."""
137
+ return ArticleMetaData.model_validate(self, from_attributes=True)
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
161
+ def to_typst_code(self) -> str:
162
+ """Converts the component into a Typst code snippet for rendering."""
108
163
 
109
164
 
110
- class SectionBase[T: SubSectionBase](Base):
165
+ class SubSectionBase(ArticleOutlineBase):
111
166
  """Base class for article sections and subsections."""
112
167
 
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."""
168
+ def to_typst_code(self) -> str:
169
+ """Converts the component into a Typst code snippet for rendering."""
170
+ return f"=== {self.title}\n"
115
171
 
172
+ def introspect(self) -> str:
173
+ """Introspects the article subsection outline."""
174
+ return ""
116
175
 
117
- class ChapterBase[T: SectionBase](Base):
118
- """Base class for article chapters."""
176
+ def resolve_update_conflict(self, other: Self) -> str:
177
+ """Resolve update errors in the article outline."""
178
+ if self.title != other.title:
179
+ return f"Title mismatched, expected `{self.title}`, got `{other.title}`"
180
+ return ""
119
181
 
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."""
122
182
 
183
+ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
184
+ """Base class for article sections and subsections."""
123
185
 
124
- class ArticleBase[T: ChapterBase](Base):
125
- """Base class for article outlines."""
186
+ subsections: List[T]
187
+ """Subsections of the section. Contains at least one subsection. You can also add more as needed."""
126
188
 
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."""
189
+ def to_typst_code(self) -> str:
190
+ """Converts the section into a Typst formatted code snippet.
129
191
 
192
+ Returns:
193
+ str: The formatted Typst code snippet.
194
+ """
195
+ return f"== {self.title}\n" + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
130
196
 
131
- class ArticleOutlineBase(Base):
132
- """Base class for article outlines."""
197
+ def resolve_update_conflict(self, other: Self) -> str:
198
+ """Resolve update errors in the article outline."""
199
+ out = ""
200
+ if self.title != other.title:
201
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
202
+ if len(self.subsections) != len(other.subsections):
203
+ out += f"Section count mismatched, expected `{len(self.subsections)}`, got `{len(other.subsections)}`"
204
+ return out or "\n".join(
205
+ [
206
+ conf
207
+ for s, o in zip(self.subsections, other.subsections, strict=True)
208
+ if (conf := s.resolve_update_conflict(o))
209
+ ]
210
+ )
211
+
212
+ def update_from_inner(self, other: Self) -> Self:
213
+ """Updates the current instance with the attributes of another instance."""
214
+ super().update_from_inner(other)
215
+ if len(self.subsections) == 0:
216
+ self.subsections = other.subsections
217
+ return self
133
218
 
134
- title: str
135
- """Title of the research component in academic style."""
136
- description: str
137
- """Description of the research component in academic style."""
219
+ for self_subsec, other_subsec in zip(self.subsections, other.subsections, strict=True):
220
+ self_subsec.update_from(other_subsec)
221
+ return self
138
222
 
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."""
223
+ def introspect(self) -> str:
224
+ """Introspects the article section outline."""
225
+ if len(self.subsections) == 0:
226
+ return f"Section `{self.title}` contains no subsections, expected at least one, but got 0, you can add one or more as needed."
227
+ return ""
147
228
 
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']"""
153
229
 
230
+ class ChapterBase[T: SectionBase](ArticleOutlineBase):
231
+ """Base class for article chapters."""
154
232
 
155
- class ArticleMainBase(CensoredAble, Display, ArticleOutlineBase, PersistentAble):
156
- """Foundation for hierarchical document components with dependency tracking."""
233
+ sections: List[T]
234
+ """Sections of the chapter. Contains at least one section. You can also add more as needed."""
157
235
 
158
- @abstractmethod
159
236
  def to_typst_code(self) -> str:
160
- """Converts the component into a Typst code snippet for rendering."""
237
+ """Converts the chapter into a Typst formatted code snippet for rendering."""
238
+ return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
239
+
240
+ def resolve_update_conflict(self, other: Self) -> str:
241
+ """Resolve update errors in the article outline."""
242
+ out = ""
161
243
 
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
244
  if self.title != other.title:
166
- raise ValueError("Cannot update from a different title.")
245
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
246
+ if len(self.sections) == len(other.sections):
247
+ out += f"Chapter count mismatched, expected `{len(self.sections)}`, got `{len(other.sections)}`"
248
+
249
+ return out or "\n".join(
250
+ [conf for s, o in zip(self.sections, other.sections, strict=True) if (conf := s.resolve_update_conflict(o))]
251
+ )
252
+
253
+ def update_from_inner(self, other: Self) -> Self:
254
+ """Updates the current instance with the attributes of another instance."""
255
+ if len(self.sections) == 0:
256
+ self.sections = other.sections
257
+ return self
258
+
259
+ for self_sec, other_sec in zip(self.sections, other.sections, strict=True):
260
+ self_sec.update_from(other_sec)
167
261
  return self
168
262
 
169
- @abstractmethod
170
- def resolve_update_error(self, other: Self) -> str:
171
- """Resolve update errors in the article outline.
263
+ def introspect(self) -> str:
264
+ """Introspects the article chapter outline."""
265
+ if len(self.sections) == 0:
266
+ return f"Chapter `{self.title}` contains no sections, expected at least one, but got 0, you can add one or more as needed."
267
+ return ""
268
+
269
+
270
+ class ArticleBase[T: ChapterBase](FinalizedDumpAble):
271
+ """Base class for article outlines."""
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."""
289
+ chapters: List[T]
290
+ """Chapters of the article. Contains at least one chapter. You can also add more as needed."""
291
+
292
+ def iter_dfs_rev(
293
+ self,
294
+ ) -> Generator[ArticleOutlineBase, None, None]:
295
+ """Performs a depth-first search (DFS) through the article structure in reverse order.
172
296
 
173
297
  Returns:
174
- str: Error message indicating update errors in the article outline.
298
+ Generator[ArticleMainBase]: Each component in the article structure in reverse order.
175
299
  """
300
+ for chap in self.chapters:
301
+ for sec in chap.sections:
302
+ yield from sec.subsections
303
+ yield sec
304
+ yield chap
176
305
 
177
- @abstractmethod
178
- def _update_from_inner(self, other: Self) -> Self:
179
- """Updates the current instance with the attributes of another instance."""
306
+ def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
307
+ """Performs a depth-first search (DFS) through the article structure.
180
308
 
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)
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
185
317
 
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()
318
+ def iter_sections(self) -> Generator[Tuple[ChapterBase, SectionBase], None, None]:
319
+ """Iterates through all sections in the article.
189
320
 
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())
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
327
+
328
+ def iter_subsections(self) -> Generator[Tuple[ChapterBase, SectionBase, SubSectionBase], None, None]:
329
+ """Iterates through all subsections in the article.
330
+
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)