fabricatio 0.2.6.dev2__cp312-cp312-win_amd64.whl → 0.2.7.dev3__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.
Files changed (35) hide show
  1. fabricatio/__init__.py +7 -24
  2. fabricatio/_rust.cp312-win_amd64.pyd +0 -0
  3. fabricatio/_rust.pyi +22 -0
  4. fabricatio/actions/article.py +150 -19
  5. fabricatio/actions/article_rag.py +35 -0
  6. fabricatio/actions/output.py +21 -6
  7. fabricatio/actions/rag.py +51 -3
  8. fabricatio/capabilities/correct.py +34 -4
  9. fabricatio/capabilities/rag.py +67 -16
  10. fabricatio/capabilities/rating.py +15 -6
  11. fabricatio/capabilities/review.py +7 -4
  12. fabricatio/capabilities/task.py +5 -5
  13. fabricatio/config.py +29 -21
  14. fabricatio/decorators.py +32 -0
  15. fabricatio/models/action.py +117 -43
  16. fabricatio/models/extra/article_essence.py +226 -0
  17. fabricatio/models/extra/article_main.py +359 -0
  18. fabricatio/models/extra/article_outline.py +276 -0
  19. fabricatio/models/extra/article_proposal.py +37 -0
  20. fabricatio/models/generic.py +95 -9
  21. fabricatio/models/kwargs_types.py +40 -10
  22. fabricatio/models/role.py +30 -6
  23. fabricatio/models/tool.py +6 -2
  24. fabricatio/models/usages.py +94 -47
  25. fabricatio/models/utils.py +29 -2
  26. fabricatio/parser.py +2 -0
  27. fabricatio/workflows/articles.py +12 -1
  28. fabricatio-0.2.7.dev3.data/scripts/tdown.exe +0 -0
  29. {fabricatio-0.2.6.dev2.dist-info → fabricatio-0.2.7.dev3.dist-info}/METADATA +6 -2
  30. fabricatio-0.2.7.dev3.dist-info/RECORD +46 -0
  31. {fabricatio-0.2.6.dev2.dist-info → fabricatio-0.2.7.dev3.dist-info}/WHEEL +1 -1
  32. fabricatio/models/extra.py +0 -171
  33. fabricatio-0.2.6.dev2.data/scripts/tdown.exe +0 -0
  34. fabricatio-0.2.6.dev2.dist-info/RECORD +0 -42
  35. {fabricatio-0.2.6.dev2.dist-info → fabricatio-0.2.7.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,359 @@
1
+ """ArticleBase and ArticleSubsection classes for managing hierarchical document components."""
2
+
3
+ from abc import abstractmethod
4
+ from itertools import chain
5
+ from typing import Generator, List, Self, Tuple, final
6
+
7
+ from fabricatio.models.extra.article_outline import ArticleOutline, ArticleOutlineBase, ArticleRef
8
+ from fabricatio.models.generic import CensoredAble, Display, PersistentAble, WithRef
9
+ from fabricatio.models.utils import ok
10
+ from loguru import logger
11
+
12
+
13
+ class Paragraph(CensoredAble):
14
+ """Structured academic paragraph blueprint for controlled content generation."""
15
+
16
+ description: str
17
+ """Functional summary of the paragraph's role in document structure."""
18
+
19
+ writing_aim: List[str]
20
+ """Specific communicative objectives for this paragraph's content."""
21
+
22
+ sentences: List[str]
23
+ """List of sentences forming the paragraph's content."""
24
+
25
+
26
+ class ArticleBase(CensoredAble, Display, ArticleOutlineBase, PersistentAble):
27
+ """Foundation for hierarchical document components with dependency tracking."""
28
+
29
+ @abstractmethod
30
+ def to_typst_code(self) -> str:
31
+ """Converts the component into a Typst code snippet for rendering."""
32
+
33
+ def _update_pre_check(self, other: Self) -> Self:
34
+ if not isinstance(other, self.__class__):
35
+ raise TypeError(f"Cannot update from a non-{self.__class__} instance.")
36
+ if self.title != other.title:
37
+ raise ValueError("Cannot update from a different title.")
38
+ return self
39
+
40
+ @abstractmethod
41
+ def resolve_update_error(self, other: Self) -> str:
42
+ """Resolve update errors in the article outline.
43
+
44
+ Returns:
45
+ str: Error message indicating update errors in the article outline.
46
+ """
47
+
48
+ @abstractmethod
49
+ def _update_from_inner(self, other: Self) -> Self:
50
+ """Updates the current instance with the attributes of another instance."""
51
+
52
+ @final
53
+ def update_from(self, other: Self) -> Self:
54
+ """Updates the current instance with the attributes of another instance."""
55
+ return self._update_pre_check(other)._update_from_inner(other)
56
+
57
+ def __eq__(self, other: "ArticleBase") -> bool:
58
+ """Compares two ArticleBase objects based on their model_dump_json representation."""
59
+ return self.model_dump_json() == other.model_dump_json()
60
+
61
+ def __hash__(self) -> int:
62
+ """Calculates a hash value for the ArticleBase object based on its model_dump_json representation."""
63
+ return hash(self.model_dump_json())
64
+
65
+
66
+ class ArticleSubsection(ArticleBase):
67
+ """Atomic argumentative unit with technical specificity."""
68
+
69
+ paragraphs: List[Paragraph]
70
+ """List of Paragraph objects containing the content of the subsection."""
71
+
72
+ def resolve_update_error(self, other: Self) -> str:
73
+ """Resolve update errors in the article outline."""
74
+ if self.title != other.title:
75
+ return f"Title `{other.title}` mismatched, expected `{self.title}`. "
76
+ return ""
77
+
78
+ def _update_from_inner(self, other: Self) -> Self:
79
+ """Updates the current instance with the attributes of another instance."""
80
+ logger.debug(f"Updating SubSection {self.title}")
81
+ self.paragraphs = other.paragraphs
82
+ return self
83
+
84
+ def to_typst_code(self) -> str:
85
+ """Converts the component into a Typst code snippet for rendering.
86
+
87
+ Returns:
88
+ str: Typst code snippet for rendering.
89
+ """
90
+ return f"=== {self.title}\n" + "\n\n".join("".join(p.sentences) for p in self.paragraphs)
91
+
92
+
93
+ class ArticleSection(ArticleBase):
94
+ """Atomic argumentative unit with high-level specificity."""
95
+
96
+ subsections: List[ArticleSubsection]
97
+ """List of ArticleSubsection objects containing the content of the section."""
98
+
99
+ def resolve_update_error(self, other: Self) -> str:
100
+ """Resolve update errors in the article outline."""
101
+ if (s_len := len(self.subsections)) == 0:
102
+ return ""
103
+
104
+ if s_len != len(other.subsections):
105
+ return f"Subsections length mismatched, expected {len(self.subsections)}, got {len(other.subsections)}"
106
+
107
+ sub_sec_err_seq = [
108
+ out for s, o in zip(self.subsections, other.subsections, strict=True) if (out := s.resolve_update_error(o))
109
+ ]
110
+
111
+ if sub_sec_err_seq:
112
+ return "\n".join(sub_sec_err_seq)
113
+ return ""
114
+
115
+ def _update_from_inner(self, other: Self) -> Self:
116
+ """Updates the current instance with the attributes of another instance."""
117
+ if len(self.subsections) == 0:
118
+ self.subsections = other.subsections
119
+ return self
120
+
121
+ for self_subsec, other_subsec in zip(self.subsections, other.subsections, strict=True):
122
+ self_subsec.update_from(other_subsec)
123
+ return self
124
+
125
+ def to_typst_code(self) -> str:
126
+ """Converts the section into a Typst formatted code snippet.
127
+
128
+ Returns:
129
+ str: The formatted Typst code snippet.
130
+ """
131
+ return f"== {self.title}\n" + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
132
+
133
+
134
+ class ArticleChapter(ArticleBase):
135
+ """Thematic progression implementing research function."""
136
+
137
+ sections: List[ArticleSection]
138
+ """List of ArticleSection objects containing the content of the chapter."""
139
+
140
+ def resolve_update_error(self, other: Self) -> str:
141
+ """Resolve update errors in the article outline."""
142
+ if (s_len := len(self.sections)) == 0:
143
+ return ""
144
+
145
+ if s_len != len(other.sections):
146
+ return f"Sections length mismatched, expected {len(self.sections)}, got {len(other.sections)}"
147
+ sec_err_seq = [
148
+ out for s, o in zip(self.sections, other.sections, strict=True) if (out := s.resolve_update_error(o))
149
+ ]
150
+ if sec_err_seq:
151
+ return "\n".join(sec_err_seq)
152
+ return ""
153
+
154
+ def _update_from_inner(self, other: Self) -> Self:
155
+ """Updates the current instance with the attributes of another instance."""
156
+ if len(self.sections) == 0:
157
+ self.sections = other.sections
158
+ return self
159
+
160
+ for self_sec, other_sec in zip(self.sections, other.sections, strict=True):
161
+ self_sec.update_from(other_sec)
162
+ return self
163
+
164
+ def to_typst_code(self) -> str:
165
+ """Converts the chapter into a Typst formatted code snippet for rendering."""
166
+ return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
167
+
168
+
169
+ class Article(Display, CensoredAble, WithRef[ArticleOutline], PersistentAble):
170
+ """Represents a complete academic paper specification, incorporating validation constraints.
171
+
172
+ This class integrates display, censorship processing, article structure referencing, and persistence capabilities,
173
+ aiming to provide a comprehensive model for academic papers.
174
+ """
175
+
176
+ article_language: str
177
+ """Written language of the article. SHALL be aligned to the language of the article outline provided."""
178
+
179
+ title: str
180
+ """Represents the title of the academic paper."""
181
+
182
+ abstract: str
183
+ """Contains a summary of the academic paper."""
184
+
185
+ chapters: List[ArticleChapter]
186
+ """Contains a list of chapters in the academic paper, each chapter is an ArticleChapter object."""
187
+
188
+ def finalized_dump(self) -> str:
189
+ """Exports the article in `typst` format.
190
+
191
+ Returns:
192
+ str: Strictly formatted outline with typst formatting.
193
+ """
194
+ return "\n\n".join(c.to_typst_code() for c in self.chapters)
195
+
196
+ @classmethod
197
+ def from_outline(cls, outline: ArticleOutline) -> "Article":
198
+ """Generates an article from the given outline.
199
+
200
+ Args:
201
+ outline (ArticleOutline): The outline to generate the article from.
202
+
203
+ Returns:
204
+ Article: The generated article.
205
+ """
206
+ # Set the title from the outline
207
+ article = Article(**outline.model_dump(include={"title", "abstract"}), chapters=[])
208
+
209
+ for chapter in outline.chapters:
210
+ # Create a new chapter
211
+ article_chapter = ArticleChapter(
212
+ sections=[],
213
+ **chapter.model_dump(exclude={"sections"}),
214
+ )
215
+ for section in chapter.sections:
216
+ # Create a new section
217
+ article_section = ArticleSection(
218
+ subsections=[],
219
+ **section.model_dump(exclude={"subsections"}),
220
+ )
221
+ for subsection in section.subsections:
222
+ # Create a new subsection
223
+ article_subsection = ArticleSubsection(
224
+ paragraphs=[],
225
+ **subsection.model_dump(),
226
+ )
227
+ article_section.subsections.append(article_subsection)
228
+ article_chapter.sections.append(article_section)
229
+ article.chapters.append(article_chapter)
230
+ return article
231
+
232
+ def chap_iter(self) -> Generator[ArticleChapter, None, None]:
233
+ """Iterates over all chapters in the article.
234
+
235
+ Yields:
236
+ ArticleChapter: Each chapter in the article.
237
+ """
238
+ yield from self.chapters
239
+
240
+ def section_iter(self) -> Generator[ArticleSection, None, None]:
241
+ """Iterates over all sections in the article.
242
+
243
+ Yields:
244
+ ArticleSection: Each section in the article.
245
+ """
246
+ for chap in self.chapters:
247
+ yield from chap.sections
248
+
249
+ def subsection_iter(self) -> Generator[ArticleSubsection, None, None]:
250
+ """Iterates over all subsections in the article.
251
+
252
+ Yields:
253
+ ArticleSubsection: Each subsection in the article.
254
+ """
255
+ for sec in self.section_iter():
256
+ yield from sec.subsections
257
+
258
+ def iter_dfs(self) -> Generator[ArticleBase, None, None]:
259
+ """Performs a depth-first search (DFS) through the article structure.
260
+
261
+ Returns:
262
+ Generator[ArticleBase]: Each component in the article structure.
263
+ """
264
+ for chap in self.chap_iter():
265
+ for sec in chap.sections:
266
+ yield from sec.subsections
267
+ yield sec
268
+ yield chap
269
+
270
+ def deref(self, ref: ArticleRef) -> ArticleBase:
271
+ """Resolves a reference to the corresponding section or subsection in the article.
272
+
273
+ Args:
274
+ ref (ArticleRef): The reference to resolve.
275
+
276
+ Returns:
277
+ ArticleBase: The corresponding section or subsection.
278
+ """
279
+ return ok(ref.deref(self), f"{ref} not found in {self.title}")
280
+
281
+ def gather_dependencies(self, article: ArticleBase) -> List[ArticleBase]:
282
+ """Gathers dependencies for all sections and subsections in the article.
283
+
284
+ This method should be called after the article is fully constructed.
285
+ """
286
+ depends = [self.deref(a) for a in article.depend_on]
287
+
288
+ supports = []
289
+ for a in self.iter_dfs():
290
+ if article in {self.deref(b) for b in a.support_to}:
291
+ supports.append(a)
292
+
293
+ return list(set(depends + supports))
294
+
295
+ def gather_dependencies_recursive(self, article: ArticleBase) -> List[ArticleBase]:
296
+ """Gathers all dependencies recursively for the given article.
297
+
298
+ Args:
299
+ article (ArticleBase): The article to gather dependencies for.
300
+
301
+ Returns:
302
+ List[ArticleBase]: A list of all dependencies for the given article.
303
+ """
304
+ q = self.gather_dependencies(article)
305
+
306
+ deps = []
307
+ while q:
308
+ a = q.pop()
309
+ deps.extend(self.gather_dependencies(a))
310
+
311
+ deps = list(
312
+ chain(
313
+ filter(lambda x: isinstance(x, ArticleChapter), deps),
314
+ filter(lambda x: isinstance(x, ArticleSection), deps),
315
+ filter(lambda x: isinstance(x, ArticleSubsection), deps),
316
+ )
317
+ )
318
+
319
+ # Initialize result containers
320
+ formatted_code = ""
321
+ processed_components = []
322
+
323
+ # Process all dependencies
324
+ while deps:
325
+ component = deps.pop()
326
+ # Skip duplicates
327
+ if (component_code := component.to_typst_code()) in formatted_code:
328
+ continue
329
+
330
+ # Add this component
331
+ formatted_code += component_code
332
+ processed_components.append(component)
333
+
334
+ return processed_components
335
+
336
+ def iter_dfs_with_deps(
337
+ self, chapter: bool = True, section: bool = True, subsection: bool = True
338
+ ) -> Generator[Tuple[ArticleBase, List[ArticleBase]], None, None]:
339
+ """Iterates through the article in a depth-first manner, yielding each component and its dependencies.
340
+
341
+ Args:
342
+ chapter (bool, optional): Whether to include chapter components. Defaults to True.
343
+ section (bool, optional): Whether to include section components. Defaults to True.
344
+ subsection (bool, optional): Whether to include subsection components. Defaults to True.
345
+
346
+ Yields:
347
+ Tuple[ArticleBase, List[ArticleBase]]: Each component and its dependencies.
348
+ """
349
+ if all((not chapter, not section, not subsection)):
350
+ raise ValueError("At least one of chapter, section, or subsection must be True.")
351
+
352
+ for component in self.iter_dfs():
353
+ if not chapter and isinstance(component, ArticleChapter):
354
+ continue
355
+ if not section and isinstance(component, ArticleSection):
356
+ continue
357
+ if not subsection and isinstance(component, ArticleSubsection):
358
+ continue
359
+ yield component, (self.gather_dependencies_recursive(component))
@@ -0,0 +1,276 @@
1
+ """A module containing the ArticleOutline class, which represents the outline of an academic paper."""
2
+
3
+ from enum import Enum
4
+ from typing import TYPE_CHECKING, Generator, List, Optional, Tuple, Union, overload
5
+
6
+ import regex
7
+ from fabricatio.models.extra.article_proposal import ArticleProposal
8
+ from fabricatio.models.generic import Base, CensoredAble, Display, PersistentAble, WithRef
9
+ from fabricatio.models.utils import ok
10
+ from pydantic import Field
11
+
12
+ if TYPE_CHECKING:
13
+ from fabricatio.models.extra.article_main import Article, ArticleBase
14
+
15
+
16
+ class ReferringType(str, Enum):
17
+ """Enumeration of different types of references that can be made in an article."""
18
+
19
+ CHAPTER: str = "chapter"
20
+ SECTION: str = "section"
21
+ SUBSECTION: str = "subsection"
22
+
23
+
24
+ class ArticleRef(CensoredAble):
25
+ """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."""
26
+
27
+ referred_chapter_title: str
28
+ """`title` Field of the referenced chapter"""
29
+
30
+ referred_section_title: Optional[str] = None
31
+ """`title` Field of the referenced section. Defaults to None if not applicable, which means the reference is pointing to the entire chapter."""
32
+
33
+ referred_subsection_title: Optional[str] = None
34
+ """`title` Field of the referenced subsection. Defaults to None if not applicable, which means the reference is pointing to the entire section."""
35
+
36
+ def __hash__(self) -> int:
37
+ """Overrides the default hash function to ensure consistent hashing across instances."""
38
+ return hash((self.referred_chapter_title, self.referred_section_title, self.referred_subsection_title))
39
+
40
+ @overload
41
+ def deref(self, article: "Article") -> Optional["ArticleBase"]:
42
+ """Dereference the reference to the actual section or subsection within the provided article."""
43
+
44
+ @overload
45
+ def deref(self, article: "ArticleOutline") -> Optional["ArticleOutlineBase"]:
46
+ """Dereference the reference to the actual section or subsection within the provided article."""
47
+
48
+ def deref(self, article: Union["ArticleOutline", "Article"]) -> Union["ArticleOutlineBase", "ArticleBase", None]:
49
+ """Dereference the reference to the actual section or subsection within the provided article.
50
+
51
+ Args:
52
+ article (ArticleOutline | Article): The article to dereference the reference from.
53
+
54
+ Returns:
55
+ ArticleBase | ArticleOutline | None: The dereferenced section or subsection, or None if not found.
56
+ """
57
+ chap = next((chap for chap in article.chapters if chap.title == self.referred_chapter_title), None)
58
+ if self.referred_section_title is None or chap is None:
59
+ return chap
60
+ sec = next((sec for sec in chap.sections if sec.title == self.referred_section_title), None)
61
+ if self.referred_subsection_title is None or sec is None:
62
+ return sec
63
+ return next((subsec for subsec in sec.subsections if subsec.title == self.referred_subsection_title), None)
64
+
65
+ @property
66
+ def referring_type(self) -> ReferringType:
67
+ """Determine the type of reference based on the presence of specific attributes."""
68
+ if self.referred_subsection_title is not None:
69
+ return ReferringType.SUBSECTION
70
+ if self.referred_section_title is not None:
71
+ return ReferringType.SECTION
72
+ return ReferringType.CHAPTER
73
+
74
+
75
+ class ArticleOutlineBase(Base):
76
+ """Base class for article outlines."""
77
+
78
+ writing_aim: List[str]
79
+ """Required: List of specific rhetorical objectives (3-5 items).
80
+ Format: Each item must be an actionable phrase starting with a verb.
81
+ Example: ['Establish metric validity', 'Compare with baseline approaches',
82
+ 'Justify threshold selection']"""
83
+ depend_on: List[ArticleRef]
84
+ """Required: List of all essential ArticleRef objects identifying components this section builds upon.
85
+ Format: Each reference must point to a previously defined chapter, section, or subsection.
86
+ Note: Circular dependencies are not permitted."""
87
+ support_to: List[ArticleRef]
88
+ """Required: List of all essential ArticleRef objects identifying components this section provides evidence for.
89
+ Format: Each reference must point to a specific chapter, section, or subsection.
90
+ Note: References form a directed acyclic graph in the document structure."""
91
+
92
+ description: str
93
+ """Description of the research component in academic style."""
94
+ title: str
95
+ """Title of the research component in academic style."""
96
+
97
+
98
+ class ArticleSubsectionOutline(ArticleOutlineBase):
99
+ """Atomic research component specification for academic paper generation."""
100
+
101
+
102
+ class ArticleSectionOutline(ArticleOutlineBase):
103
+ """A slightly more detailed research component specification for academic paper generation."""
104
+
105
+ subsections: List[ArticleSubsectionOutline] = Field(min_length=1)
106
+ """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."""
107
+
108
+
109
+ class ArticleChapterOutline(ArticleOutlineBase):
110
+ """Macro-structural unit implementing standard academic paper organization."""
111
+
112
+ sections: List[ArticleSectionOutline] = Field(min_length=1)
113
+ """Standard academic progression implementing chapter goals:
114
+ 1. Context Establishment
115
+ 2. Technical Presentation
116
+ 3. Empirical Validation
117
+ 4. Comparative Analysis
118
+ 5. Synthesis
119
+
120
+ Must contains at least 1 sections, But do remember you should always add more section as required.
121
+ """
122
+
123
+
124
+ class ArticleOutline(Display, CensoredAble, WithRef[ArticleProposal], PersistentAble):
125
+ """Complete academic paper blueprint with hierarchical validation."""
126
+
127
+ article_language: str
128
+ """Written language of the article. SHALL be aligned to the language of the article proposal provided."""
129
+
130
+ title: str
131
+ """Title of the academic paper."""
132
+
133
+ prospect: str
134
+ """Consolidated research statement with four pillars:
135
+ 1. Problem Identification: Current limitations
136
+ 2. Methodological Response: Technical approach
137
+ 3. Empirical Validation: Evaluation strategy
138
+ 4. Scholarly Impact: Field contributions
139
+
140
+ Example: 'Addressing NAS computational barriers through constrained
141
+ differentiable search spaces, validated via cross-lingual MT experiments
142
+ across 50+ languages, enabling efficient architecture discovery with
143
+ 60% reduced search costs.'"""
144
+
145
+ chapters: List[ArticleChapterOutline]
146
+ """List of ArticleChapterOutline objects representing the academic paper's structure."""
147
+
148
+ abstract: str
149
+ """The abstract is a concise summary of the academic paper's main findings."""
150
+
151
+ def finalized_dump(self) -> str:
152
+ """Generates standardized hierarchical markup for academic publishing systems.
153
+
154
+ Implements ACL 2024 outline conventions with four-level structure:
155
+ = Chapter Title (Level 1)
156
+ == Section Title (Level 2)
157
+ === Subsection Title (Level 3)
158
+ ==== Subsubsection Title (Level 4)
159
+
160
+ Returns:
161
+ str: Strictly formatted outline with academic sectioning
162
+
163
+ Example:
164
+ = Methodology
165
+ == Neural Architecture Search Framework
166
+ === Differentiable Search Space
167
+ ==== Constrained Optimization Parameters
168
+ === Implementation Details
169
+ == Evaluation Protocol
170
+ """
171
+ lines: List[str] = []
172
+ for i, chapter in enumerate(self.chapters, 1):
173
+ lines.append(f"= Chapter {i}: {chapter.title}")
174
+ for j, section in enumerate(chapter.sections, 1):
175
+ lines.append(f"== {i}.{j} {section.title}")
176
+ for k, subsection in enumerate(section.subsections, 1):
177
+ lines.append(f"=== {i}.{j}.{k} {subsection.title}")
178
+ return "\n".join(lines)
179
+
180
+ def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
181
+ """Iterates through the article outline in a depth-first manner.
182
+
183
+ Returns:
184
+ ArticleOutlineBase: Each component in the article outline.
185
+ """
186
+ for chapter in self.chapters:
187
+ for section in chapter.sections:
188
+ yield from section.subsections
189
+ yield section
190
+ yield chapter
191
+
192
+ def resolve_ref_error(self) -> str:
193
+ """Resolve reference errors in the article outline.
194
+
195
+ Returns:
196
+ str: Error message indicating reference errors in the article outline.
197
+
198
+ Notes:
199
+ 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.
200
+ """
201
+ summary = ""
202
+ for component in self.iter_dfs():
203
+ for ref in component.depend_on:
204
+ if not ref.deref(self):
205
+ 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"
206
+ for ref in component.support_to:
207
+ if not ref.deref(self):
208
+ 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"
209
+
210
+ return summary
211
+
212
+ @classmethod
213
+ def from_typst_code(
214
+ cls, typst_code: str, title: str = "", article_language: str = "en", prospect: str = "", abstract: str = ""
215
+ ) -> "ArticleOutline":
216
+ """Parses a Typst code string and creates an ArticleOutline instance."""
217
+ self = cls(article_language=article_language, prospect=prospect, abstract=abstract, chapters=[], title=title)
218
+ stack = [self] # 根节点为ArticleOutline实例
219
+
220
+ for line in typst_code.splitlines():
221
+ parsed = cls._parse_line(line)
222
+ if not parsed:
223
+ continue
224
+ level, title = parsed
225
+ cls._adjust_stack(stack, level)
226
+ parent = stack[-1]
227
+ component = cls._create_component(level, title)
228
+ cls._add_to_parent(parent, component, level)
229
+ stack.append(component)
230
+
231
+ return self
232
+
233
+ @classmethod
234
+ def _parse_line(cls, line: str) -> Optional[Tuple[int, str]]:
235
+ stripped = line.strip()
236
+ if not stripped.startswith("="):
237
+ return None
238
+ match = regex.match(r"^(\=+)(.*)", stripped)
239
+ if not match:
240
+ return None
241
+ eqs, title_part = match.groups()
242
+ return len(eqs), title_part.strip()
243
+
244
+ @classmethod
245
+ def _adjust_stack(cls, stack: List[object], target_level: int) -> None:
246
+ while len(stack) > target_level:
247
+ stack.pop()
248
+
249
+ @classmethod
250
+ def _create_component(cls, level: int, title: str) -> ArticleOutlineBase:
251
+ default_kwargs = {
252
+ "writing_aim": [],
253
+ "depend_on": [],
254
+ "support_to": [],
255
+ "description": [],
256
+ }
257
+ component_map = {
258
+ 1: lambda: ArticleChapterOutline(title=title, sections=[], **default_kwargs),
259
+ 2: lambda: ArticleSectionOutline(title=title, subsections=[], **default_kwargs),
260
+ 3: lambda: ArticleSubsectionOutline(title=title, **default_kwargs),
261
+ }
262
+ return ok(component_map.get(level, lambda: None)(), "Invalid level")
263
+
264
+ @classmethod
265
+ def _add_to_parent(
266
+ cls,
267
+ parent: Union["ArticleOutline", ArticleChapterOutline, ArticleSectionOutline],
268
+ component: ArticleOutlineBase,
269
+ level: int,
270
+ ) -> None:
271
+ if level == 1 and isinstance(component, ArticleChapterOutline):
272
+ parent.chapters.append(component)
273
+ elif level == 2 and isinstance(component, ArticleSectionOutline): # noqa: PLR2004
274
+ parent.sections.append(component)
275
+ elif level == 3 and isinstance(component, ArticleSubsectionOutline): # noqa: PLR2004
276
+ parent.subsections.append(component)
@@ -0,0 +1,37 @@
1
+ """A structured proposal for academic paper development with core research elements."""
2
+
3
+ from typing import Dict, List
4
+
5
+ from fabricatio.models.generic import AsPrompt, CensoredAble, Display, PersistentAble, WithRef
6
+ from pydantic import Field
7
+
8
+
9
+ class ArticleProposal(CensoredAble, Display, WithRef[str], AsPrompt, PersistentAble):
10
+ """Structured proposal for academic paper development with core research elements.
11
+
12
+ Guides LLM in generating comprehensive research proposals with clearly defined components.
13
+ """
14
+
15
+ article_language: str
16
+ """Written language of the article. SHALL be aligned to the language of the article briefing provided."""
17
+
18
+ title: str = Field(...)
19
+ """Paper title in academic style (Title Case, 8-15 words). Example: 'Exploring Neural Architecture Search for Low-Resource Machine Translation'"""
20
+
21
+ focused_problem: List[str]
22
+ """Specific research problem(s) or question(s) addressed (list of 1-3 concise statements).
23
+ Example: ['NAS computational overhead in low-resource settings', 'Architecture transferability across language pairs']"""
24
+
25
+ research_aim: List[str]
26
+ """Primary research objectives (list of 2-4 measurable goals).
27
+ Example: ['Develop parameter-efficient NAS framework', 'Establish cross-lingual architecture transfer metrics']"""
28
+
29
+ research_methods: List[str]
30
+ """Methodological components (list of techniques/tools).
31
+ Example: ['Differentiable architecture search', 'Transformer-based search space', 'Multi-lingual perplexity evaluation']"""
32
+
33
+ technical_approaches: List[str]
34
+ """Technical approaches"""
35
+
36
+ def _as_prompt_inner(self) -> Dict[str, str]:
37
+ return {"ArticleBriefing": self.referenced, "ArticleProposal": self.display()}