unique-sdk 2026.20.0.dev4__tar.gz → 2026.20.0.dev6__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.
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/PKG-INFO +1 -1
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/pyproject.toml +1 -1
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/__init__.py +1 -0
- unique_sdk-2026.20.0.dev6/unique_sdk/api_resources/_module.py +235 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/file_io.py +66 -74
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/README.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_api_requestor.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_api_resource.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_api_version.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_error.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_http_client.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_list_object.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_object_classes.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_request_options.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_unique_object.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_unique_ql.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_unique_response.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_util.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_version.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/_webhook.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/__init__.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_acronyms.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_agentic_table.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_benchmarking.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_chat_completion.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_content.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_elicitation.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_embedding.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_event.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_folder.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_group.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_integrated.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_llm_models.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_mcp.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_assessment.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_execution.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_log.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_tool.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_scheduled_task.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_search.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_search_string.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_short_term_memory.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_space.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_user.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/__init__.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/__main__.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/cli.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/__init__.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/elicitation.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/files.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/folders.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/mcp.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/navigation.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/search.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/config.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/formatting.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/shell.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/state.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/benchmarking_run.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/chat_history.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/chat_in_space.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/sources.py +0 -0
- {unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/token.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-sdk
|
|
3
|
-
Version: 2026.20.0.
|
|
3
|
+
Version: 2026.20.0.dev6
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
|
|
6
6
|
Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
|
|
@@ -101,6 +101,7 @@ from unique_sdk.api_resources._benchmarking import (
|
|
|
101
101
|
Benchmarking as Benchmarking,
|
|
102
102
|
)
|
|
103
103
|
from unique_sdk.api_resources._scheduled_task import ScheduledTask as ScheduledTask
|
|
104
|
+
from unique_sdk.api_resources._module import Module as Module
|
|
104
105
|
|
|
105
106
|
# Unique QL
|
|
106
107
|
from unique_sdk._unique_ql import UQLOperator as UQLOperator
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
from typing import Any, Literal, NotRequired, TypedDict, Unpack, cast
|
|
5
|
+
from urllib.parse import quote_plus
|
|
6
|
+
|
|
7
|
+
from unique_sdk._api_resource import APIResource
|
|
8
|
+
from unique_sdk._request_options import RequestOptions
|
|
9
|
+
from unique_sdk._util import classproperty
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Module(APIResource["Module"]):
|
|
13
|
+
"""An assistant module that configures behaviour, tools, and instructions.
|
|
14
|
+
|
|
15
|
+
Modules are attached to assistants (spaces) and define their capabilities,
|
|
16
|
+
configuration, and tool definitions. They control weight/priority, whether
|
|
17
|
+
the module is external, and whether custom instructions are enabled.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@classproperty
|
|
21
|
+
def OBJECT_NAME(cls) -> Literal["module"]:
|
|
22
|
+
return "module"
|
|
23
|
+
|
|
24
|
+
RESOURCE_URL = "/modules"
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def class_url(cls) -> str:
|
|
28
|
+
return cls.RESOURCE_URL
|
|
29
|
+
|
|
30
|
+
class ListParams(RequestOptions):
|
|
31
|
+
assistantId: NotRequired[str | None]
|
|
32
|
+
|
|
33
|
+
class CreateParams(RequestOptions):
|
|
34
|
+
assistantId: str
|
|
35
|
+
name: str
|
|
36
|
+
description: NotRequired[str | None]
|
|
37
|
+
weight: NotRequired[int | None]
|
|
38
|
+
isExternal: NotRequired[bool | None]
|
|
39
|
+
isCustomInstructionEnabled: NotRequired[bool | None]
|
|
40
|
+
configuration: NotRequired[dict[str, Any] | None]
|
|
41
|
+
toolDefinition: NotRequired[dict[str, Any] | None]
|
|
42
|
+
moduleTemplateId: NotRequired[str | None]
|
|
43
|
+
|
|
44
|
+
class ModifyParams(RequestOptions):
|
|
45
|
+
name: NotRequired[str | None]
|
|
46
|
+
description: NotRequired[str | None]
|
|
47
|
+
weight: NotRequired[int | None]
|
|
48
|
+
isExternal: NotRequired[bool | None]
|
|
49
|
+
isCustomInstructionEnabled: NotRequired[bool | None]
|
|
50
|
+
configuration: NotRequired[dict[str, Any] | None]
|
|
51
|
+
toolDefinition: NotRequired[dict[str, Any] | None]
|
|
52
|
+
|
|
53
|
+
class DeletedObject(TypedDict):
|
|
54
|
+
id: str
|
|
55
|
+
object: str
|
|
56
|
+
deleted: bool
|
|
57
|
+
|
|
58
|
+
id: str
|
|
59
|
+
name: str
|
|
60
|
+
description: str | None
|
|
61
|
+
toolDefinition: dict[str, Any] | None
|
|
62
|
+
configuration: dict[str, Any] | None
|
|
63
|
+
assistantId: str
|
|
64
|
+
weight: int | None
|
|
65
|
+
isExternal: bool
|
|
66
|
+
isCustomInstructionEnabled: bool
|
|
67
|
+
moduleTemplateId: str | None
|
|
68
|
+
createdAt: str
|
|
69
|
+
updatedAt: str
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def list(
|
|
73
|
+
cls,
|
|
74
|
+
*,
|
|
75
|
+
user_id: str,
|
|
76
|
+
company_id: str,
|
|
77
|
+
**params: Unpack["Module.ListParams"],
|
|
78
|
+
) -> builtins.list["Module"]:
|
|
79
|
+
result = cls._static_request(
|
|
80
|
+
"get",
|
|
81
|
+
cls.RESOURCE_URL,
|
|
82
|
+
user_id,
|
|
83
|
+
company_id,
|
|
84
|
+
params or None,
|
|
85
|
+
)
|
|
86
|
+
if isinstance(result, builtins.list):
|
|
87
|
+
data = result
|
|
88
|
+
else:
|
|
89
|
+
data = result.get("data", []) if hasattr(result, "get") else []
|
|
90
|
+
return cast(builtins.list["Module"], data)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
async def list_async(
|
|
94
|
+
cls,
|
|
95
|
+
*,
|
|
96
|
+
user_id: str,
|
|
97
|
+
company_id: str,
|
|
98
|
+
**params: Unpack["Module.ListParams"],
|
|
99
|
+
) -> builtins.list["Module"]:
|
|
100
|
+
result = await cls._static_request_async(
|
|
101
|
+
"get",
|
|
102
|
+
cls.RESOURCE_URL,
|
|
103
|
+
user_id,
|
|
104
|
+
company_id,
|
|
105
|
+
params or None,
|
|
106
|
+
)
|
|
107
|
+
if isinstance(result, builtins.list):
|
|
108
|
+
data = result
|
|
109
|
+
else:
|
|
110
|
+
data = result.get("data", []) if hasattr(result, "get") else []
|
|
111
|
+
return cast(builtins.list["Module"], data)
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def retrieve(
|
|
115
|
+
cls,
|
|
116
|
+
*,
|
|
117
|
+
user_id: str,
|
|
118
|
+
company_id: str,
|
|
119
|
+
id: str,
|
|
120
|
+
) -> "Module":
|
|
121
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
122
|
+
return cast(
|
|
123
|
+
"Module",
|
|
124
|
+
cls._static_request("get", url, user_id, company_id),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
async def retrieve_async(
|
|
129
|
+
cls,
|
|
130
|
+
*,
|
|
131
|
+
user_id: str,
|
|
132
|
+
company_id: str,
|
|
133
|
+
id: str,
|
|
134
|
+
) -> "Module":
|
|
135
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
136
|
+
return cast(
|
|
137
|
+
"Module",
|
|
138
|
+
await cls._static_request_async("get", url, user_id, company_id),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def create(
|
|
143
|
+
cls,
|
|
144
|
+
*,
|
|
145
|
+
user_id: str,
|
|
146
|
+
company_id: str,
|
|
147
|
+
**params: Unpack["Module.CreateParams"],
|
|
148
|
+
) -> "Module":
|
|
149
|
+
return cast(
|
|
150
|
+
"Module",
|
|
151
|
+
cls._static_request(
|
|
152
|
+
"post",
|
|
153
|
+
cls.RESOURCE_URL,
|
|
154
|
+
user_id,
|
|
155
|
+
company_id,
|
|
156
|
+
params,
|
|
157
|
+
),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
@classmethod
|
|
161
|
+
async def create_async(
|
|
162
|
+
cls,
|
|
163
|
+
*,
|
|
164
|
+
user_id: str,
|
|
165
|
+
company_id: str,
|
|
166
|
+
**params: Unpack["Module.CreateParams"],
|
|
167
|
+
) -> "Module":
|
|
168
|
+
return cast(
|
|
169
|
+
"Module",
|
|
170
|
+
await cls._static_request_async(
|
|
171
|
+
"post",
|
|
172
|
+
cls.RESOURCE_URL,
|
|
173
|
+
user_id,
|
|
174
|
+
company_id,
|
|
175
|
+
params,
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def modify(
|
|
181
|
+
cls,
|
|
182
|
+
*,
|
|
183
|
+
user_id: str,
|
|
184
|
+
company_id: str,
|
|
185
|
+
id: str,
|
|
186
|
+
**params: Unpack["Module.ModifyParams"],
|
|
187
|
+
) -> "Module":
|
|
188
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
189
|
+
return cast(
|
|
190
|
+
"Module",
|
|
191
|
+
cls._static_request("patch", url, user_id, company_id, params),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
async def modify_async(
|
|
196
|
+
cls,
|
|
197
|
+
*,
|
|
198
|
+
user_id: str,
|
|
199
|
+
company_id: str,
|
|
200
|
+
id: str,
|
|
201
|
+
**params: Unpack["Module.ModifyParams"],
|
|
202
|
+
) -> "Module":
|
|
203
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
204
|
+
return cast(
|
|
205
|
+
"Module",
|
|
206
|
+
await cls._static_request_async("patch", url, user_id, company_id, params),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def delete(
|
|
211
|
+
cls,
|
|
212
|
+
*,
|
|
213
|
+
user_id: str,
|
|
214
|
+
company_id: str,
|
|
215
|
+
id: str,
|
|
216
|
+
) -> "Module.DeletedObject":
|
|
217
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
218
|
+
return cast(
|
|
219
|
+
"Module.DeletedObject",
|
|
220
|
+
cls._static_request("delete", url, user_id, company_id),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
async def delete_async(
|
|
225
|
+
cls,
|
|
226
|
+
*,
|
|
227
|
+
user_id: str,
|
|
228
|
+
company_id: str,
|
|
229
|
+
id: str,
|
|
230
|
+
) -> "Module.DeletedObject":
|
|
231
|
+
url = "%s/%s" % (cls.RESOURCE_URL, quote_plus(id))
|
|
232
|
+
return cast(
|
|
233
|
+
"Module.DeletedObject",
|
|
234
|
+
await cls._static_request_async("delete", url, user_id, company_id),
|
|
235
|
+
)
|
|
@@ -44,17 +44,6 @@ def download_file(url: str, filename: str):
|
|
|
44
44
|
_PREVIEW_PDF_MIME_TYPE = "application/pdf"
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def _derive_preview_pdf_filename(displayed_filename: str) -> str:
|
|
48
|
-
"""Return ``<stem>_preview.pdf`` for *displayed_filename*.
|
|
49
|
-
|
|
50
|
-
Keeps the preview blob filename predictable and collision-free with
|
|
51
|
-
the original (which has the user-visible extension), while still
|
|
52
|
-
making it obvious in storage which content the preview belongs to.
|
|
53
|
-
"""
|
|
54
|
-
stem = displayed_filename.rsplit(".", 1)[0] or displayed_filename
|
|
55
|
-
return f"{stem}_preview.pdf"
|
|
56
|
-
|
|
57
|
-
|
|
58
47
|
def _put_preview_pdf(write_url: str, preview_pdf_path: str) -> None:
|
|
59
48
|
"""PUT *preview_pdf_path* bytes to the SAS URL returned by upsert."""
|
|
60
49
|
with open(preview_pdf_path, "rb") as preview_file:
|
|
@@ -85,17 +74,25 @@ def upload_file(
|
|
|
85
74
|
ingestion_config: Content.IngestionConfig | None = None,
|
|
86
75
|
metadata: dict[str, Any] | None = None,
|
|
87
76
|
preview_pdf_path: str | None = None,
|
|
88
|
-
preview_pdf_filename: str | None = None,
|
|
89
77
|
):
|
|
90
78
|
"""Upload *path_to_file* as a Unique :class:`Content`.
|
|
91
79
|
|
|
92
80
|
When ``preview_pdf_path`` is provided, the caller gets a single-call
|
|
93
|
-
Office → PDF preview flow:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
Office → PDF preview flow: the SDK does a two-upsert handshake — a
|
|
82
|
+
first upsert to obtain the content id, then a finalize upsert that
|
|
83
|
+
registers ``previewPdfFileName = f"{content.id}_pdfPreview"`` on
|
|
84
|
+
the row and mints a ``pdfPreviewWriteUrl`` SAS URL the SDK PUTs the
|
|
85
|
+
PDF bytes to.
|
|
86
|
+
|
|
87
|
+
Naming the preview blob ``${content.id}_pdfPreview`` matches the
|
|
88
|
+
convention used by the platform's ingestion worker
|
|
89
|
+
(``next/services/node-ingestion-worker/src/pdf-preview-converter/pdf-preview-converter.service.ts``)
|
|
90
|
+
and is collision-free across uploads sharing a ``displayed_filename``
|
|
91
|
+
because content ids are globally unique. The platform stores the
|
|
92
|
+
``previewPdfFileName`` value verbatim — collision-free naming is a
|
|
93
|
+
client contract, not a server invariant — so every client of
|
|
94
|
+
node-ingestion (workers, SDKs, integrations) MUST follow this
|
|
95
|
+
convention.
|
|
99
96
|
|
|
100
97
|
Args:
|
|
101
98
|
userId: Acting user id.
|
|
@@ -112,15 +109,14 @@ def upload_file(
|
|
|
112
109
|
metadata: Free-form metadata.
|
|
113
110
|
preview_pdf_path: Optional path to a sibling PDF that should be
|
|
114
111
|
rendered as the side-panel preview for this content. When
|
|
115
|
-
set, the
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
set, the SDK derives the blob name from the upserted
|
|
113
|
+
content id, registers it on the row via the finalize
|
|
114
|
+
upsert, and PUTs the PDF bytes to the SAS URL the server
|
|
115
|
+
returns in ``pdfPreviewWriteUrl``. Naming is the SDK's
|
|
116
|
+
responsibility — there is no override kwarg, by design,
|
|
117
|
+
so all callers land on the same ``${content.id}_pdfPreview``
|
|
118
|
+
convention as the ingestion worker.
|
|
122
119
|
"""
|
|
123
|
-
# check that chatid or scope_or_unique_path is provided
|
|
124
120
|
if not chat_id and not scope_or_unique_path:
|
|
125
121
|
raise ValueError("chat_id or scope_or_unique_path must be provided")
|
|
126
122
|
|
|
@@ -129,32 +125,13 @@ def upload_file(
|
|
|
129
125
|
f"preview_pdf_path does not point to a readable file: {preview_pdf_path}"
|
|
130
126
|
)
|
|
131
127
|
|
|
132
|
-
# A filename without a path would register a phantom preview on the
|
|
133
|
-
# Content row (the upsert mints a pdfPreviewWriteUrl, but we never
|
|
134
|
-
# PUT any bytes), leaving the side panel pointing at a nonexistent
|
|
135
|
-
# blob. Treat it as a programmer mistake and fail fast.
|
|
136
|
-
if preview_pdf_filename is not None and preview_pdf_path is None:
|
|
137
|
-
raise ValueError(
|
|
138
|
-
"preview_pdf_filename was provided without preview_pdf_path; "
|
|
139
|
-
"pass the PDF path so the preview blob actually gets uploaded."
|
|
140
|
-
)
|
|
141
|
-
|
|
142
128
|
size = os.path.getsize(path_to_file)
|
|
143
|
-
resolved_preview_filename = (
|
|
144
|
-
(preview_pdf_filename or _derive_preview_pdf_filename(displayed_filename))
|
|
145
|
-
if preview_pdf_path is not None
|
|
146
|
-
else None
|
|
147
|
-
)
|
|
148
|
-
# Only forward ``previewPdfFileName`` when we actually have one.
|
|
149
|
-
# Sending ``None`` would serialize as ``"previewPdfFileName": null``,
|
|
150
|
-
# which the server treats as a clearing operation and would erase
|
|
151
|
-
# an existing preview on a re-upload that doesn't ship one.
|
|
152
|
-
preview_kwargs: _PreviewKwargs = (
|
|
153
|
-
{"previewPdfFileName": resolved_preview_filename}
|
|
154
|
-
if resolved_preview_filename is not None
|
|
155
|
-
else {}
|
|
156
|
-
)
|
|
157
129
|
|
|
130
|
+
# Step 1 — first upsert WITHOUT ``previewPdfFileName``. The id we
|
|
131
|
+
# need to derive a collision-free preview blob name does not exist
|
|
132
|
+
# yet; deriving from ``displayed_filename`` instead would race
|
|
133
|
+
# across uploads sharing the same key (two ``report.pptx`` uploads
|
|
134
|
+
# in the same company would overwrite each other's preview blob).
|
|
158
135
|
createdContent = Content.upsert(
|
|
159
136
|
user_id=userId,
|
|
160
137
|
company_id=companyId,
|
|
@@ -168,12 +145,10 @@ def upload_file(
|
|
|
168
145
|
},
|
|
169
146
|
scopeId=scope_or_unique_path,
|
|
170
147
|
chatId=chat_id,
|
|
171
|
-
**preview_kwargs,
|
|
172
148
|
)
|
|
173
149
|
|
|
150
|
+
# Step 2 — PUT the original bytes to the SAS URL minted by Step 1.
|
|
174
151
|
uploadUrl = createdContent.writeUrl
|
|
175
|
-
|
|
176
|
-
# upload to azure blob storage SAS url uploadUrl the pdf file translatedFile make sure it is treated as a application/pdf
|
|
177
152
|
if uploadUrl is None: # guard: writeUrl is Optional, basedpyright needs narrowing
|
|
178
153
|
raise ValueError("createdContent.writeUrl is None")
|
|
179
154
|
with open(path_to_file, "rb") as file:
|
|
@@ -186,28 +161,24 @@ def upload_file(
|
|
|
186
161
|
},
|
|
187
162
|
)
|
|
188
163
|
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"Verify the API gateway and node-ingestion expose "
|
|
205
|
-
"previewPdfFileName on the upsert mutation."
|
|
206
|
-
)
|
|
207
|
-
_put_preview_pdf(preview_write_url, preview_pdf_path)
|
|
164
|
+
# Step 3 — finalize upsert: ``byteSize`` + ``fileUrl`` flip the row
|
|
165
|
+
# to bytes-on-blob, and (only when we have a preview to attach)
|
|
166
|
+
# ``previewPdfFileName = f"{content.id}_pdfPreview"`` registers the
|
|
167
|
+
# preview blob name on the row so the ``pdfPreviewWriteUrl`` field
|
|
168
|
+
# resolver mints the SAS URL on this response.
|
|
169
|
+
#
|
|
170
|
+
# ``previewPdfFileName`` is forwarded as a conditional kwarg rather
|
|
171
|
+
# than always-set-to-None; serializing ``"previewPdfFileName": null``
|
|
172
|
+
# would tell the server to clear the field, which would silently
|
|
173
|
+
# regress an existing-row re-upload that does not attach a preview.
|
|
174
|
+
preview_kwargs: _PreviewKwargs = (
|
|
175
|
+
{"previewPdfFileName": f"{createdContent.id}_pdfPreview"}
|
|
176
|
+
if preview_pdf_path is not None
|
|
177
|
+
else {}
|
|
178
|
+
)
|
|
208
179
|
|
|
209
180
|
if chat_id:
|
|
210
|
-
unique_sdk.Content.upsert(
|
|
181
|
+
finalizedContent = unique_sdk.Content.upsert(
|
|
211
182
|
user_id=userId,
|
|
212
183
|
company_id=companyId,
|
|
213
184
|
input={
|
|
@@ -224,7 +195,7 @@ def upload_file(
|
|
|
224
195
|
**preview_kwargs,
|
|
225
196
|
)
|
|
226
197
|
else:
|
|
227
|
-
unique_sdk.Content.upsert(
|
|
198
|
+
finalizedContent = unique_sdk.Content.upsert(
|
|
228
199
|
user_id=userId,
|
|
229
200
|
company_id=companyId,
|
|
230
201
|
input={
|
|
@@ -241,6 +212,27 @@ def upload_file(
|
|
|
241
212
|
**preview_kwargs,
|
|
242
213
|
)
|
|
243
214
|
|
|
215
|
+
# Step 4 — PUT the preview bytes (if requested). The SAS URL comes
|
|
216
|
+
# from the FINALIZE response, not the first one, because the first
|
|
217
|
+
# upsert did not carry ``previewPdfFileName`` and so could not have
|
|
218
|
+
# minted ``pdfPreviewWriteUrl``.
|
|
219
|
+
#
|
|
220
|
+
# ``getattr`` so an older gateway that omits the field falls
|
|
221
|
+
# through to the descriptive ``RuntimeError`` below instead of the
|
|
222
|
+
# opaque ``AttributeError`` that ``UniqueObject.__getattr__`` would
|
|
223
|
+
# otherwise raise on a missing key.
|
|
224
|
+
if preview_pdf_path is not None:
|
|
225
|
+
preview_write_url = getattr(finalizedContent, "pdfPreviewWriteUrl", None)
|
|
226
|
+
if not preview_write_url:
|
|
227
|
+
raise RuntimeError(
|
|
228
|
+
"preview_pdf_path was provided but the finalize upsert "
|
|
229
|
+
"response carries no pdfPreviewWriteUrl — refusing to "
|
|
230
|
+
"silently drop the preview. Verify the API gateway and "
|
|
231
|
+
"node-ingestion expose previewPdfFileName on the upsert "
|
|
232
|
+
"mutation."
|
|
233
|
+
)
|
|
234
|
+
_put_preview_pdf(preview_write_url, preview_pdf_path)
|
|
235
|
+
|
|
244
236
|
return createdContent
|
|
245
237
|
|
|
246
238
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/__init__.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_acronyms.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_agentic_table.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_benchmarking.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_chat_completion.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_content.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_elicitation.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_embedding.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_integrated.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_llm_models.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_log.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_message_tool.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_scheduled_task.py
RENAMED
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/api_resources/_search_string.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/elicitation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/navigation.py
RENAMED
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/cli/commands/scheduled_tasks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unique_sdk-2026.20.0.dev4 → unique_sdk-2026.20.0.dev6}/unique_sdk/utils/benchmarking_run.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|