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,33 +0,0 @@
1
- """hammad.data.types
2
-
3
- Contains various explicit data models and definitions for
4
- various file types, data formats, and other data related
5
- concepts."""
6
-
7
- from typing import TYPE_CHECKING
8
- from ...based.utils import auto_create_lazy_loader
9
-
10
- if TYPE_CHECKING:
11
- from .files.audio import Audio
12
- from .files.configuration import Configuration
13
- from .files.document import Document
14
- from .files.file import File, FileSource
15
- from .files.image import Image
16
-
17
-
18
- __all__ = (
19
- "Audio",
20
- "Configuration",
21
- "Document",
22
- "File",
23
- "FileSource",
24
- "Image",
25
- )
26
-
27
-
28
- __getattr__ = auto_create_lazy_loader(__all__)
29
-
30
-
31
- def __dir__() -> list[str]:
32
- """Get the attributes of the data.types module."""
33
- return list(__all__)
@@ -1 +0,0 @@
1
- """hammad.data.types.files"""
@@ -1,195 +0,0 @@
1
- """hammad.data.types.files.document"""
2
-
3
- import httpx
4
- from typing import Any, Self, Iterator
5
- from markdown_it import MarkdownIt
6
-
7
- from .file import File, FileSource
8
- from ....based.fields import basedfield
9
-
10
- __all__ = ("Document",)
11
-
12
-
13
- class Document(File):
14
- """A representation of a document, that is loadable from both a URL, file path
15
- or bytes. This document can additionally be used to represent web pages, as well
16
- as implement markdown formatting for both documents and web pages."""
17
-
18
- # Cached properties for text processing
19
- _lines: list[str] | None = basedfield(default=None)
20
- _content: str | None = basedfield(default=None)
21
- _md_parser: MarkdownIt | None = basedfield(default=None)
22
- metadata: dict[str, Any] = basedfield(default_factory=dict)
23
-
24
- @property
25
- def content(self) -> str:
26
- """Get the document content as string."""
27
- if self._content is None:
28
- data = self.read()
29
- self._content = (
30
- data
31
- if isinstance(data, str)
32
- else data.decode(self.source.encoding or "utf-8")
33
- )
34
- return self._content
35
-
36
- @property
37
- def lines(self) -> list[str]:
38
- """Get lines of the document (cached for efficiency)."""
39
- if self._lines is None:
40
- self._lines = self.content.splitlines(keepends=False)
41
- return self._lines
42
-
43
- @property
44
- def line_count(self) -> int:
45
- """Get the number of lines in the document."""
46
- return len(self.lines)
47
-
48
- @property
49
- def word_count(self) -> int:
50
- """Get the word count of the document."""
51
- return len(self.content.split())
52
-
53
- @property
54
- def char_count(self) -> int:
55
- """Get the character count of the document."""
56
- return len(self.content)
57
-
58
- @property
59
- def is_markdown(self) -> bool:
60
- """Check if the document is a markdown file."""
61
- return self.extension in {".md", ".markdown", ".mdown", ".mkd", ".mdx"}
62
-
63
- @property
64
- def md_parser(self) -> MarkdownIt:
65
- """Get the markdown parser (lazy initialization)."""
66
- if self._md_parser is None:
67
- self._md_parser = MarkdownIt()
68
- return self._md_parser
69
-
70
- def iter_lines(self, *, strip: bool = False) -> Iterator[str]:
71
- """Iterate over lines in the document.
72
-
73
- Args:
74
- strip: If True, strip whitespace from each line.
75
-
76
- Yields:
77
- Lines from the document.
78
- """
79
- for line in self.lines:
80
- yield line.strip() if strip else line
81
-
82
- def iter_paragraphs(self) -> Iterator[str]:
83
- """Iterate over paragraphs (text blocks separated by empty lines)."""
84
- paragraph = []
85
- for line in self.lines:
86
- if line.strip():
87
- paragraph.append(line)
88
- elif paragraph:
89
- yield "\n".join(paragraph)
90
- paragraph = []
91
- if paragraph:
92
- yield "\n".join(paragraph)
93
-
94
- def search(
95
- self, pattern: str, *, case_sensitive: bool = False
96
- ) -> list[tuple[int, str]]:
97
- """Search for a pattern in the document.
98
-
99
- Args:
100
- pattern: The pattern to search for.
101
- case_sensitive: If True, search is case-sensitive.
102
-
103
- Returns:
104
- List of tuples (line_number, line_content) for matching lines.
105
- """
106
- results = []
107
- search_pattern = pattern if case_sensitive else pattern.lower()
108
-
109
- for i, line in enumerate(self.lines):
110
- search_line = line if case_sensitive else line.lower()
111
- if search_pattern in search_line:
112
- results.append((i + 1, line)) # 1-indexed line numbers
113
-
114
- return results
115
-
116
- def render_markdown(self) -> str:
117
- """Render markdown content to HTML."""
118
- if not self.is_markdown:
119
- return self.content
120
- return self.md_parser.render(self.content)
121
-
122
- def extract_headers(self) -> list[tuple[int, str]]:
123
- """Extract headers from markdown documents.
124
-
125
- Returns:
126
- List of tuples (level, text) for each header.
127
- """
128
- headers = []
129
- if self.is_markdown:
130
- tokens = self.md_parser.parse(self.content)
131
- i = 0
132
- while i < len(tokens):
133
- if tokens[i].type == "heading_open":
134
- level = int(tokens[i].tag[1]) # h1 -> 1, h2 -> 2, etc.
135
- # Next token should be inline with the content
136
- if i + 1 < len(tokens) and tokens[i + 1].type == "inline":
137
- headers.append((level, tokens[i + 1].content))
138
- i += 1
139
- else:
140
- # For non-markdown files, look for common header patterns
141
- for line in self.lines:
142
- stripped = line.strip()
143
- if stripped.startswith("#"):
144
- level = len(line) - len(line.lstrip("#"))
145
- text = line.lstrip("#").strip()
146
- headers.append((level, text))
147
- return headers
148
-
149
- @classmethod
150
- def from_url(
151
- cls,
152
- url: str,
153
- *,
154
- lazy: bool = True,
155
- timeout: float = 30.0,
156
- ) -> Self:
157
- """Download and create a document from a URL.
158
-
159
- Args:
160
- url: The URL to download from.
161
- lazy: If True, defer loading content until needed.
162
- timeout: Request timeout in seconds.
163
-
164
- Returns:
165
- A new Document instance.
166
- """
167
- data = None
168
- size = None
169
- encoding = None
170
- type = None
171
-
172
- if not lazy:
173
- with httpx.Client(timeout=timeout) as client:
174
- response = client.get(url)
175
- response.raise_for_status()
176
-
177
- # Always get text for documents
178
- data = response.text
179
- size = len(data.encode("utf-8"))
180
- encoding = response.encoding
181
-
182
- # Get content type
183
- content_type = response.headers.get("content-type", "")
184
- type = content_type.split(";")[0] if content_type else "text/plain"
185
-
186
- return cls(
187
- data=data,
188
- type=type,
189
- source=FileSource(
190
- is_url=True,
191
- url=url,
192
- size=size,
193
- encoding=encoding,
194
- ),
195
- )
@@ -1 +0,0 @@
1
- """hammad.text.utils"""
@@ -1,229 +0,0 @@
1
- """hammad.text.utils.converters"""
2
-
3
- from docstring_parser import parse
4
- from typing import (
5
- Any,
6
- Optional,
7
- )
8
-
9
- from ...typing import (
10
- inspection,
11
- is_pydantic_basemodel,
12
- is_msgspec_struct,
13
- is_dataclass,
14
- )
15
-
16
- __all__ = [
17
- "convert_type_to_text",
18
- "convert_docstring_to_text",
19
- ]
20
-
21
-
22
- def convert_type_to_text(cls: Any) -> str:
23
- """Converts a type into a clean & human readable text representation.
24
-
25
- This function uses `typing_inspect` exclusively to infer nested types
26
- within `Optional`, `Union` types, for the cleanest possible string
27
- representation of a type.
28
-
29
- Args:
30
- cls: The type to convert to a text representation.
31
-
32
- Returns:
33
- A clean, human-readable string representation of the type.
34
- """
35
- # Handle None type
36
- if cls is None or cls is type(None):
37
- return "None"
38
-
39
- # Get origin and args using typing_inspect for better type handling
40
- origin = inspection.get_origin(cls)
41
- args = inspection.get_args(cls)
42
-
43
- if origin is not None:
44
- # Handle Optional (Union[T, None])
45
- if inspection.is_optional_type(cls):
46
- # Recursively get the name of the inner type (the one not None)
47
- inner_type = args[0]
48
- inner_type_name = convert_type_to_text(inner_type)
49
- return f"Optional[{inner_type_name}]"
50
-
51
- # Handle other Union types
52
- if inspection.is_union_type(cls):
53
- # Recursively get names of all arguments in the Union
54
- args_str = ", ".join(convert_type_to_text(arg) for arg in args)
55
- return f"Union[{args_str}]"
56
-
57
- # Handle other generic types (List, Dict, Tuple, Set, etc.)
58
- # Use origin.__name__ for built-in generics like list, dict, tuple, set
59
- origin_name = getattr(origin, "__name__", str(origin).split(".")[-1])
60
- if origin_name.startswith("_"): # Handle internal typing names like _List
61
- origin_name = origin_name[1:]
62
-
63
- # Convert to lowercase for built-in types to match modern Python style
64
- if origin_name in ["List", "Dict", "Tuple", "Set"]:
65
- origin_name = origin_name.lower()
66
-
67
- if args: # If there are type arguments
68
- # Recursively get names of type arguments
69
- args_str = ", ".join(convert_type_to_text(arg) for arg in args)
70
- return f"{origin_name}[{args_str}]"
71
- else: # Generic without arguments (e.g., typing.List)
72
- return origin_name
73
-
74
- # Handle special cases with typing_inspect
75
- if inspection.is_typevar(cls):
76
- return str(cls)
77
- if inspection.is_forward_ref(cls):
78
- return str(cls)
79
- if inspection.is_literal_type(cls):
80
- return f"Literal[{', '.join(str(arg) for arg in args)}]"
81
- if inspection.is_final_type(cls):
82
- return f"Final[{convert_type_to_text(args[0])}]" if args else "Final"
83
- if inspection.is_new_type(cls):
84
- return str(cls)
85
-
86
- # Handle Pydantic BaseModel types
87
- if is_pydantic_basemodel(cls):
88
- if hasattr(cls, "__name__"):
89
- return cls.__name__
90
- return "BaseModel"
91
-
92
- # Handle msgspec Struct types
93
- if is_msgspec_struct(cls):
94
- if hasattr(cls, "__name__"):
95
- return cls.__name__
96
- return "Struct"
97
-
98
- # Handle dataclass types
99
- if is_dataclass(cls):
100
- if hasattr(cls, "__name__"):
101
- return cls.__name__
102
- return "dataclass"
103
-
104
- # Handle basic types with __name__ attribute
105
- if hasattr(cls, "__name__") and cls.__name__ != "<lambda>":
106
- return cls.__name__
107
-
108
- # Special handling for Optional type string representation
109
- if str(cls).startswith("typing.Optional"):
110
- # Extract the inner type from the string representation
111
- inner_type_str = str(cls).replace("typing.Optional[", "").rstrip("]")
112
- return f"Optional[{inner_type_str}]"
113
-
114
- # Fallback for any other types
115
- # Clean up 'typing.' prefix and handle other common representations
116
- return str(cls).replace("typing.", "").replace("__main__.", "")
117
-
118
-
119
- def convert_docstring_to_text(
120
- obj: Any,
121
- *,
122
- params_override: Optional[str] = None,
123
- returns_override: Optional[str] = None,
124
- raises_override: Optional[str] = None,
125
- examples_override: Optional[str] = None,
126
- params_prefix: Optional[str] = None,
127
- returns_prefix: Optional[str] = None,
128
- raises_prefix: Optional[str] = None,
129
- exclude_params: bool = False,
130
- exclude_returns: bool = False,
131
- exclude_raises: bool = False,
132
- exclude_examples: bool = False,
133
- ) -> str:
134
- """
135
- Convert an object's docstring to formatted text using docstring_parser.
136
-
137
- Args:
138
- obj: The object to extract docstring from
139
- params_override: Override text for parameters section
140
- returns_override: Override text for returns section
141
- raises_override: Override text for raises section
142
- examples_override: Override text for examples section
143
- params_prefix: Prefix for parameters section
144
- returns_prefix: Prefix for returns section
145
- raises_prefix: Prefix for raises section
146
- exclude_params: Whether to exclude parameters section
147
- exclude_returns: Whether to exclude returns section
148
- exclude_raises: Whether to exclude raises section
149
- exclude_examples: Whether to exclude examples section
150
-
151
- Returns:
152
- Formatted text representation of the docstring
153
- """
154
- # Get the raw docstring
155
- doc = getattr(obj, "__doc__", None)
156
- if not doc:
157
- return ""
158
-
159
- try:
160
- # Parse the docstring using docstring_parser
161
- parsed = parse(doc)
162
-
163
- parts = []
164
-
165
- # Add short description
166
- if parsed.short_description:
167
- parts.append(parsed.short_description)
168
-
169
- # Add long description
170
- if parsed.long_description:
171
- parts.append("") # Empty line separator
172
- parts.append(parsed.long_description)
173
-
174
- # Add parameters section
175
- if not exclude_params and (params_override or parsed.params):
176
- parts.append("") # Empty line separator
177
- if params_override:
178
- parts.append(params_override)
179
- else:
180
- prefix = params_prefix or "Parameters:"
181
- parts.append(prefix)
182
- for param in parsed.params:
183
- param_line = f" {param.arg_name}"
184
- if param.type_name:
185
- param_line += f" ({param.type_name})"
186
- if param.description:
187
- param_line += f": {param.description}"
188
- parts.append(param_line)
189
-
190
- # Add returns section
191
- if not exclude_returns and (returns_override or parsed.returns):
192
- parts.append("") # Empty line separator
193
- if returns_override:
194
- parts.append(returns_override)
195
- else:
196
- prefix = returns_prefix or "Returns:"
197
- parts.append(prefix)
198
- if parsed.returns:
199
- return_line = " "
200
- if parsed.returns.type_name:
201
- return_line += f"{parsed.returns.type_name}: "
202
- if parsed.returns.description:
203
- return_line += parsed.returns.description
204
- parts.append(return_line)
205
-
206
- # Add raises section
207
- if not exclude_raises and (raises_override or parsed.raises):
208
- parts.append("") # Empty line separator
209
- if raises_override:
210
- parts.append(raises_override)
211
- else:
212
- prefix = raises_prefix or "Raises:"
213
- parts.append(prefix)
214
- for exc in parsed.raises:
215
- exc_line = f" {exc.type_name or 'Exception'}"
216
- if exc.description:
217
- exc_line += f": {exc.description}"
218
- parts.append(exc_line)
219
-
220
- # Add examples section (if available in parsed docstring)
221
- if not exclude_examples and examples_override:
222
- parts.append("") # Empty line separator
223
- parts.append(examples_override)
224
-
225
- return "\n".join(parts)
226
-
227
- except Exception:
228
- # Fallback to raw docstring if parsing fails
229
- return doc.strip()
@@ -1 +0,0 @@
1
- """hammad.text.utils.markdown"""