fabricatio 0.2.9.dev3__cp312-cp312-win_amd64.whl → 0.2.10.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.
Files changed (35) hide show
  1. fabricatio/actions/article.py +13 -113
  2. fabricatio/actions/article_rag.py +9 -2
  3. fabricatio/capabilities/check.py +15 -9
  4. fabricatio/capabilities/correct.py +5 -6
  5. fabricatio/capabilities/rag.py +39 -232
  6. fabricatio/capabilities/rating.py +46 -40
  7. fabricatio/config.py +2 -2
  8. fabricatio/constants.py +20 -0
  9. fabricatio/decorators.py +23 -0
  10. fabricatio/fs/readers.py +20 -1
  11. fabricatio/models/adv_kwargs_types.py +42 -0
  12. fabricatio/models/events.py +6 -6
  13. fabricatio/models/extra/advanced_judge.py +4 -4
  14. fabricatio/models/extra/article_base.py +25 -211
  15. fabricatio/models/extra/article_main.py +69 -95
  16. fabricatio/models/extra/article_proposal.py +15 -14
  17. fabricatio/models/extra/patches.py +6 -6
  18. fabricatio/models/extra/problem.py +12 -17
  19. fabricatio/models/extra/rag.py +72 -0
  20. fabricatio/models/extra/rule.py +1 -2
  21. fabricatio/models/generic.py +34 -10
  22. fabricatio/models/kwargs_types.py +1 -38
  23. fabricatio/models/task.py +3 -3
  24. fabricatio/models/usages.py +78 -8
  25. fabricatio/parser.py +5 -5
  26. fabricatio/rust.cp312-win_amd64.pyd +0 -0
  27. fabricatio/rust.pyi +27 -12
  28. fabricatio-0.2.10.dev0.data/scripts/tdown.exe +0 -0
  29. {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.10.dev0.dist-info}/METADATA +1 -1
  30. fabricatio-0.2.10.dev0.dist-info/RECORD +62 -0
  31. fabricatio/models/utils.py +0 -148
  32. fabricatio-0.2.9.dev3.data/scripts/tdown.exe +0 -0
  33. fabricatio-0.2.9.dev3.dist-info/RECORD +0 -61
  34. {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.10.dev0.dist-info}/WHEEL +0 -0
  35. {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.10.dev0.dist-info}/licenses/LICENSE +0 -0
@@ -2,8 +2,7 @@
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from enum import StrEnum
5
- from itertools import chain
6
- from typing import Generator, List, Optional, Self, Tuple, overload
5
+ from typing import Generator, List, Optional, Self, Tuple
7
6
 
