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,18 +1,18 @@
1
- """This module defines generic classes for models in the Fabricatio library."""
1
+ """This module defines generic classes for models in the Fabricatio library, providing a foundation for various model functionalities."""
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from datetime import datetime
5
5
  from pathlib import Path
6
- from typing import Any, Callable, Dict, Iterable, List, Optional, Self, Union, final, overload
6
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Self, Type, Union, final, overload
7
7
 
8
8
  import orjson
9
9
  import rtoml
10
- from fabricatio._rust import blake3_hash
11
- from fabricatio._rust_instances import TEMPLATE_MANAGER
12
10
  from fabricatio.config import configs
13
11
  from fabricatio.fs.readers import MAGIKA, safe_text_read
14
12
  from fabricatio.journal import logger
15
13
  from fabricatio.parser import JsonCapture
14
+ from fabricatio.rust import blake3_hash, detect_language
15
+ from fabricatio.rust_instances import TEMPLATE_MANAGER
16
16
  from fabricatio.utils import ok
17
17
  from litellm.utils import token_counter
18
18
  from pydantic import (
@@ -30,47 +30,99 @@ from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
30
30
 
31
31
 
32
32
  class Base(BaseModel):
33
- """Base class for all models with Pydantic configuration."""
33
+ """Base class for all models with Pydantic configuration.
34
+
35
+ This class sets up the basic Pydantic configuration for all models in the Fabricatio library.
36
+ The `model_config` uses `use_attribute_docstrings=True` to ensure field descriptions are
37
+ pulled from the attribute's docstring instead of the default Pydantic behavior.
38
+ """
34
39
 
35
40
  model_config = ConfigDict(use_attribute_docstrings=True)
36
41
 
37
42
 
38
43
  class Display(Base):
39
- """Class that provides a method to display the model in a formatted JSON string."""
44
+ """Class that provides formatted JSON representation utilities.
45
+
46
+ Provides methods to generate both pretty-printed and compact JSON representations of the model.
47
+ Used for debugging and logging purposes.
48
+ """
40
49
 
41
50
  def display(self) -> str:
42
- """Display the model in a formatted JSON string.
51
+ """Generate pretty-printed JSON representation.
43
52
 
44
53
  Returns:
45
- str: The formatted JSON string of the model.
54
+ str: JSON string with 1-level indentation for readability
46
55
  """
47
- return self.model_dump_json(indent=1)
56
+ return self.model_dump_json(indent=1,by_alias=True)
48
57
 
49
58
  def compact(self) -> str:
50
- """Display the model in a compact JSON string.
59
+ """Generate compact JSON representation.
60
+
61
+ Returns:
62
+ str: Minified JSON string without whitespace
63
+ """
64
+ return self.model_dump_json(by_alias=True)
65
+
66
+ @staticmethod
67
+ def seq_display(seq: Iterable["Display"], compact: bool = False) -> str:
68
+ """Generate formatted display for sequence of Display objects.
69
+
70
+ Args:
71
+ seq (Iterable[Display]): Sequence of objects to display
72
+ compact (bool): Use compact format instead of pretty print
51
73
 
52
74
  Returns:
53
- str: The compact JSON string of the model.
75
+ str: Combined display output with boundary markers
54
76
  """
55
- return self.model_dump_json()
77
+ return (
78
+ "--- Start of Extra Info Sequence ---"
79
+ + "\n".join(d.compact() if compact else d.display() for d in seq)
80
+ + "--- End of Extra Info Sequence ---"
81
+ )
56
82
 
57
83
 
58
84
  class Named(Base):
59
- """Class that includes a name attribute."""
85
+ """Class that includes a name attribute.
86
+
87
+ This class adds a name attribute to models, which is intended to be a unique identifier.
88
+ """
60
89
 
61
- name: str = Field(frozen=True)
62
- """The name of the object."""
90
+ name: str
91
+ """The name of this object,briefly and conclusively."""
63
92
 
64
93
 
65
94
  class Described(Base):
66
- """Class that includes a description attribute."""
95
+ """Class that includes a description attribute.
67
96
 
68
- description: str = Field(frozen=True)
69
- """The description of the object."""
97
+ This class adds a description attribute to models, providing additional context or information.
98
+ """
99
+
100
+ description: str
101
+ """A comprehensive description of this object, including its purpose, scope, and context.
102
+ This should clearly explain what this object is about, why it exists, and in what situations
103
+ it applies. The description should be detailed enough to provide full understanding of
104
+ this object's intent and application."""
105
+
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."""
70
119
 
71
120
 
72
121
  class AsPrompt(Base):
73
- """Class that provides a method to generate a prompt from the model."""
122
+ """Class that provides a method to generate a prompt from the model.
123
+
124
+ This class includes a method to generate a prompt based on the model's attributes.
125
+ """
74
126
 
75
127
  @final
76
128
  def as_prompt(self) -> str:
@@ -85,31 +137,53 @@ class AsPrompt(Base):
85
137
  )
86
138
 
87
139
  @abstractmethod
88
- def _as_prompt_inner(self) -> Dict[str, str]: ...
140
+ def _as_prompt_inner(self) -> Dict[str, str]:
141
+ """Generate the inner part of the prompt.
142
+
143
+ This method should be implemented by subclasses to provide the specific data for the prompt.
144
+
145
+ Returns:
146
+ Dict[str, str]: The data for the prompt.
147
+ """
89
148
 
90
149
 
91
150
  class WithRef[T](Base):
92
- """Class that provides a reference to another object."""
151
+ """Class that provides a reference to another object.
152
+
153
+ This class manages a reference to another object, allowing for easy access and updates.
154
+ """
93
155
 
94
156
  _reference: Optional[T] = PrivateAttr(None)
95
157
 
96
158
  @property
97
159
  def referenced(self) -> T:
98
- """Get the referenced object."""
160
+ """Get the referenced object.
161
+
162
+ Returns:
163
+ T: The referenced object.
164
+
165
+ Raises:
166
+ ValueError: If the reference is not set.
167
+ """
99
168
  return ok(
100
- self._reference, f"`{self.__class__.__name__}`' s `_reference` field is None. Have you called `update_ref`?"
169
+ self._reference, f"`{self.__class__.__name__}`'s `_reference` field is None. Have you called `update_ref`?"
101
170
  )
102
171
 
103
172
  @overload
104
173
  def update_ref[S: WithRef](self: S, reference: T) -> S: ...
105
-
106
174
  @overload
107
175
  def update_ref[S: WithRef](self: S, reference: "WithRef[T]") -> S: ...
108
-
109
176
  @overload
110
177
  def update_ref[S: WithRef](self: S, reference: None = None) -> S: ...
111
178
  def update_ref[S: WithRef](self: S, reference: Union[T, "WithRef[T]", None] = None) -> S: # noqa: PYI019
112
- """Update the reference of the object."""
179
+ """Update the reference of the object.
180
+
181
+ Args:
182
+ reference (Union[T, WithRef[T], None]): The new reference to set.
183
+
184
+ Returns:
185
+ S: The current instance with the updated reference.
186
+ """
113
187
  if isinstance(reference, self.__class__):
114
188
  self._reference = reference.referenced
115
189
  else:
@@ -117,29 +191,44 @@ class WithRef[T](Base):
117
191
  return self
118
192
 
119
193
  def derive[S: WithRef](self: S, reference: Any) -> S: # noqa: PYI019
120
- """Derive a new object from the current object."""
194
+ """Derive a new object from the current object.
195
+
196
+ Args:
197
+ reference (Any): The reference for the new object.
198
+
199
+ Returns:
200
+ S: A new instance derived from the current object with the provided reference.
201
+ """
121
202
  new = self.model_copy()
122
203
  new._reference = reference
123
204
  return new
124
205
 
125
206
 
126
207
  class PersistentAble(Base):
127
- """Class that provides a method to persist the object."""
208
+ """Class providing file persistence capabilities.
209
+
210
+ Enables saving model instances to disk with timestamped filenames and loading from persisted files.
211
+ Implements basic versioning through filename hashing and timestamping.
212
+ """
128
213
 
129
214
  def persist(self, path: str | Path) -> Self:
130
- """Persist the object to a file or directory.
215
+ """Save model instance to disk with versioned filename.
131
216
 
132
217
  Args:
133
- path (str | Path): The path to save the object.
218
+ path (str | Path): Target directory or file path. If directory, filename is auto-generated.
134
219
 
135
220
  Returns:
136
- Self: The current instance of the object.
221
+ Self: Current instance for method chaining
222
+
223
+ Notes:
224
+ - Filename format: <ClassName>_<YYYYMMDD_HHMMSS>_<6-char_hash>.json
225
+ - Hash generated from JSON content ensures uniqueness
137
226
  """
138
227
  p = Path(path)
139
- out = rtoml.dumps(self.model_dump())
228
+ out = self.model_dump_json(indent=1,by_alias=True)
140
229
 
141
230
  # Generate a timestamp in the format YYYYMMDD_HHMMSS
142
- timestamp = datetime.now().strftime("%Y%m%d")
231
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
143
232
 
144
233
  # Generate the hash
145
234
  file_hash = blake3_hash(out.encode())[:6]
@@ -156,32 +245,103 @@ class PersistentAble(Base):
156
245
  logger.info(f"Persisted `{self.__class__.__name__}` to {p.as_posix()}")
157
246
  return self
158
247
 
248
+ @classmethod
249
+ def from_latest_persistent(cls, dir_path: str | Path) -> Optional[Self]:
250
+ """Load most recent persisted instance from directory.
251
+
252
+ Args:
253
+ dir_path (str | Path): Directory containing persisted files
254
+
255
+ Returns:
256
+ Self: Most recently modified instance
257
+
258
+ Raises:
259
+ NotADirectoryError: If path is not a valid directory
260
+ FileNotFoundError: If no matching files found
261
+ """
262
+ dir_path = Path(dir_path)
263
+ if not dir_path.is_dir():
264
+ return None
265
+
266
+ pattern = f"{cls.__name__}_*.json"
267
+ files = list(dir_path.glob(pattern))
268
+
269
+ if not files:
270
+ return None
271
+
272
+ def _get_timestamp(file_path: Path) -> datetime:
273
+ stem = file_path.stem
274
+ parts = stem.split("_")
275
+ return datetime.strptime(f"{parts[1]}_{parts[2]}", "%Y%m%d_%H%M%S")
276
+
277
+ files.sort(key=lambda f: _get_timestamp(f), reverse=True)
278
+
279
+ return cls.from_persistent(files.pop(0))
280
+
159
281
  @classmethod
160
282
  def from_persistent(cls, path: str | Path) -> Self:
161
- """Load the object from a file.
283
+ """Load an instance from a specific persisted file.
162
284
 
163
285
  Args:
164
- path (str | Path): The path to load the object from.
286
+ path (str | Path): Path to the JSON file.
165
287
 
166
288
  Returns:
167
- Self: The current instance of the object.
289
+ Self: The loaded instance from the file.
290
+
291
+ Raises:
292
+ FileNotFoundError: If the specified file does not exist.
293
+ ValueError: If the file content is invalid for the model.
168
294
  """
169
- return cls.model_validate(rtoml.load(path))
295
+ return cls.model_validate_json(safe_text_read(path))
296
+
170
297
 
298
+ class Language(Base):
299
+ """Class that provides a language attribute."""
171
300
 
301
+ @property
302
+ def language(self)->str:
303
+ """Get the language of the object."""
304
+ if isinstance(self,Described):
305
+ return detect_language(self.description)
306
+ if isinstance(self,Titled):
307
+ return detect_language(self.title)
308
+ if isinstance(self,Named):
309
+ return detect_language(self.name)
310
+
311
+ return detect_language(self.model_dump_json(by_alias=True))
172
312
  class ModelHash(Base):
173
- """Class that provides a hash value for the object."""
313
+ """Class that provides a hash value for the object.
314
+
315
+ This class includes a method to calculate a hash value for the object based on its JSON representation.
316
+ """
174
317
 
175
318
  def __hash__(self) -> int:
176
- """Calculates a hash value for the ArticleBase object based on its model_dump_json representation."""
319
+ """Calculates a hash value for the object based on its model_dump_json representation.
320
+
321
+ Returns:
322
+ int: The hash value of the object.
323
+ """
177
324
  return hash(self.model_dump_json())
178
325
 
179
326
 
180
327
  class UpdateFrom(Base):
181
- """Class that provides a method to update the object from another object."""
328
+ """Class that provides a method to update the object from another object.
329
+
330
+ This class includes methods to update the current object with the attributes of another object.
331
+ """
182
332
 
183
333
  def update_pre_check(self, other: Self) -> Self:
184
- """Pre-check for updating the object from another object."""
334
+ """Pre-check for updating the object from another object.
335
+
336
+ Args:
337
+ other (Self): The other object to update from.
338
+
339
+ Returns:
340
+ Self: The current instance after pre-check.
341
+
342
+ Raises:
343
+ TypeError: If the other object is not of the same type.
344
+ """
185
345
  if not isinstance(other, self.__class__):
186
346
  raise TypeError(f"Cannot update from a non-{self.__class__.__name__} instance.")
187
347
 
@@ -189,16 +349,35 @@ class UpdateFrom(Base):
189
349
 
190
350
  @abstractmethod
191
351
  def update_from_inner(self, other: Self) -> Self:
192
- """Updates the current instance with the attributes of another instance."""
352
+ """Updates the current instance with the attributes of another instance.
353
+
354
+ This method should be implemented by subclasses to provide the specific update logic.
355
+
356
+ Args:
357
+ other (Self): The other instance to update from.
358
+
359
+ Returns:
360
+ Self: The current instance with updated attributes.
361
+ """
193
362
 
194
363
  @final
195
364
  def update_from(self, other: Self) -> Self:
196
- """Updates the current instance with the attributes of another instance."""
365
+ """Updates the current instance with the attributes of another instance.
366
+
367
+ Args:
368
+ other (Self): The other instance to update from.
369
+
370
+ Returns:
371
+ Self: The current instance with updated attributes.
372
+ """
197
373
  return self.update_pre_check(other).update_from_inner(other)
198
374
 
199
375
 
200
376
  class ResolveUpdateConflict(Base):
201
- """Class that provides a method to update the object from another object."""
377
+ """Class that provides a method to update the object from another object.
378
+
379
+ This class includes a method to resolve conflicts when updating the object from another object.
380
+ """
202
381
 
203
382
  @abstractmethod
204
383
  def resolve_update_conflict(self, other: Self) -> str:
@@ -213,7 +392,10 @@ class ResolveUpdateConflict(Base):
213
392
 
214
393
 
215
394
  class Introspect(Base):
216
- """Class that provides a method to introspect the object."""
395
+ """Class that provides a method to introspect the object.
396
+
397
+ This class includes a method to perform internal introspection of the object.
398
+ """
217
399
 
218
400
  @abstractmethod
219
401
  def introspect(self) -> str:
@@ -225,7 +407,10 @@ class Introspect(Base):
225
407
 
226
408
 
227
409
  class WithBriefing(Named, Described):
228
- """Class that provides a briefing based on the name and description."""
410
+ """Class that provides a briefing based on the name and description.
411
+
412
+ This class combines the name and description attributes to provide a brief summary of the object.
413
+ """
229
414
 
230
415
  @property
231
416
  def briefing(self) -> str:
@@ -238,15 +423,29 @@ class WithBriefing(Named, Described):
238
423
 
239
424
 
240
425
  class UnsortGenerate(GenerateJsonSchema):
241
- """Class that provides a reverse JSON schema of the model."""
426
+ """Class that provides a reverse JSON schema of the model.
427
+
428
+ This class overrides the sorting behavior of the JSON schema generation to maintain the original order.
429
+ """
242
430
 
243
431
  def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue:
244
- """Not sort."""
432
+ """Not sort.
433
+
434
+ Args:
435
+ value (JsonSchemaValue): The JSON schema value to sort.
436
+ parent_key (str | None): The parent key of the JSON schema value.
437
+
438
+ Returns:
439
+ JsonSchemaValue: The JSON schema value without sorting.
440
+ """
245
441
  return value
246
442
 
247
443
 
248
444
  class WithFormatedJsonSchema(Base):
249
- """Class that provides a formatted JSON schema of the model."""
445
+ """Class that provides a formatted JSON schema of the model.
446
+
447
+ This class includes a method to generate a formatted JSON schema of the model.
448
+ """
250
449
 
251
450
  @classmethod
252
451
  def formated_json_schema(cls) -> str:
@@ -262,25 +461,26 @@ class WithFormatedJsonSchema(Base):
262
461
 
263
462
 
264
463
  class CreateJsonObjPrompt(WithFormatedJsonSchema):
265
- """Class that provides a prompt for creating a JSON object."""
464
+ """Class that provides a prompt for creating a JSON object.
465
+
466
+ This class includes a method to create a prompt for creating a JSON object based on the model's schema and a requirement.
467
+ """
266
468
 
267
469
  @classmethod
268
470
  @overload
269
471
  def create_json_prompt(cls, requirement: List[str]) -> List[str]: ...
270
-
271
472
  @classmethod
272
473
  @overload
273
474
  def create_json_prompt(cls, requirement: str) -> str: ...
274
-
275
475
  @classmethod
276
476
  def create_json_prompt(cls, requirement: str | List[str]) -> str | List[str]:
277
477
  """Create the prompt for creating a JSON object with given requirement.
278
478
 
279
479
  Args:
280
- requirement (str): The requirement for the JSON object.
480
+ requirement (str | List[str]): The requirement for the JSON object.
281
481
 
282
482
  Returns:
283
- str: The prompt for creating a JSON object with given requirement.
483
+ str | List[str]: The prompt for creating a JSON object with given requirement.
284
484
  """
285
485
  if isinstance(requirement, str):
286
486
  return TEMPLATE_MANAGER.render_template(
@@ -297,7 +497,10 @@ class CreateJsonObjPrompt(WithFormatedJsonSchema):
297
497
 
298
498
 
299
499
  class InstantiateFromString(Base):
300
- """Class that provides a method to instantiate the class from a string."""
500
+ """Class that provides a method to instantiate the class from a string.
501
+
502
+ This class includes a method to instantiate the class from a JSON string representation.
503
+ """
301
504
 
302
505
  @classmethod
303
506
  def instantiate_from_string(cls, string: str) -> Self | None:
@@ -309,19 +512,37 @@ class InstantiateFromString(Base):
309
512
  Returns:
310
513
  Self | None: The instance of the class or None if the string is not valid.
311
514
  """
312
- return JsonCapture.convert_with(string, cls.model_validate_json)
515
+ obj = JsonCapture.convert_with(string, cls.model_validate_json)
516
+ logger.debug(f"Instantiate `{cls.__name__}` from string, {'Failed' if obj is None else 'Success'}.")
517
+ return obj
313
518
 
314
519
 
315
520
  class ProposedAble(CreateJsonObjPrompt, InstantiateFromString):
316
- """Class that provides a method to propose a JSON object based on the requirement."""
521
+ """Class that provides a method to propose a JSON object based on the requirement.
522
+
523
+ This class combines the functionality to create a prompt for a JSON object and instantiate it from a string.
524
+ """
525
+
526
+
527
+ class SketchedAble(ProposedAble, Display):
528
+ """Class that provides a method to scratch the object.
529
+
530
+ This class combines the functionality to propose a JSON object, instantiate it from a string, and display it.
531
+ """
317
532
 
318
533
 
319
- class ProposedUpdateAble(ProposedAble, UpdateFrom, ABC):
320
- """Make the obj can be updated from the proposed obj in place."""
534
+ class ProposedUpdateAble(SketchedAble, UpdateFrom, ABC):
535
+ """Make the obj can be updated from the proposed obj in place.
536
+
537
+ This class provides the ability to update an object in place from a proposed object.
538
+ """
321
539
 
322
540
 
323
541
  class FinalizedDumpAble(Base):
324
- """Class that provides a method to finalize the dump of the object."""
542
+ """Class that provides a method to finalize the dump of the object.
543
+
544
+ This class includes methods to finalize the JSON representation of the object and dump it to a file.
545
+ """
325
546
 
326
547
  def finalized_dump(self) -> str:
327
548
  """Finalize the dump of the object.
@@ -329,7 +550,7 @@ class FinalizedDumpAble(Base):
329
550
  Returns:
330
551
  str: The finalized dump of the object.
331
552
  """
332
- return self.model_dump_json()
553
+ return self.model_dump_json(indent=1,by_alias=True)
333
554
 
334
555
  def finalized_dump_to(self, path: str | Path) -> Self:
335
556
  """Finalize the dump of the object to a file.
@@ -344,12 +565,11 @@ class FinalizedDumpAble(Base):
344
565
  return self
345
566
 
346
567
 
347
- class CensoredAble(ProposedAble, FinalizedDumpAble):
348
- """Class that provides a method to censor the object."""
349
-
350
-
351
568
  class WithDependency(Base):
352
- """Class that manages file dependencies."""
569
+ """Class that manages file dependencies.
570
+
571
+ This class includes methods to manage file dependencies required for reading or writing.
572
+ """
353
573
 
354
574
  dependencies: List[str] = Field(default_factory=list)
355
575
  """The file dependencies which is needed to read or write to meet a specific requirement, a list of file paths."""
@@ -437,7 +657,10 @@ class WithDependency(Base):
437
657
 
438
658
 
439
659
  class Vectorizable(Base):
440
- """Class that prepares the vectorization of the model."""
660
+ """Class that prepares the vectorization of the model.
661
+
662
+ This class includes methods to prepare the model for vectorization, ensuring it fits within a specified token length.
663
+ """
441
664
 
442
665
  def _prepare_vectorization_inner(self) -> str:
443
666
  return rtoml.dumps(self.model_dump())
@@ -446,20 +669,30 @@ class Vectorizable(Base):
446
669
  def prepare_vectorization(self, max_length: Optional[int] = None) -> str:
447
670
  """Prepare the vectorization of the model.
448
671
 
672
+ Args:
673
+ max_length (Optional[int]): The maximum token length for the vectorization. Defaults to the configuration.
674
+
449
675
  Returns:
450
676
  str: The prepared vectorization of the model.
677
+
678
+ Raises:
679
+ ValueError: If the chunk exceeds the maximum sequence length.
451
680
  """
452
681
  max_length = max_length or configs.embedding.max_sequence_length
453
682
  chunk = self._prepare_vectorization_inner()
454
683
  if max_length and (length := token_counter(text=chunk)) > max_length:
455
- logger.error(err := f"Chunk exceeds maximum sequence length {max_length}, got {length},see {chunk}")
684
+ logger.error(err := f"Chunk exceeds maximum sequence length {max_length}, got {length}, see {chunk}")
456
685
  raise ValueError(err)
457
686
 
458
687
  return chunk
459
688
 
460
689
 
461
690
  class ScopedConfig(Base):
462
- """Class that manages a scoped configuration."""
691
+ """Configuration holder with hierarchical fallback mechanism.
692
+
693
+ Manages LLM, embedding, and vector database configurations with fallback logic.
694
+ Allows configuration values to be overridden in a hierarchical manner.
695
+ """
463
696
 
464
697
  llm_api_endpoint: Optional[HttpUrl] = None
465
698
  """The OpenAI API endpoint."""
@@ -517,27 +750,33 @@ class ScopedConfig(Base):
517
750
 
518
751
  embedding_dimensions: Optional[PositiveInt] = None
519
752
  """The dimensions of the embedding."""
753
+
520
754
  embedding_caching: Optional[bool] = False
521
755
  """Whether to cache the embedding result."""
522
756
 
523
757
  milvus_uri: Optional[HttpUrl] = Field(default=None)
524
758
  """The URI of the Milvus server."""
759
+
525
760
  milvus_token: Optional[SecretStr] = Field(default=None)
526
761
  """The token for the Milvus server."""
762
+
527
763
  milvus_timeout: Optional[PositiveFloat] = Field(default=None)
528
764
  """The timeout for the Milvus server."""
765
+
529
766
  milvus_dimensions: Optional[PositiveInt] = Field(default=None)
530
767
  """The dimensions of the Milvus server."""
531
768
 
532
769
  @final
533
770
  def fallback_to(self, other: "ScopedConfig") -> Self:
534
- """Fallback to another instance's attribute values if the current instance's attributes are None.
771
+ """Merge configuration values with fallback priority.
772
+
773
+ Copies non-null values from 'other' to self where current values are None.
535
774
 
536
775
  Args:
537
- other (ScopedConfig): Another instance from which to copy attribute values.
776
+ other (ScopedConfig): Configuration to fallback to
538
777
 
539
778
  Returns:
540
- Self: The current instance, allowing for method chaining.
779
+ Self: Current instance with merged values
541
780
  """
542
781
  # Iterate over the attribute names and copy values from 'other' to 'self' where applicable
543
782
  # noinspection PydanticTypeChecker,PyTypeChecker
@@ -551,13 +790,15 @@ class ScopedConfig(Base):
551
790
 
552
791
  @final
553
792
  def hold_to(self, others: Union["ScopedConfig", Iterable["ScopedConfig"]]) -> Self:
554
- """Hold to another instance's attribute values if the current instance's attributes are None.
793
+ """Propagate non-null values to other configurations.
794
+
795
+ Copies current non-null values to target configurations where they are None.
555
796
 
556
797
  Args:
557
- others (Union[ScopedConfig, Iterable[ScopedConfig]]): Another instance or iterable of instances from which to copy attribute values.
798
+ others (ScopedConfig|Iterable): Target configurations to update
558
799
 
559
800
  Returns:
560
- Self: The current instance, allowing for method chaining.
801
+ Self: Current instance unchanged
561
802
  """
562
803
  if not isinstance(others, Iterable):
563
804
  others = [others]
@@ -567,3 +808,92 @@ class ScopedConfig(Base):
567
808
  if (attr := getattr(self, attr_name)) is not None and getattr(other, attr_name) is None:
568
809
  setattr(other, attr_name, attr)
569
810
  return self
811
+
812
+
813
+ class Patch[T](ProposedAble):
814
+ """Base class for patches.
815
+
816
+ This class provides a base implementation for patches that can be applied to other objects.
817
+ """
818
+
819
+ def apply(self, other: T) -> T:
820
+ """Apply the patch to another instance.
821
+
822
+ Args:
823
+ other (T): The instance to apply the patch to.
824
+
825
+ Returns:
826
+ T: The instance with the patch applied.
827
+
828
+ Raises:
829
+ ValueError: If a field in the patch is not found in the target instance.
830
+ """
831
+ for field in self.__class__.model_fields:
832
+ if not hasattr(other, field):
833
+ raise ValueError(f"{field} not found in {other}, are you applying to the wrong type?")
834
+ setattr(other, field, getattr(self, field))
835
+ return other
836
+
837
+ def as_kwargs(self) -> Dict[str, Any]:
838
+ """Get the kwargs of the patch."""
839
+ return self.model_dump()
840
+
841
+ @staticmethod
842
+ def ref_cls() -> Optional[Type[BaseModel]]:
843
+ """Get the reference class of the model."""
844
+ return None
845
+
846
+ @classmethod
847
+ def formated_json_schema(cls) -> str:
848
+ """Get the JSON schema of the model in a formatted string.
849
+
850
+ Returns:
851
+ str: The JSON schema of the model in a formatted string.
852
+ """
853
+ my_schema = cls.model_json_schema(schema_generator=UnsortGenerate)
854
+
855
+ ref_cls = cls.ref_cls()
856
+ if ref_cls is not None:
857
+ # copy the desc info of each corresponding fields from `ref_cls`
858
+ for field_name in [f for f in cls.model_fields if f in ref_cls.model_fields]:
859
+ my_schema["properties"][field_name]["description"] = (
860
+ ref_cls.model_fields[field_name].description or my_schema["properties"][field_name]["description"]
861
+ )
862
+ my_schema["description"] = ref_cls.__doc__
863
+
864
+ return orjson.dumps(
865
+ my_schema,
866
+ option=orjson.OPT_INDENT_2,
867
+ ).decode()
868
+
869
+
870
+ class SequencePatch[T](ProposedUpdateAble):
871
+ """Base class for patches.
872
+
873
+ This class provides a base implementation for patches that can be applied to sequences of objects.
874
+ """
875
+
876
+ tweaked: List[T]
877
+ """Tweaked content list"""
878
+
879
+ def update_from_inner(self, other: Self) -> Self:
880
+ """Updates the current instance with the attributes of another instance.
881
+
882
+ Args:
883
+ other (Self): The other instance to update from.
884
+
885
+ Returns:
886
+ Self: The current instance with updated attributes.
887
+ """
888
+ self.tweaked.clear()
889
+ self.tweaked.extend(other.tweaked)
890
+ return self
891
+
892
+ @classmethod
893
+ def default(cls) -> Self:
894
+ """Defaults to empty list.
895
+
896
+ Returns:
897
+ Self: A new instance with an empty list of tweaks.
898
+ """
899
+ return cls(tweaked=[])