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.
@@ -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 and (filepath := result.unpack()) is not None:
129
- memory.file_paths[content.id] = filepath
130
- updated = True
131
-
132
- return memory, updated
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.30.0.dev20
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>
@@ -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=HvORjXjjd4uC0izkGrN4RYXdK-fQBSDfyrLKhCcuECc,8357
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=tbfcz1stGThkRVbKCDDLqHCkZ9q6uGtg5hwGaOfZA8c,4259
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=QcdWg3-X4lhRCm94ur20vl24AokWDk1sgbItMX-lyS4,5845
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=QRn-C3feMAaMWpz1Z1NvytgPRarmG_g4QFSpFzpzoII,18517
225
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/tool/service.py,sha256=fwxRHNarQxUovDigSalMjpB36D6Y-e12jF5VS1J_fsk,3554
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.dev20.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
422
- unique_toolkit-2026.30.0.dev20.dist-info/METADATA,sha256=NYQrKTpV8b6cYsPpbfFtcq-uD_QEinYobs_L9--qLoY,7460
423
- unique_toolkit-2026.30.0.dev20.dist-info/RECORD,,
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,,