unique_toolkit 2026.22.0.dev6__py3-none-any.whl → 2026.22.0.dev7__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.
@@ -81,6 +81,11 @@ class FeatureFlags(BaseSettings):
81
81
  description="Emit codeExecution fences in message.text for code interpreter outputs (UN-17972). When disabled, inline file refs are kept as-is. Can be 'true' or comma-separated company IDs.",
82
82
  )
83
83
 
84
+ enable_html_with_fence_un_17927: FeatureFlag = Field(
85
+ default=FeatureFlag(False),
86
+ description="Render HTML code interpreter files as htmlWithSource fences instead of HtmlRendering blocks (UN-17927). Requires enable_code_execution_fence_un_17972 to also be enabled. Can be 'true' or comma-separated company IDs.",
87
+ )
88
+
84
89
  enable_web_search_argument_screening_un_18741: FeatureFlag = Field(
85
90
  default=FeatureFlag(False),
86
91
  description="Enable argument screening agent for web search tool calls (UN-18741). Can be 'true' or comma-separated company IDs.",
@@ -503,6 +503,15 @@ class DisplayCodeInterpreterFilesPostProcessor(
503
503
  fence_ff_on = feature_flags.enable_code_execution_fence_un_17972.is_enabled(
504
504
  self._company_id
505
505
  )
506
+ # HTML uses htmlWithSource only when BOTH the code-execution fence FF AND the
507
+ # dedicated HTML-fence FF are on. Default (FF off) keeps HtmlRendering so
508
+ # existing deployments are unaffected.
509
+ html_fence_ff_on = (
510
+ fence_ff_on
511
+ and feature_flags.enable_html_with_fence_un_17927.is_enabled(
512
+ self._company_id
513
+ )
514
+ )
506
515
 
507
516
  replaced_files: list[str] = []
508
517
  missed_files: list[str] = []
@@ -534,7 +543,9 @@ class DisplayCodeInterpreterFilesPostProcessor(
534
543
  )
535
544
  changed |= replaced
536
545
 
537
- elif is_html:
546
+ # HTML rendered as HtmlRendering block unless the dedicated html-fence FF
547
+ # is on (enable_html_with_fence_un_17927). Default keeps HtmlRendering.
548
+ elif is_html and not html_fence_ff_on:
538
549
  loop_response.message.text, replaced = _replace_container_html_citation(
539
550
  text=loop_response.message.text or "",
540
551
  filename=filename,
@@ -542,6 +553,8 @@ class DisplayCodeInterpreterFilesPostProcessor(
542
553
  )
543
554
  changed |= replaced
544
555
 
556
+ # Non-HTML files, or HTML when html_fence_ff_on → inline content link for
557
+ # subsequent fence injection (htmlWithSource / imgWithSource / fileWithSource)
545
558
  else:
546
559
  loop_response.message.text, replaced = _replace_container_file_citation(
547
560
  text=loop_response.message.text or "",
@@ -563,6 +576,7 @@ class DisplayCodeInterpreterFilesPostProcessor(
563
576
  else:
564
577
  missed_files.append(filename)
565
578
 
579
+ # HtmlRendering and htmlWithSource both embed contentId directly — no ContentReference needed
566
580
  is_html_rendered = is_html
567
581
  if replaced and not (is_image or is_html_rendered or fence_ff_on):
568
582
  loop_response.message.references.append(
@@ -584,13 +598,17 @@ class DisplayCodeInterpreterFilesPostProcessor(
584
598
  )
585
599
 
586
600
  if fence_ff_on:
587
- code_blocks = _build_code_blocks(loop_response, self._content_map)
601
+ code_blocks = _build_code_blocks(
602
+ loop_response, self._content_map, include_html=html_fence_ff_on
603
+ )
588
604
  self._log.info(
589
605
  "Fence injection — %d code block(s), files: %s",
590
606
  len(code_blocks),
591
607
  [f.filename for b in code_blocks for f in b.files],
592
608
  )
593
- _warn_unmatched_code_blocks(self._content_map, code_blocks)
609
+ _warn_unmatched_code_blocks(
610
+ self._content_map, code_blocks, include_html=html_fence_ff_on
611
+ )
594
612
  text_before = loop_response.message.text
595
613
  loop_response.message.text = _inject_code_execution_fences(
596
614
  loop_response.message.text or "",
@@ -971,12 +989,9 @@ def _build_file_fence(file: CodeInterpreterFile, code: str, fence_id: int) -> st
971
989
  content_id, a title derived from the filename, the type, and the
972
990
  escaped source code.
973
991
 
974
- HTML is not emitted here in normal flow: it is rendered via ``HtmlRendering`` blocks
975
- in message text and excluded from fence injection. If ``type="html"`` is passed (e.g.
976
- orphan path), fall through to ``fileWithSource`` like other non-image artifacts.
977
-
978
992
  Format (4 backticks so inner code backticks never close the fence):
979
993
  ````imgWithSource(id='{n}', contentId='{id}', title="{title}", code="{escaped_code}")````
994
+ ````htmlWithSource(id='{n}', contentId='{id}', title="{title}", code="{escaped_code}")````
980
995
  ````fileWithSource(id='{n}', contentId='{id}', title="{title}", type="{type}", code="{escaped_code}")````
981
996
  """
982
997
  title = _file_title(file.filename)
@@ -984,6 +999,8 @@ def _build_file_fence(file: CodeInterpreterFile, code: str, fence_id: int) -> st
984
999
  outer = "````"
985
1000
  if file.type == "image":
986
1001
  tag = f"imgWithSource(id='{fence_id}', contentId='{file.content_id}', title=\"{title}\", code=\"{escaped}\")"
1002
+ elif file.type == "html":
1003
+ tag = f"htmlWithSource(id='{fence_id}', contentId='{file.content_id}', title=\"{title}\", code=\"{escaped}\")"
987
1004
  else:
988
1005
  ftype = _file_frontend_type(file.filename)
989
1006
  tag = f'fileWithSource(id=\'{fence_id}\', contentId=\'{file.content_id}\', title="{title}", type="{ftype}", code="{escaped}")'
@@ -1007,7 +1024,7 @@ def _inline_ref_pattern(file: CodeInterpreterFile) -> re.Pattern[str]:
1007
1024
 
1008
1025
 
1009
1026
  _FENCE_BLOCK_START = re.compile(
1010
- r"^[^\n`]+?(````(?:imgWithSource|fileWithSource)\()",
1027
+ r"^[^\n`]+?(````(?:imgWithSource|fileWithSource|htmlWithSource)\()",
1011
1028
  re.MULTILINE,
1012
1029
  )
1013
1030
 
@@ -1017,9 +1034,9 @@ _FENCE_BLOCK_START = re.compile(
1017
1034
  # - cross-line: 1 or 2 newlines optionally surrounded by horizontal whitespace
1018
1035
  # (list-item linebreak, or blank-line paragraph gap)
1019
1036
  _CONSECUTIVE_FENCES_RE = re.compile(
1020
- r"(````(?:imgWithSource|fileWithSource)\([^\n]*\)````)"
1037
+ r"(````(?:imgWithSource|fileWithSource|htmlWithSource)\([^\n]*\)````)"
1021
1038
  r"(?:[^\n`]*|[ \t]*\n{1,2}[ \t]*)"
1022
- r"(?=````(?:imgWithSource|fileWithSource)\()"
1039
+ r"(?=````(?:imgWithSource|fileWithSource|htmlWithSource)\()"
1023
1040
  )
1024
1041
 
1025
1042
 
@@ -1083,6 +1100,7 @@ def _inject_code_execution_fences(
1083
1100
  def _build_code_blocks(
1084
1101
  loop_response: ResponsesLanguageModelStreamResponse,
1085
1102
  content_map: dict[str, str | None],
1103
+ include_html: bool = False,
1086
1104
  ) -> list[CodeInterpreterBlock]:
1087
1105
  """Map each code interpreter call to the files it produced via /mnt/data/ path matching.
1088
1106
 
@@ -1180,11 +1198,9 @@ def _build_code_blocks(
1180
1198
  # overwritten across executions. Using a dict keyed by filename ensures each
1181
1199
  # file appears exactly once per block (last annotation wins, consistent with
1182
1200
  # the last-writer-wins rule applied in step 1).
1183
- # HTML files are excluded: they are always rendered via HtmlRendering blocks
1184
- # and never participate in fence injection.
1185
1201
  block_file_map: dict[int, dict[str, CodeInterpreterFile]] = {}
1186
1202
  for annotation in loop_response.container_files:
1187
- if _get_file_type(annotation.filename) == "html":
1203
+ if not include_html and _get_file_type(annotation.filename) == "html":
1188
1204
  continue
1189
1205
  content_id = content_map.get(annotation.filename)
1190
1206
  if content_id is None:
@@ -1268,6 +1284,7 @@ def _replace_dangling_sandbox_links(text: str) -> tuple[str, bool]:
1268
1284
  def _warn_unmatched_code_blocks(
1269
1285
  content_map: dict[str, str | None],
1270
1286
  code_blocks: list[CodeInterpreterBlock],
1287
+ include_html: bool = False,
1271
1288
  ) -> None:
1272
1289
  """Warn for files that were uploaded but could not be matched to any code block.
1273
1290
 
@@ -1281,7 +1298,7 @@ def _warn_unmatched_code_blocks(
1281
1298
  for filename, content_id in content_map.items():
1282
1299
  if content_id is None:
1283
1300
  continue
1284
- if _get_file_type(filename) == "html":
1301
+ if not include_html and _get_file_type(filename) == "html":
1285
1302
  continue
1286
1303
  if filename not in fenced_filenames:
1287
1304
  logger.warning(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.22.0.dev6
3
+ Version: 2026.22.0.dev7
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>
@@ -111,7 +111,7 @@ unique_toolkit/agentic/evaluation/tests/test_output_parser.py,sha256=KfltytmvqnP
111
111
  unique_toolkit/agentic/evaluation/tests/test_prompt_loaders.py,sha256=zBREdlKf5tdDyB8XSaNgpQv3-tuZJoYteeJrp6WMWDM,11897
112
112
  unique_toolkit/agentic/evaluation/utils.py,sha256=HmyPaDV8wdW-_gOjjW-wDaMKgdrsP5-SHP7OqTmGI_A,264
113
113
  unique_toolkit/agentic/feature_flags/__init__.py,sha256=LhE2cHoa9AYBOR7TjiIToOn46sttm9paKcrzE7gnDPM,149
114
- unique_toolkit/agentic/feature_flags/feature_flags.py,sha256=fYMWDbv6Yjyb52KofGQM97KEkw7mOCzoRa9UHeul4BU,5125
114
+ unique_toolkit/agentic/feature_flags/feature_flags.py,sha256=6ecUj7hrRc1H1r-ldraLs2DxZX9hhrahevEf0rYAl7M,5465
115
115
  unique_toolkit/agentic/history_manager/history_construction_with_contents.py,sha256=yqaG_tqyijgW6DEpfulg1zKtzQLewgWEegWnvZvE4P4,25849
116
116
  unique_toolkit/agentic/history_manager/history_manager.py,sha256=1W9908BoKzBRwxaLx_AVQE4QsMHj4mq3rdeNunTpHfY,15739
117
117
  unique_toolkit/agentic/history_manager/loop_token_reducer.py,sha256=egXlsiN6QxoWM3R4DAjsuhSap7RU3DqBJeDGfw9Ndc0,26846
@@ -205,7 +205,7 @@ unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py,sha256=zH
205
205
  unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/__init__.py,sha256=ZSNvtRDSRipcfJg9gEsXm9DZDU7ODsAcko9vEmKevmI,584
206
206
  unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/artifacts.py,sha256=wAgDrcXZbCdDbNWNpbMigBUXySzbKQXSfg3KsZfAWfs,3115
207
207
  unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/code_display.py,sha256=Q4crOkYG-C_uTSc7rW14cP1H4IWxsKpfuftafDHbjEo,2897
208
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/generated_files.py,sha256=ocWolZQuWZA8P8OJq4NHT4XVTAz-b8DVShdBU7-C_0o,57935
208
+ unique_toolkit/agentic/tools/openai_builtin/code_interpreter/postprocessors/generated_files.py,sha256=2bcRHoW-FwC8lniGFVIAXwUBK7NoSAmjfqCmJEvATz4,58941
209
209
  unique_toolkit/agentic/tools/openai_builtin/code_interpreter/schemas.py,sha256=W7egCmYrPrzic0aXsu9wY5fvQ8y_cl4Tzq_cjVs0mZs,542
210
210
  unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py,sha256=BnwTO2pJTYrhsp269sUkd18p3NVl1TCzfhlYCkhbUwI,14088
211
211
  unique_toolkit/agentic/tools/openai_builtin/manager.py,sha256=CkfMRsjrRDv_X-zfJHZnxxOdpCK1qzf6RGGxKtUHp9k,3519
@@ -391,6 +391,6 @@ unique_toolkit/short_term_memory/service.py,sha256=Ncw0_0CNM6kmzJSLVJGG5GcKpBduI
391
391
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
392
392
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
393
393
  unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
394
- unique_toolkit-2026.22.0.dev6.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
395
- unique_toolkit-2026.22.0.dev6.dist-info/METADATA,sha256=283AgrrJfbEjnQFJy-vtLkrxnCZclSCCqNcxK6zIYN0,7392
396
- unique_toolkit-2026.22.0.dev6.dist-info/RECORD,,
394
+ unique_toolkit-2026.22.0.dev7.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
395
+ unique_toolkit-2026.22.0.dev7.dist-info/METADATA,sha256=V7wtEuBDpUmqx-vfAstDwC8pTmwMSzG2VkdkZHDGDTI,7392
396
+ unique_toolkit-2026.22.0.dev7.dist-info/RECORD,,