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,160 +1,222 @@
1
- """Correct capability module providing advanced review and validation functionality.
1
+ """A module containing the Correct capability for reviewing, validating, and improving objects."""
2
2
 
3
- This module implements the Correct capability, which extends the Review functionality
4
- to provide mechanisms for reviewing, validating, and correcting various objects and tasks
5
- based on predefined criteria and templates.
6
- """
3
+ from asyncio import gather
4
+ from typing import Optional, Type, Unpack, cast
7
5
 
8
- from typing import Optional, Unpack, cast
9
-
10
- from fabricatio._rust_instances import TEMPLATE_MANAGER
11
- from fabricatio.capabilities.review import Review
6
+ from fabricatio.capabilities.propose import Propose
7
+ from fabricatio.capabilities.rating import Rating
12
8
  from fabricatio.config import configs
13
- from fabricatio.models.extra.problem import Improvement
14
- from fabricatio.models.generic import CensoredAble, Display, ProposedAble, ProposedUpdateAble, WithBriefing
15
- from fabricatio.models.kwargs_types import CensoredCorrectKwargs, CorrectKwargs, ReviewKwargs
16
- from fabricatio.models.task import Task
17
- from questionary import confirm, text
18
- from rich import print as rprint
9
+ from fabricatio.journal import logger
10
+ from fabricatio.models.adv_kwargs_types import CorrectKwargs
11
+ from fabricatio.models.extra.problem import Improvement, ProblemSolutions
12
+ from fabricatio.models.generic import ProposedUpdateAble, SketchedAble
13
+ from fabricatio.models.kwargs_types import (
14
+ BestKwargs,
15
+ ValidateKwargs,
16
+ )
17
+ from fabricatio.rust_instances import TEMPLATE_MANAGER
18
+ from fabricatio.utils import fallback_kwargs, ok, override_kwargs
19
+
20
+
21
+ class Correct(Rating, Propose):
22
+ """A class that provides the capability to correct objects."""
23
+
24
+ async def decide_solution(
25
+ self, problem_solutions: ProblemSolutions, **kwargs: Unpack[BestKwargs]
26
+ ) -> ProblemSolutions:
27
+ """Decide the best solution from a list of problem solutions.
28
+
29
+ Args:
30
+ problem_solutions (ProblemSolutions): The problem solutions to evaluate.
31
+ **kwargs (Unpack[BestKwargs]): Additional keyword arguments for the decision process.
19
32
 
33
+ Returns:
34
+ ProblemSolutions: The problem solutions with the best solution selected.
35
+ """
36
+ if (leng := len(problem_solutions.solutions)) == 0:
37
+ logger.error(f"No solutions found in ProblemSolutions, Skip: `{problem_solutions.problem.name}`")
38
+ if leng > 1:
39
+ logger.info(f"{leng} solutions found in Problem `{problem_solutions.problem.name}`, select the best.")
40
+ problem_solutions.solutions = await self.best(problem_solutions.solutions, **kwargs)
41
+ return problem_solutions
20
42
 
21
- class Correct(Review):
22
- """Correct capability for reviewing, validating, and improving objects.
43
+ async def decide_improvement(self, improvement: Improvement, **kwargs: Unpack[BestKwargs]) -> Improvement:
44
+ """Decide the best solution for each problem solution in an improvement.
23
45
 
24
- This class enhances the Review capability with specialized functionality for
25
- correcting and improving objects based on review feedback. It can process
26
- various inputs including tasks, strings, and generic objects that implement
27
- the required interfaces, applying corrections based on templated review processes.
28
- """
46
+ Args:
47
+ improvement (Improvement): The improvement containing problem solutions to evaluate.
48
+ **kwargs (Unpack[BestKwargs]): Additional keyword arguments for the decision process.
29
49
 
30
- async def correct_obj[M: ProposedAble](
50
+ Returns:
51
+ Improvement: The improvement with the best solutions selected for each problem solution.
52
+ """
53
+ if leng := len(improvement.problem_solutions):
54
+ logger.debug(f"{leng} problem_solutions found in Improvement, decide solution for each of them.")
55
+ await gather(
56
+ *[
57
+ self.decide_solution(
58
+ ps,
59
+ **fallback_kwargs(
60
+ kwargs, topic=f"which solution is better to deal this problem {ps.problem.description}\n\n"
61
+ ),
62
+ )
63
+ for ps in improvement.problem_solutions
64
+ ],
65
+ )
66
+ if any(not (violated := ps).decided() for ps in improvement.problem_solutions):
67
+ logger.error(f"Some problem_solutions are not decided: {violated}")
68
+ else:
69
+ logger.success(f"All problem_solutions are decided '{improvement.focused_on}'")
70
+ else:
71
+ logger.error(f"No problem_solutions found in Improvement, Skip: {improvement}")
72
+ return improvement
73
+
74
+ async def fix_troubled_obj[M: SketchedAble](
31
75
  self,
32
76
  obj: M,
77
+ problem_solutions: ProblemSolutions,
33
78
  reference: str = "",
34
- supervisor_check: bool = True,
35
- **kwargs: Unpack[ReviewKwargs[Improvement]],
79
+ **kwargs: Unpack[ValidateKwargs[M]],
36
80
  ) -> Optional[M]:
37
- """Review and correct an object based on defined criteria and templates.
38
-
39
- This method first conducts a review of the given object, then uses the review results
40
- to generate a corrected version of the object using appropriate templates.
81
+ """Fix a troubled object based on problem solutions.
41
82
 
42
83
  Args:
43
- obj (M): The object to be reviewed and corrected. Must implement ProposedAble.
84
+ obj (M): The object to be fixed.
85
+ problem_solutions (ProblemSolutions): The problem solutions to apply.
44
86
  reference (str): A reference or contextual information for the object.
45
- supervisor_check (bool, optional): Whether to perform a supervisor check on the review results. Defaults to True.
46
- **kwargs: Review configuration parameters including criteria and review options.
87
+ **kwargs (Unpack[ValidateKwargs[M]]): Additional keyword arguments for the validation process.
47
88
 
48
89
  Returns:
49
- Optional[M]: A corrected version of the input object, or None if correction fails.
50
-
51
- Raises:
52
- TypeError: If the provided object doesn't implement Display or WithBriefing interfaces.
90
+ Optional[M]: The fixed object, or None if fixing fails.
53
91
  """
54
- if not isinstance(obj, (Display, WithBriefing)):
55
- raise TypeError(f"Expected Display or WithBriefing, got {type(obj)}")
56
-
57
- review_res = await self.review_obj(obj, **kwargs)
58
- if supervisor_check:
59
- await review_res.supervisor_check()
60
- if "default" in kwargs:
61
- cast("ReviewKwargs[None]", kwargs)["default"] = None
62
92
  return await self.propose(
63
- obj.__class__,
93
+ cast("Type[M]", obj.__class__),
64
94
  TEMPLATE_MANAGER.render_template(
65
- configs.templates.correct_template,
95
+ configs.templates.fix_troubled_obj_template,
66
96
  {
67
- "content": f"{(reference + '\n\nAbove is referencing material') if reference else ''}{obj.display() if isinstance(obj, Display) else obj.briefing}",
68
- "review": review_res.display(),
97
+ "problem": problem_solutions.problem.display(),
98
+ "solution": ok(
99
+ problem_solutions.final_solution(),
100
+ f"{len(problem_solutions.solutions)} solution Found for `{problem_solutions.problem.name}`.",
101
+ ).display(),
102
+ "reference": reference,
69
103
  },
70
104
  ),
71
105
  **kwargs,
72
106
  )
73
107
 
74
- async def correct_string(
75
- self, input_text: str, supervisor_check: bool = True, **kwargs: Unpack[ReviewKwargs[Improvement]]
108
+ async def fix_troubled_string(
109
+ self,
110
+ input_text: str,
111
+ problem_solutions: ProblemSolutions,
112
+ reference: str = "",
113
+ **kwargs: Unpack[ValidateKwargs[str]],
76
114
  ) -> Optional[str]:
77
- """Review and correct a string based on defined criteria and templates.
78
-
79
- This method applies the review process to the input text and generates
80
- a corrected version based on the review results.
115
+ """Fix a troubled string based on problem solutions.
81
116
 
82
117
  Args:
83
- input_text (str): The text content to be reviewed and corrected.
84
- supervisor_check (bool, optional): Whether to perform a supervisor check on the review results. Defaults to True.
85
- **kwargs: Review configuration parameters including criteria and review options.
118
+ input_text (str): The string to be fixed.
119
+ problem_solutions (ProblemSolutions): The problem solutions to apply.
120
+ reference (str): A reference or contextual information for the string.
121
+ **kwargs (Unpack[ValidateKwargs[str]]): Additional keyword arguments for the validation process.
86
122
 
87
123
  Returns:
88
- Optional[str]: The corrected text content, or None if correction fails.
124
+ Optional[str]: The fixed string, or None if fixing fails.
89
125
  """
