hammad-python 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.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 (80) hide show
  1. hammad/__init__.py +169 -56
  2. hammad/_core/__init__.py +1 -0
  3. hammad/_core/_utils/__init__.py +4 -0
  4. hammad/_core/_utils/_import_utils.py +182 -0
  5. hammad/ai/__init__.py +59 -0
  6. hammad/ai/_utils.py +142 -0
  7. hammad/ai/completions/__init__.py +44 -0
  8. hammad/ai/completions/client.py +729 -0
  9. hammad/ai/completions/create.py +686 -0
  10. hammad/ai/completions/types.py +711 -0
  11. hammad/ai/completions/utils.py +374 -0
  12. hammad/ai/embeddings/__init__.py +35 -0
  13. hammad/ai/embeddings/client/__init__.py +1 -0
  14. hammad/ai/embeddings/client/base_embeddings_client.py +26 -0
  15. hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +200 -0
  16. hammad/ai/embeddings/client/litellm_embeddings_client.py +288 -0
  17. hammad/ai/embeddings/create.py +159 -0
  18. hammad/ai/embeddings/types.py +69 -0
  19. hammad/base/__init__.py +35 -0
  20. hammad/{based → base}/fields.py +23 -23
  21. hammad/{based → base}/model.py +124 -14
  22. hammad/base/utils.py +280 -0
  23. hammad/cache/__init__.py +30 -12
  24. hammad/cache/base_cache.py +181 -0
  25. hammad/cache/cache.py +169 -0
  26. hammad/cache/decorators.py +261 -0
  27. hammad/cache/file_cache.py +80 -0
  28. hammad/cache/ttl_cache.py +74 -0
  29. hammad/cli/__init__.py +10 -2
  30. hammad/cli/{styles/animations.py → animations.py} +79 -23
  31. hammad/cli/{plugins/__init__.py → plugins.py} +85 -90
  32. hammad/cli/styles/__init__.py +50 -0
  33. hammad/cli/styles/settings.py +4 -0
  34. hammad/configuration/__init__.py +35 -0
  35. hammad/{data/types/files → configuration}/configuration.py +96 -7
  36. hammad/data/__init__.py +14 -26
  37. hammad/data/collections/__init__.py +4 -2
  38. hammad/data/collections/collection.py +300 -75
  39. hammad/data/collections/vector_collection.py +118 -12
  40. hammad/data/databases/__init__.py +2 -2
  41. hammad/data/databases/database.py +383 -32
  42. hammad/json/__init__.py +2 -2
  43. hammad/logging/__init__.py +13 -5
  44. hammad/logging/decorators.py +404 -2
  45. hammad/logging/logger.py +442 -22
  46. hammad/multimodal/__init__.py +24 -0
  47. hammad/{data/types/files → multimodal}/audio.py +21 -6
  48. hammad/{data/types/files → multimodal}/image.py +5 -5
  49. hammad/multithreading/__init__.py +304 -0
  50. hammad/pydantic/__init__.py +2 -2
  51. hammad/pydantic/converters.py +1 -1
  52. hammad/pydantic/models/__init__.py +2 -2
  53. hammad/text/__init__.py +59 -14
  54. hammad/text/converters.py +723 -0
  55. hammad/text/{utils/markdown/formatting.py → markdown.py} +25 -23
  56. hammad/text/text.py +12 -14
  57. hammad/types/__init__.py +11 -0
  58. hammad/{data/types/files → types}/file.py +18 -18
  59. hammad/typing/__init__.py +138 -84
  60. hammad/web/__init__.py +3 -2
  61. hammad/web/models.py +245 -0
  62. hammad/web/search/client.py +75 -23
  63. hammad/web/utils.py +14 -5
  64. hammad/yaml/__init__.py +2 -2
  65. hammad/yaml/converters.py +1 -1
  66. {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/METADATA +4 -1
  67. hammad_python-0.0.13.dist-info/RECORD +85 -0
  68. hammad/based/__init__.py +0 -52
  69. hammad/based/utils.py +0 -455
  70. hammad/cache/_cache.py +0 -746
  71. hammad/data/types/__init__.py +0 -33
  72. hammad/data/types/files/__init__.py +0 -1
  73. hammad/data/types/files/document.py +0 -195
  74. hammad/text/utils/__init__.py +0 -1
  75. hammad/text/utils/converters.py +0 -229
  76. hammad/text/utils/markdown/__init__.py +0 -1
  77. hammad/text/utils/markdown/converters.py +0 -506
  78. hammad_python-0.0.11.dist-info/RECORD +0 -65
  79. {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/WHEEL +0 -0
  80. {hammad_python-0.0.11.dist-info → hammad_python-0.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- """hammad.text.utils.markdown.formatting"""
1
+ """hammad.text.utils.markdown"""
2
2
 
3
3
  from typing import (
4
4
  List,
@@ -7,60 +7,62 @@ from typing import (
7
7
  )
8
8
 
9
9
  __all__ = (
10
- "bold",
11
- "italic",
12
- "code",
13
- "code_block",
14
- "heading",
15
- "link",
16
- "list_item",
17
- "table_row",
18
- "blockquote",
19
- "horizontal_rule",
20
- "table",
10
+ "markdown_bold",
11
+ "markdown_italic",
12
+ "markdown_code",
13
+ "markdown_code_block",
14
+ "markdown_heading",
15
+ "markdown_link",
16
+ "markdown_list_item",
17
+ "markdown_table_row",
18
+ "markdown_blockquote",
19
+ "markdown_horizontal_rule",
20
+ "markdown_table",
21
21
  )
22
22
 
23
23
 
24
- def bold(text: str) -> str:
24
+ def markdown_bold(text: str) -> str:
25
25
  """Format text as bold in Markdown."""
26
26
  return f"**{text}**"
27
27
 
28
28
 
29
- def italic(text: str) -> str:
29
+ def markdown_italic(text: str) -> str:
30
30
  """Format text as italic in Markdown."""
31
31
  return f"*{text}*"
32
32
 
33
33
 
34
- def code(text: str) -> str:
34
+ def markdown_code(text: str) -> str:
35
35
  """Format text as inline code in Markdown."""
36
36
  return f"`{text}`"
37
37
 
38
38
 
39
- def code_block(text: str, language: str = "") -> str:
39
+ def markdown_code_block(text: str, language: str = "") -> str:
40
40
  """Format text as a code block in Markdown."""
41
41
  return f"```{language}\n{text}\n```"
42
42
 
43
43
 
44
- def heading(text: str, level: int = 1) -> str:
44
+ def markdown_heading(text: str, level: int = 1) -> str:
45
45
  """Format text as a heading in Markdown."""
46
46
  if not 1 <= level <= 6:
47
47
  level = 1
48
48
  return f"{'#' * level} {text}"
49
49
 
50
50
 
51
- def link(text: str, url: str) -> str:
51
+ def markdown_link(text: str, url: str) -> str:
52
52
  """Format text as a link in Markdown."""
53
53
  return f"[{text}]({url})"
54
54
 
55
55
 
56
- def list_item(text: str, level: int = 0, ordered: bool = False, index: int = 1) -> str:
56
+ def markdown_list_item(
57
+ text: str, level: int = 0, ordered: bool = False, index: int = 1
58
+ ) -> str:
57
59
  """Format text as a list item in Markdown."""
58
60
  indent = " " * level
59
61
  marker = f"{index}." if ordered else "-"
60
62
  return f"{indent}{marker} {text}"
61
63
 
62
64
 
63
- def table_row(cells: List[str], is_header: bool = False) -> str:
65
+ def markdown_table_row(cells: List[str], is_header: bool = False) -> str:
64
66
  """Format a table row in Markdown."""
65
67
  row = "| " + " | ".join(cells) + " |"
66
68
  if is_header:
@@ -69,19 +71,19 @@ def table_row(cells: List[str], is_header: bool = False) -> str:
69
71
  return row
70
72
 
71
73
 
72
- def blockquote(text: str, level: int = 1) -> str:
74
+ def markdown_blockquote(text: str, level: int = 1) -> str:
73
75
  """Format text as a blockquote in Markdown."""
74
76
  prefix = ">" * level + " "
75
77
  lines = text.split("\n")
76
78
  return "\n".join(f"{prefix}{line}" for line in lines)
77
79
 
78
80
 
79
- def horizontal_rule() -> str:
81
+ def markdown_horizontal_rule() -> str:
80
82
  """Create a horizontal rule in Markdown."""
81
83
  return "---"
82
84
 
83
85
 
84
- def table(
86
+ def markdown_table(
85
87
  headers: List[str],
86
88
  rows: List[List[str]],
87
89
  alignment: Optional[List[Literal["left", "center", "right"]]] = None,
hammad/text/text.py CHANGED
@@ -25,13 +25,11 @@ from typing import (
25
25
  Generic,
26
26
  )
27
27
 
28
- from .utils.markdown.formatting import (
29
- heading as md_heading,
30
- code_block as md_code_block,
31
- )
32
- from .utils.markdown.converters import (
33
- convert_to_markdown,
28
+ from .markdown import (
29
+ markdown_heading,
30
+ markdown_code_block,
34
31
  )
32
+ from .converters import convert_to_text
35
33
 
36
34
 
37
35
  # -----------------------------------------------------------------------------
@@ -234,7 +232,7 @@ class BaseText(ABC):
234
232
  if isinstance(self.content, str):
235
233
  parts.append(self.content)
236
234
  else:
237
- parts.append(convert_to_markdown(self.content, **kwargs))
235
+ parts.append(convert_to_text(self.content, **kwargs))
238
236
 
239
237
  # Handle subsections
240
238
  for section in self.sections:
@@ -250,7 +248,7 @@ class BaseText(ABC):
250
248
 
251
249
  # Handle title
252
250
  if self.title:
253
- parts.append(md_heading(self.title, self.heading_level))
251
+ parts.append(markdown_heading(self.title, self.heading_level))
254
252
 
255
253
  # Handle description
256
254
  if self.description:
@@ -261,7 +259,7 @@ class BaseText(ABC):
261
259
  if isinstance(self.content, str):
262
260
  parts.append(self.content)
263
261
  else:
264
- parts.append(convert_to_markdown(self.content, **kwargs))
262
+ parts.append(convert_to_text(self.content, **kwargs))
265
263
 
266
264
  # Add table of contents if requested (only for top-level documents)
267
265
  if kwargs.get("add_toc", False) and self.heading_level == 1:
@@ -271,9 +269,9 @@ class BaseText(ABC):
271
269
  toc_headings.append((section.heading_level, section.title))
272
270
 
273
271
  if toc_headings:
274
- from .utils.markdown.converters import create_markdown_toc
272
+ from .markdown import markdown_table
275
273
 
276
- parts.append(create_markdown_toc(toc_headings))
274
+ parts.append(markdown_table(toc_headings))
277
275
 
278
276
  # Handle subsections
279
277
  for section in self.sections:
@@ -437,13 +435,13 @@ class CodeSection(BaseText):
437
435
  parts = []
438
436
 
439
437
  if self.title:
440
- parts.append(md_heading(self.title, self.heading_level))
438
+ parts.append(markdown_heading(self.title, self.heading_level))
441
439
 
442
440
  if self.description:
443
441
  parts.append(self.description)
444
442
 
445
443
  if self.content:
446
- parts.append(md_code_block(str(self.content), self.language or ""))
444
+ parts.append(markdown_code_block(str(self.content), self.language or ""))
447
445
 
448
446
  # Handle subsections
449
447
  for section in self.sections:
@@ -466,7 +464,7 @@ class SchemaSection(BaseText):
466
464
  def _to_markdown(self, **kwargs) -> str:
467
465
  """Convert schema to Markdown documentation."""
468
466
  if self.schema_object:
469
- return convert_to_markdown(
467
+ return convert_to_text(
470
468
  self.schema_object,
471
469
  name=self.title,
472
470
  description=self.description,
@@ -0,0 +1,11 @@
1
+ """hammad.types
2
+
3
+ Contains functional aliases, types and model-like objects that are used
4
+ internally within the `hammad` package, as well as usable for
5
+ various other cases."""
6
+
7
+ from typing import TYPE_CHECKING
8
+ from .._core._utils._import_utils import _auto_create_getattr_loader
9
+
10
+ if TYPE_CHECKING:
11
+ from .file import File, FileSource
@@ -1,4 +1,4 @@
1
- """hammad.data.types.files.file"""
1
+ """hammad.types.file"""
2
2
 
3
3
  from pathlib import Path
4
4
  import httpx
@@ -6,8 +6,8 @@ from typing import Any, Self
6
6
  import mimetypes
7
7
  from urllib.parse import urlparse
8
8
 
9
- from ....based.model import BasedModel
10
- from ....based.fields import basedfield
9
+ from ..base.model import Model
10
+ from ..base.fields import field
11
11
 
12
12
  __all__ = ("File", "FileSource")
13
13
 
@@ -26,41 +26,41 @@ _mime_cache: dict[str, str] = {}
26
26
  """Cache for MIME types."""
27
27
 
28
28
 
29
- class FileSource(BasedModel, kw_only=True, dict=True, frozen=True):
29
+ class FileSource(Model, kw_only=True, dict=True, frozen=True):
30
30
  """Represents the source of a `File` object."""
31
31
 
32
- is_file: bool = basedfield(default=False)
32
+ is_file: bool = field(default=False)
33
33
  """Whether this data represents a file."""
34
- is_dir: bool = basedfield(default=False)
34
+ is_dir: bool = field(default=False)
35
35
  """Whether this data represents a directory."""
36
- is_url: bool = basedfield(default=False)
36
+ is_url: bool = field(default=False)
37
37
  """Whether this data originates from a URL."""
38
- path: Path | None = basedfield(default=None)
38
+ path: Path | None = field(default=None)
39
39
  """The file path if this is file-based data."""
40
- url: str | None = basedfield(default=None)
40
+ url: str | None = field(default=None)
41
41
  """The URL if this is URL-based data."""
42
- size: int | None = basedfield(default=None)
42
+ size: int | None = field(default=None)
43
43
  """Size in bytes if available."""
44
- encoding: str | None = basedfield(default=None)
44
+ encoding: str | None = field(default=None)
45
45
  """Text encoding if applicable."""
46
46
 
47
47
 
48
- class File(BasedModel, kw_only=True, dict=True):
48
+ class File(Model, kw_only=True, dict=True):
49
49
  """Base object for all file-like structure types within
50
50
  the `hammad` ecosystem."""
51
51
 
52
- data: Any | None = basedfield(default=None)
52
+ data: Any | None = field(default=None)
53
53
  """The actual data content (bytes, string, path object, etc.)"""
54
- type: str | None = basedfield(default=None)
54
+ type: str | None = field(default=None)
55
55
  """The MIME type or identifier for the data."""
56
56
 
57
- source: FileSource = basedfield(default_factory=FileSource)
57
+ source: FileSource = field(default_factory=FileSource)
58
58
  """The source of the data. Contains metadata as well."""
59
59
 
60
60
  # Private cached attributes
61
- _name: str | None = basedfield(default=None)
62
- _extension: str | None = basedfield(default=None)
63
- _repr: str | None = basedfield(default=None)
61
+ _name: str | None = field(default=None)
62
+ _extension: str | None = field(default=None)
63
+ _repr: str | None = field(default=None)
64
64
 
65
65
  @property
66
66
  def name(self) -> str | None:
hammad/typing/__init__.py CHANGED
@@ -1,18 +1,18 @@
1
- """hammad.utils.typing
1
+ """hammad.typing
2
2
 
3
- Contains various utility functions in regards to typing or working
4
- with type hints. These utils are also exposed in:
5
-
6
- ```python
7
- import hammad
8
-
9
- hammad.typing.get_type_description(Optional[int])
10
- ...
11
- ```
12
- """
3
+ 'Namespace' package extension for various **CORE** typing resources and
4
+ types. This is not a collection of built types, rather resources from the
5
+ core `typing` module, `typing_extensions`, `typing_inspect` and other
6
+ resources."""
13
7
 
14
8
  from typing import Any, TYPE_CHECKING
15
9
  import typing_inspect as inspection
10
+
11
+ try:
12
+ from typing_extensions import *
13
+ except ImportError:
14
+ from typing import *
15
+
16
16
  from typing_inspect import (
17
17
  is_callable_type,
18
18
  is_classvar,
@@ -40,6 +40,129 @@ from typing_inspection.introspection import (
40
40
  from dataclasses import is_dataclass
41
41
 
42
42
  __all__ = (
43
+ # Super-special typing primitives.
44
+ "Any",
45
+ "ClassVar",
46
+ "Concatenate",
47
+ "Final",
48
+ "LiteralString",
49
+ "ParamSpec",
50
+ "ParamSpecArgs",
51
+ "ParamSpecKwargs",
52
+ "Self",
53
+ "Type",
54
+ "TypeVar",
55
+ "TypeVarTuple",
56
+ "Unpack",
57
+ # ABCs (from collections.abc).
58
+ "Awaitable",
59
+ "AsyncIterator",
60
+ "AsyncIterable",
61
+ "Coroutine",
62
+ "AsyncGenerator",
63
+ "AsyncContextManager",
64
+ "Buffer",
65
+ "ChainMap",
66
+ # Concrete collection types.
67
+ "ContextManager",
68
+ "Counter",
69
+ "Deque",
70
+ "DefaultDict",
71
+ "NamedTuple",
72
+ "OrderedDict",
73
+ "TypedDict",
74
+ # Structural checks, a.k.a. protocols.
75
+ "SupportsAbs",
76
+ "SupportsBytes",
77
+ "SupportsComplex",
78
+ "SupportsFloat",
79
+ "SupportsIndex",
80
+ "SupportsInt",
81
+ "SupportsRound",
82
+ "Reader",
83
+ "Writer",
84
+ # One-off things.
85
+ "Annotated",
86
+ "assert_never",
87
+ "assert_type",
88
+ "clear_overloads",
89
+ "dataclass_transform",
90
+ "deprecated",
91
+ "Doc",
92
+ "evaluate_forward_ref",
93
+ "get_overloads",
94
+ "final",
95
+ "Format",
96
+ "get_annotations",
97
+ "get_args",
98
+ "get_origin",
99
+ "get_original_bases",
100
+ "get_protocol_members",
101
+ "get_type_hints",
102
+ "IntVar",
103
+ "is_protocol",
104
+ "is_typeddict",
105
+ "Literal",
106
+ "NewType",
107
+ "overload",
108
+ "override",
109
+ "Protocol",
110
+ "Sentinel",
111
+ "reveal_type",
112
+ "runtime",
113
+ "runtime_checkable",
114
+ "Text",
115
+ "TypeAlias",
116
+ "TypeAliasType",
117
+ "TypeForm",
118
+ "TypeGuard",
119
+ "TypeIs",
120
+ "TYPE_CHECKING",
121
+ "Never",
122
+ "NoReturn",
123
+ "ReadOnly",
124
+ "Required",
125
+ "NotRequired",
126
+ "NoDefault",
127
+ "NoExtraItems",
128
+ # Pure aliases, have always been in typing
129
+ "AbstractSet",
130
+ "AnyStr",
131
+ "BinaryIO",
132
+ "Callable",
133
+ "Collection",
134
+ "Container",
135
+ "Dict",
136
+ "ForwardRef",
137
+ "FrozenSet",
138
+ "Generator",
139
+ "Generic",
140
+ "Hashable",
141
+ "IO",
142
+ "ItemsView",
143
+ "Iterable",
144
+ "Iterator",
145
+ "KeysView",
146
+ "List",
147
+ "Mapping",
148
+ "MappingView",
149
+ "Match",
150
+ "MutableMapping",
151
+ "MutableSequence",
152
+ "MutableSet",
153
+ "Optional",
154
+ "Pattern",
155
+ "Reversible",
156
+ "Sequence",
157
+ "Set",
158
+ "Sized",
159
+ "TextIO",
160
+ "Tuple",
161
+ "Union",
162
+ "ValuesView",
163
+ "cast",
164
+ "no_type_check",
165
+ "no_type_check_decorator",
43
166
  "TypingError",
44
167
  "get_type_description",
45
168
  "inspection",
@@ -79,7 +202,7 @@ class TypingError(Exception):
79
202
  # ------------------------------------------------------------------------
80
203
 
81
204
 
82
- def is_pydantic_basemodel(t: Any) -> bool:
205
+ def is_pydantic_basemodel(t: "Any") -> bool:
83
206
  """Check if an object is a Pydantic BaseModel class or instance using duck typing.
84
207
 
85
208
  This function uses duck typing to identify Pydantic BaseModel objects by checking
@@ -120,7 +243,7 @@ def is_pydantic_basemodel(t: Any) -> bool:
120
243
  )
121
244
 
122
245
 
123
- def is_pydantic_basemodel_instance(t: Any) -> bool:
246
+ def is_pydantic_basemodel_instance(t: "Any") -> bool:
124
247
  """Check if an object is an instance (not class) of a Pydantic BaseModel using duck typing.
125
248
 
126
249
  This function specifically identifies Pydantic BaseModel instances by ensuring
@@ -151,7 +274,7 @@ def is_pydantic_basemodel_instance(t: Any) -> bool:
151
274
  )
152
275
 
153
276
 
154
- def is_msgspec_struct(t: Any) -> bool:
277
+ def is_msgspec_struct(t: "Any") -> bool:
155
278
  """Check if an object is a msgspec Struct class or instance using duck typing.
156
279
 
157
280
  This function uses duck typing to identify msgspec Struct objects by checking
@@ -179,7 +302,7 @@ def is_msgspec_struct(t: Any) -> bool:
179
302
  return hasattr(t, "__struct_fields__") and hasattr(t, "__struct_config__")
180
303
 
181
304
 
182
- def get_type_description(t: Any) -> str:
305
+ def get_type_description(t: "Any") -> str:
183
306
  """Creates a human-readable description of a type hint.
184
307
 
185
308
  Args:
@@ -282,72 +405,3 @@ def get_type_description(t: Any) -> str:
282
405
  return f"typevar({t.__name__})"
283
406
 
284
407
  return str(t)
285
-
286
-
287
- if TYPE_CHECKING:
288
- pass
289
-
290
-
291
- def create_lazy_loader(imports_dict: dict[str, str], package: str):
292
- """Create a lazy loader function for __getattr__.
293
-
294
- Args:
295
- imports_dict: Dictionary mapping attribute names to their module paths
296
- package: The package name for import_module
297
-
298
- Returns:
299
- A __getattr__ function that lazily imports modules
300
- """
301
-
302
- def __getattr__(name: str):
303
- if name in imports_dict:
304
- from importlib import import_module
305
-
306
- module = import_module(imports_dict[name], package)
307
- return getattr(module, name)
308
- raise AttributeError(f"module '{package}' has no attribute '{name}'")
309
-
310
- return __getattr__
311
-
312
-
313
- def parse_type_checking_imports(source_code: str) -> dict[str, str]:
314
- """Parse TYPE_CHECKING imports from source code to build import map.
315
-
316
- This is a simple parser that extracts import information from
317
- TYPE_CHECKING blocks to automatically build the module map.
318
-
319
- Args:
320
- source_code: The source code containing TYPE_CHECKING imports
321
-
322
- Returns:
323
- Dictionary mapping imported names to their module paths
324
- """
325
- import ast
326
- import re
327
-
328
- # Parse the source code
329
- tree = ast.parse(source_code)
330
-
331
- imports_map = {}
332
- in_type_checking = False
333
-
334
- for node in ast.walk(tree):
335
- # Check if we're in a TYPE_CHECKING block
336
- if isinstance(node, ast.If):
337
- if (
338
- isinstance(node.test, ast.Name) and node.test.id == "TYPE_CHECKING"
339
- ) or (
340
- isinstance(node.test, ast.Attribute)
341
- and isinstance(node.test.value, ast.Name)
342
- and node.test.value.id == "typing"
343
- and node.test.attr == "TYPE_CHECKING"
344
- ):
345
- # Process imports in the TYPE_CHECKING block
346
- for stmt in node.body:
347
- if isinstance(stmt, ast.ImportFrom):
348
- module = stmt.module or ""
349
- for alias in stmt.names:
350
- name = alias.asname or alias.name
351
- imports_map[name] = f".{module}"
352
-
353
- return imports_map
hammad/web/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """hammad.web"""
2
2
 
3
3
  from typing import TYPE_CHECKING
4
- from ..based.utils import auto_create_lazy_loader
4
+ from .._core._utils._import_utils import _auto_create_getattr_loader
5
5
 
6
6
  if TYPE_CHECKING:
7
7
  from .utils import (
@@ -34,7 +34,8 @@ __all__ = (
34
34
  "create_search_client",
35
35
  )
36
36
 
37
- __getattr__ = auto_create_lazy_loader(__all__)
37
+
38
+ __getattr__ = _auto_create_getattr_loader(__all__)
38
39
 
39
40
 
40
41
  def __dir__() -> list[str]: