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.
Files changed (48) hide show
  1. fabricatio/__init__.py +4 -11
  2. fabricatio/actions/__init__.py +1 -0
  3. fabricatio/actions/article.py +128 -165
  4. fabricatio/actions/article_rag.py +62 -46
  5. fabricatio/actions/output.py +60 -4
  6. fabricatio/actions/rag.py +2 -1
  7. fabricatio/actions/rules.py +72 -0
  8. fabricatio/capabilities/__init__.py +1 -0
  9. fabricatio/capabilities/censor.py +104 -0
  10. fabricatio/capabilities/check.py +148 -32
  11. fabricatio/capabilities/correct.py +162 -100
  12. fabricatio/capabilities/rag.py +5 -4
  13. fabricatio/capabilities/rating.py +109 -54
  14. fabricatio/capabilities/review.py +1 -1
  15. fabricatio/capabilities/task.py +2 -1
  16. fabricatio/config.py +14 -6
  17. fabricatio/fs/readers.py +20 -1
  18. fabricatio/models/action.py +63 -41
  19. fabricatio/models/adv_kwargs_types.py +25 -0
  20. fabricatio/models/extra/__init__.py +1 -0
  21. fabricatio/models/extra/advanced_judge.py +7 -4
  22. fabricatio/models/extra/article_base.py +125 -79
  23. fabricatio/models/extra/article_main.py +101 -19
  24. fabricatio/models/extra/article_outline.py +2 -3
  25. fabricatio/models/extra/article_proposal.py +15 -14
  26. fabricatio/models/extra/patches.py +20 -0
  27. fabricatio/models/extra/problem.py +64 -23
  28. fabricatio/models/extra/rule.py +39 -10
  29. fabricatio/models/generic.py +405 -75
  30. fabricatio/models/kwargs_types.py +23 -17
  31. fabricatio/models/task.py +1 -1
  32. fabricatio/models/tool.py +149 -14
  33. fabricatio/models/usages.py +55 -56
  34. fabricatio/parser.py +12 -13
  35. fabricatio/rust.cp312-win_amd64.pyd +0 -0
  36. fabricatio/{_rust.pyi → rust.pyi} +42 -4
  37. fabricatio/{_rust_instances.py → rust_instances.py} +1 -1
  38. fabricatio/utils.py +5 -5
  39. fabricatio/workflows/__init__.py +1 -0
  40. fabricatio/workflows/articles.py +3 -5
  41. fabricatio-0.2.9.data/scripts/tdown.exe +0 -0
  42. {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/METADATA +1 -1
  43. fabricatio-0.2.9.dist-info/RECORD +61 -0
  44. fabricatio/_rust.cp312-win_amd64.pyd +0 -0
  45. fabricatio-0.2.8.dev3.data/scripts/tdown.exe +0 -0
  46. fabricatio-0.2.8.dev3.dist-info/RECORD +0 -53
  47. {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/WHEEL +0 -0
  48. {fabricatio-0.2.8.dev3.dist-info → fabricatio-0.2.9.dist-info}/licenses/LICENSE +0 -0
@@ -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, /, **__) -> Optional[T | List[T]]:
96
- logger.info(f"Retrieve `{self.retrieve_cls.__name__}` from persistent file: {self.load_path}")
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)
@@ -1,91 +1,146 @@
1
1
  """A class that provides the capability to check strings and objects against rules and guidelines."""
2
- from typing import Optional, Unpack
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 that provides the capability to validate strings/objects against predefined rules and guidelines."""
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[RuleSet]]
28
+ self, ruleset_requirement: str, rule_count: int = 0, **kwargs: Unpack[ValidateKwargs[Rule]]
20
29
  ) -> Optional[RuleSet]:
21
- """Generate a rule set based on specified requirements.
30
+ """Generate rule set based on requirement description.
22
31
 
23
32
  Args:
24
- ruleset_requirement (str): Description of desired rule set characteristics
25
- **kwargs: Validation configuration parameters
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]: Generated rule set if successful
29
- """
30
- return await self.propose(RuleSet, ruleset_requirement, **kwargs)
38
+ Optional[RuleSet]: Validated ruleset object or None if generation fails
31
39
 
32
- async def draft_rule(self, rule_requirement: str, **kwargs: Unpack[ValidateKwargs[Rule]]) -> Optional[Rule]:
33
- """Create a specific rule based on given specifications.
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
- Args:
36
- rule_requirement (str): Detailed rule description requirements
37
- **kwargs: Validation configuration parameters
78
+ if ruleset_patch is None:
79
+ return None
38
80
 
39
- Returns:
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 check_string(
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
- """Evaluate text against a specific rule.
90
+ """Validate text against specific rule.
51
91
 
52
92
  Args:
53
- input_text (str): Text content to be evaluated
54
- rule (Rule): Rule instance used for validation
55
- **kwargs: Validation configuration parameters
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 violations found, else None
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 `Rule Must to follow` provided above?",
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 check_obj[M: (Display, WithBriefing)](
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 an object against specified rule.
129
+ """Validate object against rule using text representation.
81
130
 
82
131
  Args:
83
- obj (M): Object implementing Display or WithBriefing interface
84
- rule (Rule): Validation rule to apply
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 detected
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.check_string(input_text, rule, **kwargs)
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]