90
- review_res = await self.review_string(input_text, **kwargs)
91
- if supervisor_check:
92
- await review_res.supervisor_check()
93
-
94
- if "default" in kwargs:
95
- cast("ReviewKwargs[None]", kwargs)["default"] = None
96
126
  return await self.ageneric_string(
97
127
  TEMPLATE_MANAGER.render_template(
98
- configs.templates.correct_template, {"content": input_text, "review": review_res.display()}
128
+ configs.templates.fix_troubled_string_template,
129
+ {
130
+ "problem": problem_solutions.problem.display(),
131
+ "solution": ok(
132
+ problem_solutions.final_solution(),
133
+ f"No solution found for problem: {problem_solutions.problem}",
134
+ ).display(),
135
+ "reference": reference,
136
+ "string_to_fix": input_text,
137
+ },
99
138
  ),
100
139
  **kwargs,
101
140
  )
102
141
 
103
- async def correct_task[T](
104
- self, task: Task[T], **kwargs: Unpack[CorrectKwargs[Improvement]]
105
- ) -> Optional[Task[T]]:
106
- """Review and correct a task object based on defined criteria.
142
+ async def correct_obj[M: SketchedAble](
143
+ self,
144
+ obj: M,
145
+ improvement: Improvement,
146
+ reference: str = "",
147
+ **kwargs: Unpack[ValidateKwargs[M]],
148
+ ) -> Optional[M]:
149
+ """Review and correct an object based on defined criteria and templates.
107
150
 
108
- This is a specialized version of correct_obj specifically for Task objects,
109
- applying the same review and correction process to task definitions.
151
+ This method first conducts a review of the given object, then uses the review results
152
+ to generate a corrected version of the object using appropriate templates.
110
153
 
111
154
  Args:
112
- task (Task[T]): The task to be reviewed and corrected.
113
- **kwargs: Review configuration parameters including criteria and review options.
155
+ obj (M): The object to be reviewed and corrected. Must implement ProposedAble.
156
+ improvement (Improvement): The improvement object containing the review results.
157
+ reference (str): A reference or contextual information for the object.
158
+ **kwargs (Unpack[ValidateKwargs[M]]): Review configuration parameters including criteria and review options.
114
159
 
115
160
  Returns:
116
- Optional[Task[T]]: The corrected task, or None if correction fails.
161
+ Optional[M]: A corrected version of the input object, or None if correction fails.
162
+
163
+ Raises:
164
+ TypeError: If the provided object doesn't implement Display or WithBriefing interfaces.
117
165
  """
118
- return await self.correct_obj(task, **kwargs)
166
+ if not improvement.decided():
167
+ logger.info(f"Improvement {improvement.focused_on} not decided, start deciding...")
168
+ improvement = await self.decide_improvement(improvement, **override_kwargs(kwargs, default=None))
169
+
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}`")
173
+ fixed_obj = await self.fix_troubled_obj(obj, ps, reference, **kwargs)
174
+ if fixed_obj is None:
175
+ logger.error(f"[{idx + 1}/{total}] Failed to fix problem `{ps.problem.name}`")
176
+ return None
177
+ obj = fixed_obj
178
+ return obj
119
179
 
120
- async def censor_obj[M: CensoredAble](
121
- self, obj: M, **kwargs: Unpack[CensoredCorrectKwargs[Improvement]]
122
- ) -> M:
123
- """Censor and correct an object based on defined criteria and templates.
180
+ async def correct_string(
181
+ self, input_text: str, improvement: Improvement, reference: str = "", **kwargs: Unpack[ValidateKwargs[str]]
182
+ ) -> Optional[str]:
183
+ """Review and correct a string based on defined criteria and templates.
184
+
185
+ This method first conducts a review of the given string, then uses the review results
186
+ to generate a corrected version of the string using appropriate templates.
124
187
 
125
188
  Args:
126
- obj (M): The object to be reviewed and corrected.
127
- **kwargs (Unpack[CensoredCorrectKwargs]): Additional keyword
189
+ input_text (str): The string to be reviewed and corrected.
190
+ improvement (Improvement): The improvement object containing the review results.
191
+ reference (str): A reference or contextual information for the string.
192
+ **kwargs (Unpack[ValidateKwargs[str]]): Review configuration parameters including criteria and review options.
128
193
 
129
194
  Returns:
130
- M: The censored and corrected object.
195
+ Optional[str]: A corrected version of the input string, or None if correction fails.
131
196
  """
