fabricatio 0.2.9.dev3__cp312-cp312-win_amd64.whl → 0.2.9.dev4__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 +5 -9
- fabricatio/actions/article_rag.py +9 -2
- fabricatio/capabilities/check.py +13 -8
- fabricatio/capabilities/correct.py +5 -6
- fabricatio/capabilities/rating.py +46 -40
- fabricatio/config.py +2 -2
- fabricatio/fs/readers.py +20 -1
- fabricatio/models/extra/advanced_judge.py +3 -3
- fabricatio/models/extra/article_base.py +58 -60
- fabricatio/models/extra/article_main.py +89 -20
- fabricatio/models/extra/article_proposal.py +15 -14
- fabricatio/models/extra/patches.py +6 -6
- fabricatio/models/generic.py +20 -3
- fabricatio/models/usages.py +6 -4
- fabricatio/parser.py +5 -5
- fabricatio/rust.cp312-win_amd64.pyd +0 -0
- fabricatio/rust.pyi +2 -7
- fabricatio-0.2.9.dev4.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.9.dev4.dist-info}/METADATA +1 -1
- {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.9.dev4.dist-info}/RECORD +22 -22
- fabricatio-0.2.9.dev3.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.9.dev4.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.9.dev3.dist-info → fabricatio-0.2.9.dev4.dist-info}/licenses/LICENSE +0 -0
fabricatio/actions/article.py
CHANGED
@@ -105,7 +105,6 @@ class GenerateArticleProposal(Action, Propose):
|
|
105
105
|
task_input: Optional[Task] = None,
|
106
106
|
article_briefing: Optional[str] = None,
|
107
107
|
article_briefing_path: Optional[str] = None,
|
108
|
-
langauge: Optional[str] = None,
|
109
108
|
**_,
|
110
109
|
) -> Optional[ArticleProposal]:
|
111
110
|
if article_briefing is None and article_briefing_path is None and task_input is None:
|
@@ -122,17 +121,14 @@ class GenerateArticleProposal(Action, Propose):
|
|
122
121
|
)
|
123
122
|
)
|
124
123
|
|
125
|
-
|
124
|
+
logger.info("Start generating the proposal.")
|
125
|
+
return ok(
|
126
126
|
await self.propose(
|
127
127
|
ArticleProposal,
|
128
|
-
f"{briefing}\n\nWrite the value string using `{detect_language(briefing)}`",
|
128
|
+
f"{briefing}\n\nWrite the value string using `{detect_language(briefing)}` as written language.",
|
129
129
|
),
|
130
130
|
"Could not generate the proposal.",
|
131
131
|
).update_ref(briefing)
|
132
|
-
if langauge:
|
133
|
-
proposal.language = langauge
|
134
|
-
|
135
|
-
return proposal
|
136
132
|
|
137
133
|
|
138
134
|
class GenerateInitialOutline(Action, Propose):
|
@@ -149,7 +145,7 @@ class GenerateInitialOutline(Action, Propose):
|
|
149
145
|
return ok(
|
150
146
|
await self.propose(
|
151
147
|
ArticleOutline,
|
152
|
-
f"{(
|
148
|
+
f"{(article_proposal.as_prompt())}\n\nNote that you should use `{article_proposal.language}` to write the `ArticleOutline`\n"
|
153
149
|
f"You Must make sure every chapter have sections, and every section have subsections.",
|
154
150
|
),
|
155
151
|
"Could not generate the initial outline.",
|
@@ -318,7 +314,7 @@ class GenerateArticle(Action, Censor):
|
|
318
314
|
self.censor_obj_inplace(
|
319
315
|
subsec,
|
320
316
|
ruleset=ok(article_gen_ruleset or self.ruleset, "No ruleset provided"),
|
321
|
-
reference=f"{article_outline.as_prompt()}\n# Error Need to be fixed\n{err}",
|
317
|
+
reference=f"{article_outline.as_prompt()}\n# Error Need to be fixed\n{err}\nYou should use `{subsec.language}` to write the new `Subsection`.",
|
322
318
|
)
|
323
319
|
for _, _, subsec in article.iter_subsections()
|
324
320
|
if (err := subsec.introspect()) and logger.warning(f"Found Introspection Error:\n{err}") is None
|
@@ -29,6 +29,9 @@ class TweakArticleRAG(Action, RAG, Censor):
|
|
29
29
|
ruleset: Optional[RuleSet] = None
|
30
30
|
"""The ruleset to be used for censoring the article."""
|
31
31
|
|
32
|
+
ref_limit: int = 30
|
33
|
+
"""The limit of references to be retrieved"""
|
34
|
+
|
32
35
|
async def _execute(
|
33
36
|
self,
|
34
37
|
article: Article,
|
@@ -88,11 +91,15 @@ class TweakArticleRAG(Action, RAG, Censor):
|
|
88
91
|
f"{subsec.display()}\n"
|
89
92
|
f"# Requirement\n"
|
90
93
|
f"Search related articles in the base to find reference candidates, "
|
91
|
-
f"
|
94
|
+
f"provide queries in both `English` and `{subsec.language}` can get more accurate results.",
|
92
95
|
)
|
93
96
|
)
|
94
97
|
await self.censor_obj_inplace(
|
95
98
|
subsec,
|
96
99
|
ruleset=ruleset,
|
97
|
-
reference=await self.aretrieve_compact(refind_q, final_limit=
|
100
|
+
reference=f"{await self.aretrieve_compact(refind_q, final_limit=self.ref_limit)}\n\n"
|
101
|
+
f"You can use Reference above to rewrite the `{subsec.__class__.__name__}`.\n"
|
102
|
+
f"You should Always use `{subsec.language}` as written language, "
|
103
|
+
f"which is the original language of the `{subsec.title}`. "
|
104
|
+
f"since rewrite a `{subsec.__class__.__name__}` in a different language is usually a bad choice",
|
98
105
|
)
|
fabricatio/capabilities/check.py
CHANGED
@@ -8,7 +8,7 @@ from fabricatio.capabilities.advanced_judge import AdvancedJudge
|
|
8
8
|
from fabricatio.capabilities.propose import Propose
|
9
9
|
from fabricatio.config import configs
|
10
10
|
from fabricatio.journal import logger
|
11
|
-
from fabricatio.models.extra.patches import
|
11
|
+
from fabricatio.models.extra.patches import RuleSetMetadata
|
12
12
|
from fabricatio.models.extra.problem import Improvement
|
13
13
|
from fabricatio.models.extra.rule import Rule, RuleSet
|
14
14
|
from fabricatio.models.generic import Display, WithBriefing
|
@@ -42,12 +42,17 @@ class Check(AdvancedJudge, Propose):
|
|
42
42
|
- Returns None if any step in rule generation fails
|
43
43
|
- Uses `alist_str` for requirement breakdown and iterative rule proposal
|
44
44
|
"""
|
45
|
-
rule_reqs =
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
45
|
+
rule_reqs = (
|
46
|
+
await self.alist_str(
|
47
|
+
TEMPLATE_MANAGER.render_template(
|
48
|
+
configs.templates.ruleset_requirement_breakdown_template,
|
49
|
+
{"ruleset_requirement": ruleset_requirement},
|
50
|
+
),
|
51
|
+
rule_count,
|
52
|
+
**override_kwargs(kwargs, default=None),
|
53
|
+
)
|
54
|
+
if rule_count > 1
|
55
|
+
else [ruleset_requirement]
|
51
56
|
)
|
52
57
|
|
53
58
|
if rule_reqs is None:
|
@@ -65,7 +70,7 @@ class Check(AdvancedJudge, Propose):
|
|
65
70
|
return None
|
66
71
|
|
67
72
|
ruleset_patch = await self.propose(
|
68
|
-
|
73
|
+
RuleSetMetadata,
|
69
74
|
f"{ruleset_requirement}\n\nYou should use `{detect_language(ruleset_requirement)}`!",
|
70
75
|
**override_kwargs(kwargs, default=None),
|
71
76
|
)
|
@@ -57,7 +57,7 @@ class Correct(Rating, Propose):
|
|
57
57
|
self.decide_solution(
|
58
58
|
ps,
|
59
59
|
**fallback_kwargs(
|
60
|
-
kwargs, topic=f"which solution is better to deal this problem {ps.problem.
|
60
|
+
kwargs, topic=f"which solution is better to deal this problem {ps.problem.description}\n\n"
|
61
61
|
),
|
62
62
|
)
|
63
63
|
for ps in improvement.problem_solutions
|
@@ -167,13 +167,12 @@ class Correct(Rating, Propose):
|
|
167
167
|
logger.info(f"Improvement {improvement.focused_on} not decided, start deciding...")
|
168
168
|
improvement = await self.decide_improvement(improvement, **override_kwargs(kwargs, default=None))
|
169
169
|
|
170
|
-
|
171
|
-
|
170
|
+
total = len(improvement.problem_solutions)
|
171
|
+
for idx, ps in enumerate(improvement.problem_solutions):
|
172
|
+
logger.info(f"[{idx + 1}/{total}] Fixing {obj.__class__.__name__} for problem `{ps.problem.name}`")
|
172
173
|
fixed_obj = await self.fix_troubled_obj(obj, ps, reference, **kwargs)
|
173
174
|
if fixed_obj is None:
|
174
|
-
logger.error(
|
175
|
-
f"Failed to fix troubling obj {obj.__class__.__name__} when deal with problem: {ps.problem.name}",
|
176
|
-
)
|
175
|
+
logger.error(f"[{idx + 1}/{total}] Failed to fix problem `{ps.problem.name}`")
|
177
176
|
return None
|
178
177
|
obj = fixed_obj
|
179
178
|
return obj
|
@@ -5,19 +5,19 @@ from random import sample
|
|
5
5
|
from typing import Dict, List, Optional, Set, Tuple, Union, Unpack, overload
|
6
6
|
|
7
7
|
from more_itertools import flatten, windowed
|
8
|
-
from pydantic import NonNegativeInt, PositiveInt
|
8
|
+
from pydantic import Field, NonNegativeInt, PositiveInt, create_model
|
9
9
|
|
10
|
+
from fabricatio.capabilities.propose import Propose
|
10
11
|
from fabricatio.config import configs
|
11
12
|
from fabricatio.journal import logger
|
12
|
-
from fabricatio.models.generic import Display
|
13
|
+
from fabricatio.models.generic import Display, ProposedAble
|
13
14
|
from fabricatio.models.kwargs_types import CompositeScoreKwargs, ValidateKwargs
|
14
|
-
from fabricatio.models.usages import LLMUsage
|
15
15
|
from fabricatio.parser import JsonCapture
|
16
16
|
from fabricatio.rust_instances import TEMPLATE_MANAGER
|
17
|
-
from fabricatio.utils import ok, override_kwargs
|
17
|
+
from fabricatio.utils import fallback_kwargs, ok, override_kwargs
|
18
18
|
|
19
19
|
|
20
|
-
class Rating(
|
20
|
+
class Rating(Propose):
|
21
21
|
"""A class that provides functionality to rate tasks based on a rating manual and score range.
|
22
22
|
|
23
23
|
References:
|
@@ -30,7 +30,7 @@ class Rating(LLMUsage):
|
|
30
30
|
rating_manual: Dict[str, str],
|
31
31
|
score_range: Tuple[float, float],
|
32
32
|
**kwargs: Unpack[ValidateKwargs[Dict[str, float]]],
|
33
|
-
) ->
|
33
|
+
) -> Dict[str, float] | List[Dict[str, float]] | List[Optional[Dict[str, float]]] | None:
|
34
34
|
"""Rate a given string based on a rating manual and score range.
|
35
35
|
|
36
36
|
Args:
|
@@ -42,45 +42,49 @@ class Rating(LLMUsage):
|
|
42
42
|
Returns:
|
43
43
|
Dict[str, float]: A dictionary with the ratings for each dimension.
|
44
44
|
"""
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
"to_rate": to_rate,
|
62
|
-
"min_score": score_range[0],
|
63
|
-
"max_score": score_range[1],
|
64
|
-
"rating_manual": rating_manual,
|
65
|
-
},
|
45
|
+
min_score, max_score = score_range
|
46
|
+
tip = (max_score - min_score) / 9
|
47
|
+
|
48
|
+
model = create_model( # pyright: ignore [reportCallIssue]
|
49
|
+
"RatingResult",
|
50
|
+
__base__=ProposedAble,
|
51
|
+
__doc__=f"The rating result contains the scores against each criterion, with min_score={min_score} and max_score={max_score}.",
|
52
|
+
**{ # pyright: ignore [reportArgumentType]
|
53
|
+
criterion: (
|
54
|
+
float,
|
55
|
+
Field(
|
56
|
+
ge=min_score,
|
57
|
+
le=max_score,
|
58
|
+
description=desc,
|
59
|
+
examples=[round(min_score + tip * i, 2) for i in range(10)],
|
60
|
+
),
|
66
61
|
)
|
62
|
+
for criterion, desc in rating_manual.items()
|
63
|
+
},
|
64
|
+
)
|
65
|
+
|
66
|
+
res = await self.propose(
|
67
|
+
model,
|
68
|
+
TEMPLATE_MANAGER.render_template(
|
69
|
+
configs.templates.rate_fine_grind_template,
|
70
|
+
{"to_rate": to_rate, "min_score": min_score, "max_score": max_score},
|
67
71
|
)
|
68
72
|
if isinstance(to_rate, str)
|
69
73
|
else [
|
70
74
|
TEMPLATE_MANAGER.render_template(
|
71
75
|
configs.templates.rate_fine_grind_template,
|
72
|
-
{
|
73
|
-
"to_rate": item,
|
74
|
-
"min_score": score_range[0],
|
75
|
-
"max_score": score_range[1],
|
76
|
-
"rating_manual": rating_manual,
|
77
|
-
},
|
76
|
+
{"to_rate": t, "min_score": min_score, "max_score": max_score},
|
78
77
|
)
|
79
|
-
for
|
78
|
+
for t in to_rate
|
80
79
|
],
|
81
|
-
|
82
|
-
**kwargs,
|
80
|
+
**override_kwargs(kwargs, default=None),
|
83
81
|
)
|
82
|
+
default = kwargs.get("default")
|
83
|
+
if isinstance(res, list):
|
84
|
+
return [r.model_dump() if r else default for r in res]
|
85
|
+
if res is None:
|
86
|
+
return default
|
87
|
+
return res.model_dump()
|
84
88
|
|
85
89
|
@overload
|
86
90
|
async def rate(
|
@@ -112,7 +116,7 @@ class Rating(LLMUsage):
|
|
112
116
|
manual: Optional[Dict[str, str]] = None,
|
113
117
|
score_range: Tuple[float, float] = (0.0, 1.0),
|
114
118
|
**kwargs: Unpack[ValidateKwargs],
|
115
|
-
) ->
|
119
|
+
) -> Dict[str, float] | List[Dict[str, float]] | List[Optional[Dict[str, float]]] | None:
|
116
120
|
"""Rate a given string or a sequence of strings based on a topic, criteria, and score range.
|
117
121
|
|
118
122
|
Args:
|
@@ -133,7 +137,7 @@ class Rating(LLMUsage):
|
|
133
137
|
or dict(zip(criteria, criteria, strict=True))
|
134
138
|
)
|
135
139
|
|
136
|
-
return await self.rate_fine_grind(to_rate, manual, score_range, **kwargs)
|
140
|
+
return await self.rate_fine_grind(to_rate, manual, score_range, **fallback_kwargs(kwargs, co_extractor={}))
|
137
141
|
|
138
142
|
async def draft_rating_manual(
|
139
143
|
self, topic: str, criteria: Optional[Set[str]] = None, **kwargs: Unpack[ValidateKwargs[Dict[str, str]]]
|
@@ -244,7 +248,7 @@ class Rating(LLMUsage):
|
|
244
248
|
|
245
249
|
# extract reasons from the comparison of ordered pairs of extracted from examples
|
246
250
|
reasons = flatten(
|
247
|
-
await self.aask_validate(
|
251
|
+
await self.aask_validate( # pyright: ignore [reportArgumentType]
|
248
252
|
question=[
|
249
253
|
TEMPLATE_MANAGER.render_template(
|
250
254
|
configs.templates.extract_reasons_from_examples_template,
|
@@ -319,9 +323,11 @@ class Rating(LLMUsage):
|
|
319
323
|
validator=lambda resp: JsonCapture.validate_with(resp, target_type=float),
|
320
324
|
**kwargs,
|
321
325
|
)
|
326
|
+
if not all(relative_weights):
|
327
|
+
raise ValueError(f"found illegal weight: {relative_weights}")
|
322
328
|
weights = [1.0]
|
323
329
|
for rw in relative_weights:
|
324
|
-
weights.append(weights[-1] * rw)
|
330
|
+
weights.append(weights[-1] * rw) # pyright: ignore [reportOperatorIssue]
|
325
331
|
total = sum(weights)
|
326
332
|
return dict(zip(criteria_seq, [w / total for w in weights], strict=True))
|
327
333
|
|
fabricatio/config.py
CHANGED
@@ -44,7 +44,7 @@ class LLMConfig(BaseModel):
|
|
44
44
|
top_p (NonNegativeFloat): The top p of the LLM model. Controls diversity via nucleus sampling. Set to 0.35 as per request.
|
45
45
|
generation_count (PositiveInt): The number of generations to generate. Default is 1.
|
46
46
|
stream (bool): Whether to stream the LLM model's response. Default is False.
|
47
|
-
max_tokens (PositiveInt): The maximum number of tokens to generate.
|
47
|
+
max_tokens (PositiveInt): The maximum number of tokens to generate.
|
48
48
|
"""
|
49
49
|
|
50
50
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
@@ -79,7 +79,7 @@ class LLMConfig(BaseModel):
|
|
79
79
|
"""Whether to stream the LLM model's response. Default is False."""
|
80
80
|
|
81
81
|
max_tokens: Optional[PositiveInt] = Field(default=None)
|
82
|
-
"""The maximum number of tokens to generate.
|
82
|
+
"""The maximum number of tokens to generate."""
|
83
83
|
|
84
84
|
rpm: Optional[PositiveInt] = Field(default=100)
|
85
85
|
"""The rate limit of the LLM model in requests per minute. None means not checked."""
|
fabricatio/fs/readers.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
"""Filesystem readers for Fabricatio."""
|
2
2
|
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import Dict
|
4
|
+
from typing import Dict, List, Tuple
|
5
5
|
|
6
6
|
import orjson
|
7
|
+
import regex
|
7
8
|
from magika import Magika
|
8
9
|
|
9
10
|
from fabricatio.config import configs
|
@@ -44,3 +45,21 @@ def safe_json_read(path: Path | str) -> Dict:
|
|
44
45
|
except (orjson.JSONDecodeError, IsADirectoryError, FileNotFoundError) as e:
|
45
46
|
logger.error(f"Failed to read file {path}: {e!s}")
|
46
47
|
return {}
|
48
|
+
|
49
|
+
|
50
|
+
def extract_sections(string: str, level: int, section_char: str = "#") -> List[Tuple[str, str]]:
|
51
|
+
"""Extract sections from markdown-style text by header level.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
string (str): Input text to parse
|
55
|
+
level (int): Header level (e.g., 1 for '#', 2 for '##')
|
56
|
+
section_char (str, optional): The character used for headers (default: '#')
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
List[Tuple[str, str]]: List of (header_text, section_content) tuples
|
60
|
+
"""
|
61
|
+
return regex.findall(
|
62
|
+
r"^%s{%d}\s+(.+?)\n((?:(?!^%s{%d}\s).|\n)*)" % (section_char, level, section_char, level),
|
63
|
+
string,
|
64
|
+
regex.MULTILINE,
|
65
|
+
)
|
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
from typing import List
|
4
4
|
|
5
|
-
from fabricatio.models.generic import
|
5
|
+
from fabricatio.models.generic import SketchedAble
|
6
6
|
|
7
7
|
|
8
|
-
class JudgeMent(
|
8
|
+
class JudgeMent(SketchedAble):
|
9
9
|
"""Represents a judgment result containing supporting/denying evidence and final verdict.
|
10
10
|
|
11
11
|
The class stores both affirmative and denies evidence, truth and reasons lists along with the final boolean judgment.
|
12
12
|
"""
|
13
13
|
|
14
14
|
issue_to_judge: str
|
15
|
-
"""The issue to be judged"""
|
15
|
+
"""The issue to be judged, true for affirmation, false for denial."""
|
16
16
|
|
17
17
|
deny_evidence: List[str]
|
18
18
|
"""List of clues supporting the denial."""
|
@@ -17,7 +17,10 @@ from fabricatio.models.generic import (
|
|
17
17
|
ResolveUpdateConflict,
|
18
18
|
SequencePatch,
|
19
19
|
SketchedAble,
|
20
|
+
Titled,
|
21
|
+
WordCount,
|
20
22
|
)
|
23
|
+
from pydantic import Field
|
21
24
|
|
22
25
|
|
23
26
|
class ReferringType(StrEnum):
|
@@ -38,44 +41,44 @@ class ArticleRef(ProposedUpdateAble):
|
|
38
41
|
- Referring to a chapter titled `Introduction`:
|
39
42
|
Using Python
|
40
43
|
```python
|
41
|
-
ArticleRef(
|
44
|
+
ArticleRef(chap="Introduction")
|
42
45
|
```
|
43
46
|
Using JSON
|
44
47
|
```json
|
45
|
-
{
|
48
|
+
{chap="Introduction"}
|
46
49
|
```
|
47
50
|
- Referring to a section titled `Background` under the `Introduction` chapter:
|
48
51
|
Using Python
|
49
52
|
```python
|
50
|
-
ArticleRef(
|
53
|
+
ArticleRef(chap="Introduction", sec="Background")
|
51
54
|
```
|
52
55
|
Using JSON
|
53
56
|
```json
|
54
|
-
{
|
57
|
+
{chap="Introduction", sec="Background"}
|
55
58
|
```
|
56
59
|
- Referring to a subsection titled `Related Work` under the `Background` section of the `Introduction` chapter:
|
57
60
|
Using Python
|
58
61
|
```python
|
59
|
-
ArticleRef(
|
62
|
+
ArticleRef(chap="Introduction", sec="Background", subsec="Related Work")
|
60
63
|
```
|
61
64
|
Using JSON
|
62
65
|
```json
|
63
|
-
{
|
66
|
+
{chap="Introduction", sec="Background", subsec="Related Work"}
|
64
67
|
```
|
65
68
|
"""
|
66
69
|
|
67
|
-
|
70
|
+
chap: str
|
68
71
|
"""`title` Field of the referenced chapter"""
|
69
|
-
|
72
|
+
sec: Optional[str] = None
|
70
73
|
"""`title` Field of the referenced section."""
|
71
|
-
|
74
|
+
subsec: Optional[str] = None
|
72
75
|
"""`title` Field of the referenced subsection."""
|
73
76
|
|
74
77
|
def update_from_inner(self, other: Self) -> Self:
|
75
78
|
"""Updates the current instance with the attributes of another instance."""
|
76
|
-
self.
|
77
|
-
self.
|
78
|
-
self.
|
79
|
+
self.chap = other.chap
|
80
|
+
self.sec = other.sec
|
81
|
+
self.subsec = other.subsec
|
79
82
|
return self
|
80
83
|
|
81
84
|
def deref(self, article: "ArticleBase") -> Optional["ArticleOutlineBase"]:
|
@@ -87,39 +90,41 @@ class ArticleRef(ProposedUpdateAble):
|
|
87
90
|
Returns:
|
88
91
|
ArticleMainBase | ArticleOutline | None: The dereferenced section or subsection, or None if not found.
|
89
92
|
"""
|
90
|
-
chap = next((chap for chap in article.chapters if chap.title == self.
|
91
|
-
if self.
|
93
|
+
chap = next((chap for chap in article.chapters if chap.title == self.chap), None)
|
94
|
+
if self.sec is None or chap is None:
|
92
95
|
return chap
|
93
|
-
sec = next((sec for sec in chap.sections if sec.title == self.
|
94
|
-
if self.
|
96
|
+
sec = next((sec for sec in chap.sections if sec.title == self.sec), None)
|
97
|
+
if self.subsec is None or sec is None:
|
95
98
|
return sec
|
96
|
-
return next((subsec for subsec in sec.subsections if subsec.title == self.
|
99
|
+
return next((subsec for subsec in sec.subsections if subsec.title == self.subsec), None)
|
97
100
|
|
98
101
|
@property
|
99
102
|
def referring_type(self) -> ReferringType:
|
100
103
|
"""Determine the type of reference based on the presence of specific attributes."""
|
101
|
-
if self.
|
104
|
+
if self.subsec is not None:
|
102
105
|
return ReferringType.SUBSECTION
|
103
|
-
if self.
|
106
|
+
if self.sec is not None:
|
104
107
|
return ReferringType.SECTION
|
105
108
|
return ReferringType.CHAPTER
|
106
109
|
|
107
110
|
|
108
|
-
class ArticleMetaData(SketchedAble, Described, Language):
|
111
|
+
class ArticleMetaData(SketchedAble, Described, WordCount, Titled, Language):
|
109
112
|
"""Metadata for an article component."""
|
110
113
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
114
|
+
description: str = Field(
|
115
|
+
alias="elaboration",
|
116
|
+
description=Described.model_fields["description"].description,
|
117
|
+
)
|
118
|
+
|
119
|
+
title: str = Field(alias="heading", description=Titled.model_fields["title"].description)
|
115
120
|
|
116
|
-
|
121
|
+
aims: List[str]
|
117
122
|
"""List of writing aims of the research component in academic style."""
|
118
|
-
title: str
|
119
|
-
"""Do not add any prefix or suffix to the title. should not contain special characters."""
|
120
123
|
|
121
|
-
|
122
|
-
"""
|
124
|
+
support_to: List[ArticleRef]
|
125
|
+
"""List of references to other future components in this article that this component supports to."""
|
126
|
+
depend_on: List[ArticleRef]
|
127
|
+
"""List of references to other previous components in this article that this component depends on."""
|
123
128
|
|
124
129
|
|
125
130
|
class ArticleRefSequencePatch(SequencePatch[ArticleRef]):
|
@@ -147,8 +152,8 @@ class ArticleOutlineBase(
|
|
147
152
|
self.support_to.extend(other.support_to)
|
148
153
|
self.depend_on.clear()
|
149
154
|
self.depend_on.extend(other.depend_on)
|
150
|
-
self.
|
151
|
-
self.
|
155
|
+
self.aims.clear()
|
156
|
+
self.aims.extend(other.aims)
|
152
157
|
self.description = other.description
|
153
158
|
return self
|
154
159
|
|
@@ -272,22 +277,19 @@ class ChapterBase[T: SectionBase](ArticleOutlineBase):
|
|
272
277
|
return ""
|
273
278
|
|
274
279
|
|
275
|
-
class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
|
280
|
+
class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, WordCount, Described, Titled, Language, ABC):
|
276
281
|
"""Base class for article outlines."""
|
277
282
|
|
278
|
-
title: str
|
279
|
-
|
283
|
+
title: str = Field(alias="heading", description=Titled.model_fields["title"].description)
|
284
|
+
description: str = Field(alias="abstract")
|
285
|
+
"""The abstract serves as a concise summary of an academic article, encapsulating its core purpose, methodologies, key results,
|
286
|
+
and conclusions while enabling readers to rapidly assess the relevance and significance of the study.
|
287
|
+
Functioning as the article's distilled essence, it succinctly articulates the research problem, objectives,
|
288
|
+
and scope, providing a roadmap for the full text while also facilitating database indexing, literature reviews,
|
289
|
+
and citation tracking through standardized metadata. Additionally, it acts as an accessibility gateway,
|
290
|
+
allowing scholars to gauge the study's contribution to existing knowledge, its methodological rigor,
|
291
|
+
and its broader implications without engaging with the entire manuscript, thereby optimizing scholarly communication efficiency."""
|
280
292
|
|
281
|
-
prospect: str
|
282
|
-
"""Consolidated research statement with four pillars:
|
283
|
-
1. Problem Identification: Current limitations
|
284
|
-
2. Methodological Response: Technical approach
|
285
|
-
3. Empirical Validation: Evaluation strategy
|
286
|
-
4. Scholarly Impact: Field contributions
|
287
|
-
"""
|
288
|
-
|
289
|
-
abstract: str
|
290
|
-
"""The abstract is a concise summary of the academic paper's main findings."""
|
291
293
|
chapters: List[T]
|
292
294
|
"""Chapters of the article. Contains at least one chapter. You can also add more as needed."""
|
293
295
|
|
@@ -415,14 +417,12 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
|
|
415
417
|
if not ref.deref(self):
|
416
418
|
summary += f"Invalid internal reference in `{component.__class__.__name__}` titled `{component.title}`, because the referred {ref.referring_type} is not exists within the article, see the original obj dump: {ref.model_dump()}\n"
|
417
419
|
|
418
|
-
if ref.
|
419
|
-
summary += f"Chapter titled `{ref.
|
420
|
-
if ref.
|
421
|
-
summary += f"Section Titled `{ref.
|
422
|
-
if ref.
|
423
|
-
summary +=
|
424
|
-
f"Subsection Titled `{ref.referred_subsection_title}` is not any of {subsec_titles_set}"
|
425
|
-
)
|
420
|
+
if ref.chap not in (chap_titles_set):
|
421
|
+
summary += f"Chapter titled `{ref.chap}` is not any of {chap_titles_set}\n"
|
422
|
+
if ref.sec and ref.sec not in (sec_titles_set):
|
423
|
+
summary += f"Section Titled `{ref.sec}` is not any of {sec_titles_set}\n"
|
424
|
+
if ref.subsec and ref.subsec not in (subsec_titles_set):
|
425
|
+
summary += f"Subsection Titled `{ref.subsec}` is not any of {subsec_titles_set}"
|
426
426
|
|
427
427
|
if summary:
|
428
428
|
return (
|
@@ -453,17 +453,15 @@ class ArticleBase[T: ChapterBase](FinalizedDumpAble, AsPrompt, Language, ABC):
|
|
453
453
|
r for r in chain(component.depend_on, component.support_to) if not r.deref(self) and r not in res_seq
|
454
454
|
):
|
455
455
|
res_seq.append(ref)
|
456
|
-
if ref.
|
457
|
-
summary.append(
|
458
|
-
f"Chapter titled `{ref.referred_chapter_title}` is not exist, since it is not any of {chap_titles_set}."
|
459
|
-
)
|
460
|
-
if ref.referred_section_title and (ref.referred_section_title not in sec_titles_set):
|
456
|
+
if ref.chap not in chap_titles_set:
|
461
457
|
summary.append(
|
462
|
-
f"
|
458
|
+
f"Chapter titled `{ref.chap}` is not exist, since it is not any of {chap_titles_set}."
|
463
459
|
)
|
464
|
-
if ref.
|
460
|
+
if ref.sec and (ref.sec not in sec_titles_set):
|
461
|
+
summary.append(f"Section Titled `{ref.sec}` is not exist, since it is not any of {sec_titles_set}")
|
462
|
+
if ref.subsec and (ref.subsec not in subsec_titles_set):
|
465
463
|
summary.append(
|
466
|
-
f"Subsection Titled `{ref.
|
464
|
+
f"Subsection Titled `{ref.subsec}` is not exist, since it is not any of {subsec_titles_set}"
|
467
465
|
)
|
468
466
|
|
469
467
|
return res_seq, "\n".join(summary)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
from itertools import chain
|
4
4
|
from typing import Dict, Generator, List, Self, Tuple, override
|
5
5
|
|
6
|
+
from fabricatio.fs.readers import extract_sections
|
6
7
|
from fabricatio.journal import logger
|
7
8
|
from fabricatio.models.extra.article_base import (
|
8
9
|
ArticleBase,
|
@@ -14,26 +15,33 @@ from fabricatio.models.extra.article_base import (
|
|
14
15
|
from fabricatio.models.extra.article_outline import (
|
15
16
|
ArticleOutline,
|
16
17
|
)
|
17
|
-
from fabricatio.models.generic import PersistentAble, SequencePatch, SketchedAble, WithRef
|
18
|
-
from fabricatio.rust import word_count
|
18
|
+
from fabricatio.models.generic import Described, PersistentAble, SequencePatch, SketchedAble, WithRef, WordCount
|
19
|
+
from fabricatio.rust import detect_language, word_count
|
19
20
|
from fabricatio.utils import ok
|
21
|
+
from pydantic import Field
|
20
22
|
|
23
|
+
PARAGRAPH_SEP = "// - - -"
|
21
24
|
|
22
|
-
|
25
|
+
|
26
|
+
class Paragraph(SketchedAble, WordCount, Described):
|
23
27
|
"""Structured academic paragraph blueprint for controlled content generation."""
|
24
28
|
|
25
|
-
description: str
|
26
|
-
|
29
|
+
description: str = Field(
|
30
|
+
alias="elaboration",
|
31
|
+
description=Described.model_fields["description"].description,
|
32
|
+
)
|
27
33
|
|
28
|
-
|
34
|
+
aims: List[str]
|
29
35
|
"""Specific communicative objectives for this paragraph's content."""
|
30
36
|
|
31
|
-
expected_word_count: int
|
32
|
-
"""Expected word count for the paragraph."""
|
33
|
-
|
34
37
|
content: str
|
35
38
|
"""The actual content of the paragraph, represented as a string."""
|
36
39
|
|
40
|
+
@classmethod
|
41
|
+
def from_content(cls, content: str) -> Self:
|
42
|
+
"""Create a Paragraph object from the given content."""
|
43
|
+
return cls(elaboration="", aims=[], expected_word_count=word_count(content), content=content)
|
44
|
+
|
37
45
|
|
38
46
|
class ArticleParagraphSequencePatch(SequencePatch[Paragraph]):
|
39
47
|
"""Patch for `Paragraph` list of `ArticleSubsection`."""
|
@@ -57,12 +65,11 @@ class ArticleSubsection(SubSectionBase):
|
|
57
65
|
"""Introspects the subsection and returns a summary of its state."""
|
58
66
|
summary = ""
|
59
67
|
if len(self.paragraphs) == 0:
|
60
|
-
summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs!\n"
|
61
|
-
if (
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
summary += f"`{self.__class__.__name__}` titled `{self.title}` have {wc} words, expected {self.expected_word_count} words!"
|
68
|
+
summary += f"`{self.__class__.__name__}` titled `{self.title}` have no paragraphs, You should add some!\n"
|
69
|
+
if abs((wc := self.word_count) - self.expected_word_count) / self.expected_word_count > self._max_word_count_deviation:
|
70
|
+
summary += (
|
71
|
+
f"`{self.__class__.__name__}` titled `{self.title}` have {wc} words, expected {self.word_count} words!"
|
72
|
+
)
|
66
73
|
|
67
74
|
return summary
|
68
75
|
|
@@ -80,16 +87,64 @@ class ArticleSubsection(SubSectionBase):
|
|
80
87
|
Returns:
|
81
88
|
str: Typst code snippet for rendering.
|
82
89
|
"""
|
83
|
-
return f"=== {self.title}\n" + "\n\n".join(p.content for p in self.paragraphs)
|
90
|
+
return f"=== {self.title}\n" + f"\n{PARAGRAPH_SEP}\n".join(p.content for p in self.paragraphs)
|
91
|
+
|
92
|
+
@classmethod
|
93
|
+
def from_typst_code(cls, title: str, body: str, language: str) -> Self:
|
94
|
+
"""Creates an Article object from the given Typst code."""
|
95
|
+
return cls(
|
96
|
+
heading=title,
|
97
|
+
elaboration="",
|
98
|
+
paragraphs=[Paragraph.from_content(p) for p in body.split(PARAGRAPH_SEP)],
|
99
|
+
expected_word_count=word_count(body),
|
100
|
+
language=language,
|
101
|
+
aims=[],
|
102
|
+
support_to=[],
|
103
|
+
depend_on=[],
|
104
|
+
)
|
84
105
|
|
85
106
|
|
86
107
|
class ArticleSection(SectionBase[ArticleSubsection]):
|
87
108
|
"""Atomic argumentative unit with high-level specificity."""
|
88
109
|
|
110
|
+
@classmethod
|
111
|
+
def from_typst_code(cls, title: str, body: str, language: str) -> Self:
|
112
|
+
"""Creates an Article object from the given Typst code."""
|
113
|
+
return cls(
|
114
|
+
subsections=[
|
115
|
+
ArticleSubsection.from_typst_code(*pack, language=language)
|
116
|
+
for pack in extract_sections(body, level=3, section_char="=")
|
117
|
+
],
|
118
|
+
heading=title,
|
119
|
+
elaboration="",
|
120
|
+
expected_word_count=word_count(body),
|
121
|
+
language=language,
|
122
|
+
aims=[],
|
123
|
+
support_to=[],
|
124
|
+
depend_on=[],
|
125
|
+
)
|
126
|
+
|
89
127
|
|
90
128
|
class ArticleChapter(ChapterBase[ArticleSection]):
|
91
129
|
"""Thematic progression implementing research function."""
|
92
130
|
|
131
|
+
@classmethod
|
132
|
+
def from_typst_code(cls, title: str, body: str, language: str) -> Self:
|
133
|
+
"""Creates an Article object from the given Typst code."""
|
134
|
+
return cls(
|
135
|
+
sections=[
|
136
|
+
ArticleSection.from_typst_code(*pack, language=language)
|
137
|
+
for pack in extract_sections(body, level=2, section_char="=")
|
138
|
+
],
|
139
|
+
heading=title,
|
140
|
+
elaboration="",
|
141
|
+
expected_word_count=word_count(body),
|
142
|
+
language=language,
|
143
|
+
aims=[],
|
144
|
+
support_to=[],
|
145
|
+
depend_on=[],
|
146
|
+
)
|
147
|
+
|
93
148
|
|
94
149
|
class Article(
|
95
150
|
SketchedAble,
|
@@ -126,31 +181,45 @@ class Article(
|
|
126
181
|
Article: The generated article.
|
127
182
|
"""
|
128
183
|
# Set the title from the outline
|
129
|
-
article = Article(**outline.model_dump(exclude={"chapters"}), chapters=[])
|
184
|
+
article = Article(**outline.model_dump(exclude={"chapters"}, by_alias=True), chapters=[])
|
130
185
|
|
131
186
|
for chapter in outline.chapters:
|
132
187
|
# Create a new chapter
|
133
188
|
article_chapter = ArticleChapter(
|
134
189
|
sections=[],
|
135
|
-
**chapter.model_dump(exclude={"sections"}),
|
190
|
+
**chapter.model_dump(exclude={"sections"}, by_alias=True),
|
136
191
|
)
|
137
192
|
for section in chapter.sections:
|
138
193
|
# Create a new section
|
139
194
|
article_section = ArticleSection(
|
140
195
|
subsections=[],
|
141
|
-
**section.model_dump(exclude={"subsections"}),
|
196
|
+
**section.model_dump(exclude={"subsections"}, by_alias=True),
|
142
197
|
)
|
143
198
|
for subsection in section.subsections:
|
144
199
|
# Create a new subsection
|
145
200
|
article_subsection = ArticleSubsection(
|
146
201
|
paragraphs=[],
|
147
|
-
**subsection.model_dump(),
|
202
|
+
**subsection.model_dump(by_alias=True),
|
148
203
|
)
|
149
204
|
article_section.subsections.append(article_subsection)
|
150
205
|
article_chapter.sections.append(article_section)
|
151
206
|
article.chapters.append(article_chapter)
|
152
207
|
return article
|
153
208
|
|
209
|
+
@classmethod
|
210
|
+
def from_typst_code(cls, title: str, body: str) -> Self:
|
211
|
+
"""Generates an article from the given Typst code."""
|
212
|
+
return cls(
|
213
|
+
language=(lang := detect_language(body)),
|
214
|
+
chapters=[
|
215
|
+
ArticleChapter.from_typst_code(*pack, language=lang)
|
216
|
+
for pack in extract_sections(body, level=1, section_char="=")
|
217
|
+
],
|
218
|
+
heading=title,
|
219
|
+
expected_word_count=word_count(body),
|
220
|
+
abstract="",
|
221
|
+
)
|
222
|
+
|
154
223
|
def gather_dependencies(self, article: ArticleOutlineBase) -> List[ArticleOutlineBase]:
|
155
224
|
"""Gathers dependencies for all sections and subsections in the article.
|
156
225
|
|
@@ -2,21 +2,25 @@
|
|
2
2
|
|
3
3
|
from typing import Dict, List
|
4
4
|
|
5
|
-
from fabricatio.models.generic import
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
from fabricatio.models.generic import (
|
6
|
+
AsPrompt,
|
7
|
+
Described,
|
8
|
+
Language,
|
9
|
+
PersistentAble,
|
10
|
+
SketchedAble,
|
11
|
+
Titled,
|
12
|
+
WithRef,
|
13
|
+
WordCount,
|
14
|
+
)
|
15
|
+
from pydantic import Field
|
16
|
+
|
17
|
+
|
18
|
+
class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble, WordCount, Described, Titled, Language):
|
9
19
|
"""Structured proposal for academic paper development with core research elements.
|
10
20
|
|
11
21
|
Guides LLM in generating comprehensive research proposals with clearly defined components.
|
12
22
|
"""
|
13
23
|
|
14
|
-
language: str
|
15
|
-
"""The language in which the article is written. This should align with the language specified in the article briefing."""
|
16
|
-
|
17
|
-
title: str
|
18
|
-
"""The title of the academic paper, formatted in Title Case."""
|
19
|
-
|
20
24
|
focused_problem: List[str]
|
21
25
|
"""A list of specific research problems or questions that the paper aims to address."""
|
22
26
|
|
@@ -38,12 +42,9 @@ class ArticleProposal(SketchedAble, WithRef[str], AsPrompt, PersistentAble):
|
|
38
42
|
keywords: List[str]
|
39
43
|
"""A list of keywords that represent the main topics and focus areas of the research."""
|
40
44
|
|
41
|
-
|
45
|
+
description: str = Field(alias="abstract")
|
42
46
|
"""A concise summary of the research proposal, outlining the main points and objectives."""
|
43
47
|
|
44
|
-
expected_word_count: int
|
45
|
-
"""The estimated word count of the final academic paper."""
|
46
|
-
|
47
48
|
def _as_prompt_inner(self) -> Dict[str, str]:
|
48
49
|
return {
|
49
50
|
"ArticleBriefing": self.referenced,
|
@@ -3,17 +3,17 @@
|
|
3
3
|
from typing import Optional, Type
|
4
4
|
|
5
5
|
from fabricatio.models.extra.rule import RuleSet
|
6
|
-
from fabricatio.models.generic import Patch, WithBriefing
|
6
|
+
from fabricatio.models.generic import Language, Patch, WithBriefing
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
9
|
|
10
|
-
class
|
11
|
-
"""
|
10
|
+
class BriefingMetadata[T: WithBriefing](Patch[T], WithBriefing):
|
11
|
+
"""A patch class for updating the description and name of a `WithBriefing` object, all fields within this instance will be directly copied onto the target model's field."""
|
12
12
|
|
13
13
|
|
14
|
-
class
|
15
|
-
"""
|
16
|
-
|
14
|
+
class RuleSetMetadata(BriefingMetadata[RuleSet], Language):
|
15
|
+
"""A patch class for updating the description and name of a `RuleSet` object, all fields within this instance will be directly copied onto the target model's field."""
|
16
|
+
|
17
17
|
@staticmethod
|
18
18
|
def ref_cls() -> Optional[Type[BaseModel]]:
|
19
19
|
"""Get the reference class of the model."""
|
fabricatio/models/generic.py
CHANGED
@@ -36,6 +36,7 @@ class Base(BaseModel):
|
|
36
36
|
The `model_config` uses `use_attribute_docstrings=True` to ensure field descriptions are
|
37
37
|
pulled from the attribute's docstring instead of the default Pydantic behavior.
|
38
38
|
"""
|
39
|
+
|
39
40
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
40
41
|
|
41
42
|
|
@@ -45,6 +46,7 @@ class Display(Base):
|
|
45
46
|
Provides methods to generate both pretty-printed and compact JSON representations of the model.
|
46
47
|
Used for debugging and logging purposes.
|
47
48
|
"""
|
49
|
+
|
48
50
|
def display(self) -> str:
|
49
51
|
"""Generate pretty-printed JSON representation.
|
50
52
|
|
@@ -102,6 +104,20 @@ class Described(Base):
|
|
102
104
|
this object's intent and application."""
|
103
105
|
|
104
106
|
|
107
|
+
class Titled(Base):
|
108
|
+
"""Class that includes a title attribute."""
|
109
|
+
|
110
|
+
title: str
|
111
|
+
"""The title of this object, make it professional and concise.No prefixed heading number should be included."""
|
112
|
+
|
113
|
+
|
114
|
+
class WordCount(Base):
|
115
|
+
"""Class that includes a word count attribute."""
|
116
|
+
|
117
|
+
expected_word_count: int
|
118
|
+
"""Expected word count of this research component."""
|
119
|
+
|
120
|
+
|
105
121
|
class AsPrompt(Base):
|
106
122
|
"""Class that provides a method to generate a prompt from the model.
|
107
123
|
|
@@ -194,6 +210,7 @@ class PersistentAble(Base):
|
|
194
210
|
Enables saving model instances to disk with timestamped filenames and loading from persisted files.
|
195
211
|
Implements basic versioning through filename hashing and timestamping.
|
196
212
|
"""
|
213
|
+
|
197
214
|
def persist(self, path: str | Path) -> Self:
|
198
215
|
"""Save model instance to disk with versioned filename.
|
199
216
|
|
@@ -208,7 +225,7 @@ class PersistentAble(Base):
|
|
208
225
|
- Hash generated from JSON content ensures uniqueness
|
209
226
|
"""
|
210
227
|
p = Path(path)
|
211
|
-
out = self.model_dump_json()
|
228
|
+
out = self.model_dump_json(indent=1)
|
212
229
|
|
213
230
|
# Generate a timestamp in the format YYYYMMDD_HHMMSS
|
214
231
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
@@ -282,8 +299,7 @@ class Language(Base):
|
|
282
299
|
"""Class that provides a language attribute."""
|
283
300
|
|
284
301
|
language: str
|
285
|
-
"""The written language of this object
|
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."""
|
302
|
+
"""The fullname of the written language of this object."""
|
287
303
|
|
288
304
|
|
289
305
|
class ModelHash(Base):
|
@@ -670,6 +686,7 @@ class ScopedConfig(Base):
|
|
670
686
|
Manages LLM, embedding, and vector database configurations with fallback logic.
|
671
687
|
Allows configuration values to be overridden in a hierarchical manner.
|
672
688
|
"""
|
689
|
+
|
673
690
|
llm_api_endpoint: Optional[HttpUrl] = None
|
674
691
|
"""The OpenAI API endpoint."""
|
675
692
|
|
fabricatio/models/usages.py
CHANGED
@@ -299,10 +299,11 @@ class LLMUsage(ScopedConfig):
|
|
299
299
|
for lap in range(max_validations):
|
300
300
|
try:
|
301
301
|
if ((validated := validator(response := await self.aask(question=q, **kwargs))) is not None) or (
|
302
|
-
co_extractor
|
302
|
+
co_extractor is not None
|
303
|
+
and logger.debug("Co-extraction is enabled.") is None
|
303
304
|
and (
|
304
305
|
validated := validator(
|
305
|
-
await self.aask(
|
306
|
+
response:=await self.aask(
|
306
307
|
question=(
|
307
308
|
TEMPLATE_MANAGER.render_template(
|
308
309
|
configs.templates.co_validation_template,
|
@@ -319,12 +320,13 @@ class LLMUsage(ScopedConfig):
|
|
319
320
|
return validated
|
320
321
|
|
321
322
|
except RateLimitError as e:
|
322
|
-
logger.warning(f"Rate limit error
|
323
|
+
logger.warning(f"Rate limit error:\n{e}")
|
323
324
|
continue
|
324
325
|
except Exception as e: # noqa: BLE001
|
325
|
-
logger.error(f"Error during validation
|
326
|
+
logger.error(f"Error during validation:\n{e}")
|
326
327
|
logger.debug(traceback.format_exc())
|
327
328
|
break
|
329
|
+
logger.error(f"Failed to validate the response at {lap}th attempt:\n{response}")
|
328
330
|
if not kwargs.get("no_cache"):
|
329
331
|
kwargs["no_cache"] = True
|
330
332
|
logger.debug("Closed the cache for the next attempt")
|
fabricatio/parser.py
CHANGED
@@ -48,10 +48,10 @@ class Capture(BaseModel):
|
|
48
48
|
case "json" if configs.general.use_json_repair:
|
49
49
|
logger.debug("Applying json repair to text.")
|
50
50
|
if isinstance(text, str):
|
51
|
-
return repair_json(text, ensure_ascii=False)
|
52
|
-
return [repair_json(item, ensure_ascii=False) for item in text]
|
51
|
+
return repair_json(text, ensure_ascii=False) # pyright: ignore [reportReturnType]
|
52
|
+
return [repair_json(item, ensure_ascii=False) for item in text] # pyright: ignore [reportReturnType, reportGeneralTypeIssues]
|
53
53
|
case _:
|
54
|
-
return text
|
54
|
+
return text # pyright: ignore [reportReturnType]
|
55
55
|
|
56
56
|
def capture(self, text: str) -> Tuple[str, ...] | str | None:
|
57
57
|
"""Capture the first occurrence of the pattern in the given text.
|
@@ -88,7 +88,7 @@ class Capture(BaseModel):
|
|
88
88
|
if (cap := self.capture(text)) is None:
|
89
89
|
return None
|
90
90
|
try:
|
91
|
-
return convertor(cap)
|
91
|
+
return convertor(cap) # pyright: ignore [reportArgumentType]
|
92
92
|
except (ValueError, SyntaxError, ValidationError) as e:
|
93
93
|
logger.error(f"Failed to convert text using {convertor.__name__} to convert.\nerror: {e}\n {cap}")
|
94
94
|
return None
|
@@ -120,7 +120,7 @@ class Capture(BaseModel):
|
|
120
120
|
judges.append(lambda output_obj: len(output_obj) == length)
|
121
121
|
|
122
122
|
if (out := self.convert_with(text, deserializer)) and all(j(out) for j in judges):
|
123
|
-
return out
|
123
|
+
return out # pyright: ignore [reportReturnType]
|
124
124
|
return None
|
125
125
|
|
126
126
|
@classmethod
|
Binary file
|
fabricatio/rust.pyi
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
Python interface definitions for Rust-based functionality.
|
1
|
+
"""Python interface definitions for Rust-based functionality.
|
3
2
|
|
4
3
|
This module provides type stubs and documentation for Rust-implemented utilities,
|
5
4
|
including template rendering, cryptographic hashing, language detection, and
|
@@ -12,13 +11,11 @@ Key Features:
|
|
12
11
|
- Text utilities: Word boundary splitting and word counting.
|
13
12
|
"""
|
14
13
|
|
15
|
-
|
16
14
|
from pathlib import Path
|
17
15
|
from typing import List, Optional
|
18
16
|
|
19
17
|
from pydantic import JsonValue
|
20
18
|
|
21
|
-
|
22
19
|
class TemplateManager:
|
23
20
|
"""Template rendering engine using Handlebars templates.
|
24
21
|
|
@@ -97,7 +94,6 @@ def blake3_hash(content: bytes) -> str:
|
|
97
94
|
def detect_language(string: str) -> str:
|
98
95
|
"""Detect the language of a given string."""
|
99
96
|
|
100
|
-
|
101
97
|
def split_word_bounds(string: str) -> List[str]:
|
102
98
|
"""Split the string into words based on word boundaries.
|
103
99
|
|
@@ -107,6 +103,7 @@ def split_word_bounds(string: str) -> List[str]:
|
|
107
103
|
Returns:
|
108
104
|
A list of words extracted from the string.
|
109
105
|
"""
|
106
|
+
|
110
107
|
def word_count(string: str) -> int:
|
111
108
|
"""Count the number of words in the string.
|
112
109
|
|
@@ -117,8 +114,6 @@ def word_count(string: str) -> int:
|
|
117
114
|
The number of words in the string.
|
118
115
|
"""
|
119
116
|
|
120
|
-
|
121
|
-
|
122
117
|
class BibManager:
|
123
118
|
"""BibTeX bibliography manager for parsing and querying citation data."""
|
124
119
|
|
Binary file
|
@@ -1,52 +1,52 @@
|
|
1
|
-
fabricatio-0.2.9.
|
2
|
-
fabricatio-0.2.9.
|
3
|
-
fabricatio-0.2.9.
|
4
|
-
fabricatio/actions/article.py,sha256=
|
5
|
-
fabricatio/actions/article_rag.py,sha256=
|
1
|
+
fabricatio-0.2.9.dev4.dist-info/METADATA,sha256=qo6WjSrz5Br7ypVenUufk1zhMg9yW_Qzt_hoLuOPFKo,5288
|
2
|
+
fabricatio-0.2.9.dev4.dist-info/WHEEL,sha256=jABKVkLC9kJr8mi_er5jOqpiQUjARSLXDUIIxDqsS50,96
|
3
|
+
fabricatio-0.2.9.dev4.dist-info/licenses/LICENSE,sha256=do7J7EiCGbq0QPbMAL_FqLYufXpHnCnXBOuqVPwSV8Y,1088
|
4
|
+
fabricatio/actions/article.py,sha256=uwRXKCzTp5C-vwIMuCJOsTwCU_F2uJ3cVNKS74AABbo,12669
|
5
|
+
fabricatio/actions/article_rag.py,sha256=itGH-VCKTVFm7hrYIOOT4FyFXP8CbL042kpYNI9a2BE,4735
|
6
6
|
fabricatio/actions/output.py,sha256=gkC2u_VpMJ6jOnbyRAJN24UVK7iDAMzhItYukaW8Spk,6498
|
7
7
|
fabricatio/actions/rag.py,sha256=5nSih3YUkdt1uU02hSAMW6sADq9mkMOR1wDv7zIrIGQ,2737
|
8
8
|
fabricatio/actions/rules.py,sha256=SNvAvQx4xUare16Za_dEpYlYI_PJNnbiO-E0XDa5JT4,2857
|
9
9
|
fabricatio/actions/__init__.py,sha256=wVENCFtpVb1rLFxoOFJt9-8smLWXuJV7IwA8P3EfFz4,48
|
10
10
|
fabricatio/capabilities/advanced_judge.py,sha256=selB0Gwf1F4gGJlwBiRo6gI4KOUROgh3WnzO3mZFEls,706
|
11
11
|
fabricatio/capabilities/censor.py,sha256=bBT5qy-kp7fh8g4Lz3labSwxwJ60gGd_vrkc6k1cZ1U,4719
|
12
|
-
fabricatio/capabilities/check.py,sha256=
|
13
|
-
fabricatio/capabilities/correct.py,sha256=
|
12
|
+
fabricatio/capabilities/check.py,sha256=q12h9mQyGGjWiJp4r7JgYWeuWzj0fT3DxtL7s4n-0pY,8520
|
13
|
+
fabricatio/capabilities/correct.py,sha256=W_cInqlciNEhyMK0YI53jk4EvW9uAdge90IO9OElUmA,10420
|
14
14
|
fabricatio/capabilities/propose.py,sha256=hkBeSlmcTdfYWT-ph6nlbtHXBozi_JXqXlWcnBy3W78,2007
|
15
15
|
fabricatio/capabilities/rag.py,sha256=8TTJSV2Tz0naXyOQ5c_RQ4h9ZxyOOSE7BvyWxKkQMU0,17722
|
16
|
-
fabricatio/capabilities/rating.py,sha256=
|
16
|
+
fabricatio/capabilities/rating.py,sha256=Wt_H5fA1H4XuZGIMI8pr0cp_6jnXJABlo8lfU_4Fp5A,17645
|
17
17
|
fabricatio/capabilities/review.py,sha256=-EMZe0ADFPT6fPGmra16UPjJC1M3rAs6dPFdTZ88Fgg,5060
|
18
18
|
fabricatio/capabilities/task.py,sha256=JahC61X233UIPsjovxJgc_yqj_BjWZJBCzJZq11M2Xk,4417
|
19
19
|
fabricatio/capabilities/__init__.py,sha256=v1cHRHIJ2gxyqMLNCs6ERVcCakSasZNYzmMI4lqAcls,57
|
20
|
-
fabricatio/config.py,sha256=
|
20
|
+
fabricatio/config.py,sha256=gqhdKxoj4S0EmQKprAEWUARn7yJg-w5UJ7d7GPlyttw,17631
|
21
21
|
fabricatio/core.py,sha256=VQ_JKgUGIy2gZ8xsTBZCdr_IP7wC5aPg0_bsOmjQ588,6458
|
22
22
|
fabricatio/decorators.py,sha256=C0Gi7wcXC-0sWITqsSv3JdBGcgVJOlRvOt0FfO0aUsA,7554
|
23
23
|
fabricatio/fs/curd.py,sha256=p8y0LGKgVDk-CWOlm37E6wg7RK6RCD6denKo-VsW28c,4763
|
24
|
-
fabricatio/fs/readers.py,sha256=
|
24
|
+
fabricatio/fs/readers.py,sha256=M5kojKWsJQMQpE4CBbYvas0JKmPaiaYSfWmiqJx1SP4,1884
|
25
25
|
fabricatio/fs/__init__.py,sha256=PCf0s_9KDjVfNw7AfPoJzGt3jMq4gJOfbcT4pb0D0ZY,588
|
26
26
|
fabricatio/journal.py,sha256=stnEP88aUBA_GmU9gfTF2EZI8FS2OyMLGaMSTgK4QgA,476
|
27
27
|
fabricatio/models/action.py,sha256=Kfa-zojgHQ1vPoC2lQp-thTTp0oySKn7k6I4ea6iYTs,9837
|
28
28
|
fabricatio/models/adv_kwargs_types.py,sha256=dcYMLn6xcnWLZTLTBdtpgUZWi-VBeub721GzHRZFT1g,860
|
29
29
|
fabricatio/models/events.py,sha256=QvlnS8FEELg6KNabcJMeh2GV_y0ZBzKOPphcteKYWYU,4183
|
30
|
-
fabricatio/models/extra/advanced_judge.py,sha256=
|
31
|
-
fabricatio/models/extra/article_base.py,sha256=
|
30
|
+
fabricatio/models/extra/advanced_judge.py,sha256=x2FxicxqpN1eKD7PnL7--wYvxZsWnueAlCyHodRuFrU,1022
|
31
|
+
fabricatio/models/extra/article_base.py,sha256=WWfa8LJVjrs_yJGBCI81KF5VtW62rq_IcdFvBqZ2nq0,20075
|
32
32
|
fabricatio/models/extra/article_essence.py,sha256=xd6j-PDqjhrMjgUmyfk6HqkyMLu-sS9feUo0sZ3QABY,2825
|
33
|
-
fabricatio/models/extra/article_main.py,sha256=
|
33
|
+
fabricatio/models/extra/article_main.py,sha256=main-2eh-dH1SYN_zW2kFajMOrpatAVQVgQ-_7Yvsj4,11669
|
34
34
|
fabricatio/models/extra/article_outline.py,sha256=w7O0SHgC7exbptWVbR62FMHAueMgBpyWKVYMGGl_oj8,1427
|
35
|
-
fabricatio/models/extra/article_proposal.py,sha256=
|
36
|
-
fabricatio/models/extra/patches.py,sha256
|
35
|
+
fabricatio/models/extra/article_proposal.py,sha256=NbyjW-7UiFPtnVD9nte75re4xL2pD4qL29PpNV4Cg_M,1870
|
36
|
+
fabricatio/models/extra/patches.py,sha256=_WNCxtYzzsVfUxI16vu4IqsLahLYRHdbQN9er9tqhC0,997
|
37
37
|
fabricatio/models/extra/problem.py,sha256=RyvHQM8XgMVqDT7BCtXU0SEgODWGvTPBQIoHOURF5Oc,6656
|
38
38
|
fabricatio/models/extra/rule.py,sha256=ogJJYmV5F-CIRG2Dl95plgShskT8jzuh_0KWKHRonbA,2668
|
39
39
|
fabricatio/models/extra/__init__.py,sha256=XlYnS_2B9nhLhtQkjE7rvvfPmAAtXVdNi9bSDAR-Ge8,54
|
40
|
-
fabricatio/models/generic.py,sha256=
|
40
|
+
fabricatio/models/generic.py,sha256=7M1GZpgle7iwrSpzMmTBxpPx7zxCLH13l61h75woa_E,30237
|
41
41
|
fabricatio/models/kwargs_types.py,sha256=sMDA85SoC1AOJ5k6qC8qUiUv0Ne0_5ThU9FZITRNen4,5673
|
42
42
|
fabricatio/models/role.py,sha256=-CRcj5_M3_ciLPzwiNn92grBmwoSLQ-n4koVZiCNTBM,2953
|
43
43
|
fabricatio/models/task.py,sha256=YXvO3upJkTqMQjPgUGfp0bIiSyZzek2f4IagHdMW5Ik,10491
|
44
44
|
fabricatio/models/tool.py,sha256=jQ51g4lwTPfsMF1nbreDJtBczbxIHoXcPuLSOqHliq8,12506
|
45
|
-
fabricatio/models/usages.py,sha256=
|
45
|
+
fabricatio/models/usages.py,sha256=PX13lUCYB9XSM5tKrpYK-ov5jKclWlF9xGmPgUoovLk,32030
|
46
46
|
fabricatio/models/utils.py,sha256=Ac5g-8ic6q_w7dhNuh-iiofpL1sqOACxbjPPTljP2LY,4417
|
47
|
-
fabricatio/parser.py,sha256=
|
47
|
+
fabricatio/parser.py,sha256=qN2godNsArmb90btOMxgqlol57166DyYsV2JlU8DlHs,6532
|
48
48
|
fabricatio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
49
|
-
fabricatio/rust.pyi,sha256=
|
49
|
+
fabricatio/rust.pyi,sha256=5xfla5dCACfKTkHztMc5_iCEmdDZtDH9HPG2YC92L8o,6266
|
50
50
|
fabricatio/rust_instances.py,sha256=Byeo8KHW_dJiXujJq7YPGDLBX5bHNDYbBc4sY3uubVY,313
|
51
51
|
fabricatio/toolboxes/arithmetic.py,sha256=WLqhY-Pikv11Y_0SGajwZx3WhsLNpHKf9drzAqOf_nY,1369
|
52
52
|
fabricatio/toolboxes/fs.py,sha256=l4L1CVxJmjw9Ld2XUpIlWfV0_Fu_2Og6d3E13I-S4aE,736
|
@@ -56,6 +56,6 @@ fabricatio/workflows/articles.py,sha256=ObYTFUqLUk_CzdmmnX6S7APfxcGmPFqnFr9pdjU7
|
|
56
56
|
fabricatio/workflows/rag.py,sha256=-YYp2tlE9Vtfgpg6ROpu6QVO8j8yVSPa6yDzlN3qVxs,520
|
57
57
|
fabricatio/workflows/__init__.py,sha256=5ScFSTA-bvhCesj3U9Mnmi6Law6N1fmh5UKyh58L3u8,51
|
58
58
|
fabricatio/__init__.py,sha256=Rmvq2VgdS2u68vnOi2i5RbeWbAwrJDbk8D8D883PJWE,1022
|
59
|
-
fabricatio/rust.cp312-win_amd64.pyd,sha256=
|
60
|
-
fabricatio-0.2.9.
|
61
|
-
fabricatio-0.2.9.
|
59
|
+
fabricatio/rust.cp312-win_amd64.pyd,sha256=5rWQDW_NkAoojnwNoStr9qIfa-JMcNlhEf1RLYSHM2o,2193920
|
60
|
+
fabricatio-0.2.9.dev4.data/scripts/tdown.exe,sha256=bgxOSFt8NMWHddSQ27fn9f_AyO47PlYNcK1EuUJRYJo,3364864
|
61
|
+
fabricatio-0.2.9.dev4.dist-info/RECORD,,
|
Binary file
|
File without changes
|
File without changes
|