unique_toolkit 2026.30.0.dev20__py3-none-any.whl → 2026.30.0.dev22__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.
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +5 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/_files.py +57 -7
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/builder.py +17 -1
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/config.py +13 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/service.py +3 -0
- {unique_toolkit-2026.30.0.dev20.dist-info → unique_toolkit-2026.30.0.dev22.dist-info}/METADATA +1 -1
- {unique_toolkit-2026.30.0.dev20.dist-info → unique_toolkit-2026.30.0.dev22.dist-info}/RECORD +8 -8
- {unique_toolkit-2026.30.0.dev20.dist-info → unique_toolkit-2026.30.0.dev22.dist-info}/WHEEL +0 -0
|
@@ -44,6 +44,7 @@ class Analytics(TypedDict):
|
|
|
44
44
|
answer_length: int
|
|
45
45
|
artifacts_created_count: int | None
|
|
46
46
|
artifacts_created_filetype: list[str] | None
|
|
47
|
+
context_memory_updated: bool | None
|
|
47
48
|
language_model: AnalyticsLanguageModel
|
|
48
49
|
loop_iteration_count: int
|
|
49
50
|
mcp_tool_names_used: list[AnalyticsToolName]
|
|
@@ -110,6 +111,7 @@ class DebugInfoManager:
|
|
|
110
111
|
loop_iteration_count: int = 0,
|
|
111
112
|
total_time_to_answer_ms: int | None = None,
|
|
112
113
|
artifacts: ArtifactsDebugInfo | None = None,
|
|
114
|
+
context_memory_updated: bool | None = None,
|
|
113
115
|
) -> None:
|
|
114
116
|
"""Add a stable 'analytics' snapshot for downstream ROI/usage reporting.
|
|
115
117
|
|
|
@@ -150,6 +152,9 @@ class DebugInfoManager:
|
|
|
150
152
|
total_time_to_answer_ms=total_time_to_answer_ms,
|
|
151
153
|
artifacts_created_count=artifacts["count"] if artifacts else None,
|
|
152
154
|
artifacts_created_filetype=artifacts["filetypes"] if artifacts else None,
|
|
155
|
+
# None when the user-memory postprocessor is not activated for this
|
|
156
|
+
# turn; True/False when it ran and did/didn't update the profile.
|
|
157
|
+
context_memory_updated=context_memory_updated,
|
|
153
158
|
)
|
|
154
159
|
self.add("analytics", analytics)
|
|
155
160
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
from dataclasses import dataclass
|
|
3
4
|
|
|
4
|
-
from openai import AsyncOpenAI
|
|
5
|
+
from openai import APIStatusError, AsyncOpenAI
|
|
5
6
|
from tenacity import (
|
|
6
7
|
AsyncRetrying,
|
|
7
8
|
before_sleep_log,
|
|
@@ -25,6 +26,44 @@ logger = logging.getLogger(__name__)
|
|
|
25
26
|
UPLOAD_MAX_RETRIES = 2
|
|
26
27
|
UPLOAD_RETRY_BASE_DELAY = 0.5
|
|
27
28
|
|
|
29
|
+
# The `/public/openai-proxy/containers/*/files` route enforces a request-body
|
|
30
|
+
# size cap (see UN-23109); Azure OpenAI/node-chat surfaces this as an
|
|
31
|
+
# `APIStatusError` with this status code. Anything else is treated as a
|
|
32
|
+
# generic, non-actionable upload failure.
|
|
33
|
+
REQUEST_ENTITY_TOO_LARGE_STATUS_CODE = 413
|
|
34
|
+
|
|
35
|
+
# Default for node-chat's `MAX_OPENAI_PROXY_FILE_UPLOAD_MB` (UN-23109 /
|
|
36
|
+
# monorepo#26846). Kept in sync so the model can tell the user the concrete
|
|
37
|
+
# limit; operators may raise the env var, but 50 MB is the platform default.
|
|
38
|
+
DEFAULT_CODE_EXECUTION_UPLOAD_LIMIT_MB = 50
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class FailedFileUpload:
|
|
43
|
+
"""A file that could not be uploaded to the code execution container."""
|
|
44
|
+
|
|
45
|
+
content: Content
|
|
46
|
+
reason: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def describe_upload_error(exc: Exception) -> str:
|
|
50
|
+
"""Turn an upload exception into a short, user-actionable reason.
|
|
51
|
+
|
|
52
|
+
Special-cases the HTTP 413 "request body too large" response (see
|
|
53
|
+
UN-23109) so the model can tell the user the file was too large instead
|
|
54
|
+
of claiming it is simply missing. Falls back to a generic message for
|
|
55
|
+
every other failure so we never swallow the error silently.
|
|
56
|
+
"""
|
|
57
|
+
if isinstance(exc, APIStatusError) and exc.status_code == (
|
|
58
|
+
REQUEST_ENTITY_TOO_LARGE_STATUS_CODE
|
|
59
|
+
):
|
|
60
|
+
return (
|
|
61
|
+
"the file is too large to upload for code execution "
|
|
62
|
+
f"(maximum allowed size is {DEFAULT_CODE_EXECUTION_UPLOAD_LIMIT_MB} MB)"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return f"upload failed ({exc})"
|
|
66
|
+
|
|
28
67
|
|
|
29
68
|
def build_upload_retry() -> AsyncRetrying:
|
|
30
69
|
"""Exponential-backoff retry policy for transient upload/download failures.
|
|
@@ -98,7 +137,7 @@ async def upload_files_to_container(
|
|
|
98
137
|
uploaded_files: list[Content],
|
|
99
138
|
memory: CodeExecutionShortTermMemorySchema,
|
|
100
139
|
content_service: ContentService,
|
|
101
|
-
) -> tuple[CodeExecutionShortTermMemorySchema, bool]:
|
|
140
|
+
) -> tuple[CodeExecutionShortTermMemorySchema, bool, list[FailedFileUpload]]:
|
|
102
141
|
async def _check_and_upload(content: Content) -> str | None:
|
|
103
142
|
if check_file_already_uploaded(content_id=content.id, memory=memory):
|
|
104
143
|
return None
|
|
@@ -124,12 +163,23 @@ async def upload_files_to_container(
|
|
|
124
163
|
)
|
|
125
164
|
|
|
126
165
|
updated = False
|
|
166
|
+
failed_uploads: list[FailedFileUpload] = []
|
|
127
167
|
for content, result in zip(unique_contents, results):
|
|
128
|
-
if result.success
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
if result.success:
|
|
169
|
+
if (filepath := result.unpack()) is not None:
|
|
170
|
+
memory.file_paths[content.id] = filepath
|
|
171
|
+
updated = True
|
|
172
|
+
elif result.exception is not None:
|
|
173
|
+
# Do not silently drop the file (UN-23109): the model must be
|
|
174
|
+
# told the upload failed instead of just seeing the file vanish.
|
|
175
|
+
failed_uploads.append(
|
|
176
|
+
FailedFileUpload(
|
|
177
|
+
content=content,
|
|
178
|
+
reason=describe_upload_error(result.exception),
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return memory, updated, failed_uploads
|
|
133
183
|
|
|
134
184
|
|
|
135
185
|
async def resolve_kb_contents(
|
|
@@ -12,6 +12,7 @@ from unique_toolkit.agentic.tools.openai_builtin.code_interpreter.builder._conta
|
|
|
12
12
|
create_container,
|
|
13
13
|
)
|
|
14
14
|
from unique_toolkit.agentic.tools.openai_builtin.code_interpreter.builder._files import (
|
|
15
|
+
FailedFileUpload,
|
|
15
16
|
resolve_kb_contents,
|
|
16
17
|
upload_files_to_container,
|
|
17
18
|
)
|
|
@@ -137,13 +138,22 @@ class CodeInterpreterBuilder:
|
|
|
137
138
|
files_to_upload.extend(kb_files)
|
|
138
139
|
|
|
139
140
|
files_updated = False
|
|
141
|
+
failed_uploads: list[FailedFileUpload] = []
|
|
140
142
|
if files_to_upload:
|
|
141
|
-
memory, files_updated = await upload_files_to_container(
|
|
143
|
+
memory, files_updated, failed_uploads = await upload_files_to_container(
|
|
142
144
|
client=client,
|
|
143
145
|
uploaded_files=files_to_upload,
|
|
144
146
|
content_service=content_service,
|
|
145
147
|
memory=memory,
|
|
146
148
|
)
|
|
149
|
+
for failed_upload in failed_uploads:
|
|
150
|
+
# Log content id only — filenames can be sensitive (PII / client data).
|
|
151
|
+
logger.warning(
|
|
152
|
+
"File %s could not be uploaded to container %s: %s",
|
|
153
|
+
failed_upload.content.id,
|
|
154
|
+
memory.container_id,
|
|
155
|
+
failed_upload.reason,
|
|
156
|
+
)
|
|
147
157
|
|
|
148
158
|
if container_updated or files_updated:
|
|
149
159
|
await memory_manager.save_async(memory)
|
|
@@ -155,6 +165,11 @@ class CodeInterpreterBuilder:
|
|
|
155
165
|
if content.id in memory.file_paths
|
|
156
166
|
]
|
|
157
167
|
|
|
168
|
+
failed_upload_descriptions = [
|
|
169
|
+
f"{failed_upload.content.key}: {failed_upload.reason}"
|
|
170
|
+
for failed_upload in failed_uploads
|
|
171
|
+
]
|
|
172
|
+
|
|
158
173
|
return OpenAICodeInterpreterTool(
|
|
159
174
|
config=config,
|
|
160
175
|
container_id=memory.container_id,
|
|
@@ -163,4 +178,5 @@ class CodeInterpreterBuilder:
|
|
|
163
178
|
user_uploaded_files=_extract_paths(user_uploaded_files),
|
|
164
179
|
kb_uploaded_files=_extract_paths(kb_files),
|
|
165
180
|
code_execution_files=_extract_paths(code_execution_files),
|
|
181
|
+
failed_uploads=failed_upload_descriptions,
|
|
166
182
|
)
|
|
@@ -34,6 +34,19 @@ The following files have been generated by you using the python tool in previous
|
|
|
34
34
|
- {{ file }}
|
|
35
35
|
{% endfor %}
|
|
36
36
|
{% endif %}
|
|
37
|
+
|
|
38
|
+
{%- if failed_uploads and failed_uploads | length > 0 %}
|
|
39
|
+
|
|
40
|
+
## Files That Could NOT Be Uploaded
|
|
41
|
+
The following files were referenced by the user or the system but could NOT be
|
|
42
|
+
made available in this environment. They do NOT exist at any path — do not
|
|
43
|
+
attempt to open, read, or reference them, and do NOT say the file is
|
|
44
|
+
"missing" or "not found" without explanation. Instead, clearly tell the user
|
|
45
|
+
which file failed and why, using the reason given below:
|
|
46
|
+
{% for file in failed_uploads %}
|
|
47
|
+
- {{ file }}
|
|
48
|
+
{% endfor %}
|
|
49
|
+
{% endif %}
|
|
37
50
|
""".strip()
|
|
38
51
|
|
|
39
52
|
_TOOL_DESCRIPTION_FOR_SYSTEM_PROMPT_BASE = """
|
|
@@ -28,6 +28,7 @@ class OpenAICodeInterpreterTool(OpenAIBuiltInTool[CodeInterpreter]):
|
|
|
28
28
|
user_uploaded_files: list[str] | None = None,
|
|
29
29
|
kb_uploaded_files: list[str] | None = None,
|
|
30
30
|
code_execution_files: list[str] | None = None,
|
|
31
|
+
failed_uploads: list[str] | None = None,
|
|
31
32
|
) -> None:
|
|
32
33
|
self._config = config
|
|
33
34
|
|
|
@@ -41,6 +42,7 @@ class OpenAICodeInterpreterTool(OpenAIBuiltInTool[CodeInterpreter]):
|
|
|
41
42
|
self._user_uploaded_files = user_uploaded_files
|
|
42
43
|
self._kb_uploaded_files = kb_uploaded_files
|
|
43
44
|
self._code_interpreter_artifacts = code_execution_files
|
|
45
|
+
self._failed_uploads = failed_uploads
|
|
44
46
|
|
|
45
47
|
@property
|
|
46
48
|
@override
|
|
@@ -83,6 +85,7 @@ class OpenAICodeInterpreterTool(OpenAIBuiltInTool[CodeInterpreter]):
|
|
|
83
85
|
user_uploaded_files=self._user_uploaded_files,
|
|
84
86
|
kb_uploaded_files=self._kb_uploaded_files,
|
|
85
87
|
code_interpreter_artifacts=self._code_interpreter_artifacts,
|
|
88
|
+
failed_uploads=self._failed_uploads,
|
|
86
89
|
)
|
|
87
90
|
return ToolPrompts(
|
|
88
91
|
name="python", # https://platform.openai.com/docs/guides/tools-code-interpreter
|
{unique_toolkit-2026.30.0.dev20.dist-info → unique_toolkit-2026.30.0.dev22.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.30.0.
|
|
3
|
+
Version: 2026.30.0.dev22
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
|
|
6
6
|
Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
|
{unique_toolkit-2026.30.0.dev20.dist-info → unique_toolkit-2026.30.0.dev22.dist-info}/RECORD
RENAMED
|
@@ -83,7 +83,7 @@ unique_toolkit/_common/utils/write_configuration.py,sha256=fzvr4C-XBL3OSM3Od9Tbq
|
|
|
83
83
|
unique_toolkit/_common/validate_required_values.py,sha256=cA8GPwkV1EPsG6ffIkmAn3Dx4LRRMC1EAURmT7PyuCg,551
|
|
84
84
|
unique_toolkit/_common/validators.py,sha256=ElnkMsyEY24TfzfTVHvireyT39EnZgW5N40T0P4b6gE,2638
|
|
85
85
|
unique_toolkit/agentic/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
86
|
-
unique_toolkit/agentic/debug_info_manager/debug_info_manager.py,sha256=
|
|
86
|
+
unique_toolkit/agentic/debug_info_manager/debug_info_manager.py,sha256=MoLHxJSK5yYb_MIzQEx0DICPfzHYZi04ofLbVEvcNso,8666
|
|
87
87
|
unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py,sha256=bDjrHLqfwtd7ewdIdyTFHabG-15t3iXvs34ywoYXcik,11888
|
|
88
88
|
unique_toolkit/agentic/evaluation/config.py,sha256=u5-iuT-4mfA2_9UZjZ-TN3YutVf35aR4EsF9l61Odnk,1696
|
|
89
89
|
unique_toolkit/agentic/evaluation/context_relevancy/prompts/__init__.py,sha256=IKGvHW2viBylTUjEJNKvIXO4YrYwdJXBHSxA9rKtiRI,355
|
|
@@ -211,9 +211,9 @@ unique_toolkit/agentic/tools/openai_builtin/code_interpreter/activator/config.py
|
|
|
211
211
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/activator/service.py,sha256=4BS5PKlSxFf5EQQZMA5xxNTMG457B_t5azlcP8N_tqM,3182
|
|
212
212
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/__init__.py,sha256=CuVsQHjkJh0uUekW20q_-kmf2Zb2OPwjCxDCGs-97cQ,159
|
|
213
213
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/_container.py,sha256=iliRDRwLAiHBamsu6HpPTSylw-osKe8sw9EqrMamrd8,1479
|
|
214
|
-
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/_files.py,sha256=
|
|
214
|
+
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/_files.py,sha256=p3JD8O3jaZJ3M-GXWLcvdCkWEAU0gyjbSaugJcgLqOE,6304
|
|
215
215
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/_memory.py,sha256=4i-Sfhk22BmUEfpkEEKs98b8VdS8ULH-67oaj1c-hR0,1085
|
|
216
|
-
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/builder.py,sha256=
|
|
216
|
+
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/builder/builder.py,sha256=QFvfJT7PE6XIf1mftURptTevOmYBp5Ln_eIsro5O7s8,6546
|
|
217
217
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py,sha256=C4Vrj_xLgLB8AEUz-cp0JpXAIwYwz-VaSRh-xAG7PW8,2381
|
|
218
218
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/__init__.py,sha256=SzEiUqxIw8D4_51SvTt-8oTOYTGirYkmH4J6SgojzME,634
|
|
219
219
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/artifacts.py,sha256=wAgDrcXZbCdDbNWNpbMigBUXySzbKQXSfg3KsZfAWfs,3115
|
|
@@ -221,8 +221,8 @@ unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/code
|
|
|
221
221
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/generated_files.py,sha256=YZ3AWE97RaGREO3bO6r9c5CYvKeVVvOlIGwwuRvyYuU,61797
|
|
222
222
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/schemas.py,sha256=W7egCmYrPrzic0aXsu9wY5fvQ8y_cl4Tzq_cjVs0mZs,542
|
|
223
223
|
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/__init__.py,sha256=r6yXmGTLJzXNq5laHOlre9veu--VbCQVlDRVm2QUGzI,326
|
|
224
|
-
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/config.py,sha256=
|
|
225
|
-
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/service.py,sha256=
|
|
224
|
+
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/config.py,sha256=sbLV-fYt_veBAdonqAHY9sm-eHxpjIZgc1KVbm03pZY,19042
|
|
225
|
+
unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/service.py,sha256=opbNxT-COtJcszlXL41AnAaHfUKfR7hQw6Us3QK2Cgk,3698
|
|
226
226
|
unique_toolkit/agentic/tools/openai_builtin/manager.py,sha256=nWgH38k5v-pi9DadmpHJxfHkJLuVei8YIbFiZo7XSZ0,4187
|
|
227
227
|
unique_toolkit/agentic/tools/run_context.py,sha256=lk9tS1wn97NcSByNZpJWunnnZTos9H2SsxUkKKAvW_A,783
|
|
228
228
|
unique_toolkit/agentic/tools/schemas.py,sha256=8THg6r5Dsgm2PtPcIfuXorSKVd-oUgYKjSU61gyiB2I,5590
|
|
@@ -418,6 +418,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
|
|
|
418
418
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
419
419
|
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
420
420
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
421
|
-
unique_toolkit-2026.30.0.
|
|
422
|
-
unique_toolkit-2026.30.0.
|
|
423
|
-
unique_toolkit-2026.30.0.
|
|
421
|
+
unique_toolkit-2026.30.0.dev22.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
422
|
+
unique_toolkit-2026.30.0.dev22.dist-info/METADATA,sha256=g3QbTNdj0uJ646TdRxg3L2XegUywt3EVUp5vYpGHrCs,7460
|
|
423
|
+
unique_toolkit-2026.30.0.dev22.dist-info/RECORD,,
|
|
File without changes
|