132
- last_modified_obj = obj
133
- modified_obj = None
134
- rprint(obj.finalized_dump())
135
- while await confirm("Begin to correct obj above with human censorship?").ask_async():
136
- while (topic := await text("What is the topic of the obj reviewing?").ask_async()) is not None and topic:
137
- ...
138
- if (
139
- modified_obj := await self.correct_obj(
140
- last_modified_obj,
141
- topic=topic,
142
- **kwargs,
197
+ if not improvement.decided():
198
+ logger.info(f"Improvement {improvement.focused_on} not decided, start deciding...")
199
+
200
+ improvement = await self.decide_improvement(improvement, **override_kwargs(kwargs, default=None))
201
+
202
+ for ps in improvement.problem_solutions:
203
+ fixed_string = await self.fix_troubled_string(input_text, ps, reference, **kwargs)
204
+ if fixed_string is None:
205
+ logger.error(
206
+ f"Failed to fix troubling string when deal with problem: {ps.problem}",
143
207
  )
144
- ) is None:
145
- break
146
- last_modified_obj = modified_obj
147
- rprint(last_modified_obj.finalized_dump())
148
- return modified_obj or last_modified_obj
208
+ return None
209
+ input_text = fixed_string
210
+ return input_text
149
211
 
150
212
  async def correct_obj_inplace[M: ProposedUpdateAble](
151
- self, obj: M, **kwargs: Unpack[CorrectKwargs[Improvement]]
213
+ self, obj: M, **kwargs: Unpack[CorrectKwargs[M]]
152
214
  ) -> Optional[M]:
153
215
  """Correct an object in place based on defined criteria and templates.
154
216
 
155
217
  Args:
156
218
  obj (M): The object to be corrected.
157
- **kwargs (Unpack[CensoredCorrectKwargs]): Additional keyword arguments for the correction process.
219
+ **kwargs (Unpack[CorrectKwargs[M]]): Additional keyword arguments for the correction process.
158
220
 
159
221
  Returns:
160
222
  Optional[M]: The corrected object, or None if correction fails.
@@ -3,14 +3,16 @@
3
3
  try:
4
4
  from pymilvus import MilvusClient
5
5
  except ImportError as e:
6
- raise RuntimeError("pymilvus is not installed. Have you installed `fabricatio[rag]` instead of `fabricatio`") from e
6
+ raise RuntimeError("pymilvus is not installed. Have you installed `fabricatio[rag]` instead of `fabricatio`?") from e
7
7
  from functools import lru_cache
8
8
  from operator import itemgetter
9
9
  from os import PathLike
10
10
  from pathlib import Path
11
11
  from typing import Any, Callable, Dict, List, Optional, Self, Union, Unpack, cast, overload
12
12
 
13
- from fabricatio._rust_instances import TEMPLATE_MANAGER
13
+ from more_itertools.recipes import flatten, unique
14
+ from pydantic import Field, PrivateAttr
15
+
14
16
  from fabricatio.config import configs
15
17
  from fabricatio.journal import logger
16
18
  from fabricatio.models.kwargs_types import (
@@ -23,9 +25,8 @@ from fabricatio.models.kwargs_types import (
23
25
  )
24
26
  from fabricatio.models.usages import EmbeddingUsage
25
27
  from fabricatio.models.utils import MilvusData
28
+ from fabricatio.rust_instances import TEMPLATE_MANAGER
26
29
  from fabricatio.utils import ok
27
- from more_itertools.recipes import flatten, unique
28
- from pydantic import Field, PrivateAttr
29
30
 
30
31
 
31
32
  @lru_cache(maxsize=None)
@@ -4,18 +4,20 @@ from itertools import permutations
4
4
  from random import sample
5
5
  from typing import Dict, List, Optional, Set, Tuple, Union, Unpack, overload
6
6
 
7
- from fabricatio._rust_instances import TEMPLATE_MANAGER
7
+ from more_itertools import flatten, windowed
8
+ from pydantic import Field, NonNegativeInt, PositiveInt, create_model
9
+
10
+ from fabricatio.capabilities.propose import Propose
8
11
  from fabricatio.config import configs
9
12
  from fabricatio.journal import logger
10
- from fabricatio.models.kwargs_types import ValidateKwargs
11
- from fabricatio.models.usages import LLMUsage
13
+ from fabricatio.models.generic import Display, ProposedAble
14
+ from fabricatio.models.kwargs_types import CompositeScoreKwargs, ValidateKwargs
12
15
  from fabricatio.parser import JsonCapture
13
- from fabricatio.utils import override_kwargs
14
- from more_itertools import flatten, windowed
15
- from pydantic import NonNegativeInt, PositiveInt
16
+ from fabricatio.rust_instances import TEMPLATE_MANAGER
17
+ from fabricatio.utils import fallback_kwargs, ok, override_kwargs
16
18
 
17
19
 
18
- class Rating(LLMUsage):
20
+ class Rating(Propose):
19
21
  """A class that provides functionality to rate tasks based on a rating manual and score range.
20
22
 
21
23
  References:
@@ -28,7 +30,7 @@ class Rating(LLMUsage):
28
30
  rating_manual: Dict[str, str],
29
31
  score_range: Tuple[float, float],
30
32
  **kwargs: Unpack[ValidateKwargs[Dict[str, float]]],
31
- ) -> Optional[Dict[str, float] | List[Dict[str, float]]]:
33
+ ) -> Dict[str, float] | List[Dict[str, float]] | List[Optional[Dict[str, float]]] | None:
32
34
  """Rate a given string based on a rating manual and score range.
33
35
 
34
36
  Args:
@@ -40,45 +42,49 @@ class Rating(LLMUsage):
40
42
  Returns:
41
43
  Dict[str, float]: A dictionary with the ratings for each dimension.
42
44
  """
