fabricatio 0.3.13__cp312-cp312-win_amd64.whl → 0.3.14__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 (48) hide show
  1. fabricatio/__init__.py +6 -13
  2. fabricatio/actions/article.py +87 -50
  3. fabricatio/actions/article_rag.py +59 -68
  4. fabricatio/actions/output.py +58 -24
  5. fabricatio/actions/rag.py +2 -3
  6. fabricatio/capabilities/advanced_judge.py +4 -7
  7. fabricatio/capabilities/advanced_rag.py +2 -1
  8. fabricatio/capabilities/censor.py +5 -4
  9. fabricatio/capabilities/check.py +27 -27
  10. fabricatio/capabilities/correct.py +22 -22
  11. fabricatio/capabilities/extract.py +33 -33
  12. fabricatio/capabilities/persist.py +103 -0
  13. fabricatio/capabilities/propose.py +2 -2
  14. fabricatio/capabilities/rag.py +11 -10
  15. fabricatio/capabilities/rating.py +66 -70
  16. fabricatio/capabilities/review.py +12 -11
  17. fabricatio/capabilities/task.py +19 -18
  18. fabricatio/decorators.py +11 -9
  19. fabricatio/{core.py → emitter.py} +17 -19
  20. fabricatio/journal.py +2 -4
  21. fabricatio/models/action.py +15 -32
  22. fabricatio/models/extra/aricle_rag.py +13 -8
  23. fabricatio/models/extra/article_base.py +57 -25
  24. fabricatio/models/extra/article_essence.py +2 -1
  25. fabricatio/models/extra/article_main.py +24 -22
  26. fabricatio/models/extra/article_outline.py +2 -1
  27. fabricatio/models/extra/article_proposal.py +1 -1
  28. fabricatio/models/extra/rag.py +2 -2
  29. fabricatio/models/extra/rule.py +2 -1
  30. fabricatio/models/generic.py +55 -137
  31. fabricatio/models/kwargs_types.py +1 -54
  32. fabricatio/models/role.py +49 -28
  33. fabricatio/models/task.py +3 -4
  34. fabricatio/models/tool.py +6 -7
  35. fabricatio/models/usages.py +146 -149
  36. fabricatio/parser.py +59 -99
  37. fabricatio/rust.cp312-win_amd64.pyd +0 -0
  38. fabricatio/rust.pyi +58 -81
  39. fabricatio/utils.py +63 -162
  40. fabricatio-0.3.14.data/scripts/tdown.exe +0 -0
  41. fabricatio-0.3.14.data/scripts/ttm.exe +0 -0
  42. {fabricatio-0.3.13.dist-info → fabricatio-0.3.14.dist-info}/METADATA +10 -13
  43. fabricatio-0.3.14.dist-info/RECORD +64 -0
  44. {fabricatio-0.3.13.dist-info → fabricatio-0.3.14.dist-info}/WHEEL +1 -1
  45. fabricatio-0.3.13.data/scripts/tdown.exe +0 -0
  46. fabricatio-0.3.13.data/scripts/ttm.exe +0 -0
  47. fabricatio-0.3.13.dist-info/RECORD +0 -63
  48. {fabricatio-0.3.13.dist-info → fabricatio-0.3.14.dist-info}/licenses/LICENSE +0 -0
@@ -1,21 +1,21 @@
1
1
  """A Module containing the article rag models."""
2
2
 
3
3
  import re
4
+ from dataclasses import dataclass, field
4
5
  from itertools import groupby
5
6
  from pathlib import Path
6
7
  from typing import ClassVar, Dict, List, Optional, Self, Unpack
7
8
 
8
- from fabricatio.rust import BibManager, blake3_hash, split_into_chunks
9
- from more_itertools.more import first
10
- from more_itertools.recipes import flatten, unique
11
- from pydantic import Field
12
-
13
9
  from fabricatio.fs import safe_text_read
14
10
  from fabricatio.journal import logger
15
11
  from fabricatio.models.extra.rag import MilvusDataBase
16
12
  from fabricatio.models.generic import AsPrompt
