fabricatio 0.2.7.dev5__cp312-cp312-win_amd64.whl → 0.2.8.dev1__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.
@@ -1,24 +1,23 @@
1
1
  """A foundation for hierarchical document components with dependency tracking."""
2
- from abc import ABC
2
+
3
+ from abc import ABC, abstractmethod
3
4
  from enum import StrEnum
4
- from typing import TYPE_CHECKING, Generator, List, Optional, Self, Union, overload
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,
10
+ AsPrompt,
8
11
  CensoredAble,
9
12
  Display,
13
+ FinalizedDumpAble,
10
14
  Introspect,
11
15
  ModelHash,
12
16
  PersistentAble,
13
- ProposedAble,
17
+ ProposedUpdateAble,
14
18
  ResolveUpdateConflict,
15
- UpdateFrom,
16
19
  )
17
20
 
18
- if TYPE_CHECKING:
19
- from fabricatio.models.extra.article_main import Article
20
- from fabricatio.models.extra.article_outline import ArticleOutline
21
-
22
21
 
23
22
  class ReferringType(StrEnum):
24
23
  """Enumeration of different types of references that can be made in an article."""
@@ -28,7 +27,11 @@ class ReferringType(StrEnum):
28
27
  SUBSECTION = "subsection"
29
28
 
30
29
 
31
- class ArticleRef(CensoredAble):
30
+ type RefKey = Tuple[str, Optional[str], Optional[str]]
31
+
32
+
33
+ @cache
34
+ class ArticleRef(CensoredAble, Display, ProposedUpdateAble):
32
35
  """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
36
 
34
37
  Examples:
@@ -61,30 +64,21 @@ class ArticleRef(CensoredAble):
61
64
  ```
62
65
  """
63
66
 
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
67
  referred_chapter_title: str
71
68
  """`title` Field of the referenced chapter"""
69
+ referred_section_title: Optional[str] = None
70
+ """`title` Field of the referenced section."""
71
+ referred_subsection_title: Optional[str] = None
72
+ """`title` Field of the referenced subsection."""
72
73
 
73
- def __hash__(self) -> int:
74
- """Overrides the default hash function to ensure consistent hashing across instances."""
75
- return hash((self.referred_chapter_title, self.referred_section_title, self.referred_subsection_title))
76
-
77
- @overload
78
- def deref(self, article: "Article") -> Optional["ArticleMainBase"]:
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."""
74
+ def update_from_inner(self, other: Self) -> Self:
75
+ """Updates the current instance with the attributes of another instance."""
76
+ self.referred_chapter_title = other.referred_chapter_title
77
+ self.referred_section_title = other.referred_section_title
78
+ self.referred_subsection_title = other.referred_subsection_title
79
+ return self
84
80
 
85
- def deref(
86
- self, article: Union["ArticleOutline", "Article"]
87
- ) -> Union["ArticleOutlineBase", "ArticleMainBase", None]:
81
+ def deref(self, article: "ArticleBase") -> Optional["ArticleOutlineBase"]:
88
82
  """Dereference the reference to the actual section or subsection within the provided article.
89
83
 
90
84
  Args:
@@ -111,21 +105,88 @@ class ArticleRef(CensoredAble):
111
105
  return ReferringType.CHAPTER
112
106
 
113
107
 