43
-
44
- def _validator(response: str) -> Dict[str, float] | None:
45
- if (
46
- (json_data := JsonCapture.validate_with(response, dict, str))
47
- and json_data.keys() == rating_manual.keys()
48
- and all(score_range[0] <= v <= score_range[1] for v in json_data.values())
49
- ):
50
- return json_data
51
- return None
52
-
53
- logger.info(f"Rating for {to_rate}")
54
- return await self.aask_validate(
55
- question=(
56
- TEMPLATE_MANAGER.render_template(
57
- configs.templates.rate_fine_grind_template,
58
- {
59
- "to_rate": to_rate,
60
- "min_score": score_range[0],
61
- "max_score": score_range[1],
62
- "rating_manual": rating_manual,
63
- },
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
+ ),
64
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},
65
71
  )
66
72
  if isinstance(to_rate, str)
67
73
  else [
68
74
  TEMPLATE_MANAGER.render_template(
69
75
  configs.templates.rate_fine_grind_template,
70
- {
71
- "to_rate": item,
72
- "min_score": score_range[0],
73
- "max_score": score_range[1],
74
- "rating_manual": rating_manual,
75
- },
76
+ {"to_rate": t, "min_score": min_score, "max_score": max_score},
76
77
  )
77
- for item in to_rate
78
+ for t in to_rate
78
79
  ],
79
- validator=_validator,
80
- **kwargs,
80
+ **override_kwargs(kwargs, default=None),
81
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()
82
88
 
83
89
  @overload
84
90
  async def rate(
@@ -86,6 +92,7 @@ class Rating(LLMUsage):
86
92
  to_rate: str,
87
93
  topic: str,
88
94
  criteria: Set[str],
95
+ manual: Optional[Dict[str, str]] = None,
89
96
  score_range: Tuple[float, float] = (0.0, 1.0),
90
97
  **kwargs: Unpack[ValidateKwargs],
91
98
  ) -> Dict[str, float]: ...
@@ -96,6 +103,7 @@ class Rating(LLMUsage):
96
103
  to_rate: List[str],
97
104
  topic: str,
98
105
  criteria: Set[str],
106
+ manual: Optional[Dict[str, str]] = None,
99
107
  score_range: Tuple[float, float] = (0.0, 1.0),
100
108
  **kwargs: Unpack[ValidateKwargs],
101
109
  ) -> List[Dict[str, float]]: ...
