fabricatio 0.2.7.dev4__cp312-cp312-win_amd64.whl → 0.2.8__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 (49) hide show
  1. fabricatio/__init__.py +4 -11
  2. fabricatio/actions/article.py +226 -92
  3. fabricatio/actions/article_rag.py +86 -21
  4. fabricatio/actions/output.py +71 -3
  5. fabricatio/actions/rag.py +3 -3
  6. fabricatio/actions/rules.py +39 -0
  7. fabricatio/capabilities/advanced_judge.py +23 -0
  8. fabricatio/capabilities/censor.py +90 -0
  9. fabricatio/capabilities/check.py +195 -0
  10. fabricatio/capabilities/correct.py +160 -96
  11. fabricatio/capabilities/propose.py +20 -4
  12. fabricatio/capabilities/rag.py +5 -4
  13. fabricatio/capabilities/rating.py +68 -23
  14. fabricatio/capabilities/review.py +21 -190
  15. fabricatio/capabilities/task.py +9 -10
  16. fabricatio/config.py +11 -3
  17. fabricatio/fs/curd.py +4 -0
  18. fabricatio/models/action.py +24 -10
  19. fabricatio/models/adv_kwargs_types.py +25 -0
  20. fabricatio/models/extra/__init__.py +1 -0
  21. fabricatio/models/extra/advanced_judge.py +32 -0
  22. fabricatio/models/extra/article_base.py +324 -89
  23. fabricatio/models/extra/article_essence.py +49 -176
  24. fabricatio/models/extra/article_main.py +48 -127
  25. fabricatio/models/extra/article_outline.py +12 -152
  26. fabricatio/models/extra/article_proposal.py +29 -13
  27. fabricatio/models/extra/patches.py +7 -0
  28. fabricatio/models/extra/problem.py +153 -0
  29. fabricatio/models/extra/rule.py +65 -0
  30. fabricatio/models/generic.py +360 -88
  31. fabricatio/models/kwargs_types.py +23 -17
  32. fabricatio/models/role.py +4 -1
  33. fabricatio/models/task.py +1 -1
  34. fabricatio/models/tool.py +149 -14
  35. fabricatio/models/usages.py +61 -47
  36. fabricatio/models/utils.py +0 -46
  37. fabricatio/parser.py +7 -8
  38. fabricatio/rust.cp312-win_amd64.pyd +0 -0
  39. fabricatio/{_rust.pyi → rust.pyi} +50 -0
  40. fabricatio/{_rust_instances.py → rust_instances.py} +1 -1
  41. fabricatio/utils.py +54 -0
  42. fabricatio-0.2.8.data/scripts/tdown.exe +0 -0
  43. {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dist-info}/METADATA +2 -1
  44. fabricatio-0.2.8.dist-info/RECORD +58 -0
  45. fabricatio/_rust.cp312-win_amd64.pyd +0 -0
  46. fabricatio-0.2.7.dev4.data/scripts/tdown.exe +0 -0
  47. fabricatio-0.2.7.dev4.dist-info/RECORD +0 -47
  48. {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dist-info}/WHEEL +0 -0
  49. {fabricatio-0.2.7.dev4.dist-info → fabricatio-0.2.8.dist-info}/licenses/LICENSE +0 -0
@@ -1,26 +1,36 @@
1
1
  """A foundation for hierarchical document components with dependency tracking."""
2
2
 
3
- from abc import abstractmethod
3
+ from abc import ABC, 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 itertools import chain
6
+ from typing import Generator, List, Optional, Self, Tuple, overload
7
+
8
+ from fabricatio.models.generic import (
9
+ AsPrompt,
10
+ CensoredAble,
11
+ Display,
12
+ FinalizedDumpAble,
13
+ Introspect,
14
+ ModelHash,
15
+ PersistentAble,
16
+ ProposedUpdateAble,
17
+ ResolveUpdateConflict,
18
+ SequencePatch,
19
+ )
13
20
 
14
21
 
15
22
  class ReferringType(StrEnum):
16
23
  """Enumeration of different types of references that can be made in an article."""
17
24
 
18
- CHAPTER: str = "chapter"
19
- SECTION: str = "section"
20
- SUBSECTION: str = "subsection"
25
+ CHAPTER = "chapter"
26
+ SECTION = "section"
27
+ SUBSECTION = "subsection"
28
+
29
+
30
+ type RefKey = Tuple[str, Optional[str], Optional[str]]
21
31
 
22
32
 
