unique_sdk 0.10.1__py3-none-any.whl → 0.10.3__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.

Potentially problematic release.


This version of unique_sdk might be problematic. Click here for more details.

@@ -64,6 +64,7 @@ class Content(APIResource["Content"]):
64
64
  class SearchParams(RequestOptions):
65
65
  where: "Content.ContentWhereInput"
66
66
  chatId: NotRequired[str]
67
+ includeFailedContent: NotRequired[bool]
67
68
 
68
69
  class ContentInfoParams(TypedDict):
69
70
  """
@@ -163,11 +164,11 @@ class Content(APIResource["Content"]):
163
164
  class MagicTableSheetTable(TypedDict):
164
165
  rowId: str
165
166
  columns: List["Content.MagicTableSheetTableColumn"]
166
-
167
+
167
168
  class MagicTableSheetIngestionConfiguration(TypedDict):
168
169
  columnIdsInMetadata: List[str]
169
170
  columnIdsInChunkText: List[str]
170
-
171
+
171
172
  class MagicTableSheetIngestParams(TypedDict):
172
173
  data: List["Content.MagicTableSheetTable"]
173
174
  ingestionConfiguration: "Content.MagicTableSheetIngestionConfiguration"
@@ -298,10 +299,10 @@ class Content(APIResource["Content"]):
298
299
 
299
300
  @classmethod
300
301
  def ingest_magic_table_sheets(
301
- cls,
302
- user_id: str,
303
- company_id: str,
304
- **params: Unpack["Content.MagicTableSheetIngestParams"]
302
+ cls,
303
+ user_id: str,
304
+ company_id: str,
305
+ **params: Unpack["Content.MagicTableSheetIngestParams"],
305
306
  ) -> "Content.MagicTableSheetResponse":
306
307
  return cast(
307
308
  Content.MagicTableSheetResponse,
@@ -316,10 +317,10 @@ class Content(APIResource["Content"]):
316
317
 
317
318
  @classmethod
318
319
  async def ingest_magic_table_sheets_async(
319
- cls,
320
- user_id: str,
321
- company_id: str,
322
- **params: Unpack["Content.MagicTableSheetIngestParams"]
320
+ cls,
321
+ user_id: str,
322
+ company_id: str,
323
+ **params: Unpack["Content.MagicTableSheetIngestParams"],
323
324
  ) -> "Content.MagicTableSheetResponse":
324
325
  return cast(
325
326
  Content.MagicTableSheetResponse,
@@ -19,6 +19,7 @@ class Search(APIResource["Search"]):
19
19
  page: NotRequired[Optional[int]]
20
20
  metaDataFilter: NotRequired[Optional[dict[str, Any]]]
21
21
  contentIds: NotRequired[Optional[list[str]]]
22
+ scoreThreshold: NotRequired[Optional[float]]
22
23
 
23
24
  id: str
24
25
  chunkId: str
@@ -1,7 +1,9 @@
1
1
  import asyncio
2
2
  from typing import List
3
3
 
4
+ from unique_sdk.api_resources._content import Content
4
5
  from unique_sdk.api_resources._space import Space
6
+ from unique_sdk.utils.file_io import upload_file
5
7
 
6
8
 
7
9
  async def send_message_and_wait_for_completion(
@@ -30,7 +32,6 @@ async def send_message_and_wait_for_completion(
30
32
  Returns:
31
33
  The completed Space.Message.
32
34
  """