@@ -105,15 +113,17 @@ class Rating(LLMUsage):
105
113
  to_rate: Union[str, List[str]],
106
114
  topic: str,
107
115
  criteria: Set[str],
116
+ manual: Optional[Dict[str, str]] = None,
108
117
  score_range: Tuple[float, float] = (0.0, 1.0),
109
118
  **kwargs: Unpack[ValidateKwargs],
110
- ) -> Optional[Dict[str, float] | List[Dict[str, float]]]:
119
+ ) -> Dict[str, float] | List[Dict[str, float]] | List[Optional[Dict[str, float]]] | None:
111
120
  """Rate a given string or a sequence of strings based on a topic, criteria, and score range.
112
121
 
113
122
  Args:
114
123
  to_rate (Union[str, List[str]]): The string or sequence of strings to be rated.
115
124
  topic (str): The topic related to the task.
116
125
  criteria (Set[str]): A set of criteria for rating.
126
+ manual (Optional[Dict[str, str]]): A dictionary containing the rating criteria. If not provided, then this method will draft the criteria automatically.
117
127
  score_range (Tuple[float, float], optional): A tuple representing the valid score range. Defaults to (0.0, 1.0).
118
128
  **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
119
129
 
@@ -121,9 +131,13 @@ class Rating(LLMUsage):
121
131
  Union[Dict[str, float], List[Dict[str, float]]]: A dictionary with the ratings for each criterion if a single string is provided,
122
132
  or a list of dictionaries with the ratings for each criterion if a sequence of strings is provided.
123
133
  """
124
- manual = await self.draft_rating_manual(topic, criteria, **kwargs) or dict(zip(criteria, criteria, strict=True))
134
+ manual = (
135
+ manual
136
+ or await self.draft_rating_manual(topic, criteria, **override_kwargs(kwargs, default=None))
137
+ or dict(zip(criteria, criteria, strict=True))
138
+ )
125
139
 
126
- 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={}))
127
141
 
128
142
  async def draft_rating_manual(
129
143
  self, topic: str, criteria: Optional[Set[str]] = None, **kwargs: Unpack[ValidateKwargs[Dict[str, str]]]
@@ -160,7 +174,7 @@ class Rating(LLMUsage):
160
174
  configs.templates.draft_rating_manual_template,
161
175
  {
162
176
  "topic": topic,
163
- "criteria": criteria,
177
+ "criteria": list(criteria),
164
178
  },
165
179
  )
166
180
  ),
@@ -234,7 +248,7 @@ class Rating(LLMUsage):
234
248
 
235
249
  # extract reasons from the comparison of ordered pairs of extracted from examples