23
- class ArticleRef(CensoredAble):
33
+ class ArticleRef(CensoredAble, 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,324 @@ class ArticleRef(CensoredAble):
103
104
  return ReferringType.CHAPTER
104
105
 
105
106
 
106
- class SubSectionBase(Base):
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 ArticleRefSequencePatch(SequencePatch[ArticleRef]):
125
+ """Patch for article refs."""
126
+
127
+
128
+ class ArticleOutlineBase(
129
+ ArticleMetaData,
130
+ ResolveUpdateConflict,
131
+ ProposedUpdateAble,
132
+ PersistentAble,
133
+ ModelHash,
134
+ Introspect,
135
+ ):
136
+ """Base class for article outlines."""
137
+
138
+ @property
139
+ def metadata(self) -> ArticleMetaData:
140
+ """Returns the metadata of the article component."""
141
+ return ArticleMetaData.model_validate(self, from_attributes=True)
142
+
143
+ def update_metadata(self, other: ArticleMetaData) -> Self:
144
+ """Updates the metadata of the current instance with the attributes of another instance."""
145
+ self.support_to.clear()
146
+ self.support_to.extend(other.support_to)
147
+ self.depend_on.clear()
148
+ self.depend_on.extend(other.depend_on)
149
+ self.writing_aim.clear()
150
+ self.writing_aim.extend(other.writing_aim)
151
+ self.description = other.description
152
+ return self
153
+
154
+ def display_metadata(self) -> str:
155
+ """Displays the metadata of the current instance."""
156
+ return self.model_dump_json(
157
+ indent=1, include={"title", "writing_aim", "description", "support_to", "depend_on"}
158
+ )
159
+
160
+ def update_from_inner(self, other: Self) -> Self:
161
+ """Updates the current instance with the attributes of another instance."""
162
+ return self.update_metadata(other)
163
+
164
+ @abstractmethod
165
+ def to_typst_code(self) -> str:
166
+ """Converts the component into a Typst code snippet for rendering."""
167
+
168
+
169
+ class SubSectionBase(ArticleOutlineBase):
107
170
  """Base class for article sections and subsections."""
108
171
 
172
+ def to_typst_code(self) -> str:
173
+ """Converts the component into a Typst code snippet for rendering."""
174
+ return f"=== {self.title}\n"
175
+
176
+ def introspect(self) -> str:
177
+ """Introspects the article subsection outline."""
178
+ return ""
109
179
 
110
- class SectionBase[T: SubSectionBase](Base):
180
+ def resolve_update_conflict(self, other: Self) -> str:
181
+ """Resolve update errors in the article outline."""
182
+ if self.title != other.title:
183
+ return f"Title mismatched, expected `{self.title}`, got `{other.title}`"
184
+ return ""
185
+
186
+
187
+ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
111
188
  """Base class for article sections and subsections."""
112
189
 
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."""
190
+ subsections: List[T]
191
+ """Subsections of the section. Contains at least one subsection. You can also add more as needed."""
192
+
193
+ def to_typst_code(self) -> str:
194
+ """Converts the section into a Typst formatted code snippet.
195
+
196
+ Returns:
197
+ str: The formatted Typst code snippet.
198
+ """
199
+ return f"== {self.title}\n" + "\n\n".join(subsec.to_typst_code() for subsec in self.subsections)
200
+
201
+ def resolve_update_conflict(self, other: Self) -> str:
202
+ """Resolve update errors in the article outline."""
203
+ out = ""
204
+ if self.title != other.title:
205
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
206
+ if len(self.subsections) != len(other.subsections):
207
+ out += f"Section count mismatched, expected `{len(self.subsections)}`, got `{len(other.subsections)}`"
208
+ return out or "\n".join(
209
+ [
210
+ conf
211
+ for s, o in zip(self.subsections, other.subsections, strict=True)
212
+ if (conf := s.resolve_update_conflict(o))
213
+ ]
214
+ )
215
+
216
+ def update_from_inner(self, other: Self) -> Self:
217
+ """Updates the current instance with the attributes of another instance."""
218
+ super().update_from_inner(other)
219
+ if len(self.subsections) == 0:
220
+ self.subsections = other.subsections
221
+ return self
222
+
223
+ for self_subsec, other_subsec in zip(self.subsections, other.subsections, strict=True):
224
+ self_subsec.update_from(other_subsec)
225
+ return self
226
+
227
+ def introspect(self) -> str:
228
+ """Introspects the article section outline."""
229
+ if len(self.subsections) == 0:
230
+ return f"Section `{self.title}` contains no subsections, expected at least one, but got 0, you can add one or more as needed."
231
+ return ""
115
232
 
116
233
 
117
- class ChapterBase[T: SectionBase](Base):
234
+ class ChapterBase[T: SectionBase](ArticleOutlineBase):
118
235
  """Base class for article chapters."""
119
236
 
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."""
237
+ sections: List[T]
238
+ """Sections of the chapter. Contains at least one section. You can also add more as needed."""
122
239
 
240
+ def to_typst_code(self) -> str:
241
+ """Converts the chapter into a Typst formatted code snippet for rendering."""
242
+ return f"= {self.title}\n" + "\n\n".join(sec.to_typst_code() for sec in self.sections)
123
243
 
124
- class ArticleBase[T: ChapterBase](Base):
125
- """Base class for article outlines."""
244
+ def resolve_update_conflict(self, other: Self) -> str:
245
+ """Resolve update errors in the article outline."""
246
+ out = ""
247
+
248
+ if self.title != other.title:
249
+ out += f"Title mismatched, expected `{self.title}`, got `{other.title}`"
250
+ if len(self.sections) == len(other.sections):
251
+ out += f"Chapter count mismatched, expected `{len(self.sections)}`, got `{len(other.sections)}`"
252
+
253
+ return out or "\n".join(
254
+ [conf for s, o in zip(self.sections, other.sections, strict=True) if (conf := s.resolve_update_conflict(o))]
255
+ )
256
+
257
+ def update_from_inner(self, other: Self) -> Self:
258
+ """Updates the current instance with the attributes of another instance."""
259
+ if len(self.sections) == 0:
260
+ self.sections = other.sections
261
+ return self
126
262
 
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."""
263
+ for self_sec, other_sec in zip(self.sections, other.sections, strict=True):
264
+ self_sec.update_from(other_sec)
265
+ return self
266
+
267
+ def introspect(self) -> str:
268
+ """Introspects the article chapter outline."""
269
+ if len(self.sections) == 0:
270
+ return f"Chapter `{self.title}` contains no sections, expected at least one, but got 0, you can add one or more as needed."
271
+ return ""
129
272
 
130
273
 
131
- class ArticleOutlineBase(Base):
274
+ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, ABC):
132
275
  """Base class for article outlines."""
133
276
 
277
+ language: str
278
+ """Written language of the article. SHALL be aligned to the language of the article proposal provided."""
279
+
134
280
  title: str
135
- """Title of the research component in academic style."""
136
- description: str
137
- """Description of the research component in academic style."""
281
+ """Title of the academic paper."""
282
+
283
+ prospect: str
284
+ """Consolidated research statement with four pillars:
285
+ 1. Problem Identification: Current limitations
286
+ 2. Methodological Response: Technical approach
287
+ 3. Empirical Validation: Evaluation strategy
288
+ 4. Scholarly Impact: Field contributions
289
+ """
138
290
 
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."""
291
+ abstract: str
292
+ """The abstract is a concise summary of the academic paper's main findings."""
293
+ chapters: List[T]
294
+ """Chapters of the article. Contains at least one chapter. You can also add more as needed."""
147
295
 
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']"""
296
+ def iter_dfs_rev(
297
+ self,
298
+ ) -> Generator[ArticleOutlineBase, None, None]:
299
+ """Performs a depth-first search (DFS) through the article structure in reverse order.
153
300
 
301
+ Returns:
302
+ Generator[ArticleMainBase]: Each component in the article structure in reverse order.
303
+ """
304
+ for chap in self.chapters:
305
+ for sec in chap.sections:
306
+ yield from sec.subsections
307
+ yield sec
308
+ yield chap
154
309
 
155
- class ArticleMainBase(CensoredAble, Display, ArticleOutlineBase, PersistentAble):
156
- """Foundation for hierarchical document components with dependency tracking."""
310
+ def iter_dfs(self) -> Generator[ArticleOutlineBase, None, None]:
311
+ """Performs a depth-first search (DFS) through the article structure.
157
312
 
158
- @abstractmethod
159
- def to_typst_code(self) -> str:
160
- """Converts the component into a Typst code snippet for rendering."""
313
+ Returns:
314
+ Generator[ArticleMainBase]: Each component in the article structure.
315
+ """
316
+ for chap in self.chapters:
317
+ yield chap
318
+ for sec in chap.sections:
319
+ yield sec
320
+ yield from sec.subsections
161
321
 
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
- if self.title != other.title:
166
- raise ValueError("Cannot update from a different title.")
167
- return self
322
+ def iter_support_on(self, rev: bool = False) -> Generator[ArticleRef, None, None]:
323
+ """Iterates over all references that the article components support.
168
324
 
169
- @abstractmethod
170
- def resolve_update_error(self, other: Self) -> str:
171
- """Resolve update errors in the article outline.
325
+ Args:
326
+ rev (bool): If True, iterate in reverse order.
327
+
328
+ Yields:
329
+ ArticleRef: Each reference that the article components support.
330
+ """
331
+ if rev:
332
+ yield from chain(*[a.support_to for a in self.iter_dfs_rev()])
333
+ return
334
+ yield from chain(*[a.support_to for a in self.iter_dfs()])
335
+
336
+ def iter_depend_on(self, rev: bool = False) -> Generator[ArticleRef, None, None]:
337
+ """Iterates over all references that the article components depend on.
338
+
339
+ Args:
340
+ rev (bool): If True, iterate in reverse order.
341
+
342
+ Yields:
343
+ ArticleRef: Each reference that the article components depend on.
344
+ """
345
+ if rev:
346
+ yield from chain(*[a.depend_on for a in self.iter_dfs_rev()])
347
+ return
348
+ yield from chain(*[a.depend_on for a in self.iter_dfs()])
349
+
350
+ def iter_sections(self) -> Generator[Tuple[ChapterBase, SectionBase], None, None]:
351
+ """Iterates through all sections in the article.
172
352
 
173
353
  Returns:
174
- str: Error message indicating update errors in the article outline.
354
+ Generator[ArticleOutlineBase]: Each section in the article.
175
355
  """
356
+ for chap in self.chapters:
357
+ for sec in chap.sections:
358
+ yield chap, sec
176
359
 
177
- @abstractmethod
178
- def _update_from_inner(self, other: Self) -> Self:
179
- """Updates the current instance with the attributes of another instance."""
360
+ def iter_subsections(self) -> Generator[Tuple[ChapterBase, SectionBase, SubSectionBase], None, None]:
361
+ """Iterates through all subsections in the article.
180
362
 
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)
363
+ Returns:
364
+ Generator[ArticleOutlineBase]: Each subsection in the article.
365
+ """
366
+ for chap, sec in self.iter_sections():
367
+ for subsec in sec.subsections:
368
+ yield chap, sec, subsec
369
+
370
+ def find_introspected(self) -> Optional[Tuple[ArticleOutlineBase, str]]:
371
+ """Finds the first introspected component in the article structure."""
372
+ summary = ""
373
+ for component in self.iter_dfs_rev():
374
+ summary += component.introspect()
375
+ if summary:
376
+ return component, summary
377
+ return None
378
+
379
+ @overload
380
+ def find_illegal_ref(self, gather_identical: bool) -> Optional[Tuple[ArticleRef | List[ArticleRef], str]]: ...
185
381
 
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()
382
+ @overload
383
+ def find_illegal_ref(self) -> Optional[Tuple[ArticleRef, str]]: ...
384
+
385
+ def find_illegal_ref(self, gather_identical: bool = False) -> Optional[Tuple[ArticleRef | List[ArticleRef], str]]:
386
+ """Finds the first illegal component in the outline.
189
387
 
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())
388
+ Returns:
389
+ Tuple[ArticleOutlineBase, str]: A tuple containing the illegal component and an error message.
390
+ """
391
+ summary = ""
392
+ for component in self.iter_dfs_rev():
393
+ for ref in chain(component.depend_on, component.support_to):
394
+ if not ref.deref(self):
395
+ 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"
396
+ if summary and not gather_identical:
397
+ return ref, summary
398
+ if summary and gather_identical:
399
+ return [
400
+ identical_ref
401
+ for identical_ref in chain(self.iter_depend_on(), self.iter_support_on())
402
+ if identical_ref == ref
403
+ ], summary
404
+
405
+ return None
406
+
407
+ def finalized_dump(self) -> str:
408
+ """Generates standardized hierarchical markup for academic publishing systems.
409
+
410
+ Implements ACL 2024 outline conventions with four-level structure:
411
+ = Chapter Title (Level 1)
412
+ == Section Title (Level 2)
413
+ === Subsection Title (Level 3)
414
+ ==== Subsubsection Title (Level 4)
415
+
416
+ Returns:
417
+ str: Strictly formatted outline with academic sectioning
418
+
419
+ Example:
420
+ = Methodology
421
+ == Neural Architecture Search Framework
422
+ === Differentiable Search Space
423
+ ==== Constrained Optimization Parameters
424
+ === Implementation Details
425
+ == Evaluation Protocol
426
+ """
427
+ return "\n\n".join(a.to_typst_code() for a in self.chapters)