unique_toolkit 2026.42.0.dev7__py3-none-any.whl → 2026.42.0.dev8__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.
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import logging
3
3
  import time
4
+ from collections.abc import Collection
4
5
  from datetime import datetime
5
6
  from typing import Any, NamedTuple, cast
6
7
 
@@ -252,7 +253,11 @@ class AgenticTableService:
252
253
  return MagicTableCell.model_validate(cell_data)
253
254
 
254
255
  async def set_multiple_cells(
255
- self, cells: list[MagicTableCell], batch_size: int = 4000
256
+ self,
257
+ cells: list[MagicTableCell],
258
+ batch_size: int = 4000,
259
+ *,
260
+ overwrite_library_rows: bool | None = None,
256
261
  ):
257
262
  """
258
263
  Sets the values of multiple cells in the Agentic Table.
@@ -260,6 +265,14 @@ class AgenticTableService:
260
265
  Args:
261
266
  cells (list[MagicTableCell]): The cells to set sorted by row and column.
262
267
  batch_size (int): Number of cells to set in a single request.
268
+ overwrite_library_rows (bool | None): When set, forwarded as
269
+ ``overwriteLibraryRows``. ``True`` lets updates to existing cells on
270
+ library-sheet rows go through instead of being dropped; the server
271
+ only honors it on library sheets, for rows where the caller can read
272
+ every sheet named in the row's ``sourceSheetId`` metadata. Locked rows still
273
+ need unlocking first (``update_row_verification_status`` with
274
+ ``locked=False``). ``None`` omits the field; servers that predate
275
+ it reject it with a 400.
263
276
  """
264
277
  for i in range(0, len(cells), batch_size):
265
278
  batch = cells[i : i + batch_size]
@@ -275,6 +288,7 @@ class AgenticTableService:
275
288
  )
276
289
  for cell in batch
277
290
  ],
291
+ overwriteLibraryRows=overwrite_library_rows,
278
292
  )
279
293
 
