fabricatio 0.2.8.dev3__cp312-cp312-win_amd64.whl → 0.2.9__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/__init__.py +4 -11
- fabricatio/actions/__init__.py +1 -0
- fabricatio/actions/article.py +128 -165
- fabricatio/actions/article_rag.py +62 -46
- fabricatio/actions/output.py +60 -4
- fabricatio/actions/rag.py +2 -1
- fabricatio/actions/rules.py +72 -0
- fabricatio/capabilities/__init__.py +1 -0
- fabricatio/capabilities/censor.py +104 -0
- fabricatio/capabilities/check.py +148 -32
- fabricatio/capabilities/correct.py +162 -100
- fabricatio/capabilities/rag.py +5 -4
- fabricatio/capabilities/rating.py +109 -54
- fabricatio/capabilities/review.py +1 -1
- fabricatio/capabilities/task.py +2 -1
- fabricatio/config.py +14 -6
- fabricatio/fs/readers.py +20 -1
- fabricatio/models/action.py +63 -41
- fabricatio/models/adv_kwargs_types.py +25 -0
- fabricatio/models/extra/__init__.py +1 -0
- fabricatio/models/extra/advanced_judge.py +7 -4
- fabricatio/models/extra/article_base.py +125 -79
- fabricatio/models/extra/article_main.py +101 -19
- fabricatio/models/extra/article_outline.py +2 -3
- fabricatio/models/extra/article_proposal.py +15 -14
- fabricatio/models/extra/patches.py +20 -0
- fabricatio/models/extra/problem.py +64 -23
- fabricatio/models/extra/rule.py +39 -10
- fabricatio/models/generic.py +405 -75
- fabricatio/models/kwargs_types.py +23 -17
- fabricatio/models/task.py +1 -1
- fabricatio/models/tool.py +149 -14
- fabricatio/models/usages.py +55 -56
- fabricatio/parser.py +12 -13
- fabricatio/rust.cp312-win_amd64.pyd +0 -0
- fabricatio/{_rust.pyi → rust.pyi} +42 -4
- fabricatio/{_rust_instances.py → rust_instances.py} +1 -1
- fabricatio/utils.py +5 -5
- fabricatio/workflows/__init__.py +1 -0
- fabricatio/workflows/articles.py +3 -5
- fabricatio-0.2.9.data/scripts/tdown.exe +0 -0
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/METADATA +1 -1
- fabricatio-0.2.9.dist-info/RECORD +61 -0
- fabricatio/_rust.cp312-win_amd64.pyd +0 -0
- fabricatio-0.2.8.dev3.data/scripts/tdown.exe +0 -0
- fabricatio-0.2.8.dev3.dist-info/RECORD +0 -53
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/WHEEL +0 -0
- {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.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)
|
@@ -92,11 +103,56 @@ class RetrieveFromPersistent[T: PersistentAble](Action):
|
|
92
103
|
retrieve_cls: Type[T]
|
93
104
|
"""The class of the object to retrieve."""
|
94
105
|
|
95
|
-
async def _execute(self, /, **
|
96
|
-
logger.info(f"Retrieve `{self.retrieve_cls.__name__}` from
|
106
|
+
async def _execute(self, /, **_) -> Optional[T | List[T]]:
|
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 RetrieveFromLatest[T: PersistentAble](RetrieveFromPersistent[T]):
|
119
|
+
"""Retrieve the object from the latest persistent file in the dir at `load_path`."""
|
120
|
+
|
121
|
+
async def _execute(self, /, **_) -> Optional[T]:
|
122
|
+
logger.info(f"Retrieve latest `{self.retrieve_cls.__name__}` from {self.load_path}")
|
123
|
+
if not (p := Path(self.load_path)).exists():
|
124
|
+
logger.warning(f"Path {self.load_path} does not exist")
|
125
|
+
return None
|
126
|
+
|
127
|
+
if p.is_dir():
|
128
|
+
logger.info(f"Found directory with {len(list(p.glob('*')))} items")
|
129
|
+
return self.retrieve_cls.from_latest_persistent(self.load_path)
|
130
|
+
logger.error(f"Path {self.load_path} is not a directory")
|
131
|
+
return None
|
132
|
+
|
133
|
+
|
134
|
+
class GatherAsList(Action):
|
135
|
+
"""Gather the objects from the context as a list.
|
136
|
+
|
137
|
+
Notes:
|
138
|
+
If both `gather_suffix` and `gather_prefix` are specified, only the objects with the suffix will be gathered.
|
139
|
+
"""
|
140
|
+
|
141
|
+
output_key: str = "gathered"
|
142
|
+
"""Gather the objects from the context as a list."""
|
143
|
+
gather_suffix: Optional[str] = None
|
144
|
+
"""Gather the objects from the context as a list."""
|
145
|
+
gather_prefix: Optional[str] = None
|
146
|
+
"""Gather the objects from the context as a list."""
|
147
|
+
|
148
|
+
async def _execute(self, **cxt) -> List[Any]:
|
149
|
+
if self.gather_suffix is not None:
|
150
|
+
result = [cxt[k] for k in cxt if k.endswith(self.gather_suffix)]
|
151
|
+
logger.debug(f"Gathered {len(result)} items with suffix {self.gather_suffix}")
|
152
|
+
return result
|
153
|
+
if self.gather_prefix is None:
|
154
|
+
logger.error(err := "Either `gather_suffix` or `gather_prefix` must be specified.")
|
155
|
+
raise ValueError(err)
|
156
|
+
result = [cxt[k] for k in cxt if k.startswith(self.gather_prefix)]
|
157
|
+
logger.debug(f"Gathered {len(result)} items with prefix {self.gather_prefix}")
|
158
|
+
return result
|
fabricatio/actions/rag.py
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
from typing import List, Optional
|
4
4
|
|
5
|
+
from questionary import text
|
6
|
+
|
5
7
|
from fabricatio.capabilities.rag import RAG
|
6
8
|
from fabricatio.journal import logger
|
7
9
|
from fabricatio.models.action import Action
|
8
10
|
from fabricatio.models.generic import Vectorizable
|
9
11
|
from fabricatio.models.task import Task
|
10
|
-
from questionary import text
|
11
12
|
|
12
13
|
|
13
14
|
class InjectToDB(Action, RAG):
|
@@ -0,0 +1,72 @@
|
|
1
|
+
"""A module containing the DraftRuleSet action."""
|
2
|
+
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
from fabricatio.capabilities.check import Check
|
6
|
+
from fabricatio.journal import logger
|
7
|
+
from fabricatio.models.action import Action
|
8
|
+
from fabricatio.models.extra.rule import RuleSet
|
9
|
+
from fabricatio.utils import ok
|
10
|
+
|
11
|
+
|
12
|
+
class DraftRuleSet(Action, Check):
|
13
|
+
"""Action to draft a ruleset based on a given requirement description."""
|
14
|
+
|
15
|
+
output_key: str = "drafted_ruleset"
|
16
|
+
"""The key used to store the drafted ruleset in the context dictionary."""
|
17
|
+
|
18
|
+
ruleset_requirement: Optional[str] = None
|
19
|
+
"""The natural language description of the desired ruleset characteristics."""
|
20
|
+
rule_count: int = 0
|
21
|
+
"""The number of rules to generate in the ruleset (0 for no restriction)."""
|
22
|
+
|
23
|
+
async def _execute(
|
24
|
+
self,
|
25
|
+
ruleset_requirement: Optional[str] = None,
|
26
|
+
**_,
|
27
|
+
) -> Optional[RuleSet]:
|
28
|
+
"""Draft a ruleset based on the requirement description.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
ruleset_requirement (str): Natural language description of desired ruleset characteristics
|
32
|
+
rule_count (int): Number of rules to generate (0 for no restriction)
|
33
|
+
**kwargs: Validation parameters for rule generation
|
34
|
+
|
35
|
+
Returns:
|
36
|
+
Optional[RuleSet]: Drafted ruleset object or None if generation fails
|
37
|
+
"""
|
38
|
+
ruleset = await self.draft_ruleset(
|
39
|
+
ruleset_requirement=ok(ruleset_requirement or self.ruleset_requirement, "No ruleset requirement provided"),
|
40
|
+
rule_count=self.rule_count,
|
41
|
+
)
|
42
|
+
if ruleset:
|
43
|
+
logger.info(f"Drafted Ruleset length: {len(ruleset.rules)}\n{ruleset.display()}")
|
44
|
+
else:
|
45
|
+
logger.warning(f"Drafting Rule Failed for:\n{ruleset_requirement}")
|
46
|
+
return ruleset
|
47
|
+
|
48
|
+
|
49
|
+
class GatherRuleset(Action):
|
50
|
+
"""Action to gather a ruleset from a given requirement description."""
|
51
|
+
|
52
|
+
output_key: str = "gathered_ruleset"
|
53
|
+
"""The key used to store the drafted ruleset in the context dictionary."""
|
54
|
+
|
55
|
+
to_gather: List[str]
|
56
|
+
"""the cxt name of RuleSet to gather"""
|
57
|
+
|
58
|
+
async def _execute(self, **cxt) -> RuleSet:
|
59
|
+
logger.info(f"Gathering Ruleset from {self.to_gather}")
|
60
|
+
# Fix for not_found
|
61
|
+
not_found = next((t for t in self.to_gather if t not in cxt), None)
|
62
|
+
if not_found:
|
63
|
+
raise ValueError(
|
64
|
+
f"Not all required keys found in context: {self.to_gather}|`{not_found}` not found in context."
|
65
|
+
)
|
66
|
+
|
67
|
+
# Fix for invalid RuleSet check
|
68
|
+
invalid = next((t for t in self.to_gather if not isinstance(cxt[t], RuleSet)), None)
|
69
|
+
if invalid is not None:
|
70
|
+
raise TypeError(f"Invalid RuleSet instance for key `{invalid}`")
|
71
|
+
|
72
|
+
return RuleSet.gather(*[cxt[t] for t in self.to_gather])
|
@@ -0,0 +1 @@
|
|
1
|
+
"""A module containing some high level capabilities."""
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""Module for censoring objects and strings based on provided rulesets.
|
2
|
+
|
3
|
+
This module includes the Censor class which inherits from both Correct and Check classes.
|
4
|
+
It provides methods to censor objects and strings by first checking them against a ruleset and then correcting them if necessary.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Optional, Unpack
|
8
|
+
|
9
|
+
from fabricatio.capabilities.check import Check
|
10
|
+
from fabricatio.capabilities.correct import Correct
|
11
|
+
from fabricatio.journal import logger
|
12
|
+
from fabricatio.models.extra.problem import Improvement
|
13
|
+
from fabricatio.models.extra.rule import RuleSet
|
14
|
+
from fabricatio.models.generic import ProposedUpdateAble, SketchedAble
|
15
|
+
from fabricatio.models.kwargs_types import ReferencedKwargs
|
16
|
+
from fabricatio.utils import override_kwargs
|
17
|
+
|
18
|
+
|
19
|
+
class Censor(Correct, Check):
|
20
|
+
"""Class to censor objects and strings based on provided rulesets.
|
21
|
+
|
22
|
+
Inherits from both Correct and Check classes.
|
23
|
+
Provides methods to censor objects and strings by first checking them against a ruleset and then correcting them if necessary.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
async def censor_obj[M: SketchedAble](
|
28
|
+
self, obj: M, ruleset: RuleSet, **kwargs: Unpack[ReferencedKwargs[M]]
|
29
|
+
) -> Optional[M]:
|
30
|
+
"""Censors an object based on the provided ruleset.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
obj (M): The object to be censored.
|
34
|
+
ruleset (RuleSet): The ruleset to apply for censoring.
|
35
|
+
**kwargs: Additional keyword arguments to be passed to the check and correct methods.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Optional[M]: The censored object if corrections were made, otherwise None.
|
39
|
+
|
40
|
+
Note:
|
41
|
+
This method first checks the object against the ruleset and then corrects it if necessary.
|
42
|
+
"""
|
43
|
+
imp = await self.check_obj(obj, ruleset, **override_kwargs(kwargs, default=None))
|
44
|
+
if imp is None:
|
45
|
+
return None
|
46
|
+
if not imp:
|
47
|
+
logger.info(f"No improvement found for `{obj.__class__.__name__}`.")
|
48
|
+
return obj
|
49
|
+
logger.info(f'Generated {len(imp)} improvement(s) for `{obj.__class__.__name__}')
|
50
|
+
return await self.correct_obj(obj, Improvement.gather(*imp), **kwargs)
|
51
|
+
|
52
|
+
async def censor_string(
|
53
|
+
self, input_text: str, ruleset: RuleSet, **kwargs: Unpack[ReferencedKwargs[str]]
|
54
|
+
) -> Optional[str]:
|
55
|
+
"""Censors a string based on the provided ruleset.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
input_text (str): The string to be censored.
|
59
|
+
ruleset (RuleSet): The ruleset to apply for censoring.
|
60
|
+
**kwargs: Additional keyword arguments to be passed to the check and correct methods.
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
Optional[str]: The censored string if corrections were made, otherwise None.
|
64
|
+
|
65
|
+
Note:
|
66
|
+
This method first checks the string against the ruleset and then corrects it if necessary.
|
67
|
+
"""
|
68
|
+
imp = await self.check_string(input_text, ruleset, **override_kwargs(kwargs, default=None))
|
69
|
+
if imp is None:
|
70
|
+
logger.warning(f"Censor failed for string:\n{input_text}")
|
71
|
+
return None
|
72
|
+
if not imp:
|
73
|
+
logger.info("No improvement found for string.")
|
74
|
+
return input_text
|
75
|
+
logger.info(f'Generated {len(imp)} improvement(s) for string.')
|
76
|
+
return await self.correct_string(input_text, Improvement.gather(*imp), **kwargs)
|
77
|
+
|
78
|
+
async def censor_obj_inplace[M: ProposedUpdateAble](
|
79
|
+
self, obj: M, ruleset: RuleSet, **kwargs: Unpack[ReferencedKwargs[M]]
|
80
|
+
) -> Optional[M]:
|
81
|
+
"""Censors an object in-place based on the provided ruleset.
|
82
|
+
|
83
|
+
This method modifies the object directly if corrections are needed.
|
84
|
+
|
85
|
+
Args:
|
86
|
+
obj (M): The object to be censored.
|
87
|
+
ruleset (RuleSet): The ruleset to apply for censoring.
|
88
|
+
**kwargs: Additional keyword arguments to be passed to the check and correct methods.
|
89
|
+
|
90
|
+
Returns:
|
91
|
+
Optional[M]: The censored object if corrections were made, otherwise None.
|
92
|
+
|
93
|
+
Note:
|
94
|
+
This method first checks the object against the ruleset and then corrects it in-place if necessary.
|
95
|
+
"""
|
96
|
+
imp = await self.check_obj(obj, ruleset, **override_kwargs(kwargs, default=None))
|
97
|
+
if imp is None:
|
98
|
+
logger.warning(f"Censor failed for `{obj.__class__.__name__}`")
|
99
|
+
return None
|
100
|
+
if not imp:
|
101
|
+
logger.info(f"No improvement found for `{obj.__class__.__name__}`.")
|
102
|
+
return obj
|
103
|
+
logger.info(f'Generated {len(imp)} improvement(s) for `{obj.__class__.__name__}')
|
104
|
+
return await self.correct_obj_inplace(obj, improvement=Improvement.gather(*imp), **kwargs)
|
fabricatio/capabilities/check.py
CHANGED
@@ -1,91 +1,146 @@
|
|
1
1
|
"""A class that provides the capability to check strings and objects against rules and guidelines."""
|
2
|
-
|
2
|
+
|
3
|
+
from asyncio import gather
|
4
|
+
from typing import List, Optional, Unpack
|
3
5
|
|
4
6
|
from fabricatio import TEMPLATE_MANAGER
|
5
7
|
from fabricatio.capabilities.advanced_judge import AdvancedJudge
|
6
8
|
from fabricatio.capabilities.propose import Propose
|
7
9
|
from fabricatio.config import configs
|
10
|
+
from fabricatio.journal import logger
|
11
|
+
from fabricatio.models.extra.patches import RuleSetMetadata
|
8
12
|
from fabricatio.models.extra.problem import Improvement
|
9
13
|
from fabricatio.models.extra.rule import Rule, RuleSet
|
10
14
|
from fabricatio.models.generic import Display, WithBriefing
|
11
15
|
from fabricatio.models.kwargs_types import ValidateKwargs
|
16
|
+
from fabricatio.rust import detect_language
|
12
17
|
from fabricatio.utils import override_kwargs
|
13
18
|
|
14
19
|
|
15
20
|
class Check(AdvancedJudge, Propose):
|
16
|
-
"""Class
|
21
|
+
"""Class for validating strings/objects against predefined rules and guidelines.
|
22
|
+
|
23
|
+
This capability combines rule-based judgment and proposal generation to provide
|
24
|
+
structured validation results with actionable improvement suggestions.
|
25
|
+
"""
|
17
26
|
|
18
27
|
async def draft_ruleset(
|
19
|
-
self, ruleset_requirement: str, **kwargs: Unpack[ValidateKwargs[
|
28
|
+
self, ruleset_requirement: str, rule_count: int = 0, **kwargs: Unpack[ValidateKwargs[Rule]]
|
20
29
|
) -> Optional[RuleSet]:
|
21
|
-
"""Generate
|
30
|
+
"""Generate rule set based on requirement description.
|
22
31
|
|
23
32
|
Args:
|
24
|
-
ruleset_requirement (str):
|
25
|
-
|
33
|
+
ruleset_requirement (str): Natural language description of desired ruleset characteristics
|
34
|
+
rule_count (int): Number of rules to generate (0 for default count)
|
35
|
+
**kwargs: Validation parameters for rule generation
|
26
36
|
|
27
37
|
Returns:
|
28
|
-
Optional[RuleSet]:
|
29
|
-
"""
|
30
|
-
return await self.propose(RuleSet, ruleset_requirement, **kwargs)
|
38
|
+
Optional[RuleSet]: Validated ruleset object or None if generation fails
|
31
39
|
|
32
|
-
|
33
|
-
|
40
|
+
Notes:
|
41
|
+
- Requires valid template configuration in configs.templates
|
42
|
+
- Returns None if any step in rule generation fails
|
43
|
+
- Uses `alist_str` for requirement breakdown and iterative rule proposal
|
44
|
+
"""
|
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]
|
56
|
+
)
|
57
|
+
|
58
|
+
if rule_reqs is None:
|
59
|
+
return None
|
60
|
+
|
61
|
+
rules = await self.propose(
|
62
|
+
Rule,
|
63
|
+
[
|
64
|
+
TEMPLATE_MANAGER.render_template(configs.templates.rule_requirement_template, {"rule_requirement": r})
|
65
|
+
for r in rule_reqs
|
66
|
+
],
|
67
|
+
**kwargs,
|
68
|
+
)
|
69
|
+
if any(r for r in rules if r is None):
|
70
|
+
return None
|
71
|
+
|
72
|
+
ruleset_patch = await self.propose(
|
73
|
+
RuleSetMetadata,
|
74
|
+
f"{ruleset_requirement}\n\nYou should use `{detect_language(ruleset_requirement)}`!",
|
75
|
+
**override_kwargs(kwargs, default=None),
|
76
|
+
)
|
34
77
|
|
35
|
-
|
36
|
-
|
37
|
-
**kwargs: Validation configuration parameters
|
78
|
+
if ruleset_patch is None:
|
79
|
+
return None
|
38
80
|
|
39
|
-
|
40
|
-
Optional[Rule]: Generated rule instance if successful
|
41
|
-
"""
|
42
|
-
return await self.propose(Rule, rule_requirement, **kwargs)
|
81
|
+
return RuleSet(rules=rules, **ruleset_patch.as_kwargs())
|
43
82
|
|
44
|
-
async def
|
83
|
+
async def check_string_against_rule(
|
45
84
|
self,
|
46
85
|
input_text: str,
|
47
86
|
rule: Rule,
|
87
|
+
reference: str = "",
|
48
88
|
**kwargs: Unpack[ValidateKwargs[Improvement]],
|
49
89
|
) -> Optional[Improvement]:
|
50
|
-
"""
|
90
|
+
"""Validate text against specific rule.
|
51
91
|
|
52
92
|
Args:
|
53
|
-
input_text (str): Text content to
|
54
|
-
rule (Rule): Rule instance
|
55
|
-
|
93
|
+
input_text (str): Text content to validate
|
94
|
+
rule (Rule): Rule instance for validation
|
95
|
+
reference (str): Reference text for comparison (default: "")
|
96
|
+
**kwargs: Configuration for validation process
|
56
97
|
|
57
98
|
Returns:
|
58
|
-
Optional[Improvement]: Suggested improvement if
|
99
|
+
Optional[Improvement]: Suggested improvement if violation detected
|
100
|
+
|
101
|
+
Notes:
|
102
|
+
- Uses `evidently_judge` to determine violation presence
|
103
|
+
- Renders template using `check_string_template` for proposal
|
104
|
+
- Proposes Improvement only when violation is confirmed
|
59
105
|
"""
|
60
106
|
if judge := await self.evidently_judge(
|
61
|
-
f"# Content to exam\n{input_text}\n\n# Rule Must to follow\n{rule.display()}\nDoes `Content to exam` provided above violate the `
|
107
|
+
f"# Content to exam\n{input_text}\n\n# Rule Must to follow\n{rule.display()}\nDoes `Content to exam` provided above violate the `{rule.name}` provided above?"
|
108
|
+
f"should I take some measure to fix that violation? true for I do need, false for I don't need.",
|
62
109
|
**override_kwargs(kwargs, default=None),
|
63
110
|
):
|
111
|
+
logger.info(f"Rule `{rule.name}` violated: \n{judge.display()}")
|
64
112
|
return await self.propose(
|
65
113
|
Improvement,
|
66
114
|
TEMPLATE_MANAGER.render_template(
|
67
115
|
configs.templates.check_string_template,
|
68
|
-
{"to_check": input_text, "rule": rule, "judge": judge.display()},
|
116
|
+
{"to_check": input_text, "rule": rule.display(), "judge": judge.display(), "reference": reference},
|
69
117
|
),
|
70
118
|
**kwargs,
|
71
119
|
)
|
72
120
|
return None
|
73
121
|
|
74
|
-
async def
|
122
|
+
async def check_obj_against_rule[M: (Display, WithBriefing)](
|
75
123
|
self,
|
76
124
|
obj: M,
|
77
125
|
rule: Rule,
|
126
|
+
reference: str = "",
|
78
127
|
**kwargs: Unpack[ValidateKwargs[Improvement]],
|
79
128
|
) -> Optional[Improvement]:
|
80
|
-
"""Validate
|
129
|
+
"""Validate object against rule using text representation.
|
81
130
|
|
82
131
|
Args:
|
83
|
-
obj (M): Object implementing Display
|
84
|
-
rule (Rule): Validation rule
|
132
|
+
obj (M): Object implementing Display/WithBriefing interface
|
133
|
+
rule (Rule): Validation rule
|
134
|
+
reference (str): Reference text for comparison (default: "")
|
85
135
|
**kwargs: Validation configuration parameters
|
86
136
|
|
87
137
|
Returns:
|
88
|
-
Optional[Improvement]: Improvement suggestion if issues
|
138
|
+
Optional[Improvement]: Improvement suggestion if issues found
|
139
|
+
|
140
|
+
Notes:
|
141
|
+
- Requires obj to implement display() or briefing property
|
142
|
+
- Raises TypeError for incompatible object types
|
143
|
+
- Converts object to text before string validation
|
89
144
|
"""
|
90
145
|
if isinstance(obj, Display):
|
91
146
|
input_text = obj.display()
|
@@ -94,4 +149,65 @@ class Check(AdvancedJudge, Propose):
|
|
94
149
|
else:
|
95
150
|
raise TypeError("obj must be either Display or WithBriefing")
|
96
151
|
|
97
|
-
return await self.
|
152
|
+
return await self.check_string_against_rule(input_text, rule, reference, **kwargs)
|
153
|
+
|
154
|
+
async def check_string(
|
155
|
+
self,
|
156
|
+
input_text: str,
|
157
|
+
ruleset: RuleSet,
|
158
|
+
reference: str = "",
|
159
|
+
**kwargs: Unpack[ValidateKwargs[Improvement]],
|
160
|
+
) -> Optional[List[Improvement]]:
|
161
|
+
"""Validate text against full ruleset.
|
162
|
+
|
163
|
+
Args:
|
164
|
+
input_text (str): Text content to validate
|
165
|
+
ruleset (RuleSet): Collection of validation rules
|
166
|
+
reference (str): Reference text for comparison
|
167
|
+
**kwargs: Validation configuration parameters
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
Optional[Improvement]: First detected improvement
|
171
|
+
|
172
|
+
Notes:
|
173
|
+
- Checks rules sequentially and returns first violation
|
174
|
+
- Halts validation after first successful improvement proposal
|
175
|
+
- Maintains rule execution order from ruleset.rules list
|
176
|
+
"""
|
177
|
+
imp_seq = await gather(
|
178
|
+
*[self.check_string_against_rule(input_text, rule, reference, **kwargs) for rule in ruleset.rules]
|
179
|
+
)
|
180
|
+
if imp_seq is None:
|
181
|
+
logger.warning(f"Generation failed for string check against `{ruleset.name}`")
|
182
|
+
return None
|
183
|
+
return [imp for imp in imp_seq if imp]
|
184
|
+
|
185
|
+
async def check_obj[M: (Display, WithBriefing)](
|
186
|
+
self,
|
187
|
+
obj: M,
|
188
|
+
ruleset: RuleSet,
|
189
|
+
reference: str = "",
|
190
|
+
**kwargs: Unpack[ValidateKwargs[Improvement]],
|
191
|
+
) -> Optional[List[Improvement]]:
|
192
|
+
"""Validate object against full ruleset.
|
193
|
+
|
194
|
+
Args:
|
195
|
+
obj (M): Object implementing Display/WithBriefing interface
|
196
|
+
ruleset (RuleSet): Collection of validation rules
|
197
|
+
reference (str): Reference text for comparison (default: "")
|
198
|
+
**kwargs: Validation configuration parameters
|
199
|
+
|
200
|
+
Returns:
|
201
|
+
Optional[Improvement]: First detected improvement
|
202
|
+
|
203
|
+
Notes:
|
204
|
+
- Uses check_obj_against_rule for individual rule checks
|
205
|
+
- Maintains same early termination behavior as check_string
|
206
|
+
- Validates object through text conversion mechanism
|
207
|
+
"""
|
208
|
+
imp_seq = await gather(*[self.check_obj_against_rule(obj, rule, reference, **kwargs) for rule in ruleset.rules])
|
209
|
+
|
210
|
+
if imp_seq is None:
|
211
|
+
logger.warning(f"Generation Failed for `{obj.__class__.__name__}` against Ruleset `{ruleset.name}`")
|
212
|
+
return None
|
213
|
+
return [i for i in imp_seq if i]
|