33
- # Send the prompt asynchronously
34
35
  response = await Space.create_message_async(
35
36
  user_id=user_id,
36
37
  company_id=company_id,
@@ -44,10 +45,123 @@ async def send_message_and_wait_for_completion(
44
45
 
45
46
  max_attempts = int(max_wait // poll_interval)
46
47
  for _ in range(max_attempts):
47
- # Poll for the answer
48
48
  answer = Space.get_latest_message(user_id, company_id, chat_id)
49
49
  if answer.get("stoppedStreamingAt") is not None:
50
50
  return answer
51
51
  await asyncio.sleep(poll_interval)
52
52
 
53
53
  raise TimeoutError("Timed out waiting for prompt completion.")
54
+
55
+
56
+ async def chat_against_file(
57
+ user_id: str,
58
+ company_id: str,
59
+ assistant_id: str,
60
+ path_to_file: str,
61
+ displayed_filename: str,
62
+ mime_type: str,
63
+ text: str,
64
+ poll_interval: float = 1.0,
65
+ max_wait: float = 60.0,
66
+ ) -> "Space.Message":
67
+ """
68
+ Chat against a file by uploading it, sending a message and waiting for a reply.
69
+ Args:
70
+
71
+ user_id: The user ID.
72
+ company_id: The company ID.
73
+ assistant_id: The assistant ID.
74
+ path_to_file: Path to the PDF file to upload.
75
+ displayed_filename: Name to display for the uploaded file.
76
+ mime_type: MIME type of the file (e.g., "application/pdf").
77
+ text: Text to send after uploading the file and chat against.
78
+ poll_interval: Seconds between polls for file ingestion.
79
+ max_wait: Maximum seconds to wait for the final response.
80
+
81
+ Returns:
82
+ The final message response.
83
+ """
84
+ chat_id = None
85
+
86
+ try:
87
+ response = await send_message_and_wait_for_completion(
88
+ user_id=user_id,
89
+ company_id=company_id,
90
+ assistant_id=assistant_id,
91
+ text="I'm going to upload a file for analysis.",
92
+ )
93
+ chat_id = response.get("chatId")
94
+
95
+ upload_response = upload_file(
96
+ userId=user_id,
97
+ companyId=company_id,
98
+ path_to_file=path_to_file,
99
+ displayed_filename=displayed_filename,
100
+ mime_type=mime_type,
101
+ chat_id=chat_id,
102
+ )
103
+ content_id = upload_response.get("id")
104
+
105
+ await wait_for_ingestion_completion(
106
+ user_id=user_id,
107
+ company_id=company_id,
108
+ content_id=content_id,
109
+ chat_id=chat_id,
110
+ poll_interval=poll_interval,
111
+ max_wait=max_wait,
112
+ )
113
+
114
+ final_response = await send_message_and_wait_for_completion(
115
+ user_id=user_id,
116
+ company_id=company_id,
117
+ assistant_id=assistant_id,
118
+ text=text,
119
+ chat_id=chat_id,
120
+ poll_interval=poll_interval,
121
+ max_wait=max_wait,
122
+ )
123
+
124
+ return final_response
125
+
126
+ except Exception as err:
127
+ print(f"Error during chat against file: {err}")
128
+ raise
129
+ finally:
130
+ if chat_id:
131
+ Space.delete_chat(
132
+ user_id=user_id,
133
+ company_id=company_id,
134
+ chat_id=chat_id,
135
+ )
136
+
137
+
138
+ async def wait_for_ingestion_completion(
139
+ user_id: str,
140
+ company_id: str,
141
+ content_id: str,
142
+ chat_id: str,
143
+ poll_interval: float = 1.0,
144
+ max_wait: float = 60.0,
145
+ ):
146
+ """
147
+ Waits until the content ingestionState is FINISHED or the maximum wait time is reached and throws if case of failed status.
148
+ """
149
+ max_attempts = int(max_wait // poll_interval)
150
+ for _ in range(max_attempts):
151
+ searched_content = Content.search(
152
+ user_id=user_id,
153
+ company_id=company_id,
154
+ where={"id": {"equals": content_id}},
155
+ chatId=chat_id,
156
+ includeFailedContent=True,
157
+ )
158
+ if searched_content:
159
+ ingestion_state = searched_content[0].get("ingestionState")
160
+ if ingestion_state == "FINISHED":
161
+ return
162
+ if isinstance(ingestion_state, str) and ingestion_state.startswith(
163
+ "FAILED"
164
+ ):
165
+ raise RuntimeError(f"Ingestion failed with state: {ingestion_state}")
166
+ await asyncio.sleep(poll_interval)
167
+ raise TimeoutError("Timed out waiting for file ingestion to finish.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_sdk
3
- Version: 0.10.1
3
+ Version: 0.10.3
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Martin Fadler
@@ -672,6 +672,7 @@ These are the options are available for `searchType`:
672
672
  `language` Optional. The language specification for full text search.
673
673
  `reranker` Optional. The reranker service to be used for re-ranking the search results.
674
674
  `chatId` Optional, adds the documents uploaded in this chat to the scope of searched documents.
675
+ `scoreThreshold` Optional, sets the minimum similarity score for search results to be considered. Using 0 is recommended.
675
676
 
676
677
  ```python
677
678
  search = unique_sdk.Search.create(
@@ -686,6 +687,7 @@ search = unique_sdk.Search.create(
686
687
  reranker={"deploymentName": "my_deployment"},
687
688
  limit=20,
688
689
  page=1
690
+ scoreThreshold=0
689
691
  )
690
692
  ```
691
693
 
@@ -1024,6 +1026,12 @@ A metadata filter such as the one designed above can be used in a `Search.create
1024
1026
 
1025
1027
  ## Utils
1026
1028
 
1029
+ - [Chat History](#chat-history)
1030
+ - [File Io](#file-io)
1031
+ - [Sources](#sources)
1032
+ - [token](#token)
1033
+ - [Chat In Space](#chat-in-space)
1034
+
1027
1035
  ### Chat History
1028
1036
 
1029
1037
  #### `unique_sdk.utils.chat_history.load_history`
@@ -1253,33 +1261,64 @@ The script ensures you can flexibly interact with spaces in new or ongoing chats
1253
1261
 
1254
1262
  ```python
1255
1263
  latest_message = await unique_sdk.utils.chat_in_space.send_message_and_wait_for_completion(
1256
- user_id=user_id,
1257
- company_id=company_id,
1258
- assistant_id=assistant_id,
1259
- text="Tell me a short story.",
1260
- chat_id=chat_id, # Optional - if no chat id is specified, a new chat will be created
1261
- tool_choices=["WebSearch"],
1262
- scope_rules={
1263
- "or": [
1264
- {
1265
- "operator": "in",
1266
- "path": [
1267
- "contentId"
1268
- ],
1269
- "value": [
1270
- "cont_u888z7cazxxm4lugfdjq7pks"
1271
- ]
1272
- },
1273
- {
1274
- "operator": "contains",
1275
- "path": [
1276
- "folderIdPath"
1277
- ],
1278
- "value": "uniquepathid://scope_btfo28b3eeelwh5obwgea71bl/scope_fn56ta67knd6w4medgq3028fx"
1279
- }
1280
- ]
1281
- },
1282
- )
1264
+ user_id=user_id,
1265
+ company_id=company_id,
1266
+ assistant_id=assistant_id,
1267
+ text="Tell me a short story.",
1268
+ chat_id=chat_id, # Optional - if no chat id is specified, a new chat will be created
1269
+ tool_choices=["WebSearch"],
1270
+ scope_rules={
1271
+ "or": [
1272
+ {
1273
+ "operator": "in",
1274
+ "path": [
1275
+ "contentId"
1276
+ ],
1277
+ "value": [
1278
+ "cont_u888z7cazxxm4lugfdjq7pks"
1279
+ ]
1280
+ },
1281
+ {
1282
+ "operator": "contains",
1283
+ "path": [
1284
+ "folderIdPath"
1285
+ ],
1286
+ "value": "uniquepathid://scope_btfo28b3eeelwh5obwgea71bl/scope_fn56ta67knd6w4medgq3028fx"
1287
+ }
1288
+ ]
1289
+ },
1290
+ )
1291
+ ```
1292
+
1293
+ #### `unique_sdk.utils.chat_in_space.chat_against_file`
1294
+
1295
+ The following script enables you to chat against a file.
1296
+
1297
+ You must provide the following parameters:
1298
+ - `assistantId`: The assistant to be used for the chat.
1299
+ - `path_to_file`: The local path of the file to be uploaded.
1300
+ - `displayed_filename`: The name of the file to be displayed.
1301
+ - `mime_type`: The mime type of the ifle to be uploaded.
1302
+ - `text`: The text to be sent to the chat for chatting against the file.
1303
+
1304
+ The script creates a chat and uploads the file to it. It then keeps polling the `ingestionState` field of the message, waiting for it to reach `FINISHED`, signaling the upload is complete. Once the file uploads successfully, the script sends the text, continues polling for completion, and finally retrieves the response message.
1305
+
1306
+ **Optional parameters:**
1307
+ - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1308
+ - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1309
+
1310
+ Example of chatting against a PDF. (The usage can be extended to any supported file type)
1311
+
1312
+ ```python
1313
+ latest_message = await unique_sdk.utils.chat_in_space.chat_against_file(
1314
+ user_id=user_id,
1315
+ company_id=company_id,
1316
+ assistant_id="assistant_hjcdga64bkcjnhu4",
1317
+ path_to_file="/files/hello.pdf",
1318
+ displayed_filename="hello.pdf"
1319
+ mime_type="application/pdf"
1320
+ text="Give me a bullet point summary of the file.",
1321
+ )
1283
1322
  ```
1284
1323
 
1285
1324
  ## Error Handling
@@ -1299,6 +1338,12 @@ All notable changes to this project will be documented in this file.
1299
1338
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1300
1339
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1301
1340
 
1341
+ ## [0.10.3] - 2025-08-05
1342
+ - Expose scoreThreshold param for search.
1343
+
1344
+ ## [0.10.2] - 2025-08-05
1345
+ - Add script to chat against file.
1346
+
1302
1347
  ## [0.10.1] - 2025-08-05
1303
1348
  - Allow deletion of a space chat.
1304
1349
 
@@ -16,7 +16,7 @@ unique_sdk/_webhook.py,sha256=GYxbUibQN_W4XlNTHaMIksT9FQJk4LJmlKcxOu3jqiU,2855
16
16
  unique_sdk/api_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  unique_sdk/api_resources/_acronyms.py,sha256=GIU1XH1flGWQYcpsFqTYwg4ioIGxVmb15tux84nmhEg,891
18
18
  unique_sdk/api_resources/_chat_completion.py,sha256=ILCAffxkbkfh2iV9L4KKnfe80gZmT9pWfkNmf3mq68U,2172
19
- unique_sdk/api_resources/_content.py,sha256=KIbAjXcf_-YiXAByW4wRYqLAqst47Bup8YXTIQ-PnTo,9550
19
+ unique_sdk/api_resources/_content.py,sha256=Rl1Pb0vUQ9EkEQa0XTGOLZamAYlZaz7h6jNWE-zqFaw,9554
20
20
  unique_sdk/api_resources/_embedding.py,sha256=C6qak7cCUBMBINfPhgH8taCJZ9n6w1MUElqDJJ8dG10,1281
21
21
  unique_sdk/api_resources/_event.py,sha256=bpWF9vstdoAWbUzr-iiGP713ceP0zPk77GJXiImf9zg,374
22
22
  unique_sdk/api_resources/_folder.py,sha256=dmPorUoOJSe-hcxlJq3RnkQeZg3gPqGF37ZsLgnloT0,8335
@@ -24,16 +24,16 @@ unique_sdk/api_resources/_integrated.py,sha256=l1vS8kJiSLie61mqDO3KI2MNmYwFydmCI
24
24
  unique_sdk/api_resources/_mcp.py,sha256=jBHf89LrjdYKoMeKJHyzKWih870VvXvGIl7hpf8dGIU,3353
25
25
  unique_sdk/api_resources/_message.py,sha256=gEDIzg3METZU2k7m69meAuf0IWmZxnYOjbBKPRMwPYE,7688
26
26
  unique_sdk/api_resources/_message_assessment.py,sha256=SSfx6eW7zb_GKe8cFJzCqW-t-_eWEXxKP5cnIb0DhIc,2276
27
- unique_sdk/api_resources/_search.py,sha256=pAVMXL2AdJNP5JG-7LlaU2Uw1152iUaMZ2YO7fHnNWc,1906
27
+ unique_sdk/api_resources/_search.py,sha256=GQItZKoGNOVZfkLLltBmsRZYBIreRKU0lGW8Kgpj1_Q,1959
28
28
  unique_sdk/api_resources/_search_string.py,sha256=4Idw6exgZdA8qksz9WkiA68k1hTU-7yFkgT_OLU_GkE,1662
29
29
  unique_sdk/api_resources/_short_term_memory.py,sha256=vPRN-Y0WPx74E6y-A3LocGc0TxJdzT-xGL66WzZwKRg,2820
30
30
  unique_sdk/api_resources/_space.py,sha256=9e-_3oBr_25JeRLF7GfaXnIPE33tZ19g0UWxPp_yR4k,4805
31
31
  unique_sdk/utils/chat_history.py,sha256=5UqL9hF1O9pV7skbNOlEibF5rHdYsmG3m5-YEPUowOs,3037
32
- unique_sdk/utils/chat_in_space.py,sha256=kQA43P9XZ6Kdf05JZiLhVJ5ejVtrgzZSb8osyNHSZmc,1580
32
+ unique_sdk/utils/chat_in_space.py,sha256=nYmgQYhIxqQex_6zjdCfNuGnjoU14WkxUN6_zmSOiCQ,5169
33
33
  unique_sdk/utils/file_io.py,sha256=YY8B7VJcTLOPmCXByiOfNerXGlAtjCC5EVNmAbQJ3dQ,4306
34
34
  unique_sdk/utils/sources.py,sha256=wfboE-neMKa0Wuq9QzfAEFMkNLrIrmm0v-QF33sLo6k,4952
35
35
  unique_sdk/utils/token.py,sha256=AzKuAA1AwBtnvSFxGcsHLpxXr_wWE5Mj4jYBbOz2ljA,1740
36
- unique_sdk-0.10.1.dist-info/LICENSE,sha256=EJCWoHgrXVBUb47PnjeV4MFIEOR71MAdCOIgv61J-4k,1065
37
- unique_sdk-0.10.1.dist-info/METADATA,sha256=J0Ur8XcIXsgIxE9J1T0hcrf3Tdg-0RdemPMZ92QNodc,43240
38
- unique_sdk-0.10.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
39
- unique_sdk-0.10.1.dist-info/RECORD,,
36
+ unique_sdk-0.10.3.dist-info/LICENSE,sha256=EJCWoHgrXVBUb47PnjeV4MFIEOR71MAdCOIgv61J-4k,1065
37
+ unique_sdk-0.10.3.dist-info/METADATA,sha256=DLYcik4T5KW-YGa0sZmFv_6N2luJtB9IJ5jXKy3XfmA,44996
38
+ unique_sdk-0.10.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
39
+ unique_sdk-0.10.3.dist-info/RECORD,,