fabricatio 0.2.9.dev0__cp312-cp312-manylinux_2_34_x86_64.whl → 0.2.9.dev2__cp312-cp312-manylinux_2_34_x86_64.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/output.py +43 -3
- fabricatio/capabilities/censor.py +1 -3
- fabricatio/capabilities/check.py +3 -4
- fabricatio/capabilities/correct.py +4 -4
- fabricatio/config.py +1 -1
- fabricatio/models/action.py +43 -28
- fabricatio/models/extra/article_base.py +6 -4
- fabricatio/models/extra/article_main.py +3 -4
- fabricatio/models/extra/article_outline.py +2 -3
- fabricatio/models/extra/article_proposal.py +4 -4
- fabricatio/models/generic.py +91 -40
- fabricatio/rust.cpython-312-x86_64-linux-gnu.so +0 -0
- fabricatio/rust.pyi +9 -4
- {fabricatio-0.2.9.dev0.dist-info → fabricatio-0.2.9.dev2.dist-info}/METADATA +1 -1
- {fabricatio-0.2.9.dev0.dist-info → fabricatio-0.2.9.dev2.dist-info}/RECORD +18 -18
- {fabricatio-0.2.9.dev0.data → fabricatio-0.2.9.dev2.data}/scripts/tdown +0 -0
- {fabricatio-0.2.9.dev0.dist-info → fabricatio-0.2.9.dev2.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.9.dev0.dist-info → fabricatio-0.2.9.dev2.dist-info}/licenses/LICENSE +0 -0
fabricatio/actions/output.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
"""Dump the finalized output to a file."""
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import Iterable, List, Optional, Type
|
4
|
+
from typing import Any, Iterable, List, Optional, Type
|
5
5
|
|
6
6
|
from fabricatio.journal import logger
|
7
7
|
from fabricatio.models.action import Action
|
@@ -33,6 +33,7 @@ class DumpFinalizedOutput(Action):
|
|
33
33
|
"Could not find the path of file to dump the data.",
|
34
34
|
)
|
35
35
|
)
|
36
|
+
logger.info(f"Saving output to {dump_path.as_posix()}")
|
36
37
|
ok(to_dump, "Could not dump the data since the path is not specified.").finalized_dump_to(dump_path)
|
37
38
|
return dump_path.as_posix()
|
38
39
|
|
@@ -41,7 +42,11 @@ class PersistentAll(Action):
|
|
41
42
|
"""Persist all the data to a file."""
|
42
43
|
|
43
44
|
output_key: str = "persistent_count"
|
45
|
+
"""The number of objects persisted."""
|
44
46
|
persist_dir: Optional[str] = None
|
47
|
+
"""The directory to persist the data."""
|
48
|
+
override: bool = False
|
49
|
+
"""Whether to remove the existing dir before dumping."""
|
45
50
|
|
46
51
|
async def _execute(
|
47
52
|
self,
|
@@ -64,16 +69,22 @@ class PersistentAll(Action):
|
|
64
69
|
if persist_dir.is_file():
|
65
70
|
logger.warning("Dump should be a directory, but it is a file. Skip dumping.")
|
66
71
|
return count
|
67
|
-
|
72
|
+
if self.override and persist_dir.is_dir():
|
73
|
+
logger.info(f"Override the existing directory {persist_dir.as_posix()}.")
|
74
|
+
persist_dir.rmdir()
|
75
|
+
logger.info(f"Starting persistence in directory {persist_dir}")
|
68
76
|
for k, v in cxt.items():
|
69
77
|
final_dir = persist_dir.joinpath(k)
|
78
|
+
logger.debug(f"Checking key {k} for persistence")
|
70
79
|
if isinstance(v, PersistentAble):
|
80
|
+
logger.info(f"Persisting object {k} to {final_dir}")
|
71
81
|
final_dir.mkdir(parents=True, exist_ok=True)
|
72
82
|
v.persist(final_dir)
|
73
83
|
count += 1
|
74
84
|
if isinstance(v, Iterable) and any(
|
75
85
|
persistent_ables := (pers for pers in v if isinstance(pers, PersistentAble))
|
76
86
|
):
|
87
|
+
logger.info(f"Persisting collection {k} to {final_dir}")
|
77
88
|
final_dir.mkdir(parents=True, exist_ok=True)
|
78
89
|
for per in persistent_ables:
|
79
90
|
per.persist(final_dir)
|
@@ -93,10 +104,39 @@ class RetrieveFromPersistent[T: PersistentAble](Action):
|
|
93
104
|
"""The class of the object to retrieve."""
|
94
105
|
|
95
106
|
async def _execute(self, /, **__) -> Optional[T | List[T]]:
|
96
|
-
logger.info(f"Retrieve `{self.retrieve_cls.__name__}` from
|
107
|
+
logger.info(f"Retrieve `{self.retrieve_cls.__name__}` from {self.load_path}")
|
97
108
|
if not (p := Path(self.load_path)).exists():
|
109
|
+
logger.warning(f"Path {self.load_path} does not exist")
|
98
110
|
return None
|
99
111
|
|
100
112
|
if p.is_dir():
|
113
|
+
logger.info(f"Found directory with {len(list(p.glob('*')))} items")
|
101
114
|
return [self.retrieve_cls.from_persistent(per) for per in p.glob("*")]
|
102
115
|
return self.retrieve_cls.from_persistent(self.load_path)
|
116
|
+
|
117
|
+
|
118
|
+
class GatherAsList(Action):
|
119
|
+
"""Gather the objects from the context as a list.
|
120
|
+
|
121
|
+
Notes:
|
122
|
+
If both `gather_suffix` and `gather_prefix` are specified, only the objects with the suffix will be gathered.
|
123
|
+
"""
|
124
|
+
output_key: str = "gathered"
|
125
|
+
"""Gather the objects from the context as a list."""
|
126
|
+
gather_suffix: Optional[str] = None
|
127
|
+
"""Gather the objects from the context as a list."""
|
128
|
+
gather_prefix: Optional[str] = None
|
129
|
+
"""Gather the objects from the context as a list."""
|
130
|
+
|
131
|
+
async def _execute(self, **cxt) -> List[Any]:
|
132
|
+
|
133
|
+
if self.gather_suffix is not None:
|
134
|
+
result = [cxt[k] for k in cxt if k.endswith(self.gather_suffix)]
|
135
|
+
logger.debug(f"Gathered {len(result)} items with suffix {self.gather_suffix}")
|
136
|
+
return result
|
137
|
+
if self.gather_prefix is None:
|
138
|
+
logger.error(err:="Either `gather_suffix` or `gather_prefix` must be specified.")
|
139
|
+
raise ValueError(err)
|
140
|
+
result = [cxt[k] for k in cxt if k.startswith(self.gather_prefix)]
|
141
|
+
logger.debug(f"Gathered {len(result)} items with prefix {self.gather_prefix}")
|
142
|
+
return result
|
@@ -20,8 +20,6 @@ class Censor(Correct, Check):
|
|
20
20
|
Inherits from both Correct and Check classes.
|
21
21
|
Provides methods to censor objects and strings by first checking them against a ruleset and then correcting them if necessary.
|
22
22
|
|
23
|
-
Attributes:
|
24
|
-
ruleset (RuleSet): The ruleset to be used for censoring.
|
25
23
|
"""
|
26
24
|
|
27
25
|
async def censor_obj[M: SketchedAble](
|
@@ -42,7 +40,7 @@ class Censor(Correct, Check):
|
|
42
40
|
"""
|
43
41
|
imp = await self.check_obj(obj, ruleset, **override_kwargs(kwargs, default=None))
|
44
42
|
if imp is None:
|
45
|
-
return
|
43
|
+
return None
|
46
44
|
return await self.correct_obj(obj, imp, **kwargs)
|
47
45
|
|
48
46
|
async def censor_string(
|
fabricatio/capabilities/check.py
CHANGED
@@ -11,6 +11,7 @@ from fabricatio.models.extra.problem import Improvement
|
|
11
11
|
from fabricatio.models.extra.rule import Rule, RuleSet
|
12
12
|
from fabricatio.models.generic import Display, WithBriefing
|
13
13
|
from fabricatio.models.kwargs_types import ValidateKwargs
|
14
|
+
from fabricatio.rust import detect_language
|
14
15
|
from fabricatio.utils import override_kwargs
|
15
16
|
|
16
17
|
|
@@ -56,9 +57,7 @@ class Check(AdvancedJudge, Propose):
|
|
56
57
|
|
57
58
|
ruleset_patch = await self.propose(
|
58
59
|
RuleSetBriefingPatch,
|
59
|
-
f"
|
60
|
-
f"You need to write a concise and detailed patch for this ruleset that can be applied to the ruleset nicely.\n"
|
61
|
-
f"Note that all fields in this patch will be directly copied to the ruleset obj, including `name` and `description`, so write when knowing the subject.\n",
|
60
|
+
f"{ruleset_requirement}\n\nYou should use `{detect_language(ruleset_requirement)}`!",
|
62
61
|
**override_kwargs(kwargs, default=None),
|
63
62
|
)
|
64
63
|
|
@@ -98,7 +97,7 @@ class Check(AdvancedJudge, Propose):
|
|
98
97
|
Improvement,
|
99
98
|
TEMPLATE_MANAGER.render_template(
|
100
99
|
configs.templates.check_string_template,
|
101
|
-
{"to_check": input_text, "rule": rule, "judge": judge.display(), "reference": reference},
|
100
|
+
{"to_check": input_text, "rule": rule.display(), "judge": judge.display(), "reference": reference},
|
102
101
|
),
|
103
102
|
**kwargs,
|
104
103
|
)
|
@@ -78,11 +78,11 @@ class Correct(Rating, Propose):
|
|
78
78
|
TEMPLATE_MANAGER.render_template(
|
79
79
|
configs.templates.fix_troubled_obj_template,
|
80
80
|
{
|
81
|
-
"problem": problem_solutions.problem,
|
81
|
+
"problem": problem_solutions.problem.display(),
|
82
82
|
"solution": ok(
|
83
83
|
problem_solutions.final_solution(),
|
84
84
|
f"No solution found for problem: {problem_solutions.problem}",
|
85
|
-
),
|
85
|
+
).display(),
|
86
86
|
"reference": reference,
|
87
87
|
},
|
88
88
|
),
|
@@ -111,11 +111,11 @@ class Correct(Rating, Propose):
|
|
111
111
|
TEMPLATE_MANAGER.render_template(
|
112
112
|
configs.templates.fix_troubled_string_template,
|
113
113
|
{
|
114
|
-
"problem": problem_solutions.problem,
|
114
|
+
"problem": problem_solutions.problem.display(),
|
115
115
|
"solution": ok(
|
116
116
|
problem_solutions.final_solution(),
|
117
117
|
f"No solution found for problem: {problem_solutions.problem}",
|
118
|
-
),
|
118
|
+
).display(),
|
119
119
|
"reference": reference,
|
120
120
|
"string_to_fix": input_text,
|
121
121
|
},
|
fabricatio/config.py
CHANGED
@@ -303,7 +303,7 @@ class CacheConfig(BaseModel):
|
|
303
303
|
|
304
304
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
305
305
|
|
306
|
-
type:
|
306
|
+
type: LiteLLMCacheType = LiteLLMCacheType.LOCAL
|
307
307
|
"""The type of cache to use. If None, the default cache type will be used."""
|
308
308
|
params: CacheKwargs = Field(default_factory=CacheKwargs)
|
309
309
|
"""The parameters for the cache. If type is None, the default parameters will be used."""
|
fabricatio/models/action.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
-
"""Module that contains the classes for
|
1
|
+
"""Module that contains the classes for defining and executing task workflows.
|
2
2
|
|
3
|
-
This module
|
4
|
-
|
3
|
+
This module provides the Action and WorkFlow classes for creating structured
|
4
|
+
task execution pipelines. Actions represent atomic operations, while WorkFlows
|
5
|
+
orchestrate sequences of actions with shared context and error handling.
|
6
|
+
|
7
|
+
Classes:
|
8
|
+
Action: Base class for defining executable actions with context management.
|
9
|
+
WorkFlow: Manages action sequences, context propagation, and task lifecycle.
|
5
10
|
"""
|
6
11
|
|
7
12
|
import traceback
|
@@ -50,28 +55,26 @@ class Action(WithBriefing, LLMUsage):
|
|
50
55
|
self.description = self.description or self.__class__.__doc__ or ""
|
51
56
|
|
52
57
|
@abstractmethod
|
53
|
-
async def _execute(self, *_, **cxt) -> Any:
|
54
|
-
"""
|
55
|
-
|
56
|
-
This method must be implemented by subclasses to define the actual behavior.
|
58
|
+
async def _execute(self, *_:Any, **cxt) -> Any:
|
59
|
+
"""Implement the core logic of the action.
|
57
60
|
|
58
61
|
Args:
|
59
|
-
**cxt:
|
62
|
+
**cxt: Context dictionary containing input/output data.
|
60
63
|
|
61
64
|
Returns:
|
62
|
-
|
65
|
+
Result of the action execution to be stored in context.
|
63
66
|
"""
|
64
67
|
pass
|
65
68
|
|
66
69
|
@final
|
67
70
|
async def act(self, cxt: Dict[str, Any]) -> Dict[str, Any]:
|
68
|
-
"""
|
71
|
+
"""Execute action and update context.
|
69
72
|
|
70
73
|
Args:
|
71
|
-
cxt:
|
74
|
+
cxt (Dict[str, Any]): Shared context dictionary.
|
72
75
|
|
73
76
|
Returns:
|
74
|
-
|
77
|
+
Updated context dictionary with new/modified entries.
|
75
78
|
"""
|
76
79
|
ret = await self._execute(**cxt)
|
77
80
|
|
@@ -83,10 +86,10 @@ class Action(WithBriefing, LLMUsage):
|
|
83
86
|
|
84
87
|
@property
|
85
88
|
def briefing(self) -> str:
|
86
|
-
"""
|
89
|
+
"""Generate formatted action description with personality context.
|
87
90
|
|
88
91
|
Returns:
|
89
|
-
|
92
|
+
Briefing text combining personality and action description.
|
90
93
|
"""
|
91
94
|
if self.personality:
|
92
95
|
return f"## Your personality: \n{self.personality}\n# The action you are going to perform: \n{super().briefing}"
|
@@ -98,10 +101,15 @@ class Action(WithBriefing, LLMUsage):
|
|
98
101
|
return self
|
99
102
|
|
100
103
|
class WorkFlow(WithBriefing, ToolBoxUsage):
|
101
|
-
"""
|
104
|
+
"""Manages sequences of actions to fulfill tasks.
|
102
105
|
|
103
|
-
|
104
|
-
|
106
|
+
Handles context propagation between actions, error handling, and task lifecycle
|
107
|
+
events like cancellation and completion.
|
108
|
+
|
109
|
+
Attributes:
|
110
|
+
steps (Tuple): Sequence of Action instances or classes to execute.
|
111
|
+
task_input_key (str): Key for storing task instance in context.
|
112
|
+
task_output_key (str): Key to retrieve final result from context.
|
105
113
|
"""
|
106
114
|
|
107
115
|
description: str = ""
|
@@ -137,26 +145,29 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
|
|
137
145
|
self._instances = tuple(step if isinstance(step, Action) else step() for step in self.steps)
|
138
146
|
|
139
147
|
def inject_personality(self, personality: str) -> Self:
|
140
|
-
"""Set
|
148
|
+
"""Set personality for actions without existing personality.
|
141
149
|
|
142
150
|
Args:
|
143
|
-
personality:
|
151
|
+
personality (str): Shared personality context
|
144
152
|
|
145
153
|
Returns:
|
146
|
-
|
154
|
+
Workflow instance with updated actions
|
147
155
|
"""
|
148
156
|
for action in filter(lambda a: not a.personality, self._instances):
|
149
157
|
action.personality = personality
|
150
158
|
return self
|
151
159
|
|
152
160
|
async def serve(self, task: Task) -> None:
|
153
|
-
"""Execute
|
154
|
-
|
155
|
-
This method manages the complete lifecycle of processing a task through
|
156
|
-
the workflow's sequence of actions.
|
161
|
+
"""Execute workflow to complete given task.
|
157
162
|
|
158
163
|
Args:
|
159
|
-
task:
|
164
|
+
task (Task): Task instance to be processed.
|
165
|
+
|
166
|
+
Steps:
|
167
|
+
1. Initialize context with task instance and extra data
|
168
|
+
2. Execute each action sequentially
|
169
|
+
3. Handle task cancellation and exceptions
|
170
|
+
4. Extract final result from context
|
160
171
|
"""
|
161
172
|
logger.info(f"Start execute workflow: {self.name}")
|
162
173
|
|
@@ -206,10 +217,14 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
|
|
206
217
|
await task.fail()
|
207
218
|
|
208
219
|
async def _init_context[T](self, task: Task[T]) -> None:
|
209
|
-
"""Initialize
|
220
|
+
"""Initialize workflow execution context.
|
210
221
|
|
211
222
|
Args:
|
212
|
-
task:
|
223
|
+
task (Task[T]): Task being processed
|
224
|
+
|
225
|
+
Context includes:
|
226
|
+
- Task instance stored under task_input_key
|
227
|
+
- Any extra_init_context values
|
213
228
|
"""
|
214
229
|
logger.debug(f"Initializing context for workflow: {self.name}")
|
215
230
|
initial_context = {self.task_input_key: task, **dict(self.extra_init_context)}
|
@@ -230,7 +245,7 @@ class WorkFlow(WithBriefing, ToolBoxUsage):
|
|
230
245
|
Returns:
|
231
246
|
Self: The workflow instance for method chaining.
|
232
247
|
"""
|
233
|
-
self.provide_tools_to(self._instances)
|
248
|
+
self.provide_tools_to(i for i in self._instances if isinstance(i,ToolBoxUsage))
|
234
249
|
return self
|
235
250
|
|
236
251
|
def update_init_context(self, /, **kwargs) -> Self:
|
@@ -7,8 +7,6 @@ from typing import Generator, List, Optional, Self, Tuple, overload
|
|
7
7
|
|
8
8
|
from fabricatio.models.generic import (
|
9
9
|
AsPrompt,
|
10
|
-
CensoredAble,
|
11
|
-
Display,
|
12
10
|
FinalizedDumpAble,
|
13
11
|
Introspect,
|
14
12
|
ModelHash,
|
@@ -16,6 +14,7 @@ from fabricatio.models.generic import (
|
|
16
14
|
ProposedUpdateAble,
|
17
15
|
ResolveUpdateConflict,
|
18
16
|
SequencePatch,
|
17
|
+
SketchedAble,
|
19
18
|
)
|
20
19
|
|
21
20
|
|
@@ -30,7 +29,7 @@ class ReferringType(StrEnum):
|
|
30
29
|
type RefKey = Tuple[str, Optional[str], Optional[str]]
|
31
30
|
|
32
31
|
|
33
|
-
class ArticleRef(
|
32
|
+
class ArticleRef(ProposedUpdateAble):
|
34
33
|
"""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.
|
35
34
|
|
36
35
|
Examples:
|
@@ -104,7 +103,7 @@ class ArticleRef(CensoredAble, ProposedUpdateAble):
|
|
104
103
|
return ReferringType.CHAPTER
|
105
104
|
|
106
105
|
|
107
|
-
class ArticleMetaData(
|
106
|
+
class ArticleMetaData(SketchedAble):
|
108
107
|
"""Metadata for an article component."""
|
109
108
|
|
110
109
|
description: str
|
@@ -120,6 +119,9 @@ class ArticleMetaData(CensoredAble, Display):
|
|
120
119
|
title: str
|
121
120
|
"""Do not add any prefix or suffix to the title. should not contain special characters."""
|
122
121
|
|
122
|
+
expected_word_count: int
|
123
|
+
"""Expected word count of this research component."""
|
124
|
+
|
123
125
|
|
124
126
|
class ArticleRefSequencePatch(SequencePatch[ArticleRef]):
|
125
127
|
"""Patch for article refs."""
|
@@ -14,11 +14,11 @@ 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
18
|
from fabricatio.utils import ok
|
19
19
|
|
20
20
|
|
21
|
-
class Paragraph(
|
21
|
+
class Paragraph(SketchedAble):
|
22
22
|
"""Structured academic paragraph blueprint for controlled content generation."""
|
23
23
|
|
24
24
|
description: str
|
@@ -73,8 +73,7 @@ class ArticleChapter(ChapterBase[ArticleSection]):
|
|
73
73
|
|
74
74
|
|
75
75
|
class Article(
|
76
|
-
|
77
|
-
CensoredAble,
|
76
|
+
SketchedAble,
|
78
77
|
WithRef[ArticleOutline],
|
79
78
|
PersistentAble,
|
80
79
|
ArticleBase[ArticleChapter],
|
@@ -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, SketchedAble, PersistentAble, 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 {
|
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) -> 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
|
+
raise NotADirectoryError(f"{dir_path} is not a valid directory")
|
248
|
+
|
249
|
+
pattern = f"{cls.__name__}_*.json"
|
250
|
+
files = list(dir_path.glob(pattern))
|
251
|
+
|
252
|
+
if not files:
|
253
|
+
raise FileNotFoundError(f"No persistent files found for {cls.__name__} in {dir_path}")
|
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,
|
Binary file
|
fabricatio/rust.pyi
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
from pathlib import Path
|
2
|
-
from typing import
|
2
|
+
from typing import List, Optional
|
3
|
+
|
4
|
+
from pydantic import JsonValue
|
3
5
|
|
4
6
|
class TemplateManager:
|
5
7
|
"""Template rendering engine using Handlebars templates.
|
@@ -41,7 +43,7 @@ class TemplateManager:
|
|
41
43
|
This refreshes the template cache, finding any new or modified templates.
|
42
44
|
"""
|
43
45
|
|
44
|
-
def render_template(self, name: str, data:
|
46
|
+
def render_template(self, name: str, data: JsonValue) -> str:
|
45
47
|
"""Render a template with context data.
|
46
48
|
|
47
49
|
Args:
|
@@ -55,7 +57,7 @@ class TemplateManager:
|
|
55
57
|
RuntimeError: If template rendering fails
|
56
58
|
"""
|
57
59
|
|
58
|
-
def render_template_raw(self, template: str, data:
|
60
|
+
def render_template_raw(self, template: str, data: JsonValue) -> str:
|
59
61
|
"""Render a template with context data.
|
60
62
|
|
61
63
|
Args:
|
@@ -76,6 +78,9 @@ def blake3_hash(content: bytes) -> str:
|
|
76
78
|
Hex-encoded BLAKE3 hash string
|
77
79
|
"""
|
78
80
|
|
81
|
+
def detect_language(string: str) -> str:
|
82
|
+
"""Detect the language of a given string."""
|
83
|
+
|
79
84
|
class BibManager:
|
80
85
|
"""BibTeX bibliography manager for parsing and querying citation data."""
|
81
86
|
|
@@ -162,7 +167,7 @@ class BibManager:
|
|
162
167
|
Title if found, None otherwise
|
163
168
|
"""
|
164
169
|
|
165
|
-
def get_field_by_key(self, key: str, field: str)-> Optional[str]:
|
170
|
+
def get_field_by_key(self, key: str, field: str) -> Optional[str]:
|
166
171
|
"""Retrieve a specific field by citation key.
|
167
172
|
|
168
173
|
Args:
|
@@ -1,28 +1,28 @@
|
|
1
|
-
fabricatio-0.2.9.
|
2
|
-
fabricatio-0.2.9.
|
3
|
-
fabricatio-0.2.9.
|
1
|
+
fabricatio-0.2.9.dev2.dist-info/METADATA,sha256=3qxanLDFv9Y-sTj944gRiPAzjQsZkIZ3OwIVUFNDzQk,5151
|
2
|
+
fabricatio-0.2.9.dev2.dist-info/WHEEL,sha256=7FgAcpQES0h1xhfN9Ugve9FTUilU6sRAr1WJ5ph2cuw,108
|
3
|
+
fabricatio-0.2.9.dev2.dist-info/licenses/LICENSE,sha256=yDZaTLnOi03bi3Dk6f5IjhLUc5old2yOsihHWU0z-i0,1067
|
4
4
|
fabricatio/decorators.py,sha256=GrkclNTGT2bk7cjTyuca7mqSVlKwTcujcj3uBuZpT8s,7343
|
5
5
|
fabricatio/core.py,sha256=MaEKZ6DDmbdScAY-7F1gwGA6fr7ADX6Mz5rNVi2msFA,6277
|
6
|
-
fabricatio/models/generic.py,sha256=
|
6
|
+
fabricatio/models/generic.py,sha256=pe5raaUtzXVOyZfeXVXBsxz1nPEg3_nVdSa7Z7XGBFc,29281
|
7
7
|
fabricatio/models/tool.py,sha256=VM3rMeDpUbeeFAKpsPfweJ2JGHf-w5f1voDnuhK99ew,12178
|
8
8
|
fabricatio/models/role.py,sha256=iao4teYzY5QjfN1B2tEY5wbQq5afFz7jdhi6IziIong,2878
|
9
9
|
fabricatio/models/kwargs_types.py,sha256=N-CqIDGzkcDSveY8NKUntkTJykst5dAhqjzDw5Zi54I,5479
|
10
10
|
fabricatio/models/utils.py,sha256=x4ZSk5YfHjRuEz_zzcU-rI9nBV_idq9N9RFF6UT8FXg,4269
|
11
|
-
fabricatio/models/extra/article_proposal.py,sha256=
|
11
|
+
fabricatio/models/extra/article_proposal.py,sha256=DjgynelsFHIrlYeGJjZKHh5sDMAdsqucWepjhsnazEg,1969
|
12
12
|
fabricatio/models/extra/advanced_judge.py,sha256=iiiERQ12uOyloZAsSQpKoFX7LKVM5ci1zmtEgMoi6lw,967
|
13
|
-
fabricatio/models/extra/article_main.py,sha256=
|
13
|
+
fabricatio/models/extra/article_main.py,sha256=lNMWkjQTlF_KFYGjn7OHGmfzcd-PPkmpe6dU1oMLkSI,7989
|
14
14
|
fabricatio/models/extra/problem.py,sha256=fTmp0o7fHoC4wf0gSIg5nyy7DyA8gwM-S7eUZPtKaWw,5675
|
15
15
|
fabricatio/models/extra/article_essence.py,sha256=Fc1h05RFgKowHpHkdh-PNx3IxaUKLYdHVora-QM63WI,2726
|
16
|
-
fabricatio/models/extra/article_outline.py,sha256=
|
16
|
+
fabricatio/models/extra/article_outline.py,sha256=2vqMDhXwfRooHEe_cblkOj-4V96VsIAy68x22KBEQPQ,1387
|
17
17
|
fabricatio/models/extra/__init__.py,sha256=0R9eZsCNu6OV-Xtf15H7FrqhfHTFBFf3fBrcd7ChsJ0,53
|
18
18
|
fabricatio/models/extra/rule.py,sha256=sz2TPWOKKcttINaSHt0vYRGohlk4AoJpuTDiuqQzv7U,2051
|
19
|
-
fabricatio/models/extra/article_base.py,sha256=
|
19
|
+
fabricatio/models/extra/article_base.py,sha256=a3sSfw24oHEMzlRFLjz6tlqALMVKWRu2Gj0jvN5GwOQ,16683
|
20
20
|
fabricatio/models/extra/patches.py,sha256=5Pw_xNiFsbcV-Cnh6hE3PLvwdrDptTxGY_j_AWTfnjo,969
|
21
21
|
fabricatio/models/adv_kwargs_types.py,sha256=NzTrhNvLGFns2qZJCCs4Xht5DKmPgX9nztOJFJjZWQ4,835
|
22
22
|
fabricatio/models/usages.py,sha256=DwaaWJnir5vC3OTriCllYLfEMQXvvx9SZ0mpXMliDaA,31576
|
23
23
|
fabricatio/models/events.py,sha256=UvOc6V3vfjKuvh7irDezJ8EGpsNo5yzLdq4xQexVonw,4063
|
24
24
|
fabricatio/models/task.py,sha256=MygHTr3wC5MT3tdIU-seBhaRGq94aBAJFb464m5tm-g,10192
|
25
|
-
fabricatio/models/action.py,sha256=
|
25
|
+
fabricatio/models/action.py,sha256=97Rd0kLTDhVuObggtgTEomZyKMd4I4HoVmNhmgxLw1I,9517
|
26
26
|
fabricatio/toolboxes/fs.py,sha256=OQMdeokYxSNVrCZJAweJ0cYiK4k2QuEiNdIbS5IHIV8,705
|
27
27
|
fabricatio/toolboxes/__init__.py,sha256=dYm_Gd8XolSU_h4wnkA09dlaLDK146eeFz0CUgPZ8_c,380
|
28
28
|
fabricatio/toolboxes/arithmetic.py,sha256=sSTPkKI6-mb278DwQKFO9jKyzc9kCx45xNH7V6bGBpE,1307
|
@@ -31,12 +31,12 @@ fabricatio/fs/readers.py,sha256=5bLlpqcdhIwWfysh7gvfVv0PPPVAeDlTPGwNTio6j9M,1156
|
|
31
31
|
fabricatio/fs/curd.py,sha256=QqwnyLdVALNnerYGXf6a0irqoPSNE9_GJV9bJ33kbl0,4610
|
32
32
|
fabricatio/fs/__init__.py,sha256=hTuYtzmvIGtbg7PTdoqLEQJ0E63hOzZltCIrLlDKaSE,559
|
33
33
|
fabricatio/rust_instances.py,sha256=i5fIt6XkE8UwUU4JarmPt50AZs8aJW6efaypSLGLl0I,303
|
34
|
-
fabricatio/config.py,sha256=
|
34
|
+
fabricatio/config.py,sha256=NleUcBs5R1x63qZY0gQah9OVfCg9o2BXI4rC836p9LQ,17269
|
35
35
|
fabricatio/utils.py,sha256=n74jVUh8x6SqcgwAAEM8AIQTxAztAIp9ze7w_y8qogc,1627
|
36
36
|
fabricatio/journal.py,sha256=Op0wC-JlZumnAc_aDmYM4ljnSNLoKEEMfcIRbCF69ow,455
|
37
|
-
fabricatio/rust.pyi,sha256=
|
37
|
+
fabricatio/rust.pyi,sha256=nUra-xJnTumf8f4Hpj_tZOcWZzuVQrgEj9z31mcTC3M,5033
|
38
38
|
fabricatio/__init__.py,sha256=OXoMMHJKHEB_vN97_34U4I5QpAKL9xnVQEVcBCvwBCg,986
|
39
|
-
fabricatio/actions/output.py,sha256=
|
39
|
+
fabricatio/actions/output.py,sha256=NvVL4YdGHnQaYLzP9g3dsh1XBZYOFpbM8qDxT-NXAKg,5619
|
40
40
|
fabricatio/actions/article_rag.py,sha256=DViaxJQtGphcjVswN81ilyIYe_u0zakzAKFxF3KQQd8,4252
|
41
41
|
fabricatio/actions/rag.py,sha256=0jkk-weJN6HyXK18hMP-U24kL-HsurVTEFx4LST9qZE,2663
|
42
42
|
fabricatio/actions/__init__.py,sha256=ZMa1LeM5BNeqp-J-D32W-f5bD53-kdXGyt0zuueJofM,47
|
@@ -46,16 +46,16 @@ fabricatio/workflows/articles.py,sha256=oHNV5kNKEcOKP55FA7I1SlxQRlk6N26cpem_QYu0
|
|
46
46
|
fabricatio/workflows/rag.py,sha256=uOZXprD479fUhLA6sYvEM8RWcVcUZXXtP0xRbTMPdHE,509
|
47
47
|
fabricatio/workflows/__init__.py,sha256=Lq9pFo2cudwFCrQUUNgSTr1CoU0J1Nw-HNEQN7cHLp8,50
|
48
48
|
fabricatio/parser.py,sha256=AD9vgy-MljB8o3kH89RfwUyD5V296nrGnAe5u1nE00M,6165
|
49
|
-
fabricatio/capabilities/censor.py,sha256=
|
49
|
+
fabricatio/capabilities/censor.py,sha256=8PdA5RDbUtXbhWOogkpxJ6tf0nRY7hRjVRzcANVygWI,3699
|
50
50
|
fabricatio/capabilities/advanced_judge.py,sha256=bvb8fYoiKoGlBwMZVMflVE9R2MoS1VtmZAo65jMJFew,683
|
51
|
-
fabricatio/capabilities/check.py,sha256=
|
52
|
-
fabricatio/capabilities/correct.py,sha256=
|
51
|
+
fabricatio/capabilities/check.py,sha256=bYq54jcLhIj_Jv-LhSKGN0hV0bnyh1EF4j-NCFxB1dw,7850
|
52
|
+
fabricatio/capabilities/correct.py,sha256=LVN5rQ4NdxDwdJZo_KLNnrEaPIC7m7Ts0lE5bUYpI4g,9008
|
53
53
|
fabricatio/capabilities/rag.py,sha256=It5uWwqbrX64MfQ3FbSvmgcpECAo8XWkLhtRSdTb0ME,17300
|
54
54
|
fabricatio/capabilities/__init__.py,sha256=skaJ43CqAQaZMH-mCRzF4Fps3x99P2SwJ8vSM9pInX8,56
|
55
55
|
fabricatio/capabilities/rating.py,sha256=Sb9Tbd8qQCqt7eLv5C87f73g4nuaA9Cdv-zaurtfqtA,16618
|
56
56
|
fabricatio/capabilities/review.py,sha256=EPL8IlxSKO0XStBkXdW7FJMbPztDQMv9w7tHgu6r3PM,4948
|
57
57
|
fabricatio/capabilities/propose.py,sha256=vOJvmmnMBHUQB6N1AmZNFw42jf7Bl2mBRNlBK15TpNI,1942
|
58
58
|
fabricatio/capabilities/task.py,sha256=gg1xcNlvHy7voR1GXiVDpKW16IvFofLInMlXgkhzBiQ,4304
|
59
|
-
fabricatio/rust.cpython-312-x86_64-linux-gnu.so,sha256=
|
60
|
-
fabricatio-0.2.9.
|
61
|
-
fabricatio-0.2.9.
|
59
|
+
fabricatio/rust.cpython-312-x86_64-linux-gnu.so,sha256=fFxA6svoT7gfEt43u4sMaJdRirvKNvgUeiTCgeF1nf8,2237648
|
60
|
+
fabricatio-0.2.9.dev2.data/scripts/tdown,sha256=GdTCN1GtfFujyfb0JeiHdrfKN9du10s-mjVd4GUqkas,4598184
|
61
|
+
fabricatio-0.2.9.dev2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|