17
13
  from fabricatio.models.kwargs_types import ChunkKwargs
14
+ from fabricatio.rust import BibManager, blake3_hash, split_into_chunks
18
15
  from fabricatio.utils import ok, wrapp_in_block
16
+ from more_itertools.more import first
17
+ from more_itertools.recipes import flatten, unique
18
+ from pydantic import Field
19
19
 
20
20
 
21
21
  class ArticleChunk(MilvusDataBase):
@@ -56,6 +56,7 @@ class ArticleChunk(MilvusDataBase):
56
56
 
57
57
  @property
58
58
  def reference_header(self) -> str:
59
+ """Get the reference header."""
59
60
  return f"[[{ok(self._cite_number, 'You need to update cite number first.')}]] reference `{self.article_title}` from {self.as_auther_seq()}"
60
61
 
61
62
  @property
@@ -165,10 +166,11 @@ class ArticleChunk(MilvusDataBase):
165
166
  return self
166
167
 
167
168
 
169
+ @dataclass
168
170
  class CitationManager(AsPrompt):
169
171
  """Citation manager."""
170
172
 
171
- article_chunks: List[ArticleChunk] = Field(default_factory=list)
173
+ article_chunks: List[ArticleChunk] = field(default_factory=list)
172
174
  """Article chunks."""
173
175
 
174
176
  pat: str = r"(\[\[([\d\s,-]*)]])"
@@ -218,8 +220,9 @@ class CitationManager(AsPrompt):
218
220
  def _as_prompt_inner(self) -> Dict[str, str]:
219
221
  """Generate prompt inner representation."""
220
222
  seg = []
221
- for k, g in groupby(self.article_chunks, key=lambda a: a.bibtex_cite_key):
222
- g = list(g)
223
+ for k, g_iter in groupby(self.article_chunks, key=lambda a: a.bibtex_cite_key):
224
+ g = list(g_iter)
225
+
223
226
  logger.debug(f"Group [{k}]: {len(g)}")
224
227
  seg.append(wrapp_in_block("\n\n".join(a.chunk for a in g), first(g).reference_header))
225
228
  return {"References": "\n".join(seg)}
@@ -277,5 +280,7 @@ class CitationManager(AsPrompt):
277
280
  return "".join(a.as_typst_cite() for a in chunk_seq.values())
278
281
 
279
282
  def as_milvus_filter_expr(self, blacklist: bool = True) -> str:
283
+ """Asynchronously fetches documents from a Milvus database based on input vectors."""
280
284
  if blacklist:
281
285
  return " and ".join(f'bibtex_cite_key != "{a.bibtex_cite_key}"' for a in self.article_chunks)
286
+ return " or ".join(f'bibtex_cite_key == "{a.bibtex_cite_key}"' for a in self.article_chunks)
@@ -5,9 +5,7 @@ from enum import StrEnum
5
5
  from pathlib import Path
6
6
  from typing import ClassVar, Generator, List, Optional, Self, Tuple, Type
7
7
 
8
- from fabricatio.rust import extract_body, inplace_update, split_out_metadata, to_metadata, word_count
9
- from pydantic import Field
10
-
8
+ from fabricatio.capabilities.persist import PersistentAble
11
9
  from fabricatio.fs import dump_text, safe_text_read
12
10
  from fabricatio.fs.readers import extract_sections
13
11
  from fabricatio.journal import logger
@@ -18,13 +16,22 @@ from fabricatio.models.generic import (
18
16
  Introspect,
19
17
  Language,
20
18
  ModelHash,
21
- PersistentAble,
22
19
  ProposedUpdateAble,
23
20
  SketchedAble,
24
21
  Titled,
25
22
  WordCount,
26
23
  )
24
+ from fabricatio.rust import (
25
+ comment,
26
+ extract_body,
27
+ replace_thesis_body,
28
+ split_out_metadata,
29
+ strip_comment,
30
+ to_metadata,
31
+ word_count,
32
+ )
27
33
  from fabricatio.utils import fallback_kwargs, ok
34
+ from pydantic import Field
28
35
 