8
7
  from fabricatio.models.generic import (
9
8
  AsPrompt,
@@ -15,9 +14,11 @@ from fabricatio.models.generic import (
15
14
  PersistentAble,
16
15
  ProposedUpdateAble,
17
16
  ResolveUpdateConflict,
18
- SequencePatch,
19
17
  SketchedAble,
18
+ Titled,
19
+ WordCount,
20
20
  )
21
+ from pydantic import Field
21
22
 
22
23
 
23
24
  class ReferringType(StrEnum):
@@ -28,102 +29,25 @@ class ReferringType(StrEnum):
28
29
  SUBSECTION = "subsection"
29
30
 
30
31
 
31
- type RefKey = Tuple[str, Optional[str], Optional[str]]
32
-
33
-
34
- class ArticleRef(ProposedUpdateAble):
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.
36
-
37
- Examples:
38
- - Referring to a chapter titled `Introduction`:
39
- Using Python
40
- ```python
41
- ArticleRef(referred_chapter_title="Introduction")
42
- ```
43
- Using JSON
44
- ```json
45
- {referred_chapter_title="Introduction"}
46
- ```
47
- - Referring to a section titled `Background` under the `Introduction` chapter:
48
- Using Python
49
- ```python
50
- ArticleRef(referred_chapter_title="Introduction", referred_section_title="Background")
51
- ```
52
- Using JSON
53
- ```json
54
- {referred_chapter_title="Introduction", referred_section_title="Background"}
55
- ```
56
- - Referring to a subsection titled `Related Work` under the `Background` section of the `Introduction` chapter:
57
- Using Python
58
- ```python
59
- ArticleRef(referred_chapter_title="Introduction", referred_section_title="Background", referred_subsection_title="Related Work")
60
- ```
61
- Using JSON
62
- ```json
63
- {referred_chapter_title="Introduction", referred_section_title="Background", referred_subsection_title="Related Work"}
64
- ```
65
- """
66
-
67
- referred_chapter_title: str
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."""
73
-
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
80
-
81
- def deref(self, article: "ArticleBase") -> Optional["ArticleOutlineBase"]:
82
- """Dereference the reference to the actual section or subsection within the provided article.
83
-
84
- Args:
85
- article (ArticleOutline | Article): The article to dereference the reference from.
86
32
 
87
- Returns:
88
- ArticleMainBase | ArticleOutline | None: The dereferenced section or subsection, or None if not found.
89
- """
90
- chap = next((chap for chap in article.chapters if chap.title == self.referred_chapter_title), None)
91
- if self.referred_section_title is None or chap is None:
92
- return chap
93
- sec = next((sec for sec in chap.sections if sec.title == self.referred_section_title), None)
94
- if self.referred_subsection_title is None or sec is None:
95
- return sec
96
- return next((subsec for subsec in sec.subsections if subsec.title == self.referred_subsection_title), None)
33
+ type RefKey = Tuple[str, Optional[str], Optional[str]]
97
34
 
98
- @property
99
- def referring_type(self) -> ReferringType:
100
- """Determine the type of reference based on the presence of specific attributes."""
101
- if self.referred_subsection_title is not None:
102
- return ReferringType.SUBSECTION
103
- if self.referred_section_title is not None:
104
- return ReferringType.SECTION
105
- return ReferringType.CHAPTER
106
35
 
107
36
 
108
- class ArticleMetaData(SketchedAble, Described, Language):
37
+ class ArticleMetaData(SketchedAble, Described, WordCount, Titled, Language):
109
38
  """Metadata for an article component."""
110
39
 
111
- support_to: List[ArticleRef]
112
- """List of references to other component of this articles that this component supports."""
113
- depend_on: List[ArticleRef]
114
- """List of references to other component of this articles that this component depends on."""
40
+ description: str = Field(
41
+ alias="elaboration",
42
+ description=Described.model_fields["description"].description,
43
+ )
115
44
 
116
- writing_aim: List[str]
117
- """List of writing aims of the research component in academic style."""
118
- title: str
119
- """Do not add any prefix or suffix to the title. should not contain special characters."""
45
+ title: str = Field(alias="heading", description=Titled.model_fields["title"].description)
120
46
 
121
- expected_word_count: int
122
- """Expected word count of this research component."""
47
+ aims: List[str]
48
+ """List of writing aims of the research component in academic style."""
123
49
 
124
50
 
125
- class ArticleRefSequencePatch(SequencePatch[ArticleRef]):
126
- """Patch for article refs."""
127
51
 
128
52
 
129
53
  class ArticleOutlineBase(
@@ -143,12 +67,8 @@ class ArticleOutlineBase(
143
67
 
144
68
  def update_metadata(self, other: ArticleMetaData) -> Self:
145
69
  """Updates the metadata of the current instance with the attributes of another instance."""
146
- self.support_to.clear()
147
- self.support_to.extend(other.support_to)
148
- self.depend_on.clear()
149
- self.depend_on.extend(other.depend_on)
150
- self.writing_aim.clear()
151
- self.writing_aim.extend(other.writing_aim)
70
+ self.aims.clear()
71
+ self.aims.extend(other.aims)
152
72
  self.description = other.description
153
73
  return self
154
74
 
@@ -272,22 +192,19 @@ class ChapterBase[T: SectionBase](ArticleOutlineBase):
272
192
  return ""
273
193
 
274
194
 
275
- class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
195
+ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, WordCount, Described, Titled, Language, ABC):
276
196
  """Base class for article outlines."""
277
197
 
278
- title: str
279
- """Title of the academic paper."""
280
-
281
- prospect: str
282
- """Consolidated research statement with four pillars:
283
- 1. Problem Identification: Current limitations
284
- 2. Methodological Response: Technical approach
285
- 3. Empirical Validation: Evaluation strategy
286
- 4. Scholarly Impact: Field contributions
287
- """
198
+ title: str = Field(alias="heading", description=Titled.model_fields["title"].description)
199
+ description: str = Field(alias="abstract")
200
+ """The abstract serves as a concise summary of an academic article, encapsulating its core purpose, methodologies, key results,
201
+ and conclusions while enabling readers to rapidly assess the relevance and significance of the study.
202
+ Functioning as the article's distilled essence, it succinctly articulates the research problem, objectives,
203
+ and scope, providing a roadmap for the full text while also facilitating database indexing, literature reviews,
204
+ and citation tracking through standardized metadata. Additionally, it acts as an accessibility gateway,
205
+ allowing scholars to gauge the study's contribution to existing knowledge, its methodological rigor,
206
+ and its broader implications without engaging with the entire manuscript, thereby optimizing scholarly communication efficiency."""
288
207
 
289
- abstract: str
290
- """The abstract is a concise summary of the academic paper's main findings."""
291
208
  chapters: List[T]
292
209
  """Chapters of the article. Contains at least one chapter. You can also add more as needed."""
293
210
 
@@ -317,34 +234,6 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
317
234
  yield sec
318
235
  yield from sec.subsections
319
236
 
320
- def iter_support_on(self, rev: bool = False) -> Generator[ArticleRef, None, None]:
321
- """Iterates over all references that the article components support.
322
-
323
- Args:
324
- rev (bool): If True, iterate in reverse order.
325
-
326
- Yields:
327
- ArticleRef: Each reference that the article components support.
328
- """
329
- if rev:
330
- yield from chain(*[a.support_to for a in self.iter_dfs_rev()])
331
- return
332
- yield from chain(*[a.support_to for a in self.iter_dfs()])
333
-
334
- def iter_depend_on(self, rev: bool = False) -> Generator[ArticleRef, None, None]:
335
- """Iterates over all references that the article components depend on.
336
-
337
- Args:
338
- rev (bool): If True, iterate in reverse order.
339
-
340
- Yields:
341
- ArticleRef: Each reference that the article components depend on.
342
- """
343
- if rev:
344
- yield from chain(*[a.depend_on for a in self.iter_dfs_rev()])
345
- return
346
- yield from chain(*[a.depend_on for a in self.iter_dfs()])
347
-
348
237
  def iter_sections(self) -> Generator[Tuple[ChapterBase, SectionBase], None, None]:
349
238
  """Iterates through all sections in the article.
350
239
 
@@ -378,12 +267,6 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
378
267
  """Gathers all introspected components in the article structure."""
379
268
  return "\n".join([i for component in self.chapters if (i := component.introspect())])
380
269
 
381
- @overload
382
- def find_illegal_ref(self, gather_identical: bool) -> Optional[Tuple[ArticleRef | List[ArticleRef], str]]: ...
383
-
384
- @overload
385
- def find_illegal_ref(self) -> Optional[Tuple[ArticleRef, str]]: ...
386
-
387
270
  def iter_chap_title(self) -> Generator[str, None, None]:
388
271
  """Iterates through all chapter titles in the article."""
389
272
  for chap in self.chapters:
@@ -399,75 +282,6 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
399
282
  for _, _, subsec in self.iter_subsections():
400
283
  yield subsec.title
401
284
 
402
- def find_illegal_ref(self, gather_identical: bool = False) -> Optional[Tuple[ArticleRef | List[ArticleRef], str]]:
403
- """Finds the first illegal component in the outline.
404
-
405
- Returns:
406
- Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
407
- """
408
- summary = ""
409
- chap_titles_set = set(self.iter_chap_title())
410
- sec_titles_set = set(self.iter_section_title())
411
- subsec_titles_set = set(self.iter_subsection_title())
412
-
413
- for component in self.iter_dfs_rev():
414
- for ref in chain(component.depend_on, component.support_to):
415
- if not ref.deref(self):
416
- 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"
417
-
418
- if ref.referred_chapter_title not in (chap_titles_set):
419
- summary += f"Chapter titled `{ref.referred_chapter_title}` is not any of {chap_titles_set}\n"
420
- if ref.referred_section_title and ref.referred_section_title not in (sec_titles_set):
421
- summary += f"Section Titled `{ref.referred_section_title}` is not any of {sec_titles_set}\n"
422
- if ref.referred_subsection_title and ref.referred_subsection_title not in (subsec_titles_set):
423
- summary += (
424
- f"Subsection Titled `{ref.referred_subsection_title}` is not any of {subsec_titles_set}"
425
- )
426
-
427
- if summary:
428
- return (
429
- (
430
- [
431
- identical_ref
432
- for identical_ref in chain(self.iter_depend_on(), self.iter_support_on())
433
- if identical_ref == ref
434
- ],
435
- summary,
436
- )
437
- if gather_identical
438
- else (ref, summary)
439
- )
440
-
441
- return None
442
-
443
- def gather_illegal_ref(self) -> Tuple[List[ArticleRef], str]:
444
- """Gathers all illegal references in the article."""
445
- summary = []
446
- chap_titles_set = set(self.iter_chap_title())
447
- sec_titles_set = set(self.iter_section_title())
448
- subsec_titles_set = set(self.iter_subsection_title())
449
- res_seq = []
450
-
451
- for component in self.iter_dfs():
452
- for ref in (
453
- r for r in chain(component.depend_on, component.support_to) if not r.deref(self) and r not in res_seq
454
- ):
455
- res_seq.append(ref)
456
- if ref.referred_chapter_title not in chap_titles_set:
457
- summary.append(
458
- f"Chapter titled `{ref.referred_chapter_title}` is not exist, since it is not any of {chap_titles_set}."
459
- )
460
- if ref.referred_section_title and (ref.referred_section_title not in sec_titles_set):
461
- summary.append(
462
- f"Section Titled `{ref.referred_section_title}` is not exist, since it is not any of {sec_titles_set}"
463
- )
464
- if ref.referred_subsection_title and (ref.referred_subsection_title not in subsec_titles_set):
465
- summary.append(
466
- f"Subsection Titled `{ref.referred_subsection_title}` is not exist, since it is not any of {subsec_titles_set}"
467
- )
468
-
469
- return res_seq, "\n".join(summary)
470
-
471
285
  def finalized_dump(self) -> str:
472
286
  """Generates standardized hierarchical markup for academic publishing systems.
473
287
 
@@ -1,12 +1,11 @@
1
1
  """ArticleBase and ArticleSubsection classes for managing hierarchical document components."""
2
2
 
3
- from itertools import chain
4
3
  from typing import Dict, Generator, List, Self, Tuple, override
5
4
 
5
+ from fabricatio.fs.readers import extract_sections
6
6
  from fabricatio.journal import logger
7
7
  from fabricatio.models.extra.article_base import (
8
8
  ArticleBase,
9
- ArticleOutlineBase,
10
9
  ChapterBase,
11
10
  SectionBase,
12
11
  SubSectionBase,
@@ -14,26 +13,32 @@ from fabricatio.models.extra.article_base import (
14
13
  from fabricatio.models.extra.article_outline import (
15
14
  ArticleOutline,
16
15
  )
17
- from fabricatio.models.generic import PersistentAble, SequencePatch, SketchedAble, WithRef
16
+ from fabricatio.models.generic import Described, PersistentAble, SequencePatch, SketchedAble, WithRef, WordCount
18
17
  from fabricatio.rust import word_count
19
- from fabricatio.utils import ok
18
+ from pydantic import Field
20
19
 
20
+ PARAGRAPH_SEP = "// - - -"
21
21
 
22
- class Paragraph(SketchedAble):
22
+
23
+ class Paragraph(SketchedAble, WordCount, Described):
23
24
  """Structured academic paragraph blueprint for controlled content generation."""
24
25
 
25
- description: str
26
- """Functional summary of the paragraph's role in document structure."""
26
+ description: str = Field(
27
+ alias="elaboration",
28
+ description=Described.model_fields["description"].description,
29
+ )
27
30
 
28
- writing_aim: List[str]
31
+ aims: List[str]
29
32
  """Specific communicative objectives for this paragraph's content."""
30
33
 
31
- expected_word_count: int
32
- """Expected word count for the paragraph."""
33
-
34
34
  content: str
35
35
  """The actual content of the paragraph, represented as a string."""
36
36
 
37
+ @classmethod
38
+ def from_content(cls, content: str) -> Self:
39
+ """Create a Paragraph object from the given content."""
40
+ return cls(elaboration="", aims=[], expected_word_count=word_count(content), content=content)
41
+
37
42
 
38
43
  class ArticleParagraphSequencePatch(SequencePatch[Paragraph]):
39
44
  """Patch for `Paragraph` list of `ArticleSubsection`."""
@@ -57,7 +62,7 @@ class ArticleSubsection(SubSectionBase):
57
62
  """Introspects the subsection and returns a summary of its state."""
58
63
  summary = ""
59
64
  if len(self.paragraphs) == 0:
60
- summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs!\n"
65
+ summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs, You should add some!\n"
61
66
  if (
62
67
  abs((wc := self.word_count) - self.expected_word_count) / self.expected_word_count
63
68
  > self._max_word_count_deviation
@@ -80,16 +85,53 @@ class ArticleSubsection(SubSectionBase):
80
85
  Returns:
81
86
  str: Typst code snippet for rendering.
82
87
  """
83
- return f"=== {self.title}\n" + "\n\n".join(p.content for p in self.paragraphs)
88
+ return f"=== {self.title}\n" + f"\n{PARAGRAPH_SEP}\n".join(p.content for p in self.paragraphs)
89
+
90
+ @classmethod
91
+ def from_typst_code(cls, title: str, body: str) -> Self:
92
+ """Creates an Article object from the given Typst code."""
93
+ return cls(
94
+ heading=title,
95
+ elaboration="",
96
+ paragraphs=[Paragraph.from_content(p) for p in body.split(PARAGRAPH_SEP)],
97
+ expected_word_count=word_count(body),
98
+ aims=[],
99
+ )
84
100
 
85
101
 
86
102
  class ArticleSection(SectionBase[ArticleSubsection]):
87
103
  """Atomic argumentative unit with high-level specificity."""
88
104
 
105
+ @classmethod
106
+ def from_typst_code(cls, title: str, body: str) -> Self:
107
+ """Creates an Article object from the given Typst code."""
108
+ return cls(
109
+ subsections=[
110
+ ArticleSubsection.from_typst_code(*pack) for pack in extract_sections(body, level=3, section_char="=")
111
+ ],
112
+ heading=title,
113
+ elaboration="",
114
+ expected_word_count=word_count(body),
115
+ aims=[],
116
+ )
117
+
89
118
 
90
119
  class ArticleChapter(ChapterBase[ArticleSection]):
91
120
  """Thematic progression implementing research function."""
92
121
 
122
+ @classmethod
123
+ def from_typst_code(cls, title: str, body: str) -> Self:
124
+ """Creates an Article object from the given Typst code."""
125
+ return cls(
126
+ sections=[
127
+ ArticleSection.from_typst_code(*pack) for pack in extract_sections(body, level=2, section_char="=")
128
+ ],
129
+ heading=title,
130
+ elaboration="",
131
+ expected_word_count=word_count(body),
132
+ aims=[],
133
+ )
134
+
93
135
 
94
136
  class Article(
95
137
  SketchedAble,
@@ -126,107 +168,39 @@ class Article(
126
168
  Article: The generated article.
127
169
  """
128
170
  # Set the title from the outline
129
- article = Article(**outline.model_dump(exclude={"chapters"}), chapters=[])
171
+ article = Article(**outline.model_dump(exclude={"chapters"}, by_alias=True), chapters=[])
130
172
 
131
173
  for chapter in outline.chapters:
132
174
  # Create a new chapter
133
175
  article_chapter = ArticleChapter(
134
176
  sections=[],
135
- **chapter.model_dump(exclude={"sections"}),
177
+ **chapter.model_dump(exclude={"sections"}, by_alias=True),
136
178
  )
137
179
  for section in chapter.sections:
138
180
  # Create a new section
139
181
  article_section = ArticleSection(
140
182
  subsections=[],
141
- **section.model_dump(exclude={"subsections"}),
183
+ **section.model_dump(exclude={"subsections"}, by_alias=True),
142
184
  )
143
185
  for subsection in section.subsections:
144
186
  # Create a new subsection
145
187
  article_subsection = ArticleSubsection(
146
188
  paragraphs=[],
147
- **subsection.model_dump(),
189
+ **subsection.model_dump(by_alias=True),
148
190
  )
149
191
  article_section.subsections.append(article_subsection)
150
192
  article_chapter.sections.append(article_section)
151
193
  article.chapters.append(article_chapter)
152
194
  return article
153
195
 
154
- def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
155
- """Gathers dependencies for all sections and subsections in the article.
156
-
157
- This method should be called after the article is fully constructed.
158
- """
159
- depends = [ok(a.deref(self)) for a in article.depend_on]
160
-
161
- supports = []
162
- for a in self.iter_dfs_rev():
163
- if article in {ok(b.deref(self)) for b in a.support_to}:
164
- supports.append(a)
165
-
166
- return list(set(depends + supports))
167
-
168
- def gather_dependencies_recursive(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
169
- """Gathers all dependencies recursively for the given article.
170
-
171
- Args:
172
- article (ArticleOutlineBase): The article to gather dependencies for.
173
-
174
- Returns:
175
- List[ArticleBase]: A list of all dependencies for the given article.
176
- """
177
- q = self.gather_dependencies(article)
178
-
179
- deps = []
180
- while q:
181
- a = q.pop()
182
- deps.extend(self.gather_dependencies(a))
183
-
184
- deps = list(
185
- chain(
186
- filter(lambda x: isinstance(x, ArticleChapter), deps),
187
- filter(lambda x: isinstance(x, ArticleSection), deps),
188
- filter(lambda x: isinstance(x, ArticleSubsection), deps),
189
- )
196
+ @classmethod
197
+ def from_typst_code(cls, title: str, body: str) -> Self:
198
+ """Generates an article from the given Typst code."""
199
+ return cls(
200
+ chapters=[
201
+ ArticleChapter.from_typst_code(*pack) for pack in extract_sections(body, level=1, section_char="=")
202
+ ],
203
+ heading=title,
204
+ expected_word_count=word_count(body),
205
+ abstract="",
190
206
  )
191
-
192
- # Initialize result containers
193
- formatted_code = ""
194
- processed_components = []
195
-
196
- # Process all dependencies
197
- while deps:
198
- component = deps.pop()
199
- # Skip duplicates
200
- if (component_code := component.to_typst_code()) in formatted_code:
201
- continue
202
-
203
- # Add this component
204
- formatted_code += component_code
205
- processed_components.append(component)
206
-
207
- return processed_components
208
-
209
- def iter_dfs_with_deps(
210
- self, chapter: bool = True, section: bool = True, subsection: bool = True
211
- ) -> Generator[Tuple[ArticleOutlineBase, List[ArticleOutlineBase]], None, None]:
212
- """Iterates through the article in a depth-first manner, yielding each component and its dependencies.
213
-
214
- Args:
215
- chapter (bool, optional): Whether to include chapter components. Defaults to True.
216
- section (bool, optional): Whether to include section components. Defaults to True.
217
- subsection (bool, optional): Whether to include subsection components. Defaults to True.
218
-
219
- Yields:
220
- Tuple[ArticleBase, List[ArticleBase]]: Each component and its dependencies.
221
- """
222
- if all((not chapter, not section, not subsection)):
223
- raise ValueError("At least one of chapter, section, or subsection must be True.")
224
-
225
- for component in self.iter_dfs_rev():
226
- if not chapter and isinstance(component, ArticleChapter):
227
- continue
228
- if not section and isinstance(component, ArticleSection):
229
- continue
230
- if not subsection and isinstance(component, ArticleSubsection):
231
- continue
232
- yield component, (self.gather_dependencies_recursive(component))
@@ -2,21 +2,25 @@
2
2
 
3
3
  from typing import Dict, List
4
4
 
5
- from fabricatio.models.generic import AsPrompt, PersistentAble, SketchedAble, WithRef
6
-
7
-
8
- class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble):
5
+ from fabricatio.models.generic import (
6
+ AsPrompt,
7
+ Described,
8
+ Language,
9
+ PersistentAble,
10
+ SketchedAble,
11
+ Titled,
12
+ WithRef,
13
+ WordCount,
14
+ )
15
+ from pydantic import Field
16
+
17
+
18
+ class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble, WordCount, Described, Titled, Language):
9
19
  """Structured proposal for academic paper development with core research elements.
10
20
 
11
21
  Guides LLM in generating comprehensive research proposals with clearly defined components.
12
22
  """
13
23
 
14
- language: str
15
- """The language in which the article is written. This should align with the language specified in the article briefing."""
16
-
17
- title: str
18
- """The title of the academic paper, formatted in Title Case."""
19
-
20
24
  focused_problem: List[str]
21
25
  """A list of specific research problems or questions that the paper aims to address."""
22
26
 
@@ -38,12 +42,9 @@ class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble):
38
42
  keywords: List[str]
39
43
  """A list of keywords that represent the main topics and focus areas of the research."""
40
44
 
41
- abstract: str
45
+ description: str = Field(alias="abstract")
42
46
  """A concise summary of the research proposal, outlining the main points and objectives."""
43
47
 
44
- expected_word_count: int
45
- """The estimated word count of the final academic paper."""
46
-
47
48
  def _as_prompt_inner(self) -> Dict[str, str]:
48
49
  return {
49
50
  "ArticleBriefing": self.referenced,
@@ -3,17 +3,17 @@
3
3
  from typing import Optional, Type
4
4
 
5
5
  from fabricatio.models.extra.rule import RuleSet
6
- from fabricatio.models.generic import Patch, WithBriefing
6
+ from fabricatio.models.generic import Language, Patch, WithBriefing
7
7
  from pydantic import BaseModel
8
8
 
9
9
 
10
- class BriefingPatch[T: WithBriefing](Patch[T], WithBriefing):
11
- """Patch class for updating the description and name of a `WithBriefing` object, all fields within this instance will be directly copied onto the target model's field."""
10
+ class BriefingMetadata[T: WithBriefing](Patch[T], WithBriefing):
11
+ """A patch class for updating the description and name of a `WithBriefing` object, all fields within this instance will be directly copied onto the target model's field."""
12
12
 
13
13
 
14
- class RuleSetBriefingPatch(BriefingPatch[RuleSet]):
15
- """Patch class for updating the description and name of a `RuleSet` object, all fields within this instance will be directly copied onto the target model's field."""
16
- language: str
14
+ class RuleSetMetadata(BriefingMetadata[RuleSet], Language):
15
+ """A patch class for updating the description and name of a `RuleSet` object, all fields within this instance will be directly copied onto the target model's field."""
16
+
17
17
  @staticmethod
18
18
  def ref_cls() -> Optional[Type[BaseModel]]:
19
19
  """Get the reference class of the model."""