236
250
  reasons = flatten(
237
- await self.aask_validate(
251
+ await self.aask_validate( # pyright: ignore [reportArgumentType]
238
252
  question=[
239
253
  TEMPLATE_MANAGER.render_template(
240
254
  configs.templates.extract_reasons_from_examples_template,
@@ -309,9 +323,11 @@ class Rating(LLMUsage):
309
323
  validator=lambda resp: JsonCapture.validate_with(resp, target_type=float),
310
324
  **kwargs,
311
325
  )
312
- weights = [1]
326
+ if not all(relative_weights):
327
+ raise ValueError(f"found illegal weight: {relative_weights}")
328
+ weights = [1.0]
313
329
  for rw in relative_weights:
314
- weights.append(weights[-1] * rw)
330
+ weights.append(weights[-1] * rw) # pyright: ignore [reportOperatorIssue]
315
331
  total = sum(weights)
316
332
  return dict(zip(criteria_seq, [w / total for w in weights], strict=True))
317
333
 
@@ -319,27 +335,66 @@ class Rating(LLMUsage):
319
335
  self,
320
336
  topic: str,
321
337
  to_rate: List[str],
322
- reasons_count: PositiveInt = 2,
323
- criteria_count: PositiveInt = 5,
324
- **kwargs: Unpack[ValidateKwargs],
338
+ criteria: Optional[Set[str]] = None,
339
+ weights: Optional[Dict[str, float]] = None,
340
+ manual: Optional[Dict[str, str]] = None,
341
+ **kwargs: Unpack[ValidateKwargs[List[Dict[str, float]]]],
325
342
  ) -> List[float]:
326
343
  """Calculates the composite scores for a list of items based on a given topic and criteria.
327
344
 
328
345
  Args:
329
346
  topic (str): The topic for the rating.
330
347
  to_rate (List[str]): A list of strings to be rated.
331
- reasons_count (PositiveInt, optional): The number of reasons to extract from each pair of examples. Defaults to 2.
332
- criteria_count (PositiveInt, optional): The number of criteria to draft. Defaults to 5.
348
+ criteria (Optional[Set[str]]): A set of criteria for the rating. Defaults to None.
349
+ weights (Optional[Dict[str, float]]): A dictionary of rating weights for each criterion. Defaults to None.
350
+ manual (Optional[Dict[str, str]]): A dictionary of manual ratings for each item. Defaults to None.
333
351
  **kwargs (Unpack[ValidateKwargs]): Additional keyword arguments for the LLM usage.
334
352
 
335
353
  Returns:
336
354
  List[float]: A list of composite scores for the items.
337
355
  """
338
- criteria = await self.draft_rating_criteria_from_examples(
339
- topic, to_rate, reasons_count, criteria_count, **kwargs
356
+ criteria = ok(
357
+ criteria
358
+ or await self.draft_rating_criteria_from_examples(topic, to_rate, **override_kwargs(kwargs, default=None))
359
+ )
360
+ weights = ok(
361
+ weights or await self.drafting_rating_weights_klee(topic, criteria, **override_kwargs(kwargs, default=None))
340
362
  )
341
- weights = await self.drafting_rating_weights_klee(topic, criteria, **kwargs)
342
363
  logger.info(f"Criteria: {criteria}\nWeights: {weights}")
343
- ratings_seq = await self.rate(to_rate, topic, criteria, **kwargs)
364
+ ratings_seq = await self.rate(to_rate, topic, criteria, manual, **kwargs)
344
365
 
345
366
  return [sum(ratings[c] * weights[c] for c in criteria) for ratings in ratings_seq]
367
+
368
+ @overload
369
+ async def best(self, candidates: List[str], k: int = 1, **kwargs: Unpack[CompositeScoreKwargs]) -> List[str]: ...
370
+ @overload
371
+ async def best[T: Display](
372
+ self, candidates: List[T], k: int = 1, **kwargs: Unpack[CompositeScoreKwargs]
373
+ ) -> List[T]: ...
374
+
375
+ async def best[T: Display](
376
+ self, candidates: List[str] | List[T], k: int = 1, **kwargs: Unpack[CompositeScoreKwargs]
377
+ ) -> Optional[List[str] | List[T]]:
378
+ """Choose the best candidates from the list of candidates based on the composite score.
379
+
380
+ Args:
381
+ k (int): The number of best candidates to choose.
382
+ candidates (List[str]): A list of candidates to choose from.
383
+ **kwargs (CompositeScoreKwargs): Additional keyword arguments for the composite score calculation.
384
+
385
+ Returns:
386
+ List[str]: The best candidates.
387
+ """
388
+ if (leng := len(candidates)) == 0:
389
+ logger.warning(f"No candidates, got {leng}, return None.")
390
+ return None
391
+
392
+ if leng == 1:
393
+ logger.warning(f"Only one candidate, got {leng}, return it.")
394
+ return candidates
395
+ logger.info(f"Choose best {k} from {leng} candidates.")
396
+
397
+ rating_seq = await self.composite_score(
398
+ to_rate=[c.display() if isinstance(c, Display) else c for c in candidates], **kwargs
399
+ )
400
+ return [a[0] for a in sorted(zip(candidates, rating_seq, strict=True), key=lambda x: x[1], reverse=True)[:k]] # pyright: ignore [reportReturnType]
@@ -2,7 +2,6 @@
2
2
 
3
3
  from typing import Dict, Optional, Set, Unpack
4
4
 
5
- from fabricatio._rust_instances import TEMPLATE_MANAGER
6
5
  from fabricatio.capabilities.propose import Propose
7
6
  from fabricatio.capabilities.rating import Rating
8
7
  from fabricatio.config import configs
@@ -10,6 +9,7 @@ from fabricatio.models.extra.problem import Improvement
10
9
  from fabricatio.models.generic import Display, WithBriefing
11
10
  from fabricatio.models.kwargs_types import ReviewKwargs, ValidateKwargs
12
11
  from fabricatio.models.task import Task
12
+ from fabricatio.rust_instances import TEMPLATE_MANAGER
13
13
  from fabricatio.utils import ok
14
14
 
15
15