29
36
  ARTICLE_WRAPPER = "// =-=-=-=-=-=-=-=-=-="
30
37
 
@@ -53,10 +60,17 @@ class ArticleMetaData(SketchedAble, Described, WordCount, Titled, Language):
53
60
  aims: List[str]
54
61
  """List of writing aims of the research component in academic style."""
55
62
 
63
+ unstructured_body: str = ""
64
+ """Store the source of the unknown information."""
65
+
56
66
  @property
57
67
  def typst_metadata_comment(self) -> str:
58
68
  """Generates a comment for the metadata of the article component."""
59
- return to_metadata(self.model_dump(include={"description", "aims", "expected_word_count"}, by_alias=True))
69
+ data = self.model_dump(
70
+ include={"description", "aims", "expected_word_count"},
71
+ by_alias=True,
72
+ )
73
+ return to_metadata({k: v for k, v in data.items() if v})
60
74
 
61
75
 
62
76
  class FromTypstCode(ArticleMetaData):
@@ -69,12 +83,7 @@ class FromTypstCode(ArticleMetaData):
69
83
 
70
84
  return cls(
71
85
  heading=title,
72
- **fallback_kwargs(
73
- data or {},
74
- elaboration="",
75
- expected_word_count=word_count(body),
76
- aims=[],
77
- ),
86
+ **fallback_kwargs(data or {}, elaboration="", expected_word_count=word_count(body), aims=[]),
78
87
  **kwargs,
79
88
  )
80
89
 
@@ -84,7 +93,7 @@ class ToTypstCode(ArticleMetaData):
84
93
 
85
94
  def to_typst_code(self) -> str:
86
95
  """Converts the component into a Typst code snippet for rendering."""
87
- return f"{self.title}\n{self.typst_metadata_comment}\n"
96
+ return f"{self.title}\n{self.typst_metadata_comment}\n\n{self.unstructured_body}"
88
97
 
89
98
 
90
99
  class ArticleOutlineBase(
@@ -152,12 +161,12 @@ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
152
161
  @classmethod
153
162
  def from_typst_code(cls, title: str, body: str, **kwargs) -> Self:
154
163
  """Creates an Article object from the given Typst code."""
164
+ raw_subsec = extract_sections(body, level=3, section_char="=")
155
165
  return super().from_typst_code(
156
166
  title,
157
167
  body,
158
- subsections=[
159
- cls.child_type.from_typst_code(*pack) for pack in extract_sections(body, level=3, section_char="=")
160
- ],
168
+ subsections=[cls.child_type.from_typst_code(*pack) for pack in raw_subsec],
169
+ unstructured_body="" if raw_subsec else strip_comment(body),
161
170
  )
162
171
 
163
172
  def resolve_update_conflict(self, other: Self) -> str:
@@ -192,6 +201,11 @@ class SectionBase[T: SubSectionBase](ArticleOutlineBase):
192
201
  return f"Section `{self.title}` contains no subsections, expected at least one, but got 0, you can add one or more as needed."
193
202
  return ""
194
203
 
204
+ @property
205
+ def exact_word_count(self) -> int:
206
+ """Returns the exact word count of the article section outline."""
207
+ return sum(a.exact_word_count for a in self.subsections)
208
+
195
209
 
196
210
  class ChapterBase[T: SectionBase](ArticleOutlineBase):
197
211
  """Base class for article chapters."""
@@ -207,12 +221,12 @@ class ChapterBase[T: SectionBase](ArticleOutlineBase):
207
221
  @classmethod
208
222
  def from_typst_code(cls, title: str, body: str, **kwargs) -> Self:
209
223
  """Creates an Article object from the given Typst code."""
224
+ raw_sec = extract_sections(body, level=2, section_char="=")
210
225
  return super().from_typst_code(
211
226
  title,
212
227
  body,
213
- sections=[
214
- cls.child_type.from_typst_code(*pack) for pack in extract_sections(body, level=2, section_char="=")
215
- ],
228
+ sections=[cls.child_type.from_typst_code(*pack) for pack in raw_sec],
229
+ unstructured_body="" if raw_sec else strip_comment(body),
216
230
  )
217
231
 
218
232
  def resolve_update_conflict(self, other: Self) -> str:
@@ -244,6 +258,15 @@ class ChapterBase[T: SectionBase](ArticleOutlineBase):
244
258
  return f"Chapter `{self.title}` contains no sections, expected at least one, but got 0, you can add one or more as needed."
245
259
  return ""
246
260
 
261
+ @property
262
+ def exact_word_count(self) -> int:
263
+ """Calculates the total word count across all sections in the chapter.
264
+
265
+ Returns:
266
+ int: The cumulative word count of all sections.
267
+ """
268
+ return sum(a.exact_word_count for a in self.sections)
269
+
247
270
 
248
271
  class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, FromTypstCode, ToTypstCode, ABC):
