unique_sdk 0.9.38__tar.gz → 0.9.39__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/CHANGELOG.md +3 -0
  2. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/PKG-INFO +220 -172
  3. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/README.md +216 -171
  4. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/pyproject.toml +1 -1
  5. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/__init__.py +1 -0
  6. unique_sdk-0.9.39/unique_sdk/api_resources/_space.py +144 -0
  7. unique_sdk-0.9.39/unique_sdk/utils/chat_in_space.py +53 -0
  8. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/LICENSE +0 -0
  9. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_api_requestor.py +0 -0
  10. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_api_resource.py +0 -0
  11. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_api_version.py +0 -0
  12. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_error.py +0 -0
  13. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_http_client.py +0 -0
  14. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_list_object.py +0 -0
  15. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_object_classes.py +0 -0
  16. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_request_options.py +0 -0
  17. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_unique_object.py +0 -0
  18. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_unique_ql.py +0 -0
  19. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_unique_response.py +0 -0
  20. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_util.py +0 -0
  21. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_version.py +0 -0
  22. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/_webhook.py +0 -0
  23. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/__init__.py +0 -0
  24. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_acronyms.py +0 -0
  25. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_chat_completion.py +0 -0
  26. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_content.py +0 -0
  27. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_embedding.py +0 -0
  28. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_event.py +0 -0
  29. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_folder.py +0 -0
  30. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_integrated.py +0 -0
  31. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_message.py +0 -0
  32. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_message_assessment.py +0 -0
  33. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_search.py +0 -0
  34. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_search_string.py +0 -0
  35. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  36. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/utils/chat_history.py +0 -0
  37. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/utils/file_io.py +0 -0
  38. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/utils/sources.py +0 -0
  39. {unique_sdk-0.9.38 → unique_sdk-0.9.39}/unique_sdk/utils/token.py +0 -0
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.39] - 2025-07-18
9
+ - Add script to chat in a space.
10
+
8
11
  ## [0.9.38] - 2025-07-18
