unique_toolkit 0.6.2__py3-none-any.whl → 0.6.5__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.
@@ -0,0 +1,17 @@
1
+ # Re-export commonly used classes for easier imports
2
+ from unique_toolkit.chat import ChatService
3
+ from unique_toolkit.content import ContentService
4
+ from unique_toolkit.embedding import EmbeddingService
5
+ from unique_toolkit.language_model import LanguageModelMessages, LanguageModelService
6
+ from unique_toolkit.short_term_memory import ShortTermMemoryService
7
+
8
+ # You can add other classes you frequently use here as well
9
+
10
+ __all__ = [
11
+ "LanguageModelService",
12
+ "LanguageModelMessages",
13
+ "ChatService",
14
+ "ContentService",
15
+ "EmbeddingService",
16
+ "ShortTermMemoryService",
17
+ ]
@@ -204,6 +204,49 @@ def _upsert_content(
204
204
  ) # type: ignore
205
205
 
206
206
 
207
+ def upload_content_from_bytes(
208
+ user_id: str,
209
+ company_id: str,
210
+ content: bytes,
211
+ content_name: str,
212
+ mime_type: str,
213
+ scope_id: str | None = None,
214
+ chat_id: str | None = None,
215
+ skip_ingestion: bool = False,
216
+ ):
217
+ """
218
+ Uploads content to the knowledge base.
219
+
220
+ Args:
221
+ user_id (str): The user ID.
222
+ company_id (str): The company ID.
223
+ content (bytes): The content to upload.
224
+ content_name (str): The name of the content.
225
+ mime_type (str): The MIME type of the content.
226
+ scope_id (str | None): The scope ID. Defaults to None.
227
+ chat_id (str | None): The chat ID. Defaults to None.
228
+ skip_ingestion (bool): Whether to skip ingestion. Defaults to False.
229
+
230
+ Returns:
231
+ Content: The uploaded content.
232
+ """
233
+
234
+ try:
235
+ return _trigger_upload_content(
236
+ user_id=user_id,
237
+ company_id=company_id,
238
+ content=content,
239
+ content_name=content_name,
240
+ mime_type=mime_type,
241
+ scope_id=scope_id,
242
+ chat_id=chat_id,
243
+ skip_ingestion=skip_ingestion,
244
+ )
245
+ except Exception as e:
246
+ logger.error(f"Error while uploading content: {e}")
247
+ raise e
248
+
249
+
207
250
  def upload_content(
208
251
  user_id: str,
209
252
  company_id: str,
@@ -235,7 +278,7 @@ def upload_content(
235
278
  return _trigger_upload_content(
236
279
  user_id=user_id,
237
280
  company_id=company_id,
238
- path_to_content=path_to_content,
281
+ content=path_to_content,
239
282
  content_name=content_name,
240
283
  mime_type=mime_type,
241
284
  scope_id=scope_id,
@@ -250,7 +293,7 @@ def upload_content(
250
293
  def _trigger_upload_content(
251
294
  user_id: str,
252
295
  company_id: str,
253
- path_to_content: str,
296
+ content: str | Path | bytes,
254
297
  content_name: str,
255
298
  mime_type: str,
256
299
  scope_id: str | None = None,
@@ -263,7 +306,7 @@ def _trigger_upload_content(
263
306
  Args:
264
307
  user_id (str): The user ID.
265
308
  company_id (str): The company ID.
266
- path_to_content (str): The path to the content to upload.
309
+ content (str | Path | bytes): The content to upload. If string or Path, file will be read from disk.
267
310
  content_name (str): The name of the content.
268
311
  mime_type (str): The MIME type of the content.
269
312
  scope_id (str | None): The scope ID. Defaults to None.
@@ -277,7 +320,9 @@ def _trigger_upload_content(
277
320
  if not chat_id and not scope_id:
278
321
  raise ValueError("chat_id or scope_id must be provided")
279
322
 
280
- byte_size = os.path.getsize(path_to_content)
323
+ byte_size = (
324
+ os.path.getsize(content) if isinstance(content, (Path, str)) else len(content)
325
+ )
281
326
  created_content = _upsert_content(
282
327
  user_id=user_id,
283
328
  company_id=company_id,
@@ -297,16 +342,24 @@ def _trigger_upload_content(
297
342
  logger.error(error_msg)
298
343
  raise ValueError(error_msg)
299
344
 
345
+ headers = {
346
+ "X-Ms-Blob-Content-Type": mime_type,
347
+ "X-Ms-Blob-Type": "BlockBlob",
348
+ }
300
349
  # upload to azure blob storage SAS url uploadUrl the pdf file translatedFile make sure it is treated as a application/pdf
301
- with open(path_to_content, "rb") as file:
350
+ if isinstance(content, bytes):
302
351
  requests.put(
303
352
  url=write_url,
304
- data=file,
305
- headers={
306
- "X-Ms-Blob-Content-Type": mime_type,
307
- "X-Ms-Blob-Type": "BlockBlob",
308
- },
353
+ data=content,
354
+ headers=headers,
309
355
  )
356
+ else:
357
+ with open(content, "rb") as file:
358
+ requests.put(
359
+ url=write_url,
360
+ data=file,
361
+ headers=headers,
362
+ )
310
363
 
311
364
  read_url = created_content["readUrl"]
312
365
 
@@ -431,6 +484,23 @@ def download_content_to_file_by_id(
431
484
  return content_path
432
485
 
433
486
 
487
+ def download_content_to_bytes(
488
+ user_id: str,
489
+ company_id: str,
490
+ content_id: str,
491
+ chat_id: str | None,
492
+ ) -> bytes:
493
+ logger.info(f"Downloading content with content_id: {content_id}")
494
+ response = request_content_by_id(user_id, company_id, content_id, chat_id)
495
+
496
+ if response.status_code != 200:
497
+ error_msg = f"Error downloading file: Status code {response.status_code}"
498
+ logger.error(error_msg)
499
+ raise Exception(error_msg)
500
+
501
+ return response.content
502
+
503
+
434
504
  # TODO: Discuss if we should deprecate this method due to unclear use by content_name
435
505
  def download_content(
436
506
  user_id: str,
@@ -10,6 +10,7 @@ from unique_toolkit.content import DOMAIN_NAME
10
10
  from unique_toolkit.content.constants import DEFAULT_SEARCH_LANGUAGE
11
11
  from unique_toolkit.content.functions import (
12
12
  download_content,
13
+ download_content_to_bytes,
13
14
  download_content_to_file_by_id,
14
15
  request_content_by_id,
15
16
  search_content_chunks,
@@ -17,6 +18,7 @@ from unique_toolkit.content.functions import (
17
18
  search_contents,
18
19
  search_contents_async,
19
20
  upload_content,
21
+ upload_content_from_bytes,
20
22
  )
21
23
  from unique_toolkit.content.schemas import (
22
24
  Content,
@@ -222,6 +224,41 @@ class ContentService:
222
224
 
223
225
  return self.search_contents(where)
224
226
 
227
+ def upload_content_from_bytes(
228
+ self,
229
+ content: bytes,
230
+ content_name: str,
231
+ mime_type: str,
232
+ scope_id: str | None = None,
233
+ chat_id: str | None = None,
234
+ skip_ingestion: bool = False,
235
+ ) -> Content:
236
+ """
237
+ Uploads content to the knowledge base.
238
+
239
+ Args:
240
+ content (bytes): The content to upload.
241
+ content_name (str): The name of the content.
242
+ mime_type (str): The MIME type of the content.
243
+ scope_id (str | None): The scope ID. Defaults to None.
244
+ chat_id (str | None): The chat ID. Defaults to None.
245
+ skip_ingestion (bool): Whether to skip ingestion. Defaults to False.
246
+
247
+ Returns:
248
+ Content: The uploaded content.
249
+ """
250
+
251
+ return upload_content_from_bytes(
252
+ user_id=self.user_id,
253
+ company_id=self.company_id,
254
+ content=content,
255
+ content_name=content_name,
256
+ mime_type=mime_type,
257
+ scope_id=scope_id,
258
+ chat_id=chat_id,
259
+ skip_ingestion=skip_ingestion,
260
+ )
261
+
225
262
  def upload_content(
226
263
  self,
227
264
  path_to_content: str,
@@ -344,3 +381,29 @@ class ContentService:
344
381
  chat_id=chat_id,
345
382
  dir_path=dir_path,
346
383
  )
384
+
385
+ def download_content_to_bytes(
386
+ self,
387
+ content_id: str,
388
+ chat_id: str | None = None,
389
+ ) -> bytes:
390
+ """
391
+ Downloads content to memory
392
+
393
+ Args:
394
+ content_id (str): The id of the uploaded content.
395
+ chat_id (Optional[str]): The chat_id, defaults to None.
396
+
397
+ Returns:
398
+ bytes: The downloaded content.
399
+
400
+ Raises:
401
+ Exception: If the download fails.
402
+ """
403
+
404
+ return download_content_to_bytes(
405
+ user_id=self.user_id,
406
+ company_id=self.company_id,
407
+ content_id=content_id,
408
+ chat_id=chat_id,
409
+ )
@@ -1,3 +1,5 @@
1
+ from typing_extensions import Self
2
+
1
3
  from unique_toolkit.language_model import (
2
4
  LanguageModelAssistantMessage,
3
5
  LanguageModelMessage,
@@ -12,25 +14,38 @@ class MessagesBuilder:
12
14
  def __init__(self):
13
15
  self.messages: list[LanguageModelMessage] = []
14
16
 
15
- def system_message_append(self, content):
16
- """Appends a user message to the messages list."""
17
+ def system_message_append(self, content: str) -> Self:
18
+ """Appends a system message to the messages list."""
17
19
  message = LanguageModelSystemMessage(content=content)
18
20
  self.messages.append(message)
19
21
  return self # Return self to allow method chaining
20
22
 
21
- def user_message_append(self, content):
23
+ def user_message_append(self, content: str) -> Self:
22
24
  """Appends a user message to the messages list."""
23
25
  message = LanguageModelUserMessage(content=content)
24
26
  self.messages.append(message)
25
27
  return self # Return self to allow method chaining
26
28
 
27
- def assistant_message_append(self, content):
29
+ def image_message_append(self, content: str, images: list[str]) -> Self:
30
+ message = LanguageModelUserMessage(
31
+ content=[
32
+ {"type": "text", "text": content},
33
+ *[
34
+ {"type": "image_url", "imageUrl": {"url": image}}
35
+ for image in images
36
+ ],
37
+ ]
38
+ )
39
+ self.messages.append(message)
40
+ return self
41
+
42
+ def assistant_message_append(self, content: str) -> Self:
28
43
  """Appends an assistant message to the messages list."""
29
44
  message = LanguageModelAssistantMessage(content=content)
30
45
  self.messages.append(message)
31
46
  return self # Return self to allow method chaining
32
47
 
33
- def tool_message_append(self, name: str, tool_call_id: str, content: str):
48
+ def tool_message_append(self, name: str, tool_call_id: str, content: str) -> Self:
34
49
  """Appends a tool message to the messages list."""
35
50
  message = LanguageModelToolMessage(
36
51
  name=name, tool_call_id=tool_call_id, content=content
@@ -165,6 +165,14 @@ class LanguageModelMessages(RootModel):
165
165
  def __getitem__(self, item):
166
166
  return self.root[item]
167
167
 
168
+ def builder(self):
169
+ """Returns a MessagesBuilder instance pre-populated with existing messages."""
170
+ from unique_toolkit.language_model.builder import MessagesBuilder
171
+
172
+ builder = MessagesBuilder()
173
+ builder.messages = self.root.copy() # Start with existing messages
174
+ return builder
175
+
168
176
 
169
177
  class LanguageModelCompletionChoice(BaseModel):
170
178
  model_config = model_config
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 0.6.2
3
+ Version: 0.6.5
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Martin Fadler
@@ -111,6 +111,16 @@ All notable changes to this project will be documented in this file.
111
111
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
112
112
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
113
113
 
114
+ ## [0.6.5] - 2025-03-04
115
+ - Add `upload_content_from_bytes` to `ContentService`
116
+ - Add `download_content_to_bytes` to `ContentService`
117
+
118
+ ## [0.6.3] - 2025-02-27
119
+ - Simplified imports for services. `from unique_toolkit.language_model import LanguageModelService` -> `from unique_toolkit import LanguageModelService` to reduce number of import lines.
120
+
121
+ ## [0.6.3] - 2025-02-26
122
+ - Add `builder` method to `LanguageModelMessages` class
123
+
114
124
  ## [0.6.2] - 2025-02-25
115
125
  - Deprecate `LanguageModel` in favor of `LanguageModelInfo`
116
126
  - `LanguageModelTokenLimits` properties become mandatory, initialization allows
@@ -1,4 +1,4 @@
1
- unique_toolkit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1
+ unique_toolkit/__init__.py,sha256=waK7W0EK3v2RJ26hawccwVz1i3yHGvHIIu5qgGjEGHQ,583
2
2
  unique_toolkit/_common/_base_service.py,sha256=S8H0rAebx7GsOldA7xInLp3aQJt9yEPDQdsGSFRJsGg,276
3
3
  unique_toolkit/_common/_time_utils.py,sha256=ztmTovTvr-3w71Ns2VwXC65OKUUh-sQlzbHdKTQWm-w,135
4
4
  unique_toolkit/_common/exception.py,sha256=caQIE1btsQnpKCHqL2cgWUSbHup06enQu_Pt7uGUTTE,727
@@ -20,9 +20,9 @@ unique_toolkit/chat/state.py,sha256=Cjgwv_2vhDFbV69xxsn7SefhaoIAEqLx3ferdVFCnOg,
20
20
  unique_toolkit/chat/utils.py,sha256=ihm-wQykBWhB4liR3LnwPVPt_qGW6ETq21Mw4HY0THE,854
21
21
  unique_toolkit/content/__init__.py,sha256=EdJg_A_7loEtCQf4cah3QARQreJx6pdz89Rm96YbMVg,940
22
22
  unique_toolkit/content/constants.py,sha256=1iy4Y67xobl5VTnJB6SxSyuoBWbdLl9244xfVMUZi5o,60
23
- unique_toolkit/content/functions.py,sha256=0589SVNyePI-8uOc-mfWHLZr7hgSqeTpz-O9Y64KWfg,15096
23
+ unique_toolkit/content/functions.py,sha256=yB87wrbtmHzr3jGJUHetmuhy-7RVtnqG2IQ6gqFAun8,17093
24
24
  unique_toolkit/content/schemas.py,sha256=zks_Pkki2VhxICJJgHZyc-LPmRuj5dLbw3pgcUT7SW8,2362
25
- unique_toolkit/content/service.py,sha256=5_4ao-K4VtF5P_dkenh8dP94Ce7Yq2ro_dPBSKNZAc4,12412
25
+ unique_toolkit/content/service.py,sha256=CyQNW3TJX4trMnQhm5Ec2XGE9GHIW_NSGGT7YuOZIHQ,14234
26
26
  unique_toolkit/content/utils.py,sha256=GUVPrkZfMoAj4MRoBs5BD_7vSuLZTZx69hyWzYFrI50,7747
27
27
  unique_toolkit/embedding/__init__.py,sha256=uUyzjonPvuDCYsvXCIt7ErQXopLggpzX-MEQd3_e2kE,250
28
28
  unique_toolkit/embedding/constants.py,sha256=Lj8-Lcy1FvuC31PM9Exq7vaFuxQV4pEI1huUMFX-J2M,52
@@ -45,12 +45,12 @@ unique_toolkit/evaluators/hallucination/utils.py,sha256=4KTJH8low_fBzOcuVlcHB2FR
45
45
  unique_toolkit/evaluators/output_parser.py,sha256=eI72qkzK1dZyUvnfP2SOAQCGBj_-PwX5wy_aLPMsJMY,883
46
46
  unique_toolkit/evaluators/schemas.py,sha256=Jaue6Uhx75X1CyHKWj8sT3RE1JZXTqoLtfLt2xQNCX8,2507
47
47
  unique_toolkit/language_model/__init__.py,sha256=jWko_vQj48wjnpTtlkg8iNdef0SMI3FN2kGywXRTMzg,1880
48
- unique_toolkit/language_model/builder.py,sha256=nsRqWO_2dgFehK5CgtqR5aqXgYUU0QL6mR0lALPrQXM,1898
48
+ unique_toolkit/language_model/builder.py,sha256=qP1SlUnYJHLqT-fpXs4lgUixnekhx8IIfuoXnMHvRKE,2408
49
49
  unique_toolkit/language_model/constants.py,sha256=B-topqW0r83dkC_25DeQfnPk3n53qzIHUCBS7YJ0-1U,119
50
50
  unique_toolkit/language_model/functions.py,sha256=I5jHhHsKoq7GwEQyTrM8LXB2n_6dvMAk7UklenjuHSY,7945
51
51
  unique_toolkit/language_model/infos.py,sha256=-axWHj55mp6tZfX_3i-FSkfh8e9fwORXWMfi9xQ_UjA,12232
52
52
  unique_toolkit/language_model/prompt.py,sha256=JSawaLjQg3VR-E2fK8engFyJnNdk21zaO8pPIodzN4Q,3991
53
- unique_toolkit/language_model/schemas.py,sha256=76FtgWc0qtk9fpEwoecA59WxG__aR7_kYyWJooHIaF8,8297
53
+ unique_toolkit/language_model/schemas.py,sha256=rrwzUgKANFOrdehCULW8Hh03uRW3tsE5dXpWqxmClfg,8618
54
54
  unique_toolkit/language_model/service.py,sha256=GupYD4uDZjy1TfVQW3jichmgQwiSgQCj350FtL4O0W4,5569
55
55
  unique_toolkit/language_model/utils.py,sha256=bPQ4l6_YO71w-zaIPanUUmtbXC1_hCvLK0tAFc3VCRc,1902
56
56
  unique_toolkit/short_term_memory/__init__.py,sha256=2mI3AUrffgH7Yt-xS57EGqnHf7jnn6xquoKEhJqk3Wg,185
@@ -58,7 +58,7 @@ unique_toolkit/short_term_memory/constants.py,sha256=698CL6-wjup2MvU19RxSmQk3gX7
58
58
  unique_toolkit/short_term_memory/functions.py,sha256=3WiK-xatY5nh4Dr5zlDUye1k3E6kr41RiscwtTplw5k,4484
59
59
  unique_toolkit/short_term_memory/schemas.py,sha256=OhfcXyF6ACdwIXW45sKzjtZX_gkcJs8FEZXcgQTNenw,1406
60
60
  unique_toolkit/short_term_memory/service.py,sha256=gdsVzoNqTXmLoBR_-p_lJlZDBo8L7Cr5EKchTNVJg1Q,5233
61
- unique_toolkit-0.6.2.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
62
- unique_toolkit-0.6.2.dist-info/METADATA,sha256=X3vk5KZ3npmcio6kiyCcOKhw2WE7g4zLCkA-urhZsSA,19409
63
- unique_toolkit-0.6.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
64
- unique_toolkit-0.6.2.dist-info/RECORD,,
61
+ unique_toolkit-0.6.5.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
62
+ unique_toolkit-0.6.5.dist-info/METADATA,sha256=QpySQwwkqfBL9Mm9g8urq7LIhQrBy4LF7ZTWQroXED4,19835
63
+ unique_toolkit-0.6.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
64
+ unique_toolkit-0.6.5.dist-info/RECORD,,