249
272
  """Base class for article outlines."""
@@ -264,19 +287,28 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, FromTypstCode, To
264
287
 
265
288
  child_type: ClassVar[Type[ChapterBase]]
266
289
 
290
+ @property
291
+ def exact_word_count(self) -> int:
292
+ """Calculates the total word count across all chapters in the article.
293
+
294
+ Returns:
295
+ int: The cumulative word count of all chapters.
296
+ """
297
+ return sum(ch.exact_word_count for ch in self.chapters)
298
+
267
299
  @classmethod
268
300
  def from_typst_code(cls, title: str, body: str, **kwargs) -> Self:
269
301
  """Generates an article from the given Typst code."""
302
+ raw_chap = extract_sections(body, level=1, section_char="=")
270
303
  return super().from_typst_code(
271
304
  title,
272
305
  body,
273
- chapters=[
274
- cls.child_type.from_typst_code(*pack) for pack in extract_sections(body, level=1, section_char="=")
275
- ],
306
+ chapters=[cls.child_type.from_typst_code(*pack) for pack in raw_chap],
307
+ unstructured_body="" if raw_chap else strip_comment(body),
276
308
  )
277
309
 
278
310
  def iter_dfs_rev(
279
- self,
311
+ self,
280
312
  ) -> Generator[ArticleOutlineBase, None, None]:
281
313
  """Performs a depth-first search (DFS) through the article structure in reverse order.
282
314
 
@@ -351,7 +383,7 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, FromTypstCode, To
351
383
 
352
384
  def to_typst_code(self) -> str:
353
385
  """Generates the Typst code representation of the article."""
354
- return f"// #{super().to_typst_code()}\n\n" + "\n\n".join(a.to_typst_code() for a in self.chapters)
386
+ return comment(f"#Title: {super().to_typst_code()}\n") + "\n\n".join(a.to_typst_code() for a in self.chapters)
355
387
 
356
388
  def finalized_dump(self) -> str:
357
389
  """Generates standardized hierarchical markup for academic publishing systems.
@@ -402,11 +434,11 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, FromTypstCode, To
402
434
  """Set all chap, sec, subsec have same word count sum up to be `self.expected_word_count`."""
403
435
  return self.avg_chap_wordcount().avg_sec_wordcount().avg_subsec_wordcount()
404
436
 
405
- def update_article_file(self, file: str | Path) -> Self:
437
+ def update_article_file[S: "ArticleBase"](self: S, file: str | Path) -> S:
406
438
  """Update the article file."""
407
439
  file = Path(file)
408
440
  string = safe_text_read(file)
409
- if updated := inplace_update(string, ARTICLE_WRAPPER, self.to_typst_code()):
441
+ if updated := replace_thesis_body(string, ARTICLE_WRAPPER, f"\n\n{self.to_typst_code()}\n\n"):
410
442
  dump_text(file, updated)
411
443
  logger.success(f"Successfully updated {file.as_posix()}.")
412
444
  else:
@@ -2,8 +2,9 @@
2
2
 
3
3
  from typing import List
4
4
 
5
+ from fabricatio.capabilities.persist import PersistentAble
5
6
  from fabricatio.models.extra.rag import MilvusDataBase
