fabricatio 0.2.9.dev1__cp312-cp312-win_amd64.whl → 0.2.9.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.
- fabricatio/actions/article.py +82 -101
- fabricatio/actions/article_rag.py +7 -9
- fabricatio/actions/output.py +60 -4
- fabricatio/actions/rules.py +37 -4
- fabricatio/capabilities/censor.py +22 -8
- fabricatio/capabilities/check.py +30 -18
- fabricatio/capabilities/correct.py +32 -12
- fabricatio/capabilities/rating.py +8 -8
- fabricatio/config.py +1 -1
- fabricatio/models/action.py +49 -34
- fabricatio/models/extra/article_base.py +83 -19
- fabricatio/models/extra/article_main.py +26 -8
- fabricatio/models/extra/article_outline.py +2 -3
- fabricatio/models/extra/article_proposal.py +4 -4
- fabricatio/models/extra/problem.py +20 -7
- fabricatio/models/extra/rule.py +16 -4
- fabricatio/models/generic.py +91 -40
- fabricatio/models/usages.py +1 -12
- fabricatio/rust.cp312-win_amd64.pyd +0 -0
- fabricatio/rust.pyi +45 -5
- fabricatio/utils.py +5 -5
- fabricatio/workflows/articles.py +3 -5
- fabricatio-0.2.9.dev3.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.9.dev1.dist-info → fabricatio-0.2.9.dev3.dist-info}/METADATA +1 -1
- {fabricatio-0.2.9.dev1.dist-info → fabricatio-0.2.9.dev3.dist-info}/RECORD +27 -27
- fabricatio-0.2.9.dev1.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.9.dev1.dist-info → fabricatio-0.2.9.dev3.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.9.dev1.dist-info → fabricatio-0.2.9.dev3.dist-info}/licenses/LICENSE +0 -0
@@ -14,11 +14,12 @@ from fabricatio.models.extra.article_base import (
|
|
14
14
|
from fabricatio.models.extra.article_outline import (
|
15
15
|
ArticleOutline,
|
16
16
|
)
|
17
|
-
from fabricatio.models.generic import
|
17
|
+
from fabricatio.models.generic import PersistentAble, SequencePatch, SketchedAble, WithRef
|
18
|
+
from fabricatio.rust import word_count
|
18
19
|
from fabricatio.utils import ok
|
19
20
|
|
20
21
|
|
21
|
-
class Paragraph(
|
22
|
+
class Paragraph(SketchedAble):
|
22
23
|
"""Structured academic paragraph blueprint for controlled content generation."""
|
23
24
|
|
24
25
|
description: str
|
@@ -27,6 +28,9 @@ class Paragraph(CensoredAble):
|
|
27
28
|
writing_aim: List[str]
|
28
29
|
"""Specific communicative objectives for this paragraph's content."""
|
29
30
|
|
31
|
+
expected_word_count: int
|
32
|
+
"""Expected word count for the paragraph."""
|
33
|
+
|
30
34
|
content: str
|
31
35
|
"""The actual content of the paragraph, represented as a string."""
|
32
36
|
|
@@ -41,11 +45,26 @@ class ArticleSubsection(SubSectionBase):
|
|
41
45
|
paragraphs: List[Paragraph]
|
42
46
|
"""List of Paragraph objects containing the content of the subsection."""
|
43
47
|
|
48
|
+
_max_word_count_deviation: float = 0.3
|
49
|
+
"""Maximum allowed deviation from the expected word count, as a percentage."""
|
50
|
+
|
51
|
+
@property
|
52
|
+
def word_count(self) -> int:
|
53
|
+
"""Calculates the total word count of all paragraphs in the subsection."""
|
54
|
+
return sum(word_count(p.content) for p in self.paragraphs)
|
55
|
+
|
44
56
|
def introspect(self) -> str:
|
45
|
-
"""Introspects the subsection and returns a
|
57
|
+
"""Introspects the subsection and returns a summary of its state."""
|
58
|
+
summary = ""
|
46
59
|
if len(self.paragraphs) == 0:
|
47
|
-
|
48
|
-
|
60
|
+
summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs!\n"
|
61
|
+
if (
|
62
|
+
abs((wc := self.word_count) - self.expected_word_count) / self.expected_word_count
|
63
|
+
> self._max_word_count_deviation
|
64
|
+
):
|
65
|
+
summary += f"`{self.__class__.__name__}` titled `{self.title}` have {wc} words, expected {self.expected_word_count} words!"
|
66
|
+
|
67
|
+
return summary
|
49
68
|
|
50
69
|
def update_from_inner(self, other: Self) -> Self:
|
51
70
|
"""Updates the current instance with the attributes of another instance."""
|
@@ -73,8 +92,7 @@ class ArticleChapter(ChapterBase[ArticleSection]):
|
|
73
92
|
|
74
93
|
|
75
94
|
class Article(
|
76
|
-
|
77
|
-
CensoredAble,
|
95
|
+
SketchedAble,
|
78
96
|
WithRef[ArticleOutline],
|
79
97
|
PersistentAble,
|
80
98
|
ArticleBase[ArticleChapter],
|
@@ -95,7 +113,7 @@ class Article(
|
|
95
113
|
|
96
114
|
@override
|
97
115
|
def iter_subsections(self) -> Generator[Tuple[ArticleChapter, ArticleSection, ArticleSubsection], None, None]:
|
98
|
-
return super().iter_subsections()
|
116
|
+
return super().iter_subsections() # pyright: ignore [reportReturnType]
|
99
117
|
|
100
118
|
@classmethod
|
101
119
|
def from_outline(cls, outline: ArticleOutline) -> "Article":
|
@@ -9,7 +9,7 @@ from fabricatio.models.extra.article_base import (
|
|
9
9
|
SubSectionBase,
|
10
10
|
)
|
11
11
|
from fabricatio.models.extra.article_proposal import ArticleProposal
|
12
|
-
from fabricatio.models.generic import
|
12
|
+
from fabricatio.models.generic import PersistentAble, SketchedAble, WithRef
|
13
13
|
|
14
14
|
|
15
15
|
class ArticleSubsectionOutline(SubSectionBase):
|
@@ -25,8 +25,7 @@ class ArticleChapterOutline(ChapterBase[ArticleSectionOutline]):
|
|
25
25
|
|
26
26
|
|
27
27
|
class ArticleOutline(
|
28
|
-
|
29
|
-
CensoredAble,
|
28
|
+
SketchedAble,
|
30
29
|
WithRef[ArticleProposal],
|
31
30
|
PersistentAble,
|
32
31
|
ArticleBase[ArticleChapterOutline],
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
from typing import Dict, List
|
4
4
|
|
5
|
-
from fabricatio.models.generic import AsPrompt,
|
5
|
+
from fabricatio.models.generic import AsPrompt, PersistentAble, SketchedAble, WithRef
|
6
6
|
|
7
7
|
|
8
|
-
class ArticleProposal(
|
8
|
+
class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble):
|
9
9
|
"""Structured proposal for academic paper development with core research elements.
|
10
10
|
|
11
11
|
Guides LLM in generating comprehensive research proposals with clearly defined components.
|
@@ -41,8 +41,8 @@ class ArticleProposal(CensoredAble, Display, WithRef[str], AsPrompt, PersistentA
|
|
41
41
|
abstract: str
|
42
42
|
"""A concise summary of the research proposal, outlining the main points and objectives."""
|
43
43
|
|
44
|
-
|
45
|
-
"""The
|
44
|
+
expected_word_count: int
|
45
|
+
"""The estimated word count of the final academic paper."""
|
46
46
|
|
47
47
|
def _as_prompt_inner(self) -> Dict[str, str]:
|
48
48
|
return {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
"""A class representing a problem-solution pair identified during a review process."""
|
2
2
|
|
3
3
|
from itertools import chain
|
4
|
-
from typing import List, Literal, Optional, Self
|
4
|
+
from typing import Any, List, Literal, Optional, Self, Tuple, Unpack
|
5
5
|
|
6
6
|
from fabricatio.journal import logger
|
7
7
|
from fabricatio.models.generic import SketchedAble, WithBriefing
|
@@ -51,7 +51,12 @@ class ProblemSolutions(SketchedAble):
|
|
51
51
|
problem: Problem
|
52
52
|
"""The problem identified in the review."""
|
53
53
|
solutions: List[Solution]
|
54
|
-
"""A collection of potential solutions."""
|
54
|
+
"""A collection of potential solutions, spread the thought, add more solution as possible.Do not leave this as blank"""
|
55
|
+
|
56
|
+
def model_post_init(self, context: Any, /) -> None:
|
57
|
+
"""Initialize the problem-solution pair with a problem and a list of solutions."""
|
58
|
+
if len(self.solutions) == 0:
|
59
|
+
logger.warning(f"No solution found for problem {self.problem.name}, please add more solutions manually.")
|
55
60
|
|
56
61
|
def update_from_inner(self, other: Self) -> Self:
|
57
62
|
"""Update the current instance with another instance's attributes."""
|
@@ -69,6 +74,10 @@ class ProblemSolutions(SketchedAble):
|
|
69
74
|
self.solutions = solutions
|
70
75
|
return self
|
71
76
|
|
77
|
+
def has_solutions(self) -> bool:
|
78
|
+
"""Check if the problem-solution pair has any solutions."""
|
79
|
+
return len(self.solutions) > 0
|
80
|
+
|
72
81
|
async def edit_problem(self) -> Self:
|
73
82
|
"""Interactively edit the problem description."""
|
74
83
|
self.problem = Problem.model_validate_strings(
|
@@ -87,11 +96,11 @@ class ProblemSolutions(SketchedAble):
|
|
87
96
|
"""Check if the improvement is decided."""
|
88
97
|
return len(self.solutions) == 1
|
89
98
|
|
90
|
-
def final_solution(self) -> Optional[Solution]:
|
99
|
+
def final_solution(self, always_use_first: bool = False) -> Optional[Solution]:
|
91
100
|
"""Get the final solution."""
|
92
|
-
if not self.decided():
|
101
|
+
if not always_use_first and not self.decided():
|
93
102
|
logger.error(
|
94
|
-
f"There is
|
103
|
+
f"There is {len(self.solutions)} solutions for problem {self.problem.name}, please decide which solution is eventually adopted."
|
95
104
|
)
|
96
105
|
return None
|
97
106
|
return self.solutions[0]
|
@@ -106,6 +115,10 @@ class Improvement(SketchedAble):
|
|
106
115
|
problem_solutions: List[ProblemSolutions]
|
107
116
|
"""Collection of problems identified during review along with their potential solutions."""
|
108
117
|
|
118
|
+
def all_problems_have_solutions(self) -> bool:
|
119
|
+
"""Check if all problems have solutions."""
|
120
|
+
return all(ps.has_solutions() for ps in self.problem_solutions)
|
121
|
+
|
109
122
|
async def supervisor_check(self, check_solutions: bool = True) -> Self:
|
110
123
|
"""Perform an interactive review session to filter problems and solutions.
|
111
124
|
|
@@ -145,9 +158,9 @@ class Improvement(SketchedAble):
|
|
145
158
|
return all(ps.decided() for ps in self.problem_solutions)
|
146
159
|
|
147
160
|
@classmethod
|
148
|
-
def gather(cls, *improvements:
|
161
|
+
def gather(cls, *improvements: Unpack[Tuple["Improvement", ...]]) -> Self:
|
149
162
|
"""Gather multiple improvements into a single instance."""
|
150
163
|
return cls(
|
151
|
-
focused_on="
|
164
|
+
focused_on=";".join(imp.focused_on for imp in improvements),
|
152
165
|
problem_solutions=list(chain(*(imp.problem_solutions for imp in improvements))),
|
153
166
|
)
|
fabricatio/models/extra/rule.py
CHANGED
@@ -8,15 +8,15 @@ descriptions, examples, and metadata for each rule and rule set, making it suita
|
|
8
8
|
complex rule management systems.
|
9
9
|
"""
|
10
10
|
|
11
|
-
from typing import List
|
11
|
+
from typing import List, Self, Tuple, Unpack
|
12
12
|
|
13
13
|
from fabricatio.models.generic import Language, PersistentAble, SketchedAble, WithBriefing
|
14
|
+
from more_itertools import flatten
|
14
15
|
|
15
16
|
|
16
|
-
class Rule(WithBriefing,Language, SketchedAble,PersistentAble):
|
17
|
+
class Rule(WithBriefing, Language, SketchedAble, PersistentAble):
|
17
18
|
"""Represents a rule or guideline for a specific topic."""
|
18
19
|
|
19
|
-
|
20
20
|
violation_examples: List[str]
|
21
21
|
"""A list of concrete examples demonstrating violations of this rule. Each example should
|
22
22
|
be a clear scenario or case that illustrates how the rule can be broken, including the
|
@@ -30,7 +30,7 @@ class Rule(WithBriefing,Language, SketchedAble,PersistentAble):
|
|
30
30
|
serve as practical guidance for implementing the rule correctly."""
|
31
31
|
|
32
32
|
|
33
|
-
class RuleSet(
|
33
|
+
class RuleSet(SketchedAble, PersistentAble, WithBriefing, Language):
|
34
34
|
"""Represents a collection of rules and guidelines for a particular topic."""
|
35
35
|
|
36
36
|
rules: List[Rule]
|
@@ -38,3 +38,15 @@ class RuleSet( SketchedAble, PersistentAble, WithBriefing,Language):
|
|
38
38
|
a well-defined, specific guideline that contributes to the overall purpose of the rule set.
|
39
39
|
The rules should be logically organized and consistent with each other, forming a coherent
|
40
40
|
framework for the topic or domain covered by the rule set."""
|
41
|
+
|
42
|
+
@classmethod
|
43
|
+
def gather(cls, *rulesets: Unpack[Tuple["RuleSet",...]]) -> Self:
|
44
|
+
"""Gathers multiple rule sets into a single rule set."""
|
45
|
+
if not rulesets:
|
46
|
+
raise ValueError("No rulesets provided")
|
47
|
+
return cls(
|
48
|
+
language=rulesets[0].language,
|
49
|
+
name=";".join(ruleset.name for ruleset in rulesets),
|
50
|
+
description=";".join(ruleset.description for ruleset in rulesets),
|
51
|
+
rules=list(flatten(r.rules for r in rulesets)),
|
52
|
+
)
|
fabricatio/models/generic.py
CHANGED
@@ -33,45 +33,50 @@ class Base(BaseModel):
|
|
33
33
|
"""Base class for all models with Pydantic configuration.
|
34
34
|
|
35
35
|
This class sets up the basic Pydantic configuration for all models in the Fabricatio library.
|
36
|
+
The `model_config` uses `use_attribute_docstrings=True` to ensure field descriptions are
|
37
|
+
pulled from the attribute's docstring instead of the default Pydantic behavior.
|
36
38
|
"""
|
37
|
-
|
38
39
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
39
40
|
|
40
41
|
|
41
42
|
class Display(Base):
|
42
|
-
"""Class that provides
|
43
|
+
"""Class that provides formatted JSON representation utilities.
|
43
44
|
|
44
|
-
|
45
|
+
Provides methods to generate both pretty-printed and compact JSON representations of the model.
|
46
|
+
Used for debugging and logging purposes.
|
45
47
|
"""
|
46
|
-
|
47
48
|
def display(self) -> str:
|
48
|
-
"""
|
49
|
+
"""Generate pretty-printed JSON representation.
|
49
50
|
|
50
51
|
Returns:
|
51
|
-
str:
|
52
|
+
str: JSON string with 1-level indentation for readability
|
52
53
|
"""
|
53
54
|
return self.model_dump_json(indent=1)
|
54
55
|
|
55
56
|
def compact(self) -> str:
|
56
|
-
"""
|
57
|
+
"""Generate compact JSON representation.
|
57
58
|
|
58
59
|
Returns:
|
59
|
-
str:
|
60
|
+
str: Minified JSON string without whitespace
|
60
61
|
"""
|
61
62
|
return self.model_dump_json()
|
62
63
|
|
63
64
|
@staticmethod
|
64
65
|
def seq_display(seq: Iterable["Display"], compact: bool = False) -> str:
|
65
|
-
"""
|
66
|
+
"""Generate formatted display for sequence of Display objects.
|
66
67
|
|
67
68
|
Args:
|
68
|
-
seq (Iterable[Display]):
|
69
|
-
compact (bool):
|
69
|
+
seq (Iterable[Display]): Sequence of objects to display
|
70
|
+
compact (bool): Use compact format instead of pretty print
|
70
71
|
|
71
72
|
Returns:
|
72
|
-
str:
|
73
|
+
str: Combined display output with boundary markers
|
73
74
|
"""
|
74
|
-
return
|
75
|
+
return (
|
76
|
+
"--- Start of Extra Info Sequence ---"
|
77
|
+
+ "\n".join(d.compact() if compact else d.display() for d in seq)
|
78
|
+
+ "--- End of Extra Info Sequence ---"
|
79
|
+
)
|
75
80
|
|
76
81
|
|
77
82
|
class Named(Base):
|
@@ -184,25 +189,29 @@ class WithRef[T](Base):
|
|
184
189
|
|
185
190
|
|
186
191
|
class PersistentAble(Base):
|
187
|
-
"""Class
|
192
|
+
"""Class providing file persistence capabilities.
|
188
193
|
|
189
|
-
|
194
|
+
Enables saving model instances to disk with timestamped filenames and loading from persisted files.
|
195
|
+
Implements basic versioning through filename hashing and timestamping.
|
190
196
|
"""
|
191
|
-
|
192
197
|
def persist(self, path: str | Path) -> Self:
|
193
|
-
"""
|
198
|
+
"""Save model instance to disk with versioned filename.
|
194
199
|
|
195
200
|
Args:
|
196
|
-
path (str | Path):
|
201
|
+
path (str | Path): Target directory or file path. If directory, filename is auto-generated.
|
197
202
|
|
198
203
|
Returns:
|
199
|
-
Self:
|
204
|
+
Self: Current instance for method chaining
|
205
|
+
|
206
|
+
Notes:
|
207
|
+
- Filename format: <ClassName>_<YYYYMMDD_HHMMSS>_<6-char_hash>.json
|
208
|
+
- Hash generated from JSON content ensures uniqueness
|
200
209
|
"""
|
201
210
|
p = Path(path)
|
202
211
|
out = self.model_dump_json()
|
203
212
|
|
204
213
|
# Generate a timestamp in the format YYYYMMDD_HHMMSS
|
205
|
-
timestamp = datetime.now().strftime("%Y%m%
|
214
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
206
215
|
|
207
216
|
# Generate the hash
|
208
217
|
file_hash = blake3_hash(out.encode())[:6]
|
@@ -219,15 +228,52 @@ class PersistentAble(Base):
|
|
219
228
|
logger.info(f"Persisted `{self.__class__.__name__}` to {p.as_posix()}")
|
220
229
|
return self
|
221
230
|
|
231
|
+
@classmethod
|
232
|
+
def from_latest_persistent(cls, dir_path: str | Path) -> Optional[Self]:
|
233
|
+
"""Load most recent persisted instance from directory.
|
234
|
+
|
235
|
+
Args:
|
236
|
+
dir_path (str | Path): Directory containing persisted files
|
237
|
+
|
238
|
+
Returns:
|
239
|
+
Self: Most recently modified instance
|
240
|
+
|
241
|
+
Raises:
|
242
|
+
NotADirectoryError: If path is not a valid directory
|
243
|
+
FileNotFoundError: If no matching files found
|
244
|
+
"""
|
245
|
+
dir_path = Path(dir_path)
|
246
|
+
if not dir_path.is_dir():
|
247
|
+
return None
|
248
|
+
|
249
|
+
pattern = f"{cls.__name__}_*.json"
|
250
|
+
files = list(dir_path.glob(pattern))
|
251
|
+
|
252
|
+
if not files:
|
253
|
+
return None
|
254
|
+
|
255
|
+
def _get_timestamp(file_path: Path) -> datetime:
|
256
|
+
stem = file_path.stem
|
257
|
+
parts = stem.split("_")
|
258
|
+
return datetime.strptime(f"{parts[1]}_{parts[2]}", "%Y%m%d_%H%M%S")
|
259
|
+
|
260
|
+
files.sort(key=lambda f: _get_timestamp(f), reverse=True)
|
261
|
+
|
262
|
+
return cls.from_persistent(files.pop(0))
|
263
|
+
|
222
264
|
@classmethod
|
223
265
|
def from_persistent(cls, path: str | Path) -> Self:
|
224
|
-
"""Load
|
266
|
+
"""Load an instance from a specific persisted file.
|
225
267
|
|
226
268
|
Args:
|
227
|
-
path (str | Path):
|
269
|
+
path (str | Path): Path to the JSON file.
|
228
270
|
|
229
271
|
Returns:
|
230
|
-
Self: The
|
272
|
+
Self: The loaded instance from the file.
|
273
|
+
|
274
|
+
Raises:
|
275
|
+
FileNotFoundError: If the specified file does not exist.
|
276
|
+
ValueError: If the file content is invalid for the model.
|
231
277
|
"""
|
232
278
|
return cls.model_validate_json(safe_text_read(path))
|
233
279
|
|
@@ -236,7 +282,7 @@ class Language(Base):
|
|
236
282
|
"""Class that provides a language attribute."""
|
237
283
|
|
238
284
|
language: str
|
239
|
-
"""The written language of this object, which should be aligned to the original requirement
|
285
|
+
"""The written language of this object, which should be aligned to the original requirement
|
240
286
|
For example if the requirement is in Chinese, the language should be set to `zh`, if the requirement is in English, the language should be set to `en` and etc."""
|
241
287
|
|
242
288
|
|
@@ -619,11 +665,11 @@ class Vectorizable(Base):
|
|
619
665
|
|
620
666
|
|
621
667
|
class ScopedConfig(Base):
|
622
|
-
"""
|
668
|
+
"""Configuration holder with hierarchical fallback mechanism.
|
623
669
|
|
624
|
-
|
670
|
+
Manages LLM, embedding, and vector database configurations with fallback logic.
|
671
|
+
Allows configuration values to be overridden in a hierarchical manner.
|
625
672
|
"""
|
626
|
-
|
627
673
|
llm_api_endpoint: Optional[HttpUrl] = None
|
628
674
|
"""The OpenAI API endpoint."""
|
629
675
|
|
@@ -698,13 +744,15 @@ class ScopedConfig(Base):
|
|
698
744
|
|
699
745
|
@final
|
700
746
|
def fallback_to(self, other: "ScopedConfig") -> Self:
|
701
|
-
"""
|
747
|
+
"""Merge configuration values with fallback priority.
|
748
|
+
|
749
|
+
Copies non-null values from 'other' to self where current values are None.
|
702
750
|
|
703
751
|
Args:
|
704
|
-
other (ScopedConfig):
|
752
|
+
other (ScopedConfig): Configuration to fallback to
|
705
753
|
|
706
754
|
Returns:
|
707
|
-
Self:
|
755
|
+
Self: Current instance with merged values
|
708
756
|
"""
|
709
757
|
# Iterate over the attribute names and copy values from 'other' to 'self' where applicable
|
710
758
|
# noinspection PydanticTypeChecker,PyTypeChecker
|
@@ -718,13 +766,15 @@ class ScopedConfig(Base):
|
|
718
766
|
|
719
767
|
@final
|
720
768
|
def hold_to(self, others: Union["ScopedConfig", Iterable["ScopedConfig"]]) -> Self:
|
721
|
-
"""
|
769
|
+
"""Propagate non-null values to other configurations.
|
770
|
+
|
771
|
+
Copies current non-null values to target configurations where they are None.
|
722
772
|
|
723
773
|
Args:
|
724
|
-
others (
|
774
|
+
others (ScopedConfig|Iterable): Target configurations to update
|
725
775
|
|
726
776
|
Returns:
|
727
|
-
Self:
|
777
|
+
Self: Current instance unchanged
|
728
778
|
"""
|
729
779
|
if not isinstance(others, Iterable):
|
730
780
|
others = [others]
|
@@ -777,14 +827,15 @@ class Patch[T](ProposedAble):
|
|
777
827
|
str: The JSON schema of the model in a formatted string.
|
778
828
|
"""
|
779
829
|
my_schema = cls.model_json_schema(schema_generator=UnsortGenerate)
|
780
|
-
|
830
|
+
|
831
|
+
ref_cls = cls.ref_cls()
|
832
|
+
if ref_cls is not None:
|
781
833
|
# copy the desc info of each corresponding fields from `ref_cls`
|
782
|
-
for field_name
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
my_schema["properties"][field_name]["examples"] = example
|
834
|
+
for field_name in [f for f in cls.model_fields if f in ref_cls.model_fields]:
|
835
|
+
my_schema["properties"][field_name]["description"] = (
|
836
|
+
ref_cls.model_fields[field_name].description or my_schema["properties"][field_name]["description"]
|
837
|
+
)
|
838
|
+
my_schema["description"] = ref_cls.__doc__
|
788
839
|
|
789
840
|
return orjson.dumps(
|
790
841
|
my_schema,
|
fabricatio/models/usages.py
CHANGED
@@ -130,7 +130,6 @@ class LLMUsage(ScopedConfig):
|
|
130
130
|
question: str,
|
131
131
|
system_message: str = "",
|
132
132
|
n: PositiveInt | None = None,
|
133
|
-
stream_buffer_size: int = 50,
|
134
133
|
**kwargs: Unpack[LLMKwargs],
|
135
134
|
) -> Sequence[TextChoices | Choices | StreamingChoices]:
|
136
135
|
"""Asynchronously invokes the language model with a question and optional system message.
|
@@ -139,7 +138,6 @@ class LLMUsage(ScopedConfig):
|
|
139
138
|
question (str): The question to ask the model.
|
140
139
|
system_message (str): The system message to provide context to the model. Defaults to an empty string.
|
141
140
|
n (PositiveInt | None): The number of responses to generate. Defaults to the instance's `llm_generation_count` or the global configuration.
|
142
|
-
stream_buffer_size (int): The buffer size for streaming responses. Defaults to 50.
|
143
141
|
**kwargs (Unpack[LLMKwargs]): Additional keyword arguments for the LLM usage.
|
144
142
|
|
145
143
|
Returns:
|
@@ -155,16 +153,7 @@ class LLMUsage(ScopedConfig):
|
|
155
153
|
if isinstance(resp, CustomStreamWrapper):
|
156
154
|
if not configs.debug.streaming_visible and (pack := stream_chunk_builder(await asyncstdlib.list())):
|
157
155
|
return pack.choices
|
158
|
-
|
159
|
-
buffer = ""
|
160
|
-
async for chunk in resp:
|
161
|
-
chunks.append(chunk)
|
162
|
-
buffer += chunk.choices[0].delta.content or ""
|
163
|
-
if len(buffer) > stream_buffer_size:
|
164
|
-
print(buffer, end="") # noqa: T201
|
165
|
-
buffer = ""
|
166
|
-
print(buffer) # noqa: T201
|
167
|
-
if pack := stream_chunk_builder(chunks):
|
156
|
+
if pack := stream_chunk_builder(await asyncstdlib.list(resp)):
|
168
157
|
return pack.choices
|
169
158
|
logger.critical(err := f"Unexpected response type: {type(resp)}")
|
170
159
|
raise ValueError(err)
|
Binary file
|
fabricatio/rust.pyi
CHANGED
@@ -1,5 +1,23 @@
|
|
1
|
+
"""
|
2
|
+
Python interface definitions for Rust-based functionality.
|
3
|
+
|
4
|
+
This module provides type stubs and documentation for Rust-implemented utilities,
|
5
|
+
including template rendering, cryptographic hashing, language detection, and
|
6
|
+
bibliography management. The actual implementations are provided by Rust modules.
|
7
|
+
|
8
|
+
Key Features:
|
9
|
+
- TemplateManager: Handles Handlebars template rendering and management.
|
10
|
+
- BibManager: Manages BibTeX bibliography parsing and querying.
|
11
|
+
- Cryptographic utilities: BLAKE3 hashing.
|
12
|
+
- Text utilities: Word boundary splitting and word counting.
|
13
|
+
"""
|
14
|
+
|
15
|
+
|
1
16
|
from pathlib import Path
|
2
|
-
from typing import
|
17
|
+
from typing import List, Optional
|
18
|
+
|
19
|
+
from pydantic import JsonValue
|
20
|
+
|
3
21
|
|
4
22
|
class TemplateManager:
|
5
23
|
"""Template rendering engine using Handlebars templates.
|
@@ -41,7 +59,7 @@ class TemplateManager:
|
|
41
59
|
This refreshes the template cache, finding any new or modified templates.
|
42
60
|
"""
|
43
61
|
|
44
|
-
def render_template(self, name: str, data:
|
62
|
+
def render_template(self, name: str, data: JsonValue) -> str:
|
45
63
|
"""Render a template with context data.
|
46
64
|
|
47
65
|
Args:
|
@@ -55,7 +73,7 @@ class TemplateManager:
|
|
55
73
|
RuntimeError: If template rendering fails
|
56
74
|
"""
|
57
75
|
|
58
|
-
def render_template_raw(self, template: str, data:
|
76
|
+
def render_template_raw(self, template: str, data: JsonValue) -> str:
|
59
77
|
"""Render a template with context data.
|
60
78
|
|
61
79
|
Args:
|
@@ -76,9 +94,31 @@ def blake3_hash(content: bytes) -> str:
|
|
76
94
|
Hex-encoded BLAKE3 hash string
|
77
95
|
"""
|
78
96
|
|
79
|
-
def detect_language(string:str)->str:
|
97
|
+
def detect_language(string: str) -> str:
|
80
98
|
"""Detect the language of a given string."""
|
81
99
|
|
100
|
+
|
101
|
+
def split_word_bounds(string: str) -> List[str]:
|
102
|
+
"""Split the string into words based on word boundaries.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
string: The input string to be split.
|
106
|
+
|
107
|
+
Returns:
|
108
|
+
A list of words extracted from the string.
|
109
|
+
"""
|
110
|
+
def word_count(string: str) -> int:
|
111
|
+
"""Count the number of words in the string.
|
112
|
+
|
113
|
+
Args:
|
114
|
+
string: The input string to count words from.
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
The number of words in the string.
|
118
|
+
"""
|
119
|
+
|
120
|
+
|
121
|
+
|
82
122
|
class BibManager:
|
83
123
|
"""BibTeX bibliography manager for parsing and querying citation data."""
|
84
124
|
|
@@ -165,7 +205,7 @@ class BibManager:
|
|
165
205
|
Title if found, None otherwise
|
166
206
|
"""
|
167
207
|
|
168
|
-
def get_field_by_key(self, key: str, field: str)-> Optional[str]:
|
208
|
+
def get_field_by_key(self, key: str, field: str) -> Optional[str]:
|
169
209
|
"""Retrieve a specific field by citation key.
|
170
210
|
|
171
211
|
Args:
|
fabricatio/utils.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""A collection of utility functions for the fabricatio package."""
|
2
2
|
|
3
|
-
from typing import Any, Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Mapping, Optional
|
4
4
|
|
5
5
|
from questionary import text
|
6
6
|
|
@@ -25,16 +25,16 @@ async def ask_edit(
|
|
25
25
|
return res
|
26
26
|
|
27
27
|
|
28
|
-
def override_kwargs(kwargs:
|
28
|
+
def override_kwargs(kwargs: Mapping[str,Any], **overrides) -> Dict[str, Any]:
|
29
29
|
"""Override the values in kwargs with the provided overrides."""
|
30
|
-
new_kwargs = kwargs.
|
30
|
+
new_kwargs = dict(kwargs.items())
|
31
31
|
new_kwargs.update({k: v for k, v in overrides.items() if v is not None})
|
32
32
|
return new_kwargs
|
33
33
|
|
34
34
|
|
35
|
-
def fallback_kwargs(kwargs:
|
35
|
+
def fallback_kwargs(kwargs: Mapping[str, Any], **overrides) -> Dict[str, Any]:
|
36
36
|
"""Fallback the values in kwargs with the provided overrides."""
|
37
|
-
new_kwargs = kwargs.
|
37
|
+
new_kwargs = dict(kwargs.items())
|
38
38
|
new_kwargs.update({k: v for k, v in overrides.items() if k not in new_kwargs and v is not None})
|
39
39
|
return new_kwargs
|
40
40
|
|
fabricatio/workflows/articles.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""Store article essence in the database."""
|
2
2
|
|
3
|
-
from fabricatio.actions.article import
|
3
|
+
from fabricatio.actions.article import GenerateArticleProposal, GenerateInitialOutline
|
4
4
|
from fabricatio.actions.output import DumpFinalizedOutput
|
5
5
|
from fabricatio.models.action import WorkFlow
|
6
6
|
|
@@ -9,7 +9,7 @@ WriteOutlineWorkFlow = WorkFlow(
|
|
9
9
|
description="Generate an outline for an article. dump the outline to the given path. in typst format.",
|
10
10
|
steps=(
|
11
11
|
GenerateArticleProposal,
|
12
|
-
|
12
|
+
GenerateInitialOutline(output_key="article_outline"),
|
13
13
|
DumpFinalizedOutput(output_key="task_output"),
|
14
14
|
),
|
15
15
|
)
|
@@ -18,9 +18,7 @@ WriteOutlineCorrectedWorkFlow = WorkFlow(
|
|
18
18
|
description="Generate an outline for an article. dump the outline to the given path. in typst format.",
|
19
19
|
steps=(
|
20
20
|
GenerateArticleProposal,
|
21
|
-
|
22
|
-
GenerateOutline,
|
23
|
-
CorrectOutline(output_key="to_dump"),
|
21
|
+
GenerateInitialOutline(output_key="article_outline"),
|
24
22
|
DumpFinalizedOutput(output_key="task_output"),
|
25
23
|
),
|
26
24
|
)
|
Binary file
|