280
294
  async def set_activity(
@@ -875,6 +889,133 @@ class AgenticTableService:
875
889
  if not result.get("status"):
876
890
  raise Exception(result.get("message") or "Failed to create row metadata")
877
891
 
892
+ async def update_row_metadata(
893
+ self,
894
+ metadata_id: str,
895
+ entry: RowMetadataEntryInput,
896
+ ) -> None:
897
+ """Update a single row metadata entry, addressed by its id.
898
+
899
+ Args:
900
+ metadata_id (str): The ``id`` of the row metadata entry to update.
901
+ entry (RowMetadataEntryInput): The new key, value and filter flag.
902
+
903
+ Raises:
904
+ Exception: If the API reports a non-success status.
905
+ """
906
+ result = await AgenticTable.update_row_metadata(
907
+ user_id=self._user_id,
908
+ company_id=self._company_id,
909
+ tableId=self.table_id,
910
+ metadataId=metadata_id,
911
+ key=entry.key,
912
+ value=entry.value,
913
+ exactFilter=entry.exact_filter,
914
+ )
915
+ if not result.get("status"):
916
+ raise Exception(result.get("message") or "Failed to update row metadata")
917
+
918
+ async def delete_row_metadata(self, metadata_id: str) -> None:
919
+ """Delete a single row metadata entry, addressed by its id.
920
+
921
+ Raises:
922
+ Exception: If the API reports a non-success status.
923
+ """
924
+ result = await AgenticTable.delete_row_metadata(
925
+ user_id=self._user_id,
926
+ company_id=self._company_id,
927
+ tableId=self.table_id,
928
+ metadataId=metadata_id,
929
+ )
930
+ if not result.get("status"):
931
+ raise Exception(result.get("message") or "Failed to delete row metadata")
932
+
933
+ async def set_row_metadata_by_key(
934
+ self,
935
+ row: int,
936
+ entries: list[RowMetadataEntryInput],
937
+ row_id: str | None = None,
938
+ existing: list[RowMetadataEntry] | None = None,
939
+ managed_keys: Collection[str] | None = None,
940
+ ) -> None:
941
+ """Merge row metadata by key, and optionally remove keys the caller owns.
942
+
943
+ For each entry: a key the row lacks is created; a key the row holds with
944
+ a different value or filter flag is updated in place; a key that already
945
+ matches is left alone. If the row holds the key more than once, one entry
946
+ is kept and the others are deleted, so the key ends up with exactly the
947
+ given value.
948
+
949
+ Keys not in ``entries`` are only removed when listed in ``managed_keys``.
950
+ This is not a full replace: rows also carry metadata the caller does not
951
+ own (e.g. the ``sourceSheetId`` stamp that library overwrites are checked
952
+ against), which must survive.
953
+
954
+ Args:
955
+ row (int): The row index (row order).
956
+ entries (list[RowMetadataEntryInput]): Key/value entries to set. Keys
957
+ must be unique.
958
+ row_id (str | None): The backend row id, used when creating keys.
959
+ existing (list[RowMetadataEntry] | None): The row's current metadata,
960
+ when the caller already read it (e.g. ``get_sheet`` with
961
+ ``include_row_metadata=True``). When ``None``, the row's metadata
962
+ and id are read with ``get_cell``.
963
+ managed_keys (Collection[str] | None): Keys the caller owns. Every
964
+ entry key must be in it. Any of these keys missing from
965
+ ``entries`` is deleted from the row, duplicates included. ``None``
966
+ deletes nothing beyond duplicates of keys in ``entries``.
967
+
968
+ Raises:
969
+ ValueError: If ``entries`` repeats a key, an entry key is not in
970
+ ``managed_keys``, or a key must be created and the row id cannot
971
+ be resolved.
972
+ Exception: If the API reports a non-success status.
973
+ """
974
+ keys = [entry.key for entry in entries]
975
+ if len(set(keys)) != len(keys):
976
+ raise ValueError(f"Duplicate row metadata keys in {keys}")
977
+ if managed_keys is not None:
978
+ unmanaged = sorted(set(keys) - set(managed_keys))
979
+ if unmanaged:
980
+ raise ValueError(f"Row metadata keys {unmanaged} not in managed_keys")
981
+ if not entries and not managed_keys:
982
+ return
983
+ if existing is None:
984
+ cell = await self.get_cell(row, 0, include_row_metadata=True)
985
+ existing = cell.row_metadata
986
+ row_id = row_id or cell.row_id
987
+
988
+ existing_by_key: dict[str, list[RowMetadataEntry]] = {}
989
+ for current in existing:
990
+ existing_by_key.setdefault(current.key, []).append(current)
991
+
992
+ to_create: list[RowMetadataEntryInput] = []
993
+ for entry in entries:
994
+ matches = existing_by_key.get(entry.key)
995
+ if not matches:
996
+ to_create.append(entry)
997
+ continue
998
+ keep = next(
999
+ (
1000
+ m
1001
+ for m in matches
1002
+ if m.value == entry.value and m.exact_filter == entry.exact_filter
1003
+ ),
1004
+ None,
1005
+ )
1006
+ if keep is None:
1007
+ keep = matches[0]
1008
+ await self.update_row_metadata(keep.id, entry)
1009
+ for duplicate in matches:
1010
+ if duplicate.id != keep.id:
1011
+ await self.delete_row_metadata(duplicate.id)
1012
+
1013
+ for key in set(managed_keys or ()) - set(keys):
1014
+ for stale in existing_by_key.get(key, []):
1015
+ await self.delete_row_metadata(stale.id)
1016
+
1017
+ await self.create_row_metadata(row, to_create, row_id=row_id)
1018
+
878
1019
  async def delete_sheet_metadata(self, metadata_id: str) -> None:
879
1020
  """Delete a sheet metadata entry by its id.
880
1021
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-toolkit
3
- Version: 2026.42.0.dev7
3
+ Version: 2026.42.0.dev8
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>
@@ -16,7 +16,7 @@ Requires-Dist: sseclient>=0.0.27,<0.0.28
16
16
  Requires-Dist: openai>=1.105.0,<3
17
17
  Requires-Dist: platformdirs>=4.0.0,<5
18
18
  Requires-Dist: pillow>=12.1.1
19
- Requires-Dist: unique-sdk>=2026.42.0.dev1,<2026.42.0rc0
19
+ Requires-Dist: unique-sdk>=2026.42.0.dev3,<2026.42.0rc0
20
20
  Requires-Dist: jambo>=0.1.2,<0.2
21
21
  Requires-Dist: jinja2>=3.1.6,<4
22
22
  Requires-Dist: docxtpl>=0.20.1,<0.21
@@ -242,7 +242,7 @@ unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py,sha256=u
242
242
  unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py,sha256=EA8iVvb3L91OFk2XMbGcFuhe2etqm3Sx9QCYDGiOSOM,6995
243
243
  unique_toolkit/agentic_table/__init__.py,sha256=RwBShHaD15R3117mU3ZLVzjgPz2ngdbzif7WJwhm8zw,2552
244
244
  unique_toolkit/agentic_table/schemas.py,sha256=hi4vUn95ct2dm3nbkvke7j8RVVCp_nTlzSzLro1OT34,18663
245
- unique_toolkit/agentic_table/service.py,sha256=An2D701eGdUTCaZYHZHQEZsHe50PlDw3yKEK5ypCD-g,42891
245
+ unique_toolkit/agentic_table/service.py,sha256=_aDjxux1Dvdf11-jHzVvEw0PSDNDSjZ-DzKuoxJd09Y,49059
246
246
  unique_toolkit/agentic_table/synthetic_chat_event.py,sha256=MylvYuhx5CVt2DbPetzV0sR8GlkL218DmNFi7A6RYVs,2373
247
247
  unique_toolkit/app/__init__.py,sha256=qear5ErW3smLDATxtnUsgkX3f177zsn65ixVW8T0JVE,1641
248
248
  unique_toolkit/app/chat_event_filter_options_settings.py,sha256=W0Ydio1UeED5_xqaTzbcGzK2tH4EpA1WHUzdF-iWpqI,364
@@ -426,6 +426,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
426
426
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
427
427
  unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
428
428
  unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
429
- unique_toolkit-2026.42.0.dev7.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
430
- unique_toolkit-2026.42.0.dev7.dist-info/METADATA,sha256=lerw6mdP9Cp9f4mxf3nMFsJEZg9R_iak69i2o-uZcwU,7821
431
- unique_toolkit-2026.42.0.dev7.dist-info/RECORD,,
429
+ unique_toolkit-2026.42.0.dev8.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
430
+ unique_toolkit-2026.42.0.dev8.dist-info/METADATA,sha256=ahv1aNL9F18iHC057Ry2rFbO6X6zaOb1-TZ080dzbSg,7821
431
+ unique_toolkit-2026.42.0.dev8.dist-info/RECORD,,