hammad-python 0.0.13__py3-none-any.whl → 0.0.15__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.
- hammad_python-0.0.15.dist-info/METADATA +184 -0
- hammad_python-0.0.15.dist-info/RECORD +4 -0
- hammad/__init__.py +0 -180
- hammad/_core/__init__.py +0 -1
- hammad/_core/_utils/__init__.py +0 -4
- hammad/_core/_utils/_import_utils.py +0 -182
- hammad/ai/__init__.py +0 -59
- hammad/ai/_utils.py +0 -142
- hammad/ai/completions/__init__.py +0 -44
- hammad/ai/completions/client.py +0 -729
- hammad/ai/completions/create.py +0 -686
- hammad/ai/completions/types.py +0 -711
- hammad/ai/completions/utils.py +0 -374
- hammad/ai/embeddings/__init__.py +0 -35
- hammad/ai/embeddings/client/__init__.py +0 -1
- hammad/ai/embeddings/client/base_embeddings_client.py +0 -26
- hammad/ai/embeddings/client/fastembed_text_embeddings_client.py +0 -200
- hammad/ai/embeddings/client/litellm_embeddings_client.py +0 -288
- hammad/ai/embeddings/create.py +0 -159
- hammad/ai/embeddings/types.py +0 -69
- hammad/base/__init__.py +0 -35
- hammad/base/fields.py +0 -546
- hammad/base/model.py +0 -1078
- hammad/base/utils.py +0 -280
- hammad/cache/__init__.py +0 -48
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -604
- hammad/cli/plugins.py +0 -781
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -480
- hammad/configuration/__init__.py +0 -35
- hammad/configuration/configuration.py +0 -564
- hammad/data/__init__.py +0 -39
- hammad/data/collections/__init__.py +0 -34
- hammad/data/collections/base_collection.py +0 -58
- hammad/data/collections/collection.py +0 -452
- hammad/data/collections/searchable_collection.py +0 -556
- hammad/data/collections/vector_collection.py +0 -603
- hammad/data/databases/__init__.py +0 -21
- hammad/data/databases/database.py +0 -902
- hammad/json/__init__.py +0 -21
- hammad/json/converters.py +0 -152
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -954
- hammad/multimodal/__init__.py +0 -24
- hammad/multimodal/audio.py +0 -96
- hammad/multimodal/image.py +0 -80
- hammad/multithreading/__init__.py +0 -304
- hammad/py.typed +0 -0
- hammad/pydantic/__init__.py +0 -43
- hammad/pydantic/converters.py +0 -623
- hammad/pydantic/models/__init__.py +0 -28
- hammad/pydantic/models/arbitrary_model.py +0 -46
- hammad/pydantic/models/cacheable_model.py +0 -79
- hammad/pydantic/models/fast_model.py +0 -318
- hammad/pydantic/models/function_model.py +0 -176
- hammad/pydantic/models/subscriptable_model.py +0 -63
- hammad/text/__init__.py +0 -82
- hammad/text/converters.py +0 -723
- hammad/text/markdown.py +0 -131
- hammad/text/text.py +0 -1066
- hammad/types/__init__.py +0 -11
- hammad/types/file.py +0 -358
- hammad/typing/__init__.py +0 -407
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -245
- hammad/web/openapi/__init__.py +0 -0
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -988
- hammad/web/utils.py +0 -472
- hammad/yaml/__init__.py +0 -30
- hammad/yaml/converters.py +0 -19
- hammad_python-0.0.13.dist-info/METADATA +0 -38
- hammad_python-0.0.13.dist-info/RECORD +0 -85
- {hammad_python-0.0.13.dist-info → hammad_python-0.0.15.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.13.dist-info → hammad_python-0.0.15.dist-info}/licenses/LICENSE +0 -0
hammad/ai/completions/utils.py
DELETED
@@ -1,374 +0,0 @@
|
|
1
|
-
"""hammad.ai.completions.utils"""
|
2
|
-
|
3
|
-
import json
|
4
|
-
from typing import (
|
5
|
-
Optional,
|
6
|
-
List,
|
7
|
-
Iterator,
|
8
|
-
AsyncIterator,
|
9
|
-
TypeVar,
|
10
|
-
Type,
|
11
|
-
Any,
|
12
|
-
)
|
13
|
-
|
14
|
-
try:
|
15
|
-
from pydantic import BaseModel
|
16
|
-
except ImportError:
|
17
|
-
raise ImportError(
|
18
|
-
"Using completion stream parsing requires the `openai` and `instructor` packages."
|
19
|
-
"Please install with: pip install 'hammad-python[ai]'"
|
20
|
-
)
|
21
|
-
|
22
|
-
from ...cache import cached
|
23
|
-
from .types import (
|
24
|
-
CompletionsInputParam,
|
25
|
-
ChatCompletionMessageParam,
|
26
|
-
CompletionStream,
|
27
|
-
AsyncCompletionStream,
|
28
|
-
Completion,
|
29
|
-
)
|
30
|
-
|
31
|
-
T = TypeVar("T", bound=BaseModel)
|
32
|
-
|
33
|
-
__all__ = (
|
34
|
-
"parse_completions_input",
|
35
|
-
"create_completion_stream",
|
36
|
-
"create_async_completion_stream",
|
37
|
-
"format_tool_calls",
|
38
|
-
"convert_response_to_completion",
|
39
|
-
)
|
40
|
-
|
41
|
-
|
42
|
-
@cached
|
43
|
-
def parse_completions_input(
|
44
|
-
input: CompletionsInputParam,
|
45
|
-
instructions: Optional[str] = None,
|
46
|
-
) -> List[ChatCompletionMessageParam]:
|
47
|
-
"""Parse various input formats into a list of ChatCompletionMessageParam.
|
48
|
-
|
49
|
-
This function handles:
|
50
|
-
- Plain strings (converted to user messages)
|
51
|
-
- Strings with message blocks like [system], [user], [assistant]
|
52
|
-
- Single ChatCompletionMessageParam objects
|
53
|
-
- Lists of ChatCompletionMessageParam objects
|
54
|
-
- Objects with model_dump() method
|
55
|
-
|
56
|
-
Args:
|
57
|
-
input: The input to parse
|
58
|
-
instructions: Optional system instructions to prepend
|
59
|
-
|
60
|
-
Returns:
|
61
|
-
List of ChatCompletionMessageParam objects
|
62
|
-
"""
|
63
|
-
messages: List[ChatCompletionMessageParam] = []
|
64
|
-
|
65
|
-
# Handle string inputs
|
66
|
-
if isinstance(input, str):
|
67
|
-
# Check if string contains message blocks like [system], [user], [assistant]
|
68
|
-
import re
|
69
|
-
|
70
|
-
# Pattern to match only allowed message blocks (system, user, assistant)
|
71
|
-
pattern = (
|
72
|
-
r"\[(system|user|assistant)\]\s*(.*?)(?=\[(?:system|user|assistant)\]|$)"
|
73
|
-
)
|
74
|
-
matches = re.findall(pattern, input, re.DOTALL | re.IGNORECASE)
|
75
|
-
|
76
|
-
if matches:
|
77
|
-
# Validate that we only have allowed roles
|
78
|
-
allowed_roles = {"system", "user", "assistant"}
|
79
|
-
found_roles = {role.lower() for role, _ in matches}
|
80
|
-
|
81
|
-
if not found_roles.issubset(allowed_roles):
|
82
|
-
invalid_roles = found_roles - allowed_roles
|
83
|
-
raise ValueError(
|
84
|
-
f"Invalid message roles found: {invalid_roles}. Only 'system', 'user', and 'assistant' are allowed."
|
85
|
-
)
|
86
|
-
|
87
|
-
# Parse message blocks
|
88
|
-
system_contents = []
|
89
|
-
|
90
|
-
for role, content in matches:
|
91
|
-
content = content.strip()
|
92
|
-
if content:
|
93
|
-
if role.lower() == "system":
|
94
|
-
system_contents.append(content)
|
95
|
-
else:
|
96
|
-
messages.append({"role": role.lower(), "content": content})
|
97
|
-
|
98
|
-
# Combine system contents if any exist
|
99
|
-
if system_contents:
|
100
|
-
combined_system = "\n\n".join(system_contents)
|
101
|
-
if instructions:
|
102
|
-
combined_system = f"{combined_system}\n\n{instructions}"
|
103
|
-
messages.insert(0, {"role": "system", "content": combined_system})
|
104
|
-
elif instructions:
|
105
|
-
messages.insert(0, {"role": "system", "content": instructions})
|
106
|
-
else:
|
107
|
-
# Plain string - create user message
|
108
|
-
if instructions:
|
109
|
-
messages.append({"role": "system", "content": instructions})
|
110
|
-
messages.append({"role": "user", "content": input})
|
111
|
-
|
112
|
-
# Handle single message object
|
113
|
-
elif hasattr(input, "model_dump"):
|
114
|
-
message_dict = input.model_dump()
|
115
|
-
if instructions:
|
116
|
-
messages.append({"role": "system", "content": instructions})
|
117
|
-
messages.append(message_dict)
|
118
|
-
|
119
|
-
# Handle list of messages
|
120
|
-
elif isinstance(input, list):
|
121
|
-
system_contents = []
|
122
|
-
other_messages = []
|
123
|
-
|
124
|
-
for item in input:
|
125
|
-
if hasattr(item, "model_dump"):
|
126
|
-
msg_dict = item.model_dump()
|
127
|
-
else:
|
128
|
-
msg_dict = item
|
129
|
-
|
130
|
-
if msg_dict.get("role") == "system":
|
131
|
-
system_contents.append(msg_dict.get("content", ""))
|
132
|
-
else:
|
133
|
-
other_messages.append(msg_dict)
|
134
|
-
|
135
|
-
# Combine system messages and instructions
|
136
|
-
if system_contents or instructions:
|
137
|
-
combined_system_parts = []
|
138
|
-
if system_contents:
|
139
|
-
combined_system_parts.extend(system_contents)
|
140
|
-
if instructions:
|
141
|
-
combined_system_parts.append(instructions)
|
142
|
-
|
143
|
-
messages.append(
|
144
|
-
{"role": "system", "content": "\n\n".join(combined_system_parts)}
|
145
|
-
)
|
146
|
-
|
147
|
-
messages.extend(other_messages)
|
148
|
-
|
149
|
-
# Handle single dictionary or other object
|
150
|
-
else:
|
151
|
-
if hasattr(input, "model_dump"):
|
152
|
-
message_dict = input.model_dump()
|
153
|
-
else:
|
154
|
-
message_dict = input
|
155
|
-
|
156
|
-
if instructions:
|
157
|
-
messages.append({"role": "system", "content": instructions})
|
158
|
-
messages.append(message_dict)
|
159
|
-
|
160
|
-
return messages
|
161
|
-
|
162
|
-
|
163
|
-
def create_completion_stream(
|
164
|
-
stream: Iterator[Any], output_type: Type[T] = str, model: str | None = None
|
165
|
-
) -> CompletionStream[T]:
|
166
|
-
"""Create a unified completion stream from a raw stream.
|
167
|
-
|
168
|
-
This function wraps raw streams from both LiteLLM and Instructor
|
169
|
-
into a unified CompletionStream interface. It automatically detects
|
170
|
-
the stream type based on the output_type parameter.
|
171
|
-
|
172
|
-
Args:
|
173
|
-
stream: The raw stream from LiteLLM or Instructor
|
174
|
-
output_type: The expected output type (str for LiteLLM, model class for Instructor)
|
175
|
-
model: The model name for metadata
|
176
|
-
|
177
|
-
Returns:
|
178
|
-
CompletionStream: Unified stream interface
|
179
|
-
|
180
|
-
Examples:
|
181
|
-
# For LiteLLM string completions
|
182
|
-
litellm_stream = litellm.completion(model="gpt-4", messages=messages, stream=True)
|
183
|
-
unified_stream = create_completion_stream(litellm_stream, str, "gpt-4")
|
184
|
-
|
185
|
-
# For Instructor structured outputs
|
186
|
-
instructor_stream = instructor_client.completion(response_model=User, messages=messages, stream=True)
|
187
|
-
unified_stream = create_completion_stream(instructor_stream, User, "gpt-4")
|
188
|
-
"""
|
189
|
-
return CompletionStream(stream, output_type, model)
|
190
|
-
|
191
|
-
|
192
|
-
def create_async_completion_stream(
|
193
|
-
stream: AsyncIterator[Any], output_type: Type[T] = str, model: str | None = None
|
194
|
-
) -> AsyncCompletionStream[T]:
|
195
|
-
"""Create a unified async completion stream from a raw async stream.
|
196
|
-
|
197
|
-
This function wraps raw async streams from both LiteLLM and Instructor
|
198
|
-
into a unified AsyncCompletionStream interface. It automatically detects
|
199
|
-
the stream type based on the output_type parameter.
|
200
|
-
|
201
|
-
Args:
|
202
|
-
stream: The raw async stream from LiteLLM or Instructor
|
203
|
-
output_type: The expected output type (str for LiteLLM, model class for Instructor)
|
204
|
-
model: The model name for metadata
|
205
|
-
|
206
|
-
Returns:
|
207
|
-
AsyncCompletionStream: Unified async stream interface
|
208
|
-
|
209
|
-
Examples:
|
210
|
-
```python
|
211
|
-
# For LiteLLM async string completions
|
212
|
-
litellm_stream = await litellm.acompletion(model="gpt-4", messages=messages, stream=True)
|
213
|
-
unified_stream = create_async_completion_stream(litellm_stream, str, "gpt-4")
|
214
|
-
|
215
|
-
# For Instructor async structured outputs
|
216
|
-
instructor_stream = await instructor_client.acompletion(response_model=User, messages=messages, stream=True)
|
217
|
-
unified_stream = create_async_completion_stream(instructor_stream, User, "gpt-4")
|
218
|
-
```
|
219
|
-
"""
|
220
|
-
return AsyncCompletionStream(stream, output_type, model)
|
221
|
-
|
222
|
-
|
223
|
-
def format_tool_calls(
|
224
|
-
messages: List[ChatCompletionMessageParam],
|
225
|
-
) -> List[ChatCompletionMessageParam]:
|
226
|
-
"""Format message thread by replacing tool call blocks with readable assistant messages.
|
227
|
-
|
228
|
-
This function processes a message thread and replaces sequences of:
|
229
|
-
assistant(with tool_calls) + tool + tool + ... with a single clean assistant message
|
230
|
-
that describes what tools were called and their results.
|
231
|
-
|
232
|
-
Args:
|
233
|
-
messages: List of messages in the conversation thread
|
234
|
-
|
235
|
-
Returns:
|
236
|
-
List[ChatCompletionMessageParam]: Cleaned message thread with tool calls formatted
|
237
|
-
|
238
|
-
Example:
|
239
|
-
```python
|
240
|
-
messages = [
|
241
|
-
{"role": "user", "content": "What's the weather in NYC?"},
|
242
|
-
{"role": "assistant", "tool_calls": [...]},
|
243
|
-
{"role": "tool", "tool_call_id": "call_1", "content": "Sunny, 72°F"},
|
244
|
-
{"role": "user", "content": "Thanks!"}
|
245
|
-
]
|
246
|
-
|
247
|
-
formatted = format_tool_calls(messages)
|
248
|
-
# Returns: [
|
249
|
-
# {"role": "user", "content": "What's the weather in NYC?"},
|
250
|
-
# {"role": "assistant", "content": "I called get_weather tool with parameters (city=NYC), and got result: Sunny, 72°F"},
|
251
|
-
# {"role": "user", "content": "Thanks!"}
|
252
|
-
# ]
|
253
|
-
```
|
254
|
-
"""
|
255
|
-
if not messages:
|
256
|
-
return messages
|
257
|
-
|
258
|
-
formatted_messages = []
|
259
|
-
i = 0
|
260
|
-
|
261
|
-
while i < len(messages):
|
262
|
-
current_msg = messages[i]
|
263
|
-
|
264
|
-
# Check if this is an assistant message with tool calls
|
265
|
-
if current_msg.get("role") == "assistant" and current_msg.get("tool_calls"):
|
266
|
-
# Collect all following tool messages
|
267
|
-
tool_results = {}
|
268
|
-
j = i + 1
|
269
|
-
|
270
|
-
# Gather tool results that follow this assistant message
|
271
|
-
while j < len(messages) and messages[j].get("role") == "tool":
|
272
|
-
tool_msg = messages[j]
|
273
|
-
tool_call_id = tool_msg.get("tool_call_id")
|
274
|
-
if tool_call_id:
|
275
|
-
tool_results[tool_call_id] = tool_msg.get("content", "No result")
|
276
|
-
j += 1
|
277
|
-
|
278
|
-
# Format the tool calls with their results
|
279
|
-
tool_calls = current_msg.get("tool_calls", [])
|
280
|
-
formatted_calls = []
|
281
|
-
|
282
|
-
for tool_call in tool_calls:
|
283
|
-
tool_name = tool_call.function.name
|
284
|
-
tool_args = tool_call.function.arguments
|
285
|
-
tool_id = tool_call.id
|
286
|
-
|
287
|
-
# Parse arguments for cleaner display
|
288
|
-
try:
|
289
|
-
args_dict = json.loads(tool_args) if tool_args else {}
|
290
|
-
args_str = ", ".join([f"{k}={v}" for k, v in args_dict.items()])
|
291
|
-
except json.JSONDecodeError:
|
292
|
-
args_str = tool_args or "no parameters"
|
293
|
-
|
294
|
-
# Get the result for this tool call
|
295
|
-
result = tool_results.get(tool_id, "No result available")
|
296
|
-
|
297
|
-
# Format the tool call description
|
298
|
-
call_description = f"I called {tool_name} tool with parameters ({args_str}), and got result: {result}"
|
299
|
-
formatted_calls.append(call_description)
|
300
|
-
|
301
|
-
# Create the formatted assistant message
|
302
|
-
if len(formatted_calls) == 1:
|
303
|
-
content = formatted_calls[0]
|
304
|
-
elif len(formatted_calls) > 1:
|
305
|
-
content = "I made the following tool calls:\n" + "\n".join(
|
306
|
-
[f"- {call}" for call in formatted_calls]
|
307
|
-
)
|
308
|
-
else:
|
309
|
-
content = "I made tool calls but no results were available."
|
310
|
-
|
311
|
-
# Add the formatted message
|
312
|
-
formatted_messages.append({"role": "assistant", "content": content})
|
313
|
-
|
314
|
-
# Skip past all the tool messages we processed
|
315
|
-
i = j
|
316
|
-
else:
|
317
|
-
# Regular message, add as-is
|
318
|
-
formatted_messages.append(current_msg)
|
319
|
-
i += 1
|
320
|
-
|
321
|
-
return formatted_messages
|
322
|
-
|
323
|
-
|
324
|
-
def convert_response_to_completion(response: Any) -> Completion[str]:
|
325
|
-
"""Convert a LiteLLM ModelResponse to a Completion object.
|
326
|
-
|
327
|
-
This function converts LiteLLM's ModelResponse (which is based on OpenAI's
|
328
|
-
ChatCompletion format) into our unified Completion type for standard
|
329
|
-
string completions.
|
330
|
-
|
331
|
-
Args:
|
332
|
-
response: The ModelResponse from LiteLLM
|
333
|
-
|
334
|
-
Returns:
|
335
|
-
Completion[str]: Unified completion object with string output
|
336
|
-
|
337
|
-
Example:
|
338
|
-
```python
|
339
|
-
# For LiteLLM completions
|
340
|
-
response = await litellm.acompletion(model="gpt-4", messages=messages)
|
341
|
-
completion = convert_response_to_completion(response)
|
342
|
-
```
|
343
|
-
"""
|
344
|
-
# Handle empty or invalid response
|
345
|
-
if not hasattr(response, "choices") or not response.choices:
|
346
|
-
return Completion(
|
347
|
-
output="",
|
348
|
-
model=getattr(response, "model", "unknown"),
|
349
|
-
content=None,
|
350
|
-
completion=response,
|
351
|
-
)
|
352
|
-
|
353
|
-
choice = response.choices[0]
|
354
|
-
|
355
|
-
# Extract message data
|
356
|
-
if hasattr(choice, "message"):
|
357
|
-
message = choice.message
|
358
|
-
content = getattr(message, "content", None)
|
359
|
-
tool_calls = getattr(message, "tool_calls", None)
|
360
|
-
refusal = getattr(message, "refusal", None)
|
361
|
-
else:
|
362
|
-
# Fallback for different response structures
|
363
|
-
content = None
|
364
|
-
tool_calls = None
|
365
|
-
refusal = None
|
366
|
-
|
367
|
-
return Completion(
|
368
|
-
output=content or "",
|
369
|
-
model=getattr(response, "model", "unknown"),
|
370
|
-
content=content,
|
371
|
-
tool_calls=tool_calls,
|
372
|
-
refusal=refusal,
|
373
|
-
completion=response,
|
374
|
-
)
|
hammad/ai/embeddings/__init__.py
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
"""hammad.ai.embeddings"""
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
from ..._core._utils._import_utils import _auto_create_getattr_loader
|
5
|
-
|
6
|
-
if TYPE_CHECKING:
|
7
|
-
from .client.base_embeddings_client import BaseEmbeddingsClient
|
8
|
-
from .client.fastembed_text_embeddings_client import FastEmbedTextEmbeddingsClient
|
9
|
-
from .client.litellm_embeddings_client import LiteLlmEmbeddingsClient
|
10
|
-
from .types import Embedding, EmbeddingResponse, EmbeddingUsage
|
11
|
-
from .create import create_embeddings, async_create_embeddings
|
12
|
-
|
13
|
-
|
14
|
-
__all__ = (
|
15
|
-
# hammad.ai.embeddings.client.base_embeddings_client
|
16
|
-
"BaseEmbeddingsClient",
|
17
|
-
# hammad.ai.embeddings.client.fastembed_text_embeddings_client
|
18
|
-
"FastEmbedTextEmbeddingsClient",
|
19
|
-
# hammad.ai.embeddings.client.litellm_embeddings_client
|
20
|
-
"LiteLlmEmbeddingsClient",
|
21
|
-
# hammad.ai.embeddings.types
|
22
|
-
"Embedding",
|
23
|
-
"EmbeddingResponse",
|
24
|
-
"EmbeddingUsage",
|
25
|
-
# hammad.ai.embeddings.create
|
26
|
-
"create_embeddings",
|
27
|
-
"async_create_embeddings",
|
28
|
-
)
|
29
|
-
|
30
|
-
|
31
|
-
__getattr__ = _auto_create_getattr_loader(__all__)
|
32
|
-
|
33
|
-
|
34
|
-
def __dir__() -> list[str]:
|
35
|
-
return list(__all__)
|
@@ -1 +0,0 @@
|
|
1
|
-
"""hammad.ai.embeddings.client"""
|
@@ -1,26 +0,0 @@
|
|
1
|
-
"""hammad.ai.embeddings.client.base_embeddings_client"""
|
2
|
-
|
3
|
-
from abc import ABC, abstractmethod
|
4
|
-
|
5
|
-
from ..types import (
|
6
|
-
EmbeddingResponse,
|
7
|
-
)
|
8
|
-
|
9
|
-
__all__ = ("BaseEmbeddingsClient",)
|
10
|
-
|
11
|
-
|
12
|
-
class BaseEmbeddingsClient(ABC):
|
13
|
-
"""Base class for the various supported embeddings clients within
|
14
|
-
the `hammad.ai` extension."""
|
15
|
-
|
16
|
-
@staticmethod
|
17
|
-
@abstractmethod
|
18
|
-
def async_embed(input: list, model: str, **kwargs) -> EmbeddingResponse:
|
19
|
-
""""""
|
20
|
-
pass
|
21
|
-
|
22
|
-
@staticmethod
|
23
|
-
@abstractmethod
|
24
|
-
def embed(input: list, model: str, **kwargs) -> EmbeddingResponse:
|
25
|
-
""""""
|
26
|
-
pass
|
@@ -1,200 +0,0 @@
|
|
1
|
-
"""hammad.ai.embeddings.client.fastembed_text_embeddings_client"""
|
2
|
-
|
3
|
-
from typing import Any, List, Optional, Union, Literal
|
4
|
-
import sys
|
5
|
-
|
6
|
-
if sys.version_info >= (3, 12):
|
7
|
-
from typing import TypedDict
|
8
|
-
else:
|
9
|
-
from typing_extensions import TypedDict
|
10
|
-
|
11
|
-
from .base_embeddings_client import BaseEmbeddingsClient
|
12
|
-
from ..types import (
|
13
|
-
Embedding,
|
14
|
-
EmbeddingUsage,
|
15
|
-
EmbeddingResponse,
|
16
|
-
)
|
17
|
-
from ....text.converters import convert_to_text
|
18
|
-
from ..._utils import (
|
19
|
-
get_fastembed_text_embedding_model,
|
20
|
-
)
|
21
|
-
|
22
|
-
|
23
|
-
__all__ = (
|
24
|
-
"FastEmbedTextEmbeddingsClient",
|
25
|
-
"FastEmbedTextEmbeddingModel",
|
26
|
-
"FastEmbedTextEmbeddingModelSettings",
|
27
|
-
)
|
28
|
-
|
29
|
-
|
30
|
-
FastEmbedTextEmbeddingModel = Literal[
|
31
|
-
"BAAI/bge-small-en-v1.5",
|
32
|
-
"BAAI/bge-small-zh-v1.5",
|
33
|
-
"snowflake/snowflake-arctic-embed-xs",
|
34
|
-
"sentence-transformers/all-MiniLM-L6-v2",
|
35
|
-
"jinaai/jina-embeddings-v2-small-en",
|
36
|
-
"BAAI/bge-small-en",
|
37
|
-
"snowflake/snowflake-arctic-embed-s",
|
38
|
-
"nomic-ai/nomic-embed-text-v1.5-Q",
|
39
|
-
"BAAI/bge-base-en-v1.5",
|
40
|
-
"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
|
41
|
-
"Qdrant/clip-ViT-B-32-text",
|
42
|
-
"jinaai/jina-embeddings-v2-base-de",
|
43
|
-
"BAAI/bge-base-en",
|
44
|
-
"snowflake/snowflake-arctic-embed-m",
|
45
|
-
"nomic-ai/nomic-embed-text-v1.5",
|
46
|
-
"jinaai/jina-embeddings-v2-base-en",
|
47
|
-
"nomic-ai/nomic-embed-text-v1",
|
48
|
-
"snowflake/snowflake-arctic-embed-m-long",
|
49
|
-
"mixedbread-ai/mxbai-embed-large-v1",
|
50
|
-
"jinaai/jina-embeddings-v2-base-code",
|
51
|
-
"sentence-transformers/paraphrase-multilingual-mpnet-base-v2",
|
52
|
-
"snowflake/snowflake-arctic-embed-l",
|
53
|
-
"thenlper/gte-large",
|
54
|
-
"BAAI/bge-large-en-v1.5",
|
55
|
-
"intfloat/multilingual-e5-large",
|
56
|
-
]
|
57
|
-
"""All supported text embedding models supported by `fastembed`."""
|
58
|
-
|
59
|
-
|
60
|
-
class FastEmbedTextEmbeddingModelSettings(TypedDict):
|
61
|
-
"""Valid settings for the `fastembed` text embedding models."""
|
62
|
-
|
63
|
-
model: FastEmbedTextEmbeddingModel | str
|
64
|
-
parallel: Optional[int]
|
65
|
-
batch_size: Optional[int]
|
66
|
-
format: bool
|
67
|
-
|
68
|
-
|
69
|
-
class FastEmbedTextEmbeddingError(Exception):
|
70
|
-
"""Exception raised when an error occurs while generating embeddings
|
71
|
-
using `fastembed` text embedding models."""
|
72
|
-
|
73
|
-
def __init__(self, message: str, response: Any):
|
74
|
-
self.message = message
|
75
|
-
self.response = response
|
76
|
-
|
77
|
-
|
78
|
-
def _parse_fastembed_response_to_embedding_response(
|
79
|
-
response: Any,
|
80
|
-
model: FastEmbedTextEmbeddingModel | str,
|
81
|
-
) -> EmbeddingResponse:
|
82
|
-
"""Parse the response from the `fastembed` text embedding model to an `EmbeddingResponse` object."""
|
83
|
-
try:
|
84
|
-
embedding_data: List[Embedding] = []
|
85
|
-
|
86
|
-
# Convert generator to list if needed
|
87
|
-
if hasattr(response, "__iter__") and not isinstance(response, (list, tuple)):
|
88
|
-
response = list(response)
|
89
|
-
|
90
|
-
for i, item in enumerate(response):
|
91
|
-
# Ensure item is a list of floats
|
92
|
-
if hasattr(item, "tolist"):
|
93
|
-
item = item.tolist()
|
94
|
-
elif not isinstance(item, list):
|
95
|
-
item = list(item)
|
96
|
-
|
97
|
-
embedding_data.append(
|
98
|
-
Embedding(embedding=item, index=i, object="embedding")
|
99
|
-
)
|
100
|
-
|
101
|
-
return EmbeddingResponse(
|
102
|
-
data=embedding_data,
|
103
|
-
model=str(model),
|
104
|
-
object="list",
|
105
|
-
usage=EmbeddingUsage(prompt_tokens=0, total_tokens=0),
|
106
|
-
)
|
107
|
-
except Exception as e:
|
108
|
-
raise FastEmbedTextEmbeddingError(
|
109
|
-
f"Failed to parse fastembed response to embedding response: {e}",
|
110
|
-
response,
|
111
|
-
)
|
112
|
-
|
113
|
-
|
114
|
-
class FastEmbedTextEmbeddingsClient(BaseEmbeddingsClient):
|
115
|
-
"""Client for the `fastembed` text embedding models."""
|
116
|
-
|
117
|
-
@staticmethod
|
118
|
-
def embed(
|
119
|
-
input: List[Any] | Any,
|
120
|
-
model: FastEmbedTextEmbeddingModel | str,
|
121
|
-
parallel: Optional[int] = None,
|
122
|
-
batch_size: Optional[int] = None,
|
123
|
-
format: bool = False,
|
124
|
-
**kwargs: Any,
|
125
|
-
) -> EmbeddingResponse:
|
126
|
-
"""Generate embeddings for the given input using
|
127
|
-
a valid `fastembed` text embedding model.
|
128
|
-
|
129
|
-
Args:
|
130
|
-
input (List[Any] | Any) : The input text / content to generate embeddings for.
|
131
|
-
model (FastEmbedTextEmbeddingModel | str) : The model to use for generating embeddings.
|
132
|
-
parallel (Optional[int]) : The number of parallel processes to use for the embedding.
|
133
|
-
batch_size (Optional[int]) : The batch size to use for the embedding.
|
134
|
-
format (bool) : Whether to format each non-string input as a markdown string.
|
135
|
-
**kwargs : Any : Additional keyword arguments to pass to the `fastembed` text embedding model.
|
136
|
-
|
137
|
-
Returns:
|
138
|
-
EmbeddingResponse : The embedding response from the `fastembed` text embedding model.
|
139
|
-
"""
|
140
|
-
if not isinstance(input, list):
|
141
|
-
input = [input]
|
142
|
-
|
143
|
-
if format:
|
144
|
-
for i in input:
|
145
|
-
try:
|
146
|
-
i = convert_to_text(i)
|
147
|
-
except Exception as e:
|
148
|
-
raise FastEmbedTextEmbeddingError(
|
149
|
-
f"Failed to format input to text: {e}",
|
150
|
-
i,
|
151
|
-
)
|
152
|
-
|
153
|
-
model = get_fastembed_text_embedding_model(model)
|
154
|
-
|
155
|
-
try:
|
156
|
-
response = model.embed(
|
157
|
-
documents=input,
|
158
|
-
parallel=parallel,
|
159
|
-
batch_size=batch_size,
|
160
|
-
**kwargs,
|
161
|
-
)
|
162
|
-
except Exception as e:
|
163
|
-
raise FastEmbedTextEmbeddingError(
|
164
|
-
f"Failed to generate embeddings: {e}",
|
165
|
-
input,
|
166
|
-
)
|
167
|
-
|
168
|
-
return _parse_fastembed_response_to_embedding_response(response, str(model))
|
169
|
-
|
170
|
-
@staticmethod
|
171
|
-
async def async_embed(
|
172
|
-
input: List[Any] | Any,
|
173
|
-
model: FastEmbedTextEmbeddingModel | str,
|
174
|
-
parallel: Optional[int] = None,
|
175
|
-
batch_size: Optional[int] = None,
|
176
|
-
format: bool = False,
|
177
|
-
**kwargs: Any,
|
178
|
-
) -> EmbeddingResponse:
|
179
|
-
"""Async generate embeddings for the given input using
|
180
|
-
a valid `fastembed` text embedding model.
|
181
|
-
|
182
|
-
Args:
|
183
|
-
input (List[Any] | Any) : The input text / content to generate embeddings for.
|
184
|
-
model (FastEmbedTextEmbeddingModel | str) : The model to use for generating embeddings.
|
185
|
-
parallel (Optional[int]) : The number of parallel processes to use for the embedding.
|
186
|
-
batch_size (Optional[int]) : The batch size to use for the embedding.
|
187
|
-
format (bool) : Whether to format each non-string input as a markdown string.
|
188
|
-
**kwargs : Any : Additional keyword arguments to pass to the `fastembed` text embedding model.
|
189
|
-
|
190
|
-
Returns:
|
191
|
-
EmbeddingResponse : The embedding response from the `fastembed` text embedding model.
|
192
|
-
"""
|
193
|
-
return FastEmbedTextEmbeddingsClient.embed(
|
194
|
-
input=input,
|
195
|
-
model=model,
|
196
|
-
parallel=parallel,
|
197
|
-
batch_size=batch_size,
|
198
|
-
format=format,
|
199
|
-
**kwargs,
|
200
|
-
)
|