114
- class SubSectionBase(
115
- UpdateFrom,
108
+ class ArticleMetaData(CensoredAble, Display):
109
+ """Metadata for an article component."""
110
+
111
+ description: str
112
+ """Description of the research component in academic style."""
113
+
114
+ support_to: List[ArticleRef]
115
+ """List of references to other component of this articles that this component supports."""
116
+ depend_on: List[ArticleRef]
117
+ """List of references to other component of this articles that this component depends on."""
118
+
119
+ writing_aim: List[str]
120
+ """List of writing aims of the research component in academic style."""
121
+ title: str
122
+ """Do not add any prefix or suffix to the title. should not contain special characters."""
123
+
124
+
125
+ class ArticleRefPatch(ProposedUpdateAble, Display):
126
+ """Patch for article refs."""
127
+
128
+ tweaked: List[ArticleRef]
129
+ """Tweaked refs"""
130
+
131
+ def update_from_inner(self, other: Self) -> Self:
132
+ """Updates the current instance with the attributes of another instance."""
133
+ self.tweaked.clear()
134
+ self.tweaked.extend(other.tweaked)
135
+ return self
136
+
137
+ @classmethod
138
+ def default(cls) -> "ArticleRefPatch":
139
+ """Defaults to empty list."""
140
+ return cls(tweaked=[])
141
+
142
+
143
+ class ArticleOutlineBase(
144
+ ArticleMetaData,
145
+ ResolveUpdateConflict,
146
+ ProposedUpdateAble,
147
+ PersistentAble,
148
+ ModelHash,
116
149
  Introspect,
117
150
  ):
118
- """Base class for article sections and subsections."""
151
+ """Base class for article outlines."""
119
152
 
120
- title: str
121
- """Title of the subsection, do not add any prefix or suffix to the title. should not contain special characters."""
153
+ @property
154
+ def metadata(self) -> ArticleMetaData:
155
+ """Returns the metadata of the article component."""
156
+ return ArticleMetaData.model_validate(self, from_attributes=True)
122
157
 
158
+ def update_metadata(self, other: ArticleMetaData) -> Self:
159
+ """Updates the metadata of the current instance with the attributes of another instance."""
160
+ self.support_to.clear()
161
+ self.support_to.extend(other.support_to)
162
+ self.depend_on.clear()
163
+ self.depend_on.extend(other.depend_on)
164
+ self.writing_aim.clear()
165
+ self.writing_aim.extend(other.writing_aim)
166
+ self.description = other.description
167
+ return self
168
+
169
+ def display_metadata(self) -> str:
170
+ """Displays the metadata of the current instance."""
171
+ return self.model_dump_json(
172
+ indent=1, include={"title", "writing_aim", "description", "support_to", "depend_on"}
173
+ )
174
+
175
+ def update_from_inner(self, other: Self) -> Self:
176
+ """Updates the current instance with the attributes of another instance."""
177
+ return self.update_metadata(other)
178
+
179
+ @abstractmethod
123
180
  def to_typst_code(self) -> str:
124
181
  """Converts the component into a Typst code snippet for rendering."""
125
- return f"=== {self.title}\n"
126
182
 
127
- def update_from_inner(self, other: Self) -> Self:
128
- return self
183
+
184
+ class SubSectionBase(ArticleOutlineBase):
185
+ """Base class for article sections and subsections."""
186
+
187
+ def to_typst_code(self) -> str:
188
+ """Converts the component into a Typst code snippet for rendering."""
189
+ return f"=== {self.title}\n"
129
190
 
130
191
  def introspect(self) -> str:
131
192
  """Introspects the article subsection outline."""
@@ -138,16 +199,11 @@ class SubSectionBase(
138
199
  return ""
139
200
 
140
201
 
141
- class SectionBase[T: SubSectionBase](
142
- UpdateFrom,
143
- Introspect,
144
- ):
202
+ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
145
203
  """Base class for article sections and subsections."""
146
204
 
147
205
  subsections: List[T]
148
206
  """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
207
 
152
208
  def to_typst_code(self) -> str:
153
209
  """Converts the section into a Typst formatted code snippet.
@@ -174,6 +230,7 @@ class SectionBase[T: SubSectionBase](
174
230
 
175
231
  def update_from_inner(self, other: Self) -> Self:
176
232
  """Updates the current instance with the attributes of another instance."""
233
+ super().update_from_inner(other)
177
234
  if len(self.subsections) == 0:
178
235
  self.subsections = other.subsections
179
236
  return self
@@ -189,16 +246,12 @@ class SectionBase[T: SubSectionBase](
189
246
  return ""
190
247
 
191
248
 
192
- class ChapterBase[T: SectionBase](
193
- UpdateFrom,
194
- Introspect,
195
- ):
249
+ class ChapterBase[T: SectionBase](ArticleOutlineBase):
196
250
  """Base class for article chapters."""
197
251
 
198
252
  sections: List[T]
199
253
  """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."""
254
+
202
255
  def to_typst_code(self) -> str:
203
256
  """Converts the chapter into a Typst formatted code snippet for rendering."""
204
257
  return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
@@ -233,21 +286,35 @@ class ChapterBase[T: SectionBase](
233
286
  return ""
234
287
 
235
288
 
236
- class ArticleBase[T: ChapterBase](Base):
289
+ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, ABC):
237
290
  """Base class for article outlines."""
238
291
 
292
+ language: str
293
+ """Written language of the article. SHALL be aligned to the language of the article proposal provided."""
294
+
295
+ title: str
296
+ """Title of the academic paper."""
297
+
298
+ prospect: str
299
+ """Consolidated research statement with four pillars:
300
+ 1. Problem Identification: Current limitations
301
+ 2. Methodological Response: Technical approach
302
+ 3. Empirical Validation: Evaluation strategy
303
+ 4. Scholarly Impact: Field contributions
304
+ """
305
+
306
+ abstract: str
307
+ """The abstract is a concise summary of the academic paper's main findings."""
239
308
  chapters: List[T]
240
309
  """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
310
 
244
- def iter_dfs(
311
+ def iter_dfs_rev(
245
312
  self,
246
- ) -> Generator[ChapterBase | SectionBase | SubSectionBase, None, None]:
247
- """Performs a depth-first search (DFS) through the article structure.
313
+ ) -> Generator[ArticleOutlineBase, None, None]:
314
+ """Performs a depth-first search (DFS) through the article structure in reverse order.
248
315
 
249
316
  Returns:
250
- Generator[ArticleMainBase]: Each component in the article structure.
317
+ Generator[ArticleMainBase]: Each component in the article structure in reverse order.
251
318
  """
252
319
  for chap in self.chapters:
253
320
  for sec in chap.sections:
@@ -255,37 +322,80 @@ class ArticleBase[T: ChapterBase](Base):
255
322
  yield sec
256
323
  yield chap
257
324
 
325
+ def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
326
+ """Performs a depth-first search (DFS) through the article structure.
258
327
 
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."""
328
+ Returns:
329
+ Generator[ArticleMainBase]: Each component in the article structure.
330
+ """
331
+ for chap in self.chapters:
332
+ yield chap
333
+ for sec in chap.sections:
334
+ yield sec
335
+ yield from sec.subsections
270
336
 
271
- description: str
272
- """Description of the research component in academic style."""
337
+ def iter_sections(self) -> Generator[Tuple[ChapterBase, SectionBase], None, None]:
338
+ """Iterates through all sections in the article.
273
339
 
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."""
340
+ Returns:
341
+ Generator[ArticleOutlineBase]: Each section in the article.
342
+ """
343
+ for chap in self.chapters:
344
+ for sec in chap.sections:
345
+ yield chap, sec
278
346
 
279
- writing_aim: List[str]
280
- """List of writing aims of the research component in academic style."""
347
+ def iter_subsections(self) -> Generator[Tuple[ChapterBase, SectionBase, SubSectionBase], None, None]:
348
+ """Iterates through all subsections in the article.
281
349
 
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
350
+ Returns:
351
+ Generator[ArticleOutlineBase]: Each subsection in the article.
352
+ """
353
+ for chap, sec in self.iter_sections():
354
+ for subsec in sec.subsections:
355
+ yield chap, sec, subsec
356
+
357
+ def find_introspected(self) -> Optional[Tuple[ArticleOutlineBase, str]]:
358
+ """Finds the first introspected component in the article structure."""
359
+ summary = ""
360
+ for component in self.iter_dfs_rev():
361
+ summary += component.introspect()
362
+ if summary:
363
+ return component, summary
364
+ return None
365
+
366
+ def find_illegal_ref(self) -> Optional[Tuple[ArticleRef, str]]:
367
+ """Finds the first illegal component in the outline.
368
+
369
+ Returns:
370
+ Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
371
+ """
372
+ summary = ""
373
+ for component in self.iter_dfs_rev():
374
+ for ref in chain(component.depend_on, component.support_to):
375
+ if not ref.deref(self):
376
+ 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"
377
+ if summary:
378
+ return ref, summary
379
+ return None
380
+
381
+ def finalized_dump(self) -> str:
382
+ """Generates standardized hierarchical markup for academic publishing systems.
383
+
384
+ Implements ACL 2024 outline conventions with four-level structure:
385
+ = Chapter Title (Level 1)
386
+ == Section Title (Level 2)
387
+ === Subsection Title (Level 3)
388
+ ==== Subsubsection Title (Level 4)
389
+
390
+ Returns:
391
+ str: Strictly formatted outline with academic sectioning
392
+
393
+ Example:
394
+ = Methodology
395
+ == Neural Architecture Search Framework
396
+ === Differentiable Search Space
397
+ ==== Constrained Optimization Parameters
398
+ === Implementation Details
399
+ == Evaluation Protocol
400
+ """
401
+ return "\n\n".join(a.to_typst_code() for a in self.chapters)
@@ -134,10 +134,10 @@ class Highlightings(BaseModel):
134
134
 
135
135
 
136
136
  class ArticleEssence(ProposedAble, Display, PrepareVectorization):
137
- """Semantic fingerprint of academic paper for structured analysis.
137
+ """ArticleEssence is a structured representation of the core elements of a scientific article."""
138
138
 
139
- Encodes research artifacts with dual human-machine interpretability.
140
- """
139
+ language: str = Field(...)
140
+ """Language of the original article."""
141
141
 
142
142
  title: str = Field(...)
143
143
  """Exact title of the original article without any modification.
@@ -149,7 +149,7 @@ class ArticleEssence(ProposedAble, Display, PrepareVectorization):
149
149
  """
150
150
 
151
151
  authors: List[str]
152
- """Original author names exactly as they appear in the source document. No translation or paraphrasing.
152
+ """Original author full names exactly as they appear in the source document. No translation or paraphrasing.
153
153
  Extract complete list without any modifications or formatting changes."""
154
154
 
155
155
  keywords: List[str]
@@ -170,11 +170,7 @@ class ArticleEssence(ProposedAble, Display, PrepareVectorization):
170
170
  """Domain tags for research focus."""
171
171
 
172
172
  abstract: str = Field(...)
173
- """Three-paragraph structured abstract:
174
- Paragraph 1: Problem & Motivation (2-3 sentences)
175
- Paragraph 2: Methodology & Innovations (3-4 sentences)
176
- Paragraph 3: Results & Impact (2-3 sentences)
177
- Total length: 150-250 words"""
173
+ """Abstract text with original language."""
178
174
 
179
175
  core_contributions: List[str]
180
176
  """3-5 technical contributions using CRediT taxonomy verbs.
@@ -184,6 +180,7 @@ class ArticleEssence(ProposedAble, Display, PrepareVectorization):
184
180
  - 'Established cross-lingual transfer metrics'"""
185
181
 
186
182
  technical_novelty: List[str]
183
+
187
184
  """Patent-style claims with technical specificity.
188
185
  Format: 'A [system/method] comprising [novel components]...'
189
186
  Example:
@@ -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, override
4
+ from typing import Dict, 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
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
- sentences: List[str]
32
- """List of sentences forming the paragraph's content."""
30
+ content: str
31
+ """The actual content of the paragraph, represented as a string."""
33
32
 
34
33
 
35
- class ArticleSubsection(ArticleOutlineBase, SubSectionBase):
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
- SubSectionBase.update_from(self, other)
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("".join(p.sentences) for p in self.paragraphs)
60
+ return f"=== {self.title}\n" + "\n\n".join(p.content for p in self.paragraphs)
57
61
 
58
62
 
59
- class ArticleSection(ArticleOutlineBase, SectionBase[ArticleSubsection]):
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,17 @@ class Article(
79
81
  aiming to provide a comprehensive model for academic papers.
80
82
  """
81
83
 
82
- abstract: str
83
- """Contains a summary of the academic paper."""
84
-
85
- title: str
86
- """Represents the title of the academic paper."""
84
+ def _as_prompt_inner(self) -> Dict[str, str]:
85
+ return {
86
+ "Original Article Briefing": self.referenced.referenced.referenced,
87
+ "Original Article Proposal": self.referenced.referenced.display(),
88
+ "Original Article Outline": self.referenced.display(),
89
+ "Original Article": self.display(),
90
+ }
87
91
 
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)
92
+ @override
93
+ def iter_subsections(self) -> Generator[Tuple[ArticleChapter, ArticleSection, ArticleSubsection], None, None]:
94
+ return super().iter_subsections()
98
95
 
99
96
  @classmethod
100
97
  def from_outline(cls, outline: ArticleOutline) -> "Article":
@@ -107,7 +104,7 @@ class Article(
107
104
  Article: The generated article.
108
105
  """
109
106
  # Set the title from the outline
110
- article = Article(**outline.model_dump(include={"title", "abstract"}), chapters=[])
107
+ article = Article(**outline.model_dump(exclude={"chapters"}), chapters=[])
111
108
 
112
109
  for chapter in outline.chapters:
113
110
  # Create a new chapter
@@ -132,33 +129,16 @@ class Article(
132
129
  article.chapters.append(article_chapter)
133
130
  return article
134
131
 
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
132
  def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
153
133
  """Gathers dependencies for all sections and subsections in the article.
154
134
 
155
135
  This method should be called after the article is fully constructed.
156
136
  """
157
- depends = [self.deref(a) for a in article.depend_on]
137
+ depends = [ok(a.deref(self)) for a in article.depend_on]
158
138
 
159
139
  supports = []
160
- for a in self.iter_dfs():
161
- if article in {self.deref(b) for b in a.support_to}:
140
+ for a in self.iter_dfs_rev():
141
+ if article in {ok(b.deref(self)) for b in a.support_to}:
162
142
  supports.append(a)
163
143
 
164
144
  return list(set(depends + supports))
@@ -220,7 +200,7 @@ class Article(
220
200
  if all((not chapter, not section, not subsection)):
221
201
  raise ValueError("At least one of chapter, section, or subsection must be True.")
222
202
 
223
- for component in self.iter_dfs():
203
+ for component in self.iter_dfs_rev():
224
204
  if not chapter and isinstance(component, ArticleChapter):
225
205
  continue
226
206
  if not section and isinstance(component, ArticleSection):