6
- from fabricatio.models.generic import PersistentAble, SketchedAble
7
+ from fabricatio.models.generic import SketchedAble
7
8
  from pydantic import BaseModel
8
9
 
9
10
 
@@ -2,15 +2,7 @@
2
2
 
3
3
  from typing import ClassVar, Dict, Generator, List, Self, Tuple, Type, override
4
4
 
5
- from fabricatio.rust import (
6
- convert_all_block_tex,
7
- convert_all_inline_tex,
8
- fix_misplaced_labels,
9
- split_out_metadata,
10
- word_count,
11
- )
12
- from pydantic import Field, NonNegativeInt
13
-
5
+ from fabricatio.capabilities.persist import PersistentAble
14
6
  from fabricatio.decorators import precheck_package
15
7
  from fabricatio.journal import logger
16
8
  from fabricatio.models.extra.article_base import (
@@ -25,9 +17,16 @@ from fabricatio.models.extra.article_outline import (
25
17
  ArticleSectionOutline,
26
18
  ArticleSubsectionOutline,
27
19
  )
28
- from fabricatio.models.generic import Described, PersistentAble, SequencePatch, SketchedAble, WithRef, WordCount
20
+ from fabricatio.models.generic import Described, SequencePatch, SketchedAble, WithRef, WordCount
21
+ from fabricatio.rust import (
22
+ convert_all_tex_math,
23
+ fix_misplaced_labels,
24
+ split_out_metadata,
25
+ word_count,
26
+ )
27
+ from pydantic import Field, NonNegativeInt
29
28
 
30
- PARAGRAPH_SEP = "\n\n// - - -\n\n"
29
+ PARAGRAPH_SEP = "// - - -"
31
30
 
32
31
 
33
32
  class Paragraph(SketchedAble, WordCount, Described):
@@ -50,10 +49,10 @@ class Paragraph(SketchedAble, WordCount, Described):
50
49
  @classmethod
51
50
  def from_content(cls, content: str) -> Self:
52
51
  """Create a Paragraph object from the given content."""
53
- return cls(elaboration="", aims=[], expected_word_count=word_count(content), content=content)
52
+ return cls(elaboration="", aims=[], expected_word_count=word_count(content), content=content.strip())
54
53
 
55
54
  @property
56
- def exact_wordcount(self) -> int:
55
+ def exact_word_count(self) -> int:
57
56
  """Calculates the exact word count of the content."""
58
57
  return word_count(self.content)
59
58
 
@@ -71,6 +70,11 @@ class ArticleSubsection(SubSectionBase):
71
70
  _max_word_count_deviation: float = 0.3
72
71
  """Maximum allowed deviation from the expected word count, as a percentage."""
73
72
 
73
+ @property
74
+ def exact_word_count(self) -> int:
75
+ """Calculates the exact word count of all paragraphs in the subsection."""
76
+ return sum(a.exact_word_count for a in self.paragraphs)
77
+
74
78
  @property
75
79
  def word_count(self) -> int:
76
80
  """Calculates the total word count of all paragraphs in the subsection."""
@@ -82,8 +86,8 @@ class ArticleSubsection(SubSectionBase):
82
86
  if len(self.paragraphs) == 0:
83
87
  summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs, You should add some!\n"
84
88
  if (
85
- abs((wc := self.word_count) - self.expected_word_count) / self.expected_word_count
86
- > self._max_word_count_deviation
89
+ abs((wc := self.word_count) - self.expected_word_count) / self.expected_word_count
90
+ > self._max_word_count_deviation
87
91
  ):
88
92
  summary += f"`{self.__class__.__name__}` titled `{self.title}` have {wc} words, expected {self.expected_word_count} words!"
89
93
 
@@ -103,7 +107,7 @@ class ArticleSubsection(SubSectionBase):
103
107
  Returns:
104
108
  str: Typst code snippet for rendering.
105
109
  """
106
- return super().to_typst_code() + PARAGRAPH_SEP.join(p.content for p in self.paragraphs)
110
+ return super().to_typst_code() + f"\n\n{PARAGRAPH_SEP}\n\n".join(p.content for p in self.paragraphs)
107
111
 
108
112
  @classmethod
109
113
  def from_typst_code(cls, title: str, body: str, **kwargs) -> Self:
@@ -155,15 +159,13 @@ class Article(
155
159
  if descriptions:
156
160
  for a in self.iter_dfs():
157
161
  a.description = fix_misplaced_labels(a.description)
158
- a.description = convert_all_inline_tex(a.description)
159
- a.description = convert_all_block_tex(a.description)
162
+ a.description = convert_all_tex_math(a.description)
160
163
 
161
164
  if paragraphs:
162
165
  for _, _, subsec in self.iter_subsections():
163
166
  for p in subsec.paragraphs:
164
167
  p.content = fix_misplaced_labels(p.content)
165
- p.content = convert_all_inline_tex(p.content)
166
- p.content = convert_all_block_tex(p.content)
168
+ p.content = convert_all_tex_math(p.content)
167
169
  return self
168
170
 
169
171
  @override
@@ -276,9 +278,9 @@ class Article(
276
278
  err = []
277
279
  for chap, sec, subsec in self.iter_subsections():
278
280
  for i, p in enumerate(subsec.paragraphs):
279
- if p.exact_wordcount <= threshold:
281
+ if p.exact_word_count <= threshold:
280
282
  err.append(
281
- f"{chap.title}->{sec.title}->{subsec.title}-> Paragraph [{i}] is too short, {p.exact_wordcount} words."
283
+ f"{chap.title}->{sec.title}->{subsec.title}-> Paragraph [{i}] is too short, {p.exact_word_count} words."
282
284
  )
283
285
 
284
286
  return "\n".join(err)
@@ -2,6 +2,7 @@
2
2
 
3
3
  from typing import ClassVar, Dict, Type
4
4
 
5
+ from fabricatio.capabilities.persist import PersistentAble
5
6
  from fabricatio.models.extra.article_base import (
6
7
  ArticleBase,
7
8
  ChapterBase,
@@ -9,7 +10,7 @@ from fabricatio.models.extra.article_base import (
9
10
  SubSectionBase,
10
11
  )
11
12
  from fabricatio.models.extra.article_proposal import ArticleProposal
12
- from fabricatio.models.generic import PersistentAble, WithRef
13
+ from fabricatio.models.generic import WithRef
13
14
 
14
15
 
15
16
  class ArticleSubsectionOutline(SubSectionBase):
@@ -2,11 +2,11 @@
2
2
 
3
3
  from typing import Dict, List
4
4
 
5
+ from fabricatio.capabilities.persist import PersistentAble
5
6
  from fabricatio.models.generic import (
6
7
  AsPrompt,
7
8
  Described,
8
9
  Language,
9
- PersistentAble,
10
10
  SketchedAble,
11
11
  Titled,
12
12
  WithRef,
@@ -5,7 +5,7 @@ from functools import partial
5
5
  from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Self, Sequence, Set
6
6
 
7
7
  from fabricatio.decorators import precheck_package
8
- from fabricatio.models.generic import Vectorizable
8
+ from fabricatio.models.generic import Base, Vectorizable
9
9
  from fabricatio.utils import ok
10
10
  from pydantic import JsonValue
11
11
 
@@ -18,7 +18,7 @@ if TYPE_CHECKING:
18
18
  from pymilvus import CollectionSchema
19
19
 
20
20
 
21
- class MilvusDataBase(Vectorizable, ABC):
21
+ class MilvusDataBase(Base, Vectorizable, ABC):
22
22
  """A base class for Milvus data."""
23
23
 
24
24
  primary_field_name: ClassVar[str] = "id"
@@ -10,7 +10,8 @@ complex rule management systems.
10
10
 
11
11
  from typing import List, Self, Tuple, Unpack
12
12
 
13
- from fabricatio.models.generic import Language, PersistentAble, SketchedAble, WithBriefing
13
+ from fabricatio.capabilities.persist import PersistentAble
14
+ from fabricatio.models.generic import Language, SketchedAble, WithBriefing
14
15
  from more_itertools import flatten
15
16
 
16
17