9
12
  - [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`.
10
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_sdk
3
- Version: 0.9.38
3
+ Version: 0.9.39
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: Martin Fadler
@@ -28,7 +28,6 @@ The Unique Python SDK provides access to the public API of Unique FinanceGPT. It
28
28
  4. [Webhook Triggers](#webhook-triggers)
29
29
  5. [Available API Resources](#available-api-resources)
30
30
  - [Content](#content)
31
- - [Folder](#folder)
32
31
  - [Message](#message)
33
32
  - [Chat Completion](#chat-completion)
34
33
  - [Embeddings](#embeddings)
@@ -46,6 +45,7 @@ The Unique Python SDK provides access to the public API of Unique FinanceGPT. It
46
45
  - [File Io](#file-io)
47
46
  - [Sources](#sources)
48
47
  - [token](#token)
48
+ - [Chat In Space](#chat-in-space)
49
49
  8. [Error Handling](#error-handling)
50
50
  9. [Examples](#examples)
51
51
 
@@ -244,8 +244,12 @@ unique_sdk.Message.modify(
244
244
  - [Content](#content)
245
245
  - [Message](#message)
246
246
  - [Chat Completion](#chat-completion)
247
+ - [Embeddings](#embeddings)
248
+ - [Acronyms](#acronyms)
247
249
  - [Search](#search)
248
250
  - [Search String](#search-string)
251
+ - [Short Term Memory](#short-term-memory)
252
+ - [Message Assessment](#message-assessment)
249
253
  - [Folder](#folder)
250
254
 
251
255
  Most of the API services provide an asynchronous version of the method. The async methods are suffixed with `_async`.
@@ -469,175 +473,6 @@ Allows you to ingest a magic table sheet, each row is processed and converted in
469
473
  unique_sdk.Content.ingest_magic_table_sheets(**params)
470
474
  ```
471
475
 
472
- ### Folder
473
-
474
- #### `unique_sdk.Folder.get`
475
-
476
- Get a folder by scope id or by path.
477
-
478
- By scope id:
479
-
480
- ```python
481
- unique_sdk.Folder.get_info(
482
- user_id=user_id,
483
- company_id=company_id,
484
- scopeId="scope_w78wfn114va9o22s13r03yq",
485
- )
486
- ```
487
-
488
- By path:
489
-
490
- ```python
491
- unique_sdk.Folder.get_info(
492
- user_id=user_id,
493
- company_id=company_id,
494
- folderPath="/Company/Atlas/Due Dilligence/Arch,
495
- )
496
- ```
497
-
498
- #### `unique_sdk.Folder.create_paths`
499
-
500
- Create each folder in the provided list of paths if it does not already exist.
501
-
502
- ```python
503
- unique_sdk.Folder.create_paths(
504
- user_id=user_id,
505
- company_id=company_id,
506
- paths=["/unique/path1", "/unique/path2"],
507
- )
508
- ```
509
-
510
- #### `unique_sdk.Folder.update_ingestion_config`
511
-
512
- Allows you to update the ingestion config of a folder and choose whether to apply to the subscopes or not: `
513
-
514
- - `ingestionConfig`
515
- - `applyToSubScopes`
516
-
517
- 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.
518
-
519
- Example of updating the ingestion config of a folder and its subfolders using the id.
520
-
521
- ```python
522
- unique_sdk.Folder.update_ingestion_config(
523
- user_id=user_id,
524
- company_id=company_id,
525
- scopeId="scope_qbnkde820dbmuw2900,
526
- ingestionConfig={
527
- "chunkStrategy": "default",
528
- "uniqueIngestionMode": "standard",
529
- },
530
- applyToSubScopes=True
531
- )
532
- ```
533
-
534
- Example of updating the ingestion config of a folder and its subfolders using the path.
535
-
536
- ```python
537
- unique_sdk.Folder.update_ingestion_config(
538
- user_id=user_id,
539
- company_id=company_id,
540
- folderPath="/Company/folder1/folder2",
541
- ingestionConfig={
542
- "chunkStrategy": "default",
543
- "uniqueIngestionMode": "standard",
544
- },
545
- applyToSubScopes=True
546
- )
547
- ```
548
-
549
- #### `unique_sdk.Folder.add_access`
550
-
551
- Allows you to add access to a folder and apply to the subfolders or not: `
552
-
553
- - `scopeAccesses`
554
- - `applyToSubScopes`
555
-
556
- 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.
557
-
558
- Example of adding access to a folder and its subfolders using the id.
559
-
560
- ```python
561
- unique_sdk.Folder.add_access(
562
- user_id=user_id,
563
- company_id=company_id,
564
- scopeId="scope_231e4kjn4foffww34",
565
- scopeAccesses=[
566
- {
567
- "entityId": "group_id",
568
- "type": "WRITE",
569
- "entityType": "GROUP",
570
- }
571
- ],
572
- applyToSubScopes=True,
573
- )
574
- ```
575
-
576
- Example of adding access to a folder and its subfolders using the folder path.
577
-
578
- ```python
579
- unique_sdk.Folder.add_access(
580
- user_id=user_id,
581
- company_id=company_id,
582
- folderPath="/Company/folder1/folder2"
583
- scopeAccesses=[
584
- {
585
- "entityId": "group_id",
586
- "type": "WRITE",
587
- "entityType": "GROUP",
588
- }
589
- ],
590
- applyToSubScopes=True,
591
- )
592
- ```
593
-
594
- #### `unique_sdk.Folder.remove_access`
595
-
596
- Allows you to delete access from a folder and apply to the subfolders or not: `
597
-
598
- - `scopeAccesses`
599
- - `applyToSubScopes`
600
-
601
- 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.
602
-
603
-
604
- Example of deleting the access from a folder and its subfolders using the id.
605
-
606
- ```python
607
- unique_sdk.Folder.remove_access(
608
- user_id=user_id,
609
- company_id=company_id,
610
- scopeId="scope_dwekjnf3330woioppm,
611
- scopeAccesses=[
612
- {
613
- "entityId": "group_id",
614
- "type": "WRITE",
615
- "entityType": "GROUP",
616
- }
617
- ],
618
- applyToSubScopes=True,
619
- )
620
- ```
621
-
622
-
623
- Example of deleting the access from a folder and its subfolders using the path.
624
-
625
- ```python
626
- unique_sdk.Folder.remove_access(
627
- user_id=user_id,
628
- company_id=company_id,
629
- folderPath="/Company/folder1/folder2"
630
- scopeAccesses=[
631
- {
632
- "entityId": "group_id",
633
- "type": "WRITE",
634
- "entityType": "GROUP",
635
- }
636
- ],
637
- applyToSubScopes=True,
638
- )
639
- ```
640
-
641
476
  ### Message
642
477
 
643
478
  #### `unique_sdk.Message.list`
@@ -946,6 +781,175 @@ assessment = unique_sdk.MessageAssessment.modify(
946
781
  )
947
782
  ```
948
783
 
784
+ ### Folder
785
+
786
+ #### `unique_sdk.Folder.get`
787
+
788
+ Get a folder by scope id or by path.
789
+
790
+ By scope id:
791
+
792
+ ```python
793
+ unique_sdk.Folder.get_info(
794
+ user_id=user_id,
795
+ company_id=company_id,
796
+ scopeId="scope_w78wfn114va9o22s13r03yq",
797
+ )
798
+ ```
799
+
800
+ By path:
801
+
802
+ ```python
803
+ unique_sdk.Folder.get_info(
804
+ user_id=user_id,
805
+ company_id=company_id,
806
+ folderPath="/Company/Atlas/Due Dilligence/Arch,
807
+ )
808
+ ```
809
+
810
+ #### `unique_sdk.Folder.create_paths`
811
+
812
+ Create each folder in the provided list of paths if it does not already exist.
813
+
814
+ ```python
815
+ unique_sdk.Folder.create_paths(
816
+ user_id=user_id,
817
+ company_id=company_id,
818
+ paths=["/unique/path1", "/unique/path2"],
819
+ )
820
+ ```
821
+
822
+ #### `unique_sdk.Folder.update_ingestion_config`
823
+
824
+ Allows you to update the ingestion config of a folder and choose whether to apply to the subscopes or not: `
825
+
826
+ - `ingestionConfig`
827
+ - `applyToSubScopes`
828
+
829
+ 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.
830
+
831
+ Example of updating the ingestion config of a folder and its subfolders using the id.
832
+
833
+ ```python
834
+ unique_sdk.Folder.update_ingestion_config(
835
+ user_id=user_id,
836
+ company_id=company_id,
837
+ scopeId="scope_qbnkde820dbmuw2900,
838
+ ingestionConfig={
839
+ "chunkStrategy": "default",
840
+ "uniqueIngestionMode": "standard",
841
+ },
842
+ applyToSubScopes=True
843
+ )
844
+ ```
845
+
846
+ Example of updating the ingestion config of a folder and its subfolders using the path.
847
+
848
+ ```python
849
+ unique_sdk.Folder.update_ingestion_config(
850
+ user_id=user_id,
851
+ company_id=company_id,
852
+ folderPath="/Company/folder1/folder2",
853
+ ingestionConfig={
854
+ "chunkStrategy": "default",
855
+ "uniqueIngestionMode": "standard",
856
+ },
857
+ applyToSubScopes=True
858
+ )
859
+ ```
860
+
861
+ #### `unique_sdk.Folder.add_access`
862
+
863
+ Allows you to add access to a folder and apply to the subfolders or not: `
864
+
865
+ - `scopeAccesses`
866
+ - `applyToSubScopes`
867
+
868
+ 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.
869
+
870
+ Example of adding access to a folder and its subfolders using the id.
871
+
872
+ ```python
873
+ unique_sdk.Folder.add_access(
874
+ user_id=user_id,
875
+ company_id=company_id,
876
+ scopeId="scope_231e4kjn4foffww34",
877
+ scopeAccesses=[
878
+ {
879
+ "entityId": "group_id",
880
+ "type": "WRITE",
881
+ "entityType": "GROUP",
882
+ }
883
+ ],
884
+ applyToSubScopes=True,
885
+ )
886
+ ```
887
+
888
+ Example of adding access to a folder and its subfolders using the folder path.
889
+
890
+ ```python
891
+ unique_sdk.Folder.add_access(
892
+ user_id=user_id,
893
+ company_id=company_id,
894
+ folderPath="/Company/folder1/folder2"
895
+ scopeAccesses=[
896
+ {
897
+ "entityId": "group_id",
898
+ "type": "WRITE",
899
+ "entityType": "GROUP",
900
+ }
901
+ ],
902
+ applyToSubScopes=True,
903
+ )
904
+ ```
905
+
906
+ #### `unique_sdk.Folder.remove_access`
907
+
908
+ Allows you to delete access from a folder and apply to the subfolders or not: `
909
+
910
+ - `scopeAccesses`
911
+ - `applyToSubScopes`
912
+
913
+ 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.
914
+
915
+
916
+ Example of deleting the access from a folder and its subfolders using the id.
917
+
918
+ ```python
919
+ unique_sdk.Folder.remove_access(
920
+ user_id=user_id,
921
+ company_id=company_id,
922
+ scopeId="scope_dwekjnf3330woioppm,
923
+ scopeAccesses=[
924
+ {
925
+ "entityId": "group_id",
926
+ "type": "WRITE",
927
+ "entityType": "GROUP",
928
+ }
929
+ ],
930
+ applyToSubScopes=True,
931
+ )
932
+ ```
933
+
934
+
935
+ Example of deleting the access from a folder and its subfolders using the path.
936
+
937
+ ```python
938
+ unique_sdk.Folder.remove_access(
939
+ user_id=user_id,
940
+ company_id=company_id,
941
+ folderPath="/Company/folder1/folder2"
942
+ scopeAccesses=[
943
+ {
944
+ "entityId": "group_id",
945
+ "type": "WRITE",
946
+ "entityType": "GROUP",
947
+ }
948
+ ],
949
+ applyToSubScopes=True,
950
+ )
951
+ ```
952
+
949
953
  ## UniqueQL
950
954
 
951
955
  [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.
@@ -1051,7 +1055,7 @@ pdfFile = download_content(
1051
1055
  content_id="cont_12412",
1052
1056
  filename="hello.pdf",
1053
1057
  chat_id=None # If specified, it downloads it from the chat
1054
- }
1058
+ )
1055
1059
  ```
1056
1060
 
1057
1061
  #### `unique_sdk.utils.file_io.upload_file`
@@ -1214,6 +1218,47 @@ hello = "hello you!"
1214
1218
  searchContext = unique_sdk.utils.token.count_tokens(hello)
1215
1219
  ```
1216
1220
 
1221
+ ### Chat In Space
1222
+
1223
+ #### `unique_sdk.utils.chat_in_space.send_message_and_wait_for_completion`
1224
+
1225
+ 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.
1226
+
1227
+ The script sends a prompt asynchronously and continuously polls for completion, which is determined when the `stoppedStreamingAt` field of the message becomes non-null.
1228
+
1229
+ **Optional parameters:**
1230
+ - `tool_choices`: A list of tool names to be used for the message (e.g., `["WebSearch"]`). If not provided, no tools will be used.
1231
+ - `scope_rules`: A dictionary specifying scope rules for the message, allowing you to restrict the context or data sources available to the assistant.
1232
+ - `chat_id`: The ID of the chat where the message should be sent. If omitted, a new chat will be created.
1233
+ - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1234
+ - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1235
+
1236
+ The script ensures you can flexibly interact with spaces in new or ongoing chats, with fine-grained control over tools, context, and polling behavior.
1237
+
1238
+ ```python
1239
+ latest_message = unique_sdk.utils.send_message_and_wait_for_completion(
1240
+ user_id=user_id,
1241
+ company_id=company_id,
1242
+ assistant_id=assistant_id,
1243
+ text="Tell me a short story.",
1244
+ chat_id=chat_id, # Optional - if no chat id is specified, a new chat will be created
1245
+ tool_choices=["WebSearch"],
1246
+ scope_rules={
1247
+ "or": [
1248
+ {
1249
+ "operator": "in",
1250
+ "path": [
1251
+ "contentId"
1252
+ ],
1253
+ "value": [
1254
+ "cont_u888z7cazxxm4lugfdjq7pks"
1255
+ ]
1256
+ }
1257
+ ]
1258
+ },
1259
+ )
1260
+ ```
1261
+
1217
1262
  ## Error Handling
1218
1263
 
1219
1264
  ## Examples
@@ -1231,6 +1276,9 @@ All notable changes to this project will be documented in this file.
1231
1276
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1232
1277
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1233
1278
 
1279
+ ## [0.9.39] - 2025-07-18
1280
+ - Add script to chat in a space.
1281
+
1234
1282
  ## [0.9.38] - 2025-07-18
1235
1283
  - [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`.
1236
1284
 
@@ -12,7 +12,6 @@ The Unique Python SDK provides access to the public API of Unique FinanceGPT. It
12
12
  4. [Webhook Triggers](#webhook-triggers)
13
13
  5. [Available API Resources](#available-api-resources)
14
14
  - [Content](#content)
15
- - [Folder](#folder)
16
15
  - [Message](#message)
17
16
  - [Chat Completion](#chat-completion)
18
17
  - [Embeddings](#embeddings)
@@ -30,6 +29,7 @@ The Unique Python SDK provides access to the public API of Unique FinanceGPT. It
30
29
  - [File Io](#file-io)
31
30
  - [Sources](#sources)
32
31
  - [token](#token)
32
+ - [Chat In Space](#chat-in-space)
33
33
  8. [Error Handling](#error-handling)
34
34
  9. [Examples](#examples)
35
35
 
@@ -228,8 +228,12 @@ unique_sdk.Message.modify(
228
228
  - [Content](#content)
229
229
  - [Message](#message)
230
230
  - [Chat Completion](#chat-completion)
231
+ - [Embeddings](#embeddings)
232
+ - [Acronyms](#acronyms)
231
233
  - [Search](#search)
232
234
  - [Search String](#search-string)
235
+ - [Short Term Memory](#short-term-memory)
236
+ - [Message Assessment](#message-assessment)
233
237
  - [Folder](#folder)
234
238
 
235
239
  Most of the API services provide an asynchronous version of the method. The async methods are suffixed with `_async`.
@@ -453,175 +457,6 @@ Allows you to ingest a magic table sheet, each row is processed and converted in
453
457
  unique_sdk.Content.ingest_magic_table_sheets(**params)
454
458
  ```
455
459
 
456
- ### Folder
457
-
458
- #### `unique_sdk.Folder.get`
459
-
460
- Get a folder by scope id or by path.
461
-
462
- By scope id:
463
-
464
- ```python
465
- unique_sdk.Folder.get_info(
466
- user_id=user_id,
467
- company_id=company_id,
468
- scopeId="scope_w78wfn114va9o22s13r03yq",
469
- )
470
- ```
471
-
472
- By path:
473
-
474
- ```python
475
- unique_sdk.Folder.get_info(
476
- user_id=user_id,
477
- company_id=company_id,
478
- folderPath="/Company/Atlas/Due Dilligence/Arch,
479
- )
480
- ```
481
-
482
- #### `unique_sdk.Folder.create_paths`
483
-
484
- Create each folder in the provided list of paths if it does not already exist.
485
-
486
- ```python
487
- unique_sdk.Folder.create_paths(
488
- user_id=user_id,
489
- company_id=company_id,
490
- paths=["/unique/path1", "/unique/path2"],
491
- )
492
- ```
493
-
494
- #### `unique_sdk.Folder.update_ingestion_config`
495
-
496
- Allows you to update the ingestion config of a folder and choose whether to apply to the subscopes or not: `
497
-
498
- - `ingestionConfig`
499
- - `applyToSubScopes`
500
-
501
- 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.
502
-
503
- Example of updating the ingestion config of a folder and its subfolders using the id.
504
-
505
- ```python
506
- unique_sdk.Folder.update_ingestion_config(
507
- user_id=user_id,
508
- company_id=company_id,
509
- scopeId="scope_qbnkde820dbmuw2900,
510
- ingestionConfig={
511
- "chunkStrategy": "default",
512
- "uniqueIngestionMode": "standard",
513
- },
514
- applyToSubScopes=True
515
- )
516
- ```
517
-
518
- Example of updating the ingestion config of a folder and its subfolders using the path.
519
-
520
- ```python
521
- unique_sdk.Folder.update_ingestion_config(
522
- user_id=user_id,
523
- company_id=company_id,
524
- folderPath="/Company/folder1/folder2",
525
- ingestionConfig={
526
- "chunkStrategy": "default",
527
- "uniqueIngestionMode": "standard",
528
- },
529
- applyToSubScopes=True
530
- )
531
- ```
532
-
533
- #### `unique_sdk.Folder.add_access`
534
-
535
- Allows you to add access to a folder and apply to the subfolders or not: `
536
-
537
- - `scopeAccesses`
538
- - `applyToSubScopes`
539
-
540
- 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.
541
-
542
- Example of adding access to a folder and its subfolders using the id.
543
-
544
- ```python
545
- unique_sdk.Folder.add_access(
546
- user_id=user_id,
547
- company_id=company_id,
548
- scopeId="scope_231e4kjn4foffww34",
549
- scopeAccesses=[
550
- {
551
- "entityId": "group_id",
552
- "type": "WRITE",
553
- "entityType": "GROUP",
554
- }
555
- ],
556
- applyToSubScopes=True,
557
- )
558
- ```
559
-
560
- Example of adding access to a folder and its subfolders using the folder path.
561
-
562
- ```python
563
- unique_sdk.Folder.add_access(
564
- user_id=user_id,
565
- company_id=company_id,
566
- folderPath="/Company/folder1/folder2"
567
- scopeAccesses=[
568
- {
569
- "entityId": "group_id",
570
- "type": "WRITE",
571
- "entityType": "GROUP",
572
- }
573
- ],
574
- applyToSubScopes=True,
575
- )
576
- ```
577
-
578
- #### `unique_sdk.Folder.remove_access`
579
-
580
- Allows you to delete access from a folder and apply to the subfolders or not: `
581
-
582
- - `scopeAccesses`
583
- - `applyToSubScopes`
584
-
585
- 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.
586
-
587
-
588
- Example of deleting the access from a folder and its subfolders using the id.
589
-
590
- ```python
591
- unique_sdk.Folder.remove_access(
592
- user_id=user_id,
593
- company_id=company_id,
594
- scopeId="scope_dwekjnf3330woioppm,
595
- scopeAccesses=[
596
- {
597
- "entityId": "group_id",
598
- "type": "WRITE",
599
- "entityType": "GROUP",
600
- }
601
- ],
602
- applyToSubScopes=True,
603
- )
604
- ```
605
-
606
-
607
- Example of deleting the access from a folder and its subfolders using the path.
608
-
609
- ```python
610
- unique_sdk.Folder.remove_access(
611
- user_id=user_id,
612
- company_id=company_id,
613
- folderPath="/Company/folder1/folder2"
614
- scopeAccesses=[
615
- {
616
- "entityId": "group_id",
617
- "type": "WRITE",
618
- "entityType": "GROUP",
619
- }
620
- ],
621
- applyToSubScopes=True,
622
- )
623
- ```
624
-
625
460
  ### Message
626
461
 
627
462
  #### `unique_sdk.Message.list`
@@ -930,6 +765,175 @@ assessment = unique_sdk.MessageAssessment.modify(
930
765
  )
931
766
  ```
932
767
 
768
+ ### Folder
769
+
770
+ #### `unique_sdk.Folder.get`
771
+
772
+ Get a folder by scope id or by path.
773
+
774
+ By scope id:
775
+
776
+ ```python
777
+ unique_sdk.Folder.get_info(
778
+ user_id=user_id,
779
+ company_id=company_id,
780
+ scopeId="scope_w78wfn114va9o22s13r03yq",
781
+ )
782
+ ```
783
+
784
+ By path:
785
+
786
+ ```python
787
+ unique_sdk.Folder.get_info(
788
+ user_id=user_id,
789
+ company_id=company_id,
790
+ folderPath="/Company/Atlas/Due Dilligence/Arch,
791
+ )
792
+ ```
793
+
794
+ #### `unique_sdk.Folder.create_paths`
795
+
796
+ Create each folder in the provided list of paths if it does not already exist.
797
+
798
+ ```python
799
+ unique_sdk.Folder.create_paths(
800
+ user_id=user_id,
801
+ company_id=company_id,
802
+ paths=["/unique/path1", "/unique/path2"],
803
+ )
804
+ ```
805
+
806
+ #### `unique_sdk.Folder.update_ingestion_config`
807
+
808
+ Allows you to update the ingestion config of a folder and choose whether to apply to the subscopes or not: `
809
+
810
+ - `ingestionConfig`
811
+ - `applyToSubScopes`
812
+
813
+ 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.
814
+
815
+ Example of updating the ingestion config of a folder and its subfolders using the id.
816
+
817
+ ```python
818
+ unique_sdk.Folder.update_ingestion_config(
819
+ user_id=user_id,
820
+ company_id=company_id,
821
+ scopeId="scope_qbnkde820dbmuw2900,
822
+ ingestionConfig={
823
+ "chunkStrategy": "default",
824
+ "uniqueIngestionMode": "standard",
825
+ },
826
+ applyToSubScopes=True
827
+ )
828
+ ```
829
+
830
+ Example of updating the ingestion config of a folder and its subfolders using the path.
831
+
832
+ ```python
833
+ unique_sdk.Folder.update_ingestion_config(
834
+ user_id=user_id,
835
+ company_id=company_id,
836
+ folderPath="/Company/folder1/folder2",
837
+ ingestionConfig={
838
+ "chunkStrategy": "default",
839
+ "uniqueIngestionMode": "standard",
840
+ },
841
+ applyToSubScopes=True
842
+ )
843
+ ```
844
+
845
+ #### `unique_sdk.Folder.add_access`
846
+
847
+ Allows you to add access to a folder and apply to the subfolders or not: `
848
+
849
+ - `scopeAccesses`
850
+ - `applyToSubScopes`
851
+
852
+ 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.
853
+
854
+ Example of adding access to a folder and its subfolders using the id.
855
+
856
+ ```python
857
+ unique_sdk.Folder.add_access(
858
+ user_id=user_id,
859
+ company_id=company_id,
860
+ scopeId="scope_231e4kjn4foffww34",
861
+ scopeAccesses=[
862
+ {
863
+ "entityId": "group_id",
864
+ "type": "WRITE",
865
+ "entityType": "GROUP",
866
+ }
867
+ ],
868
+ applyToSubScopes=True,
869
+ )
870
+ ```
871
+
872
+ Example of adding access to a folder and its subfolders using the folder path.
873
+
874
+ ```python
875
+ unique_sdk.Folder.add_access(
876
+ user_id=user_id,
877
+ company_id=company_id,
878
+ folderPath="/Company/folder1/folder2"
879
+ scopeAccesses=[
880
+ {
881
+ "entityId": "group_id",
882
+ "type": "WRITE",
883
+ "entityType": "GROUP",
884
+ }
885
+ ],
886
+ applyToSubScopes=True,
887
+ )
888
+ ```
889
+
890
+ #### `unique_sdk.Folder.remove_access`
891
+
892
+ Allows you to delete access from a folder and apply to the subfolders or not: `
893
+
894
+ - `scopeAccesses`
895
+ - `applyToSubScopes`
896
+
897
+ 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.
898
+
899
+
900
+ Example of deleting the access from a folder and its subfolders using the id.
901
+
902
+ ```python
903
+ unique_sdk.Folder.remove_access(
904
+ user_id=user_id,
905
+ company_id=company_id,
906
+ scopeId="scope_dwekjnf3330woioppm,
907
+ scopeAccesses=[
908
+ {
909
+ "entityId": "group_id",
910
+ "type": "WRITE",
911
+ "entityType": "GROUP",
912
+ }
913
+ ],
914
+ applyToSubScopes=True,
915
+ )
916
+ ```
917
+
918
+
919
+ Example of deleting the access from a folder and its subfolders using the path.
920
+
921
+ ```python
922
+ unique_sdk.Folder.remove_access(
923
+ user_id=user_id,
924
+ company_id=company_id,
925
+ folderPath="/Company/folder1/folder2"
926
+ scopeAccesses=[
927
+ {
928
+ "entityId": "group_id",
929
+ "type": "WRITE",
930
+ "entityType": "GROUP",
931
+ }
932
+ ],
933
+ applyToSubScopes=True,
934
+ )
935
+ ```
936
+
933
937
  ## UniqueQL
934
938
 
935
939
  [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.
@@ -1035,7 +1039,7 @@ pdfFile = download_content(
1035
1039
  content_id="cont_12412",
1036
1040
  filename="hello.pdf",
1037
1041
  chat_id=None # If specified, it downloads it from the chat
1038
- }
1042
+ )
1039
1043
  ```
1040
1044
 
1041
1045
  #### `unique_sdk.utils.file_io.upload_file`
@@ -1198,6 +1202,47 @@ hello = "hello you!"
1198
1202
  searchContext = unique_sdk.utils.token.count_tokens(hello)
1199
1203
  ```
1200
1204
 
1205
+ ### Chat In Space
1206
+
1207
+ #### `unique_sdk.utils.chat_in_space.send_message_and_wait_for_completion`
1208
+
1209
+ 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.
1210
+
1211
+ The script sends a prompt asynchronously and continuously polls for completion, which is determined when the `stoppedStreamingAt` field of the message becomes non-null.
1212
+
1213
+ **Optional parameters:**
1214
+ - `tool_choices`: A list of tool names to be used for the message (e.g., `["WebSearch"]`). If not provided, no tools will be used.
1215
+ - `scope_rules`: A dictionary specifying scope rules for the message, allowing you to restrict the context or data sources available to the assistant.
1216
+ - `chat_id`: The ID of the chat where the message should be sent. If omitted, a new chat will be created.
1217
+ - `poll_interval`: The number of seconds to wait between polling attempts (default: `1` second).
1218
+ - `max_wait`: The maximum number of seconds to wait for the message to complete (default: `60` seconds).
1219
+
1220
+ The script ensures you can flexibly interact with spaces in new or ongoing chats, with fine-grained control over tools, context, and polling behavior.
1221
+
1222
+ ```python
1223
+ latest_message = unique_sdk.utils.send_message_and_wait_for_completion(
1224
+ user_id=user_id,
1225
+ company_id=company_id,
1226
+ assistant_id=assistant_id,
1227
+ text="Tell me a short story.",
1228
+ chat_id=chat_id, # Optional - if no chat id is specified, a new chat will be created
1229
+ tool_choices=["WebSearch"],
1230
+ scope_rules={
1231
+ "or": [
1232
+ {
1233
+ "operator": "in",
1234
+ "path": [
1235
+ "contentId"
1236
+ ],
1237
+ "value": [
1238
+ "cont_u888z7cazxxm4lugfdjq7pks"
1239
+ ]
1240
+ }
1241
+ ]
1242
+ },
1243
+ )
1244
+ ```
1245
+
1201
1246
  ## Error Handling
1202
1247
 
1203
1248
  ## Examples
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "unique_sdk"
3
- version = "0.9.38"
3
+ version = "0.9.39"
4
4
  description = ""
5
5
  authors = [
6
6
  "Martin Fadler <martin.fadler@unique.ch>",
@@ -86,6 +86,7 @@ from unique_sdk.api_resources._acronyms import Acronyms as Acronyms
86
86
  from unique_sdk.api_resources._message_assessment import (
87
87
  MessageAssessment as MessageAssessment,
88
88
  )
89
+ from unique_sdk.api_resources._space import Space as Space
89
90
 
90
91
  # Unique QL
91
92
  from unique_sdk._unique_ql import UQLOperator as UQLOperator
@@ -0,0 +1,144 @@
1
+ from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict, Unpack, cast
2
+
3
+ from unique_sdk._api_resource import APIResource
4
+ from unique_sdk._request_options import RequestOptions
5
+
6
+
7
+ class Space(APIResource["Space"]):
8
+ OBJECT_NAME: ClassVar[Literal["space"]] = "space"
9
+
10
+ class CreateMessageParams(RequestOptions):
11
+ """
12
+ Parameters for querying the assistant for a message.
13
+ """
14
+
15
+ chatId: str | None = None
16
+ assistantId: str
17
+ text: str | None = None
18
+ toolChoices: List[str] = None
19
+ scopeRules: dict | None = None
20
+
21
+ class Reference(TypedDict):
22
+ """
23
+ Reference information for a message.
24
+ """
25
+
26
+ name: str
27
+ url: str | None
28
+ sequenceNumber: int
29
+ originalIndex: List[int] | None
30
+ sourceId: str
31
+ source: str
32
+
33
+ class Assessment(TypedDict):
34
+ """
35
+ Assessment information for a message.
36
+ """
37
+
38
+ id: str
39
+ createdAt: str
40
+ updatedAt: str
41
+ messageId: str
42
+ status: str
43
+ explanation: str | None
44
+ label: str | None
45
+ type: str | None
46
+ title: str | None
47
+ companyId: str
48
+ userId: str
49
+ isVisible: bool
50
+ createdBy: str | None
51
+
52
+ class Message(TypedDict):
53
+ """
54
+ Represents a message in the space.
55
+ """
56
+
57
+ id: str
58
+ chatId: str
59
+ text: str | None = None
60
+ originalText: str | None = None
61
+ role: Literal["system", "user", "assistant"]
62
+ debugInfo: Optional[Dict[str, Any]] = None
63
+ completedAt: str | None
64
+ createdAt: str | None
65
+ updatedAt: str | None
66
+ stoppedStreamingAt: str | None
67
+ assessment: Optional[List["Space.Reference"]]
68
+ messageAssessment: Optional[List["Space.Assessment"]]
69
+
70
+ @classmethod
71
+ def create_message(
72
+ cls,
73
+ user_id: str,
74
+ company_id: str,
75
+ **params: Unpack["Space.CreateMessageParams"],
76
+ ) -> "Space.Message":
77
+ """
78
+ Send a message in a space.
79
+ """
80
+ return cast(
81
+ "Space.Message",
82
+ cls._static_request(
83
+ "post",
84
+ "/space/message",
85
+ user_id,
86
+ company_id,
87
+ params=params,
88
+ ),
89
+ )
90
+
91
+ @classmethod
92
+ async def create_message_async(
93
+ cls,
94
+ user_id: str,
95
+ company_id: str,
96
+ **params: Unpack["Space.CreateMessageParams"],
97
+ ) -> "Space.Message":
98
+ """
99
+ Async send a message in a space.
100
+ """
101
+ return cast(
102
+ "Space.Message",
103
+ await cls._static_request_async(
104
+ "post",
105
+ "/space/message",
106
+ user_id,
107
+ company_id,
108
+ params=params,
109
+ ),
110
+ )
111
+
112
+ @classmethod
113
+ def get_latest_message(
114
+ cls, user_id: str, company_id: str, chat_id: str
115
+ ) -> "Space.Message":
116
+ """
117
+ Get the latest message in a space.
118
+ """
119
+ return cast(
120
+ "Space.Message",
121
+ cls._static_request(
122
+ "get",
123
+ f"/space/{chat_id}/messages/latest",
124
+ user_id,
125
+ company_id,
126
+ ),
127
+ )
128
+
129
+ @classmethod
130
+ async def get_latest_message_async(
131
+ cls, user_id: str, company_id: str, chat_id: str
132
+ ) -> "Space.Message":
133
+ """
134
+ Async get the latest message in a space.
135
+ """
136
+ return cast(
137
+ "Space.Message",
138
+ await cls._static_request_async(
139
+ "get",
140
+ f"/space/{chat_id}/messages/latest",
141
+ user_id,
142
+ company_id,
143
+ ),
144
+ )
@@ -0,0 +1,53 @@
1
+ import asyncio
2
+ from typing import List
3
+
4
+ from unique_sdk.api_resources._space import Space
5
+
6
+
7
+ async def send_message_and_wait_for_completion(
8
+ user_id: str,
9
+ company_id: str,
10
+ assistant_id: str,
11
+ text: str,
12
+ tool_choices: List[str] = None,
13
+ scope_rules: dict | None = None,
14
+ chat_id: str = None,
15
+ poll_interval: float = 1.0,
16
+ max_wait: float = 60.0,
17
+ ) -> "Space.Message":
18
+ """
19
+ Sends a prompt asynchronously and polls for completion. (until stoppedStreamingAt is not None)
20
+
21
+ Args:
22
+ user_id: The user ID.
23
+ company_id: The company ID.
24
+ assistant_id: The assistant ID.
25
+ text: The prompt text.
26
+ poll_interval: Seconds between polls.
27
+ max_wait: Maximum seconds to wait for completion.
28
+ **kwargs: Additional parameters for the prompt.
29
+
30
+ Returns:
31
+ The completed Space.Message.
32
+ """
33
+ # Send the prompt asynchronously
34
+ response = await Space.create_message_async(
35
+ user_id=user_id,
36
+ company_id=company_id,
37
+ assistantId=assistant_id,
38
+ chatId=chat_id,
39
+ text=text,
40
+ toolChoices=tool_choices,
41
+ scopeRules=scope_rules,
42
+ )
43
+ chat_id = response.get("chatId")
44
+
45
+ max_attempts = int(max_wait // poll_interval)
46
+ for _ in range(max_attempts):
47
+ # Poll for the answer
48
+ answer = Space.get_latest_message(user_id, company_id, chat_id)
49
+ if answer.get("stoppedStreamingAt") is not None:
50
+ return answer
51
+ await asyncio.sleep(poll_interval)
52
+
53
+ raise TimeoutError("Timed out waiting for prompt completion.")
File without changes