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

@@ -1,1774 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: unique_sdk
3
- Version: 0.10.19
4
- Summary:
5
- License: MIT
6
- Author: Martin Fadler
7
- Author-email: martin.fadler@unique.ch
8
- Requires-Python: >=3.11,<4.0
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Requires-Dist: requests (>=2.32.3,<3.0.0)
14
- Requires-Dist: typing-extensions (>=4.9.0,<5.0.0)
15
- Description-Content-Type: text/markdown
16
-
17
- # Unique Python SDK
18
-
19
- Unique FinanceGPT is a tailored solution for the financial industry, designed to increase productivity by automating manual workloads through AI and ChatGPT solutions.
20
-
21
- The Unique Python SDK provides access to the public API of Unique FinanceGPT. It also enables verification of Webhook signatures to ensure the authenticity of incoming Webhook requests.
22
-
23
- ## Table of Contents
24
-
25
- 1. [Installation](#installation)
26
- 2. [Requirements](#requirements)
27
- 3. [Usage Instructions](#usage-instructions)
28
- 4. [Webhook Triggers](#webhook-triggers)
29
- 5. [Available API Resources](#available-api-resources)
30
- - [Content](#content)
31
- - [Message](#message)
32
- - [Chat Completion](#chat-completion)
33
- - [Embeddings](#embeddings)
34
- - [Acronyms](#acronyms)
35
- - [Search](#search)
36
- - [Search String](#search-string)
37
- - [Short Term Memory](#short-term-memory)
38
- - [Message Assessment](#message-assessment)
39
- - [Folder](#folder)
40
- - [Space](#space)
41
- 6. [UniqueQL](#uniqueql)
42
- - [Query Structure](#uniqueql-query-structure)
43
- - [Metadata Filtering](#metadata-filtering)
44
- 7. [Util functions](#utils)
45
- - [Chat History](#chat-history)
46
- - [File Io](#file-io)
47
- - [Sources](#sources)
48
- - [token](#token)
49
- - [Chat In Space](#chat-in-space)
50
- 8. [Error Handling](#error-handling)
51
- 9. [Examples](#examples)
52
-
53
- ## Installation
54
-
55
- Install UniqueSDK and its peer dependency `requests` and when planning to run async requests also `httpx` or `aiohttp` via pip using the following commands:
56
-
57
- ```bash
58
- pip install unique_sdk
59
- pip install requests
60
- ```
61
-
62
- Optional for async requests:
63
-
64
- ```bash
65
- pip install httpx
66
- ```
67
-
68
- or
69
-
70
- ```bash
71
- pip install aiohttp
72
- ```
73
-
74
- ## Requirements
75
-
76
- - Python >=3.11 (Other Python versions 3.6+ might work but are not tested)
77
- - requests (peer dependency. Other HTTP request libraries might be supported in the future)
78
- - Unique App-ID & API Key
79
-
80
- Please contact your customer success manager at Unique for your personal developer App-ID & API Key.
81
-
82
- ## Usage instructions
83
-
84
- The library needs to be configured with your Unique `app_id` & `api_key`. Additionally, each individual request must be scoped to a User and provide a `user_id` & `company_id`.
85
-
86
- ```python
87
- import unique_sdk
88
- unique_sdk.api_key = "ukey_..."
89
- unique_sdk.app_id = "app_..."
90
- ```
91
-
92
- The SDK includes a set of classes for API resources. Each class contains CRUD methods to interact with the resource.
93
-
94
- ### Example
95
-
96
- ```python
97
- import unique_sdk
98
- unique_sdk.api_key = "ukey_..."
99
- unique_sdk.app_id = "app_..."
100
-
101
- # list messages for a single chat
102
- messages = unique_sdk.Message.list(
103
- user_id=user_id,
104
- company_id=company_id,
105
- chatId=chat_id,
106
- )
107
-
108
- print(messages.data[0].text)
109
- ```
110
-
111
- ## Webhook Triggers
112
-
113
- A core functionality of FinanceGPT is the ability for users to engage in an interactive chat feature. SDK developers can hook into this chat to provide new functionalities.
114
-
115
- Your App (refer to `app-id` in [Requirements](#requirements)) must be subscribed to each individual Unique event in order to receive a webhook.
116
-
117
- Each webhook sent by Unique includes a set of headers:
118
-
119
- ```yaml
120
- X-Unique-Id: evt_... # Event id, same as in the body.
121
- X-Unique-Signature: ... # A HMAC-SHA256 hex signature of the entire body.
122
- X-Unique-Version: 1.0.0 # Event payload version.
123
- X-Unique-Created-At: 1705960141 # Unix timestamp (seconds) of the delivery time.
124
- X-Unique-User-Id: ... # The user who initiated the message.
125
- X-Unique-Company-Id: ... # The company to which the user belongs.
126
- ```
127
-
128
- ### Success & Retry on Failure
129
-
130
- - Webhooks are considered successfully delivered if your endpoint returns a status code between `200` and `299`.
131
- - If your endpoint returns a status code of `300` - `399`, `429`, or `500` - `599`, Unique will retry the delivery of the webhook with an exponential backoff up to five times.
132
- - If your endpoint returns any other status (e.g., `404`), it is marked as expired and will not receive any further requests.
133
-
134
- ### Webhook Signature Verification
135
-
136
- The webhook body, containing a timestamp of the delivery time, is signed with HMAC-SHA256. Verify the signature by constructing the `event` with the `unique_sdk.Webhook` class:
137
-
138
- ```python
139
- from http import HTTPStatus
140
- from flask import Flask, jsonify, request
141
- import unique_sdk
142
-
143
- endpoint_secret = "YOUR_ENDPOINT_SECRET"
144
-
145
- @app.route("/webhook", methods=["POST"])
146
- def webhook():
147
- event = None
148
- payload = request.data
149
-
150
- sig_header = request.headers.get("X-Unique-Signature")
151
- timestamp = request.headers.get("X-Unique-Created-At")
152
-
153
- if not sig_header or not timestamp:
154
- print("⚠️ Webhook signature or timestamp headers missing.")
155
- return jsonify(success=False), HTTPStatus.BAD_REQUEST
156
-
157
- try:
158
- event = unique_sdk.Webhook.construct_event(
159
- payload, sig_header, timestamp, endpoint_secret
160
- )
161
- except unique_sdk.SignatureVerificationError as e:
162
- print("⚠️ Webhook signature verification failed. " + str(e))
163
- return jsonify(success=False), HTTPStatus.BAD_REQUEST
164
- ```
165
-
166
- The `construct_event` method will compare the signature and raise a `unique_sdk.SignatureVerificationError` if the signature does not match. It will also raise this error if the `createdAt` timestamp is outside of a default tolerance of 5 minutes. Adjust the `tolerance` by passing a fifth parameter to the method (tolerance in seconds), e.g.:
167
-
168
- ```python
169
- event = unique_sdk.Webhook.construct_event(
170
- payload, sig_header, timestamp, endpoint_secret, 0
171
- )
172
- ```
173
-
174
- ### Available Unique Events
175
-
176
- #### User Message Created
177
-
178
- ```json
179
- {
180
- "id": "evt_...", // see header
181
- "version": "1.0.0", // see header
182
- "event": "unique.chat.user-message.created", // The name of the event
183
- "createdAt": "1705960141", // see header
184
- "userId": "...", // see header
185
- "companyId": "...", // see header
186
- "payload": {
187
- "chatId": "chat_...", // The id of the chat
188
- "assistantId": "assistant_...", // The id of the selected assistant
189
- "text": "Hello, how can I help you?" // The user message
190
- }
191
- }
192
- ```
193
-
194
- This webhook is triggered for every new chat message sent by the user. This event occurs regardless of whether it is the first or a subsequent message in a chat. Use the `unique_sdk.Message` class to retrieve other messages from the same `chatId` or maintain a local state of the messages in a single chat.
195
-
196
- This trigger can be used in combination with assistants marked as `external`. Those assistants will not execute any logic, enabling your code to respond to the user message and create an answer.
197
-
198
- #### External Module Chosen
199
-
200
- ```json
201
- {
202
- "id": "evt_...",
203
- "version": "1.0.0",
204
- "event": "unique.chat.external-module.chosen",
205
- "createdAt": "1705960141", // Unix timestamp (seconds)
206
- "userId": "...",
207
- "companyId": "...",
208
- "payload": {
209
- "name": "example-sdk", // The name of the module selected by the module chooser
210
- "description": "Example SDK", // The description of the module
211
- "configuration": {}, // Module configuration in JSON format
212
- "chatid": "chat_...", // The chat ID
213
- "assistantId:": "assistant_...", // The assistant ID
214
- "userMessage": {
215
- "id": "msg_...",
216
- "text": "Hello World!", // The user message leading to the module selection
217
- "createdAt": "2024-01-01T00:00:00.000Z" // ISO 8601
218
- },
219
- "assistantMessage": {
220
- "id": "msg_...",
221
- "createdAt": "2024-01-01T00:00:00.000Z" // ISO 8601
222
- }
223
- }
224
- }
225
- ```
226
-
227
- This Webhook is triggered when the Unique FinanceGPT AI selects an external module as the best response to a user message. The module must be marked as `external` and available for the assistant used in the chat to be selected by the AI.
228
-
229
- Unique's UI will create an empty `assistantMessage` below the user message and update this message with status updates.
230
-
231
- **The SDK is expected to modify this assistantMessage with its answer to the user message.**
232
-
233
- ```python
234
- unique_sdk.Message.modify(
235
- user_id=user_id,
236
- company_id=company_id,
237
- id=assistant_message_id,
238
- chatId=chat_id,
239
- text="Here is your answer.",
240
- )
241
- ```
242
-
243
- ## Available API Resources
244
-
245
- - [Content](#content)
246
- - [Message](#message)
247
- - [Chat Completion](#chat-completion)
248
- - [Embeddings](#embeddings)
249
- - [Acronyms](#acronyms)
250
- - [Search](#search)
251
- - [Search String](#search-string)
252
- - [Short Term Memory](#short-term-memory)
253
- - [Message Assessment](#message-assessment)
254
- - [Folder](#folder)
255
- - [Space](#space)
256
-
257
- Most of the API services provide an asynchronous version of the method. The async methods are suffixed with `_async`.
258
-
259
- ### Content
260
-
261
- #### `unique_sdk.Content.search`
262
-
263
- Allows you to load full content/files from the knowledge-base of unique with the rights of the userId and companyId. Provided a `where` query for filtering. Filtering can be done on any of the following fields: `
264
-
265
- - `id`
266
- - `key`
267
- - `ownerId`
268
- - `title`
269
- - `url`
270
-
271
- Here an example of retrieving all files that contain the number 42 in the `title` or the `key` typically this is used to search by filename.
272
-
273
- ```python
274
- unique_sdk.Content.search(
275
- user_id=userId,
276
- company_id=companyId,
277
- where={
278
- "OR": [
279
- {
280
- "title": {
281
- "contains": "42",
282
- },
283
- },
284
- {
285
- "key": {
286
- "contains": "42",
287
- },
288
- },
289
- ],
290
- },
291
- chatId=chatId,
292
- )
293
- ```
294
-
295
- #### `unique_sdk.Content.get_info`
296
-
297
- [Deprecated, use `unique_sdk.Content.get_infos` instead.] Allows you to get content info. To filter the results you can define a metadata filter in UniqueQL language. Find out more about it in the UniqueQL section. An example of a metadata filter defined with UniqueQL is the following:
298
-
299
- ```python
300
- metadataFilter: {
301
- "or": [
302
- {
303
- "and": [
304
- {
305
- "operator": "contains",
306
- "path": [
307
- "folderIdPath"
308
- ],
309
- "value": "uniquepathid://test_id"
310
- },
311
- {
312
- "operator": "contains",
313
- "path": [
314
- "title"
315
- ],
316
- "value": "ai"
317
- }
318
- ]
319
- }
320
- ]
321
- },
322
- ```
323
-
324
- Pagination is also enabled for this functionality, and the default number of returned results is 50 with no entries skipped. Use the following paramteres to get the desired page:`
325
-
326
- - `skip`
327
- - `take`
328
-
329
- Here is an example of retrieving the first 3 content infos that contain the value `uniquepathid://scope_abcdibgznc4bkdcx120zm5d` in the `folderIdPath` metadata and the value `ai` for the `title` metadata.
330
-
331
- ```python
332
- content_info_result = unique_sdk.Content.get_info(
333
- user_id=user_id,
334
- company_id=company_id,
335
- metadataFilter={
336
- "or": [
337
- {
338
- "and": [
339
- {
340
- "operator": "contains",
341
- "path": [
342
- "folderIdPath"
343
- ],
344
- "value": "uniquepathid://scope_abcdibgznc4bkdcx120zm5d"
345
- },
346
- {
347
- "operator": "contains",
348
- "path": [
349
- "title"
350
- ],
351
- "value": "ai"
352
- }
353
- ]
354
- }
355
- ]
356
- },
357
- skip=0,
358
- take=3,
359
- )
360
- ```
361
-
362
- #### `unique_sdk.Content.get_infos`
363
-
364
- Allows you to get content infos. To filter the results you can define a either metadata filter in UniqueQL language or specify a parentId. If both are defined, the function will throw an error.
365
-
366
- I f you want to learn more about UniqueQL, you can find out more about it in the [UniqueQL](#uniqueql) section. An example of a metadata filter defined with UniqueQL is the following:
367
-
368
- ```python
369
- metadataFilter: {
370
- "or": [
371
- {
372
- "and": [
373
- {
374
- "operator": "contains",
375
- "path": [
376
- "folderIdPath"
377
- ],
378
- "value": "uniquepathid://test_id"
379
- },
380
- {
381
- "operator": "contains",
382
- "path": [
383
- "title"
384
- ],
385
- "value": "ai"
386
- }
387
- ]
388
- }
389
- ]
390
- },
391
- ```
392
-
393
- Pagination is also enabled for this functionality, and the default number of returned results is 50 with no entries skipped. Use the following paramteres to get the desired page:`
394
-
395
- - `skip`
396
- - `take`
397
-
398
- Here is an example of retrieving the first 3 content infos that contain the value `uniquepathid://scope_abcdibgznc4bkdcx120zm5d` in the `folderIdPath` metadata and the value `ai` for the `title` metadata.
399
-
400
- ```python
401
- content_info_result = unique_sdk.Content.get_infos(
402
- user_id=user_id,
403
- company_id=company_id,
404
- metadataFilter={
405
- "or": [
406
- {
407
- "and": [
408
- {
409
- "operator": "contains",
410
- "path": [
411
- "folderIdPath"
412
- ],
413
- "value": "uniquepathid://scope_abcdibgznc4bkdcx120zm5d"
414
- },
415
- {
416
- "operator": "contains",
417
- "path": [
418
- "title"
419
- ],
420
- "value": "ai"
421
- }
422
- ]
423
- }
424
- ]
425
- },
426
- skip=0,
427
- take=3,
428
- )
429
- ```
430
-
431
- Here is an example of retrieving the contents based on a parentId.
432
-
433
- ```python
434
- content_info_result = unique_sdk.Content.get_infos(
435
- user_id=user_id,
436
- company_id=company_id,
437
- parentId="scope_ahefgj389srjbfejkkk98u"
438
- )
439
- ```
440
-
441
- #### `unique_sdk.Content.upsert`
442
-
443
- Enables upload of a new Content into the Knowledge base of unique into a specific scope with `scopeId` or a specific `chatId`. One of the two must be set.
444
-
445
- Typical usage is the following. That creates a Content and uploads a file
446
-
447
- ```python
448
-
449
- createdContent = upload_file(
450
- userId,
451
- companyId,
452
- "/path/to/file.pdf",
453
- "test.pdf",
454
- "application/pdf",
455
- "scope_stcj2osgbl722m22jayidx0n",
456
- ingestionConfig={
457
- "chunkMaxTokens": 1000,
458
- "chunkStrategy": "default",
459
- "uniqueIngestionMode": "standard",
460
- },
461
- metadata={
462
- "folderIdPath": "uniquepathid://scope_id"
463
- }
464
- )
465
-
466
- def upload_file(
467
- userId,
468
- companyId,
469
- path_to_file,
470
- displayed_filename,
471
- mimeType,
472
- scope_or_unique_path,
473
- ingestion_config=None,
474
- metadata=None,
475
- ):
476
- size = os.path.getsize(path_to_file)
477
- createdContent = unique_sdk.Content.upsert(
478
- user_id=userId,
479
- company_id=companyId,
480
- input={
481
- "key": displayed_filename,
482
- "title": displayed_filename,
483
- "mimeType": mimeType,
484
- "ingestionConfig": ingestionConfig,
485
- "metadata": metadata,
486
- },
487
- scopeId=scope_or_unique_path,
488
- )
489
-
490
- uploadUrl = createdContent.writeUrl
491
-
492
- # upload to azure blob storage SAS url uploadUrl the pdf file translatedFile make sure it is treated as a application/pdf
493
- with open(path_to_file, "rb") as file:
494
- requests.put(
495
- uploadUrl,
496
- data=file,
497
- headers={
498
- "X-Ms-Blob-Content-Type": mimeType,
499
- "X-Ms-Blob-Type": "BlockBlob",
500
- },
501
- )
502
-
503
- unique_sdk.Content.upsert(
504
- user_id=userId,
505
- company_id=companyId,
506
- input={
507
- "key": displayed_filename,
508
- "title": displayed_filename,
509
- "mimeType": mimeType,
510
- "byteSize": size,
511
- "ingestionConfig": ingestionConfig,
512
- "metadata": metadata,
513
- },
514
- scopeId=scope_or_unique_path,
515
- readUrl=createdContent.readUrl,
516
- )
517
-
518
- return createdContent
519
-
520
- ```
521
-
522
- #### `unique_sdk.Content.ingest_magic_table_sheets`
523
-
524
- Allows you to ingest a magic table sheet, each row is processed and converted into a content.
525
- ```python
526
- params = {
527
- "user_id": user_id,
528
- "company_id": company_id,
529
- "data": [
530
- {
531
- "rowId": "2",
532
- "columns": [
533
- {"columnId": "0", "columnName": "Section", "content": "Other"},
534
- {"columnId": "1", "columnName": "Question", "content": "What do you know?"},
535
- {
536
- "columnId": "2",
537
- "columnName": "Knowledge Base Answer",
538
- "content": "Lorem Ipsum is simply dummy texktop publishing software.",
539
- },
540
- ],
541
- },
542
- ],
543
- "ingestionConfiguration": {
544
- "columnIdsInMetadata": ["1", "2"],
545
- "columnIdsInChunkText": ["1", "2"],
546
- },
547
- "metadata": {
548
- "libraryName": "foo",
549
- },
550
- "scopeId": scope_id,
551
- "sheetName": "Sheet1",
552
- }
553
-
554
- unique_sdk.Content.ingest_magic_table_sheets(**params)
555
- ```
556
-
557
- #### `unique_sdk.Content.update` (Compatible with release >.36)
558
-
559
- Allows you to update a file specified by its `contentId`.
560
-
561
- - `contentId` the id of the file to be updated
562
-
563
- Currently, the following updates are supported:
564
-
565
- Title update:
566
- - `title` optional, allows updating the title of the folder
567
-
568
- Move the file to a different folder. this can be done by specifying either the `ownerId`.
569
- - `ownerId` optional, allows moving the file to a different folder. Represents the new folder for the file and it should be the id of a folder e.g.: `scope_dhjfieurfloakmdle`.
570
-
571
-
572
- Example of moving a file specified by its content id.
573
-
574
- ```python
575
- unique_sdk.Content.update(
576
- user_id=user_id,
577
- company_id=company_id,
578
- contentId="cont_ok2343q5owbce80w78hudawu5",
579
- ownerId="scope_e68yz5asho7glfh7c7d041el"
580
- )
581
- ```
582
-
583
- Example of moving a file and updating its title.
584
-
585
- ```python
586
- unique_sdk.Content.update(
587
- user_id=user_id,
588
- company_id=company_id,
589
- contentId="cont_ok2343q5owbce80w78hudawu5",
590
- ownerId="scope_e68yz5asho7glfh7c7d041el",
591
- title="Revision Deck (1)"
592
- )
593
- ```
594
-
595
- #### `unique_sdk.Content.delete` (Compatible with release >.36)
596
-
597
- Allows you to delete a file by its `contentId`. If the file is part of a chat, the `chatId` also needs do be set.
598
-
599
- - `contentId` the id of the file to be deleted
600
- - `chatId` optional, the id of the chat where the file is. Only needed if the file is part of a chat
601
-
602
- Example of deleting a file from a chat.
603
-
604
- ```python
605
- unique_sdk.Content.delete(
606
- user_id=user_id,
607
- company_id=company_id,
608
- contentId="cont_ok2343q5owbce80w78hudawu5",
609
- chatId="chat_v3xfa7liv876h89vuiibus1"
610
- )
611
- ```
612
-
613
- ### Message
614
-
615
- #### `unique_sdk.Message.list`
616
-
617
- Retrieve a list of messages for a provided `chatId`.
618
-
619
- ```python
620
- messages = unique_sdk.Message.list(
621
- user_id=user_id,
622
- company_id=company_id,
623
- chatId=chat_id,
624
- )
625
- ```
626
-
627
- #### `unique_sdk.Message.retrieve`
628
-
629
- Get a single chat message.
630
-
631
- ```python
632
- message = unique_sdk.Message.retrieve(
633
- user_id=user_id,
634
- company_id=company_id,
635
- id=message_id,
636
- chatId=chat_id,
637
- )
638
- ```
639
-
640
- #### `unique_sdk.Message.create`
641
-
642
- Create a new message in a chat.
643
-
644
- ```python
645
- message = unique_sdk.Message.create(
646
- user_id=user_id,
647
- company_id=company_id,
648
- chatId=chat_id,
649
- assistantId=assistant_id,
650
- text="Hello.",
651
- role="ASSISTANT",
652
- )
653
- ```
654
-
655
- #### `unique_sdk.Message.modify`
656
-
657
- Modify an existing chat message.
658
-
659
- ℹ️ if you modify the debugInfo only do it on the user message as this is the only place that is displayed in the frontend.
660
-
661
- ```python
662
- message = unique_sdk.Message.modify(
663
- user_id=user_id,
664
- company_id=company_id,
665
- id=message_id,
666
- chatId=chat_id,
667
- text="Updated message text"
668
- )
669
- ```
670
-
671
- #### `unique_sdk.Message.delete`
672
-
673
- Delete a chat message.
674
-
675
- ```python
676
- message = unique_sdk.Message.delete(
677
- message_id,
678
- user_id=user_id,
679
- company_id=company_id,
680
- chatId=chat_id,
681
- )
682
- ```
683
-
684
- #### `unique_sdk.Integrated.stream`
685
-
686
- Streams the answer to the chat frontend. Given the messages.
687
-
688
- if the stream creates [source0] it is referenced with the references from the search context.
689
-
690
- E.g.
691
-
692
- ```
693
- Hello this information is from [srouce1]
694
- ```
695
-
696
- adds the reference at index 1 and then changes the text to:
697
-
698
- ```
699
- Hello this information is from <sub>0</sub>
700
- ```
701
-
702
- ```python
703
- unique_sdk.Integrated.chat_stream_completion(
704
- user_id=userId,
705
- company_id=companyId,
706
- assistantMessageId=assistantMessageId,
707
- userMessageId=userMessageId,
708
- messages=[
709
- {
710
- "role": "system",
711
- "content": "be friendly and helpful"
712
- },
713
- {
714
- "role": "user",
715
- "content": "hello"
716
- }
717
- ],
718
- chatId=chatId,
719
-
720
- searchContext= [
721
- {
722
- "id": "ref_qavsg0dcl5cbfwm1fvgogrvo",
723
- "chunkId": "0",
724
- "key": "some reference.pdf : 8,9,10,11",
725
- "sequenceNumber": 1,
726
- "url": "unique://content/cont_p8n339trfsf99oc9f36rn4wf"
727
- }
728
- ], # optional
729
- debugInfo={
730
- "hello": "test"
731
- }, # optional
732
- startText= "I want to tell you about: ", # optional
733
- model= "AZURE_GPT_4_32K_0613", # optional
734
- timeout=8000, # optional in ms
735
- options={
736
- "temperature": 0.5
737
- } # optional
738
- )
739
- ```
740
-
741
- **Warning:** Currently, the deletion of a chat message does not automatically sync with the user UI. Users must refresh the chat page to view the updated state. This issue will be addressed in a future update of our API.
742
-
743
-
744
- #### `unique_sdk.Integrated.responses_stream`
745
-
746
- Streams the answer to the chat frontend using the Responses API. Given the messages.
747
-
748
- if the stream creates [source0] it is referenced with the references from the search context.
749
-
750
- E.g.
751
-
752
- ```
753
- Hello this information is from [source1]
754
- ```
755
-
756
- adds the reference at index 1 and then changes the text to:
757
-
758
- ```
759
- Hello this information is from <sub>0</sub>
760
- ```
761
-
762
- ```python
763
- unique_sdk.Integrated.responses_stream(
764
- user_id=userId,
765
- company_id=companyId,
766
- model="AZURE_o3_2025_0416",
767
- assistantMessageId=assistantMessageId,
768
- userMessageId=userMessageId,
769
- input="Tell me about the curious case of neural text degeneration",
770
- chatId=chatId,
771
- )
772
- ```
773
-
774
- **Warning:** Currently, the deletion of a chat message does not automatically sync with the user UI. Users must refresh the chat page to view the updated state. This issue will be addressed in a future update of our API.
775
-
776
- ### Chat Completion
777
-
778
- #### `unique_sdk.ChatCompletion.create`
779
-
780
- Send a prompt to an AI model supported by Unique FinanceGPT and receive a result. The `messages` attribute must follow the [OpenAI API format](https://platform.openai.com/docs/api-reference/chat).
781
-
782
- ```python
783
- chat_completion = unique_sdk.ChatCompletion.create(
784
- company_id=company_id,
785
- user_id=user_id
786
- model="AZURE_GPT_35_TURBO",
787
- messages=[
788
- {"role": "system", "content": "You are a helpful assistant."},
789
- {"role": "user", "content": "Hello!"},
790
- ],
791
- options={
792
- "temperature": 0.5
793
- } # optional
794
- )
795
- ```
796
-
797
- ### Embeddings
798
-
799
- #### `unique_sdk.Embeddings.create`
800
-
801
- Sends an array of `text` to the AI model for embedding. And retrieve a vector of embeddings.
802
-
803
- ```python
804
- result = unique_sdk.Embeddings.create(
805
- user_id=user_id,
806
- company_id=company_id,
807
- texts=["hello", "hello"],
808
- )
809
- print(result.embeddings[0][0])
810
- ```
811
-
812
- ### Acronyms
813
-
814
- #### `unique_sdk.Acronyms.get`
815
-
816
- Fetches the acronyms defined on the company. Often used to replace in user prompts, so the acronym is resolved to give better guidance to the LLM during completion.
817
-
818
- ```python
819
- result = unique_sdk.Acronyms.get(
820
- user_id=user_id,
821
- company_id=company_id,
822
- )
823
- print(result)
824
- ```
825
-
826
- ### Search
827
-
828
- #### `unique_sdk.Search.create`
829
-
830
- Search the Unique FinanceGPT Knowledge database for RAG (Retrieval-Augmented Generation). The API supports vector search and a `searchType` that combines vector and full-text search, enhancing the precision of search results.
831
-
832
- These are the options are available for `searchType`:
833
-
834
- - `VECTOR`
835
- - `COMBINED`
836
-
837
- `limit` (max 1000) and `page` are optional for iterating over results.
838
- `chatOnly` Restricts the search exclusively to documents uploaded within the chat.
839
- `scopeIds` Specifies a collection of scope IDs to confine the search.
840
- `language` Optional. The language specification for full text search.
841
- `reranker` Optional. The reranker service to be used for re-ranking the search results.
842
- `chatId` Optional, adds the documents uploaded in this chat to the scope of searched documents.
843
- `scoreThreshold` Optional, sets the minimum similarity score for search results to be considered. Using 0 is recommended.
844
-
845
- ```python
846
- search = unique_sdk.Search.create(
847
- user_id=user_id,
848
- company_id=company_id,
849
- chatId=chat_id
850
- searchString="What is the meaning of life, the universe and everything?",
851
- searchType="VECTOR",
852
- chatOnly=false,
853
- scopeIds=["scope_..."],
854
- language="German",
855
- reranker={"deploymentName": "my_deployment"},
856
- limit=20,
857
- page=1
858
- scoreThreshold=0
859
- )
860
- ```
861
-
862
- ### Search String
863
-
864
- #### `unique_sdk.SearchString.create`
865
-
866
- User messages are sometimes suboptimal as input prompts for vector or full-text knowledge base searches. This is particularly true as a conversation progresses and a user question may lack crucial context for a successful search.
867
-
868
- This API transforms and translates (into English) the user's message into an ideal search string for use in the [Search.create](#unique_sdksearchcreate) API method.
869
-
870
- Adding a `chatId` or `messages` as arguments allows the message history to provide additional context to the search string. For example, "Who is the author?" will be expanded to "Who is the author of the book 'The Hitchhiker's Guide to the Galaxy'?" if previous messages referenced the book.
871
-
872
- ```python
873
- search_string = unique_sdk.SearchString.create(
874
- user_id=user_id,
875
- company_id=company_id,
876
- prompt="Was ist der Sinn des Lebens, des Universums und des ganzen Rests?",
877
- chat_id=chat_id
878
- )
879
- ```
880
-
881
- ### Short Term Memory
882
-
883
- For saving data in between chats there is the Short Term Memory functionality to save small data in between rounds of chat e.g. language, search results and so on.
884
- For this 10k chars can be used. You can save a short term memory for a chat `chatId` or for a message `messageId`.
885
- you need to provide an `memoryName` as an identifier.
886
-
887
- you can then save it and retreive it live defined below.
888
-
889
-
890
- #### `unique_sdk.ShortTermMemory.create`
891
-
892
- ```python
893
- c = unique_sdk.ShortTermMemory.create(
894
- user_id=user_id,
895
- company_id=company_id,
896
- data="hello",
897
- chatId="chat_x0xxtj89f7drjp4vmued3q",
898
- # messageId = "msg_id",
899
- memoryName="your memory name",
900
- )
901
- print(c)
902
- ```
903
-
904
- #### `unique_sdk.ShortTermMemory.find-latest`
905
-
906
- ```python
907
- m = unique_sdk.ShortTermMemory.find_latest(
908
- user_id=user_id,
909
- company_id=company_id,
910
- chatId="chat_x0xxtj89f7drjp4vmued3q",
911
- # messageId = "msg_id",
912
- memoryName="your memory name",
913
- )
914
- print(m)
915
- ```
916
-
917
- ### Message Assessment
918
-
919
- Used to create and modify message assessments for tracking hallucinations and compliance evaluations of assistant messages.
920
-
921
- #### `unique_sdk.MessageAssessment.create`
922
-
923
- Create a new message assessment for an assistant message.
924
-
925
- ```python
926
- assessment = unique_sdk.MessageAssessment.create(
927
- user_id=user_id,
928
- company_id=company_id,
929
- assistant_message_id="msg_...",
930
- status="DONE",
931
- explanation="This response contains incorrect information about...",
932
- label="RED",
933
- type="HALLUCINATION",
934
- title="Hallucination detected",
935
- isVisible=True
936
- )
937
- ```
938
-
939
- #### `unique_sdk.MessageAssessment.modify`
940
-
941
- Modify an existing message assessment.
942
-
943
- ```python
944
- assessment = unique_sdk.MessageAssessment.modify(
945
- user_id=user_id,
946
- company_id=company_id,
947
- assistant_message_id="msg_...",
948
- status="DONE",
949
- explanation="Updated explanation...",
950
- label="RED",
951
- title="update title"
952
- type="HALLUCINATION"
953
- )
954
- ```
955
-
956
- ### Folder
957
-
958
- #### `unique_sdk.Folder.get_info`
959
-
960
- Get a folder by scope id or by path.
961
-
962
- By scope id:
963
-
964
- ```python
965
- unique_sdk.Folder.get_info(
966
- user_id=user_id,
967
- company_id=company_id,
968
- scopeId="scope_w78wfn114va9o22s13r03yq",
969
- )
970
- ```
971
-
972
- By path:
973
-
974
- ```python
975
- unique_sdk.Folder.get_info(
976
- user_id=user_id,
977
- company_id=company_id,
978
- folderPath="/Company/Atlas/Due Dilligence/Arch,
979
- )
980
- ```
981
-
982
- #### `unique_sdl.Folder.get_infos`
983
-
984
- Get paginated folders info based on parentId. If the parentId is not defined, the root folders will be returned.
985
-
986
- ```python
987
- unique_sdk.Folder.get_infos(
988
- user_id=user_id,
989
- company_id=company_id,
990
- take=10, #optional
991
- skip=5, #optional
992
- parentId="scope_s18seqpnltf35niydg77xgyp" #optional
993
- )
994
- ```
995
-
996
- #### `unique_sdk.Folder.create_paths`
997
-
998
- Create each folder in the provided list of paths if it does not already exist.
999
-
1000
- ```python
1001
- unique_sdk.Folder.create_paths(
1002
- user_id=user_id,
1003
- company_id=company_id,
1004
- paths=["/unique/path1", "/unique/path2"],
1005
- )
1006
- ```
1007
-
1008
- #### `unique_sdk.Folder.update_ingestion_config`
1009
-
1010
- Allows you to update the ingestion config of a folder and choose whether to apply to the subscopes or not: `
1011
-
1012
- - `ingestionConfig`
1013
- - `applyToSubScopes`
1014
-
1015
- The update can be done by referencing the folder by id or by path. If none of them are provided. the API will return an error. If both of them are provided, the scope id will take precedence.
1016
-
1017
- Example of updating the ingestion config of a folder and its subfolders using the id.
1018
-
1019
- ```python
1020
- unique_sdk.Folder.update_ingestion_config(
1021
- user_id=user_id,
1022
- company_id=company_id,
1023
- scopeId="scope_qbnkde820dbmuw2900,
1024
- ingestionConfig={
1025
- "chunkStrategy": "default",
1026
- "uniqueIngestionMode": "standard",
1027
- },
1028
- applyToSubScopes=True
1029
- )
1030
- ```
1031
-
1032
- Example of updating the ingestion config of a folder and its subfolders using the path.
1033
-
1034
- ```python
1035
- unique_sdk.Folder.update_ingestion_config(
1036
- user_id=user_id,
1037
- company_id=company_id,
1038
- folderPath="/Company/folder1/folder2",
1039
- ingestionConfig={
1040
- "chunkStrategy": "default",
1041
- "uniqueIngestionMode": "standard",
1042
- },
1043
- applyToSubScopes=True
1044
- )
1045
- ```
1046
-
1047
- #### `unique_sdk.Folder.add_access`
1048
-
1049
- Allows you to add access to a folder and apply to the subfolders or not: `
1050
-
1051
- - `scopeAccesses`
1052
- - `applyToSubScopes`
1053
-
1054
- The update can be done by referencing the folder by id or by path. If none of them are provided. the API will return an error. If both of them are provided, the scope id will take precedence.
1055
-
1056
- Example of adding access to a folder and its subfolders using the id.
1057
-
1058
- ```python
1059
- unique_sdk.Folder.add_access(
1060
- user_id=user_id,
1061
- company_id=company_id,
1062
- scopeId="scope_231e4kjn4foffww34",
1063
- scopeAccesses=[
1064
- {
1065
- "entityId": "group_id",
1066
- "type": "WRITE",
1067
- "entityType": "GROUP",
1068
- }
1069
- ],
1070
- applyToSubScopes=True,
1071
- )
1072
- ```
1073
-
1074
- Example of adding access to a folder and its subfolders using the folder path.
1075
-
1076
- ```python
1077
- unique_sdk.Folder.add_access(
1078
- user_id=user_id,
1079
- company_id=company_id,
1080
- folderPath="/Company/folder1/folder2"
1081
- scopeAccesses=[
1082
- {
1083
- "entityId": "group_id",
1084
- "type": "WRITE",
1085
- "entityType": "GROUP",
1086
- }
1087
- ],
1088
- applyToSubScopes=True,
1089
- )
1090
- ```
1091
-
1092
- #### `unique_sdk.Folder.remove_access`
1093
-
1094
- Allows you to delete access from a folder and apply to the subfolders or not:
1095
-
1096
- - `scopeAccesses`
1097
- - `applyToSubScopes`
1098
-
1099
- The update can be done by referencing the folder by id or by path. If none of them are provided. the API will return an error. If both of them are provided, the scope id will take precedence.
1100
-
1101
-
1102
- Example of deleting the access from a folder and its subfolders using the id.
1103
-
1104
- ```python
1105
- unique_sdk.Folder.remove_access(
1106
- user_id=user_id,
1107
- company_id=company_id,
1108
- scopeId="scope_dwekjnf3330woioppm,
1109
- scopeAccesses=[
1110
- {
1111
- "entityId": "group_id",
1112
- "type": "WRITE",
1113
- "entityType": "GROUP",
1114
- }
1115
- ],
1116
- applyToSubScopes=True,
1117
- )
1118
- ```
1119
-
1120
-
1121
- Example of deleting the access from a folder and its subfolders using the path.
1122
-
1123
- ```python
1124
- unique_sdk.Folder.remove_access(
1125
- user_id=user_id,
1126
- company_id=company_id,
1127
- folderPath="/Company/folder1/folder2"
1128
- scopeAccesses=[
1129
- {
1130
- "entityId": "group_id",
1131
- "type": "WRITE",
1132
- "entityType": "GROUP",
1133
- }
1134
- ],
1135
- applyToSubScopes=True,
1136
- )
1137
- ```
1138
-
1139
- #### `unique_sdk.Folder.delete` (Compatible with release >.36)
1140
-
1141
- Given a `scopeId` or `folderPath`, the function deletes the folder. If the folder is not empty or if the user has no WRITE access, the delete will fail.
1142
-
1143
- If `recursive` is set to true, the function also deletes its subfolders and its contents, behaving exactly like the `rm -rf`. In case a subfolder has no write access, that folder is considered as failed to delete and the function continues with the other subfolders. At the end, the function returns a list of `successFolders` and `failedFolders`.
1144
-
1145
- Examples:
1146
- Deleting recursively by scope id:
1147
-
1148
- ```python
1149
- unique_sdk.Folder.delete(
1150
- user_id=user_id,
1151
- company_id=company_id,
1152
- scopeId="scope_w78wfn114va9o22s13r03yq",
1153
- recursive=True
1154
- )
1155
- ```
1156
-
1157
- Deleting by path (non-recursive):
1158
-
1159
- ```python
1160
- unique_sdk.Folder.delete(
1161
- user_id=user_id,
1162
- company_id=company_id,
1163
- folderPath="/Company/Atlas/Due Dilligence/Arch",
1164
- )
1165
- ```
1166
-
1167
- ### Space
1168
-
1169
- #### `unique_sdk.Space.delete_chat`
1170
-
1171
- Delete a space chat by id. If the chat does not exist, the function will return an error.
1172
-
1173
- ```python
1174
- unique_sdk.Space.delete_chat(
1175
- user_id=user_id,
1176
- company_id=company_id,
1177
- chat_id="chat_dejfhe729br398",
1178
- )
1179
- ```
1180
-
1181
- ## UniqueQL
1182
-
1183
- [UniqueQL](https://unique-ch.atlassian.net/wiki/x/coAXHQ) is an advanced query language designed to enhance search capabilities within various search modes such as Vector, Full-Text Search (FTS), and Combined. This query language enables users to perform detailed searches by filtering through metadata attributes like filenames, URLs, dates, and more. UniqueQL is versatile and can be translated into different query formats for various database systems, including PostgreSQL and Qdrant.
1184
-
1185
- ### UniqueQL Query Structure
1186
-
1187
- A UniqueQL query is composed of a path, an operator, and a value. The path specifies the metadata attribute to be filtered, the operator defines the type of comparison, and the value provides the criteria for the filter.
1188
-
1189
- A metadata filter can be designed with UniqueQL's `UQLOperator` and `UQLCombinator` as follows:
1190
-
1191
- ```python
1192
- metadata_filter = {
1193
- "path": ['diet', '*'],
1194
- "operator": UQLOperator.NESTED,
1195
- "value": {
1196
- UQLCombinator.OR : [
1197
- {
1198
- UQLCombinator.OR: [
1199
- {
1200
- "path": ['food'],
1201
- "operator": UQLOperator.EQUALS,
1202
- "value": "meat",
1203
- },
1204
- {
1205
- "path": ['food'],
1206
- "operator": UQLOperator.EQUALS,
1207
- "value": 'vegis',
1208
- },
1209
- ],
1210
- },
1211
- {
1212
- "path": ['likes'],
1213
- "operator": UQLOperator.EQUALS,
1214
- "value": true,
1215
- },
1216
- ],
1217
- },
1218
- }
1219
- ```
1220
-
1221
- ### Metadata Filtering
1222
-
1223
- A metadata filter such as the one designed above can be used in a `Search.create` call by passing it the `metaDataFilter` parameter.
1224
-
1225
- ```python
1226
- search_results = unique_sdk.Search.create(
1227
- user_id=user_id,
1228
- company_id=company_id,
1229
- chatId=chat_id,
1230
- searchString=search_string,
1231
- searchType="COMBINED",
1232
- # limit=2,
1233
- metaDataFilter=metadata_filter,
1234
- )
1235
- ```
1236
-
1237
- ## Utils
1238
-
1239
- - [Chat History](#chat-history)
1240
- - [File Io](#file-io)
1241
- - [Sources](#sources)
1242
- - [token](#token)
1243
- - [Chat In Space](#chat-in-space)
1244
-
1245
- ### Chat History
1246
-
1247
- #### `unique_sdk.utils.chat_history.load_history`
1248
-
1249
- A helper function that makes sure the chat history is fully loaded and cut to the size of the token window that it fits into the next round of chat interactions.
1250
-
1251
- - `maxTokens` max tokens of the model used
1252
- - `percentOfMaxTokens`=0.15 % max history in % of `maxTokens`
1253
- - `maxMessages`=4, maximal number of messages included in the history.
1254
-
1255
- this method also directly returns a correct formatted history that can be used in the next chat round.
1256
-
1257
- ```python
1258
- history = unique_sdk.utils.chat_history.load_history(
1259
- userId,
1260
- companyId,
1261
- chatId
1262
- )
1263
- ```
1264
-
1265
- #### `unique_sdk.utils.chat_history.convert_chat_history_to_injectable_string`
1266
-
1267
- convert history into a string that can be injected into a prompt. it als returns the token length of the converted history.
1268
-
1269
- ```
1270
- chat_history-string, chat_context_token_length = unique_sdk.utils.chat_history.convert_chat_history_to_injectable_string(
1271
- history
1272
- )
1273
- ```
1274
-
1275
- ### File Io
1276
-
1277
- Interacting with the knowledge-base.
1278
-
1279
- #### `unique_sdk.utils.file_io.download_content`
1280
-
1281
- download files and save them into a folder in `/tmp`
1282
-
1283
- for example using the readUrl from a content.
1284
-
1285
- ```python
1286
- pdfFile = download_content(
1287
- companyId=companyId,
1288
- userId=userId,
1289
- content_id="cont_12412",
1290
- filename="hello.pdf",
1291
- chat_id=None # If specified, it downloads it from the chat
1292
- )
1293
- ```
1294
-
1295
- #### `unique_sdk.utils.file_io.upload_file`
1296
-
1297
- Allows for uploading files that then get ingested in a scope or a chat.
1298
-
1299
- ```python
1300
- createdContent = upload_file(
1301
- companyId=companyId,
1302
- userId=userId,
1303
- path_to_file="/tmp/hello.pdf",
1304
- displayed_filename="hello.pdf",
1305
- mime_type="application/pdf",
1306
- scope_or_unique_path="scope_stcj2osgbl722m22jayidx0n",
1307
- chat_id=None,
1308
- )
1309
- ```
1310
-
1311
- ### Sources
1312
-
1313
- #### `unique_sdk.utils.sources.merge_sources`
1314
-
1315
- Merges multiple search results based on their 'id', removing redundant document and info markers.
1316
-
1317
- This function groups search results by their 'id' and then concatenates their texts,
1318
- cleaning up any document or info markers in subsequent chunks beyond the first one.
1319
-
1320
- Parameters:
1321
-
1322
- - searchContext (list): A list of dictionaries, each representing a search result with 'id' and 'text' keys.
1323
-
1324
- Returns:
1325
-
1326
- - list: A list of dictionaries with merged texts for each unique 'id'.
1327
-
1328
- `searchContext` is an list of search objects that are returned by the search.
1329
-
1330
- ```python
1331
- search = unique_sdk.Search.create(
1332
- user_id=userId,
1333
- company_id=companyId,
1334
- chatId=chatId,
1335
- searchString="Who is Harry P?",
1336
- searchType="COMBINED",
1337
- scopeIds="scope_dsf...",
1338
- limit=30,
1339
- chatOnly=False,
1340
- )
1341
-
1342
- searchContext = unique_sdk.utils.token.pick_search_results_for_token_window(
1343
- search["data"], config["maxTokens"] - historyLength
1344
- )
1345
-
1346
- searchContext = unique_sdk.utils.sources.merge_sources(search)
1347
-
1348
- ```
1349
-
1350
- #### `unique_sdk.utils.sources.sort_sources`
1351
-
1352
- Sort sources by order of appearance in documents
1353
-
1354
- ```python
1355
-
1356
- search = unique_sdk.Search.create(
1357
- user_id=userId,
1358
- company_id=companyId,
1359
- chatId=chatId,
1360
- searchString="Who is Harry P?",
1361
- searchType="COMBINED",
1362
- scopeIds="scope_dsf...",
1363
- limit=30,
1364
- chatOnly=False,
1365
- )
1366
-
1367
- searchContext = unique_sdk.utils.token.pick_search_results_for_token_window(
1368
- search["data"], config["maxTokens"] - historyLength
1369
- )
1370
-
1371
- searchContext = unique_sdk.utils.sources.sort_sources(search)
1372
- ```
1373
-
1374
- #### `unique_sdk.utils.sources.post_process_sources`
1375
-
1376
- Post-processes the provided text by converting source references into superscript numerals (required
1377
- format by backend to display sources in the chat window)
1378
-
1379
- This function searches the input text for patterns that represent source references (e.g., [source1])
1380
- and replaces them with superscript tags, incrementing the number by one.
1381
-
1382
- Parameters:
1383
-
1384
- - text (str): The text to be post-processed.
1385
-
1386
- Returns:
1387
-
1388
- - str: The text with source references replaced by superscript numerals.
1389
-
1390
- Examples:
1391
-
1392
- - postprocessSources("This is a reference [source0]") will return "This is a reference <sup>1</sup>".
1393
-
1394
- ```python
1395
-
1396
- text_with_sup = post_process_sources(text)
1397
- ```
1398
-
1399
- ### Token
1400
-
1401
- #### unique_sdk.utils.token.pick_search_results_for_token_window
1402
-
1403
- Selects and returns a list of search results that fit within a specified token limit.
1404
-
1405
- This function iterates over a list of search results, each with a 'text' field, and
1406
- encodes the text using a predefined encoding scheme. It accumulates search results
1407
- until the token limit is reached or exceeded.
1408
-
1409
- Parameters:
1410
-
1411
- - searchResults (list): A list of dictionaries, each containing a 'text' key with string value.
1412
- - tokenLimit (int): The maximum number of tokens to include in the output.
1413
-
1414
- Returns:
1415
-
1416
- - list: A list of dictionaries representing the search results that fit within the token limit.
1417
-
1418
- ```python
1419
- search = unique_sdk.Search.create(
1420
- user_id=userId,
1421
- company_id=companyId,
1422
- chatId=chatId,
1423
- searchString="Who is Harry P?",
1424
- searchType="COMBINED",
1425
- scopeIds="scope_dsf...",
1426
- limit=30,
1427
- chatOnly=False,
1428
- )
1429
-
1430
- searchContext = unique_sdk.utils.token.pick_search_results_for_token_window(
1431
- search["data"], config["maxTokens"] - historyLength
1432
- )
1433
- ```
1434
-
1435
- #### unique_sdk.utils.token.count_tokens
1436
-
1437
- Counts the number of tokens in the provided text.
1438
-
1439
- This function encodes the input text using a predefined encoding scheme
1440
- and returns the number of tokens in the encoded text.
1441
-
1442
- Parameters:
1443
-
1444
- - text (str): The text to count tokens for.
1445
-
1446
- Returns:
1447
-
1448
- - int: The number of tokens in the text.
1449
-
1450
- ```python
1451
- hello = "hello you!"
1452
- searchContext = unique_sdk.utils.token.count_tokens(hello)
1453
- ```
1454
-
1455
- ### Chat In Space
1456
-
1457
- #### `unique_sdk.utils.chat_in_space.send_message_and_wait_for_completion`
1458
-
1459
- The following script enables you to chat within a space using an assistant. You must provide an `assistantId` (e.g., `assistant_hjcdga64bkcjnhu4`) and the message `text` to initiate the conversation. You can send the message in an existing chat by specifying a `chat_id`, or omit the `chat_id` to automatically create a new chat session. Check the optional parameteres list for more configs.
1460
-
1461
- The script sends a prompt asynchronously and continuously polls for completion, which is determined when the `stoppedStreamingAt` field of the message becomes non-null.
1462
-
1463
- **Optional parameters:**
1464
- - `tool_choices`: A list of tool names to be used for the message (e.g., `["WebSearch"]`). If not provided, no tools will be used. The tools supported right now are `WebSearch` and `InternalSearch`.
1465
- - `scope_rules`: A filter to specify the scope rules for the message, allowing you to restrict the context or data sources available to the assistant. The filter is written in UniqueQL language. Find out more about the language in the UniqueQL section.
1466
- - `chat_id`: The ID of the chat where the message should be sent. If omitted, a new chat will be created.
1467
- - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1468
- - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1469
- - `stop_condition`: Defines when to expect a response back, when the assistant stop streaming or when it completes the message. (default: "stoppedStreamingAt")
1470
-
1471
- The script ensures you can flexibly interact with spaces in new or ongoing chats, with fine-grained control over tools, context, and polling behavior.
1472
-
1473
- ```python
1474
- from unique_sdk.utils.chat_in_space import send_message_and_wait_for_completion
1475
- latest_message = await send_message_and_wait_for_completion(
1476
- user_id=user_id,
1477
- company_id=company_id,
1478
- assistant_id=assistant_id,
1479
- text="Tell me a short story.",
1480
- chat_id=chat_id, # Optional - if no chat id is specified, a new chat will be created
1481
- tool_choices=["WebSearch"],
1482
- scope_rules={
1483
- "or": [
1484
- {
1485
- "operator": "in",
1486
- "path": [
1487
- "contentId"
1488
- ],
1489
- "value": [
1490
- "cont_u888z7cazxxm4lugfdjq7pks"
1491
- ]
1492
- },
1493
- {
1494
- "operator": "contains",
1495
- "path": [
1496
- "folderIdPath"
1497
- ],
1498
- "value": "uniquepathid://scope_btfo28b3eeelwh5obwgea71bl/scope_fn56ta67knd6w4medgq3028fx"
1499
- }
1500
- ]
1501
- },
1502
- stop_condition = "completedAt" # If not specified, stoppedStreamingAt will be set by default
1503
- )
1504
- ```
1505
-
1506
- #### `unique_sdk.utils.chat_in_space.chat_against_file`
1507
-
1508
- The following script enables you to chat against a file.
1509
-
1510
- You must provide the following parameters:
1511
- - `assistantId`: The assistant to be used for the chat.
1512
- - `path_to_file`: The local path of the file to be uploaded.
1513
- - `displayed_filename`: The name of the file to be displayed.
1514
- - `mime_type`: The mime type of the ifle to be uploaded.
1515
- - `text`: The text to be sent to the chat for chatting against the file.
1516
-
1517
- 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.
1518
-
1519
- **Optional parameters:**
1520
- - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1521
- - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1522
-
1523
- Example of chatting against a PDF. (The usage can be extended to any supported file type)
1524
-
1525
- ```python
1526
- latest_message = await unique_sdk.utils.chat_in_space.chat_against_file(
1527
- user_id=user_id,
1528
- company_id=company_id,
1529
- assistant_id="assistant_hjcdga64bkcjnhu4",
1530
- path_to_file="/files/hello.pdf",
1531
- displayed_filename="hello.pdf"
1532
- mime_type="application/pdf"
1533
- text="Give me a bullet point summary of the file.",
1534
- )
1535
- ```
1536
-
1537
- #### `unique_sdk.utils.chat_in_space.wait_for_ingestion_completion`
1538
-
1539
- The following script enables you to wait for the ingestion of a file. This should be used carefully as it continuously polls for the status. In case of bigger files, adjust the `poll_interval` and `max_waits`.
1540
-
1541
- You must provide the following parameter:
1542
- - `content_id`: The id of the content to check.
1543
-
1544
- The script polls until the content ingestion is finished or the maximum wait time is reached and throws in case ingestion fails. The function assumes that the content exists.
1545
-
1546
- **Optional parameters:**
1547
- - `chat_id`: In case the content is uploaded to a chat, the `chat_id` must be provided.
1548
- - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1549
- - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1550
-
1551
- Example of waiting for the ingestion of a file in the Knowledge Base.
1552
-
1553
- ```python
1554
- await unique_sdk.utils.chat_in_space.wait_for_ingestion_completion(
1555
- user_id=user_id,
1556
- company_id=company_id,
1557
- content_id="cont_ddlezvag4kzxudfr24lrjc5mx",
1558
- )
1559
- ```
1560
-
1561
- ## Error Handling
1562
-
1563
- ## Examples
1564
-
1565
- An example Flask app demonstrating the usage of each API resource and how to interact with Webhooks is available in our repository at `/examples/custom-assistant`.
1566
-
1567
- ## Credits
1568
-
1569
- This is a _fork_ / inspired-by the fantastic Stripe Python SDK (https://github.com/stripe/stripe-python).
1570
-
1571
- # Changelog
1572
-
1573
- All notable changes to this project will be documented in this file.
1574
-
1575
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1576
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1577
-
1578
- ## [0.10.19] - 2025-09-02
1579
- - Improve `send_message_and_wait_for_completion`:
1580
- - Add option to select stop_condition `["stoppedStreamingAt", "completedAt"]`.
1581
- - Load `debugInfo` from `last_user_message` for better developer experience.
1582
-
1583
- ## [0.10.18] - 2025-09-02
1584
- - Temporarily remove support for update and delete files by filePath.
1585
-
1586
- ## [0.10.17] - 2025-09-01
1587
- - Add function to update a file
1588
-
1589
- ## [0.10.16] - 2025-08-31
1590
- - Add function to delete a content.
1591
-
1592
- ## [0.10.15] - 2025-08-28
1593
- - Add default values for message log types
1594
-
1595
- ## [0.10.14] - 2025-08-28
1596
- - Add function to delete folders and files recursively
1597
-
1598
- ## [0.10.13] - 2025-08-24
1599
- - Add functions to create, get and update a message eecution and create and update a message log.
1600
-
1601
- ## [0.10.12] - 2025-08-24
1602
- - Switch to using Content get info deprecated endpoint to make sure we support older release versions.
1603
-
1604
- ## [0.10.11] - 2025-08-24
1605
- - Enforce usage of ruff using pipeline
1606
-
1607
- ## [0.10.10] - 2025-08-18
1608
- - Fix wrong name of references in `Space.Message`.
1609
- - Fix wrong name of assessment in `Space.Message`.
1610
- - Remove default values for `text`, `originalText` and `debugInfo` in `Space.Message` as these don't have an effect.
1611
-
1612
- ## [0.10.9] - 2025-08-15
1613
- - Add script to wait for content ingestion finished.
1614
-
1615
- ## [0.10.8] - 2025-08-13
1616
- - Add support for Agentic Table.
1617
-
1618
- ## [0.10.7] - 2025-08-13
1619
- - Make metadata optional when uploading a file.
1620
-
1621
- ## [0.10.6] - 2025-08-06
1622
- - Make tools optional for running an agent.
1623
-
1624
- ## [0.10.5] - 2025-08-06
1625
- - Get paginated files and folders info.
1626
-
1627
- ## [0.10.4] - 2025-08-05
1628
- - Add support for reasoning API with streaming within a chat.
1629
-
1630
- ## [0.10.3] - 2025-08-05
1631
- - Expose scoreThreshold param for search.
1632
-
1633
- ## [0.10.2] - 2025-08-05
1634
- - Add script to chat against file.
1635
-
1636
- ## [0.10.1] - 2025-08-05
1637
- - Allow deletion of a space chat.
1638
-
1639
- ## [0.10.0] - 2025-08-04
1640
- - Add MCP support
1641
-
1642
- ## [0.9.42] - 2025-07-31
1643
- - Fix wrong chat in space example.
1644
-
1645
- ## [0.9.41] - 2025-07-31
1646
- - Fix double-slash error in open ai proxy script.
1647
-
1648
- ## [0.9.40] - 2025-07-22
1649
- - Fixed bug where get requests send body with the request. This is not allowed by WAF policies.
1650
-
1651
- ## [0.9.39] - 2025-07-18
1652
- - Add script to chat in a space.
1653
-
1654
- ## [0.9.38] - 2025-07-18
1655
- - [Experimental] Add support for Unique OpenAI proxy. You can now use the OpenAI SDK directly through Unique. Checkout how to do this and a few examples here: `tutorials/unique_basics/sdk_examples/openai_scripts.py`.
1656
-
1657
- ## [0.9.37] - 2025-07-10
1658
- - Add `sheetName` property to the `MagicTableSheetIngestParams` object used by function that ingests magic table sheets.
1659
-
1660
- ## [0.9.36] - 2025-06-23
1661
- - Allow passing a user id when creating chat completions. This is optional and it does not impact the current behaviour.
1662
-
1663
- ## [0.9.35] - 2025-06-18
1664
- - Allow scope access updates (add/remove) on folder based on scope id or path.
1665
-
1666
- ## [0.9.34] - 2025-06-17
1667
- - Allow ingestion config updates on folder based on scope id or path.
1668
-
1669
- ## [0.9.33] - 2025-06-11
1670
- - Add function to get a folder by id or by path.
1671
-
1672
- ## [0.9.32] - 2025-06-11
1673
- - Add function to ingest magic table sheets.
1674
-
1675
- ## [0.9.31] - 2025-05-21
1676
- - Add function to update folder access (add or remove).
1677
-
1678
- ## [0.9.30] - 2025-05-21
1679
- - Add function to update folder ingestion config.
1680
-
1681
- ## [0.9.29] - 2025-05-20
1682
- - Add function to create folder paths if they do not exist.
1683
-
1684
- ## [0.9.28] - 2025-05-20
1685
- - Add function to search content info. This also allows filtering content info by metadata info.
1686
-
1687
- ## [0.9.27] - 2025-05-14
1688
- - Add the possibility to specify metadata when creating or updating a Content.
1689
-
1690
- ## [0.9.26] - 2025-05-13
1691
- - Add the possibility to specify ingestionConfig when creating or updating a Content.
1692
-
1693
- ## [0.9.25] - 2025-05-02
1694
- - Fixed typos in `README.md`, including incorrect `sdk.utils` imports and code example errors.
1695
-
1696
- ## [0.9.24] - 2025-04-23
1697
- - Make `chatId` property in `Search.CreateParams` optional
1698
-
1699
- ## [0.9.23] - 2025-03-25
1700
- - Define programming language classifier explicitly for python 3.11
1701
-
1702
- ## [0.9.22] - 2025-02-25
1703
- - update the retry_on_error to only `APIError` and `APIConnectionError` update the `resp["error"]` to be `resp.get("error")` to avoid key error
1704
-
1705
- ## [0.9.21] - 2025-02-21
1706
- - Add title parameter and change labels in `MessageAssessment`
1707
-
1708
- ## [0.9.20] - 2025-02-01
1709
- - Add url parameter to `MessageAssessment.create_async` and `MessageAssessment.modify_async`
1710
-
1711
- ## [0.9.19] - 2025-01-31
1712
- - Add `MessageAssessment` resource
1713
-
1714
- ## [0.9.18] - 2025-01-22
1715
- - Removed `Invalid response body from API` from `retry_dict` as it's our own artificail error.
1716
-
1717
- ## [0.9.17] - 2025-01-03
1718
- - BREAKING CHANGE!! Removed unused `id` from `ShortTermMemory` create and find methods.
1719
-
1720
- ## [0.9.16] - 2024-12-19
1721
- - Corrected return type of `Search.create` and `Search.create_async` to `List[Search]`
1722
- - Retry on `Connection aborted` error
1723
-
1724
- ## [0.9.15] - 2024-12-06
1725
- - Add `Internal server error` and `You can retry your request` to the retry logic
1726
-
1727
- ## [0.9.14] - 2024-12-06
1728
- - Add `contentIds` to `Search.create` and `Search.create_async`
1729
-
1730
- ## [0.9.13] - 2024-10-23
1731
- - Add retry for `5xx` errors, add additional error message.
1732
-
1733
- ## [0.9.12] - 2024-11-21
1734
- - Include original error message in returned exceptions
1735
-
1736
- ## [0.9.11] - 2024-11-18
1737
- - Add `ingestionConfig` to `UpsertParams.Input` parameters
1738
-
1739
- ## [0.9.10] - 2024-10-23
1740
- - Remove `temperature` parameter from `Integrated.chat_stream_completion`, `Integrated.chat_stream_completion_async`, `ChatCompletion.create` and `ChatCompletion.create_async` methods. To use `temperature` parameter, set the attribute in `options` parameter instead.
1741
-
1742
- ## [0.9.9] - 2024-10-23
1743
- - Revert deletion of `Message.retrieve` method
1744
-
1745
- ## [0.9.8] - 2024-10-16
1746
- - Add `retries` for `_static_request` and `_static_request_async` in `APIResource` - When the error messages contains either `"problem proxying the request"`,
1747
- or `"Upstream service reached a hard timeout"`,
1748
- ## [0.9.7] - 2024-09-23
1749
- - Add `completedAt` to `CreateParams` of `Message`
1750
-
1751
- ## [0.9.6] - 2024-09-03
1752
- - Added `metaDataFilter` to `Search` parameters.
1753
-
1754
- ## [0.9.5] - 2024-08-07
1755
- - Add `completedAt` to `ModifyParams`
1756
-
1757
- ## [0.9.4] - 2024-07-31
1758
- - Add `close` and `close_async` to `http_client`
1759
- - Make `httpx` the default client for async requests
1760
-
1761
- ## [0.9.3] - 2024-07-31
1762
- - `Search.create`, `Message`, `ChatCompletion` parameters that were marked `NotRequired` are now also `Optional`
1763
-
1764
- ## [0.9.2] - 2024-07-30
1765
- - Bug fix in `Search.create`: langugage -> language
1766
-
1767
- ## [0.9.1] - 2024-07-30
1768
- - Added parameters to `Search.create` and `Search.create_async`
1769
- - `language` for full text search
1770
- - `reranker` to reranker search results
1771
-
1772
- ## [0.9.0] - 2024-07-29
1773
- - Added the possibility to make async requests to the unique APIs using either aiohttp or httpx as client
1774
-