unique-sdk 2026.30.0.dev8__tar.gz → 2026.30.0.dev10__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.
Files changed (89) hide show
  1. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/PKG-INFO +1 -1
  2. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/pyproject.toml +1 -1
  3. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/__init__.py +4 -0
  4. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_agentic_table.py +229 -0
  5. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/chat_in_space.py +29 -4
  6. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/README.md +0 -0
  7. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_api_requestor.py +0 -0
  8. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_api_resource.py +0 -0
  9. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_api_version.py +0 -0
  10. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_error.py +0 -0
  11. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_http_client.py +0 -0
  12. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_list_object.py +0 -0
  13. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_object_classes.py +0 -0
  14. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_request_options.py +0 -0
  15. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_unique_object.py +0 -0
  16. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_unique_ql.py +0 -0
  17. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_unique_response.py +0 -0
  18. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_util.py +0 -0
  19. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_version.py +0 -0
  20. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/_webhook.py +0 -0
  21. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/__init__.py +0 -0
  22. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_acronyms.py +0 -0
  23. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_analytics_order.py +0 -0
  24. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_benchmarking.py +0 -0
  25. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_briefing.py +0 -0
  26. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_chat_completion.py +0 -0
  27. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_content.py +0 -0
  28. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_dynamic_frontend.py +0 -0
  29. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_elicitation.py +0 -0
  30. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_embedding.py +0 -0
  31. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_event.py +0 -0
  32. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_folder.py +0 -0
  33. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_group.py +0 -0
  34. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_integrated.py +0 -0
  35. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_llm_models.py +0 -0
  36. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_mcp.py +0 -0
  37. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_message.py +0 -0
  38. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_message_assessment.py +0 -0
  39. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_message_execution.py +0 -0
  40. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_message_log.py +0 -0
  41. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_message_tool.py +0 -0
  42. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_module.py +0 -0
  43. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  44. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_search.py +0 -0
  45. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_search_string.py +0 -0
  46. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  47. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_space.py +0 -0
  48. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_user.py +0 -0
  49. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/api_resources/_web_search.py +0 -0
  50. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/__init__.py +0 -0
  51. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/__main__.py +0 -0
  52. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/cli.py +0 -0
  53. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/browser.py +0 -0
  56. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/cite_file.py +0 -0
  57. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/dynamic_frontend.py +0 -0
  58. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/elicitation.py +0 -0
  59. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/files.py +0 -0
  60. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/folders.py +0 -0
  61. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/mcp.py +0 -0
  62. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/navigation.py +0 -0
  63. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/read.py +0 -0
  64. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  65. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/search.py +0 -0
  66. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/subagent.py +0 -0
  67. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/web_search.py +0 -0
  68. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/commands/web_search_config.py +0 -0
  69. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/config.py +0 -0
  70. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/formatting.py +0 -0
  71. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/identity.py +0 -0
  72. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/metadata_filter.py +0 -0
  73. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/shell.py +0 -0
  74. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-dynamic-frontend/SKILL.md +0 -0
  75. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  76. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  77. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  78. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  79. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  80. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  81. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
  82. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  83. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/cli/state.py +0 -0
  84. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/analytics_order_run.py +0 -0
  85. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/benchmarking_run.py +0 -0
  86. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/chat_history.py +0 -0
  87. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/file_io.py +0 -0
  88. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/unique_sdk/utils/sources.py +0 -0
  89. {unique_sdk-2026.30.0.dev8 → unique_sdk-2026.30.0.dev10}/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.30.0.dev8
3
+ Version: 2026.30.0.dev10
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>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.30.0.dev8"
3
+ version = "2026.30.0.dev10"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -136,7 +136,11 @@ from .api_resources._agentic_table import (
136
136
  AgreementStatus as AgreementStatus,
137
137
  RowVerificationStatus as RowVerificationStatus,
138
138
  MagicTableAction as MagicTableAction,
139
+ MagicTableActionResult as MagicTableActionResult,
139
140
  MagicTableActivityResponse as MagicTableActivityResponse,
141
+ MagicTableArtifact as MagicTableArtifact,
142
+ MagicTableArtifactState as MagicTableArtifactState,
140
143
  MagicTableArtifactType as MagicTableArtifactType,
141
144
  MagicTableMetadataEntry as MagicTableMetadataEntry,
145
+ CreatedAgenticTableSheet as CreatedAgenticTableSheet,
142
146
  )
@@ -102,6 +102,14 @@ class MagicTableArtifactType(StrEnum):
102
102
  AGENTIC_REPORT = "AGENTIC_REPORT"
103
103
 
104
104
 
105
+ class MagicTableArtifactState(StrEnum):
106
+ """Artifact generation state (matches `MagicTableArtifactState`)."""
107
+
108
+ IN_PROGRESS = "IN_PROGRESS"
109
+ DONE = "DONE"
110
+ ERROR = "ERROR"
111
+
112
+
105
113
  class MagicTableMetadataEntry(TypedDict, total=False):
106
114
  """Row or sheet metadata entry as returned by the public magic-table API."""
107
115
 
@@ -146,6 +154,54 @@ class MagicTableActivityResponse(TypedDict):
146
154
  status: bool
147
155
 
148
156
 
157
+ class MagicTableActionResult(TypedDict, total=False):
158
+ """Generic `{status, message?}` response from lifecycle mutations (import, generate-artifact, sheet metadata writes)."""
159
+
160
+ status: bool
161
+ message: str | None
162
+
163
+
164
+ class _CreatedAgenticTableSheetRequired(TypedDict):
165
+ sheetId: str
166
+ dueDiligenceId: str
167
+ name: str
168
+ state: AgenticTableSheetState
169
+ createdBy: str
170
+ companyId: str
171
+ createdAt: str
172
+
173
+
174
+ class CreatedAgenticTableSheet(_CreatedAgenticTableSheetRequired, total=False):
175
+ """Response body from `POST /magic-table` (create sheet)."""
176
+
177
+ chatId: str | None
178
+ dueAt: str | None
179
+
180
+
181
+ class MagicTableArtifact(TypedDict, total=False):
182
+ """Artifact entry from `GET /magic-table/{tableId}/artifacts`.
183
+
184
+ ``contentId`` is only present once ``artifactState`` is ``DONE``; download the
185
+ file via the Content API.
186
+ """
187
+
188
+ id: str
189
+ name: str | None
190
+ contentId: str | None
191
+ mimeType: str | None
192
+ artifactType: MagicTableArtifactType
193
+ artifactState: MagicTableArtifactState
194
+ createdAt: str
195
+ updatedAt: str
196
+
197
+
198
+ class MagicTableArtifactList(TypedDict):
199
+ """List envelope returned by `GET /magic-table/{tableId}/artifacts` (`{object: "list", data: [...]}`)."""
200
+
201
+ object: Literal["list"]
202
+ data: list[MagicTableArtifact]
203
+
204
+
149
205
  class _AgenticTableSheetRequired(TypedDict):
150
206
  sheetId: str
151
207
  name: str
@@ -238,6 +294,39 @@ class AgenticTable(APIResource["AgenticTable"]):
238
294
  status: RowVerificationStatus
239
295
  locked: NotRequired[bool]
240
296
 
297
+ class CreateSheet(RequestOptions):
298
+ """Body for `POST /magic-table` (create a new sheet in a space)."""
299
+
300
+ assistantId: str
301
+ name: NotRequired[str]
302
+ dueAt: NotRequired[str]
303
+
304
+ class AddMetaData(RequestOptions):
305
+ """Body for `POST /magic-table/{tableId}/metadata` (import questions/sources).
306
+
307
+ Delta semantics: ids/texts already on the sheet are silently skipped. The
308
+ agent run is only triggered when new questions (file ids or texts) are
309
+ provided; adding only sources does not trigger a run.
310
+ """
311
+
312
+ tableId: str
313
+ questionFileIds: NotRequired[list[str]]
314
+ questionTexts: NotRequired[list[str]]
315
+ sourceFileIds: NotRequired[list[str]]
316
+ context: NotRequired[str]
317
+
318
+ class GenerateArtifact(RequestOptions):
319
+ """Body for `POST /magic-table/{tableId}/generate-artifact` (trigger export)."""
320
+
321
+ tableId: str
322
+ artifactTypes: list[MagicTableArtifactType]
323
+
324
+ class CreateSheetMetadata(RequestOptions):
325
+ """Body for `POST /magic-table/{tableId}/sheet/metadata` (create sheet metadata entries)."""
326
+
327
+ tableId: str
328
+ entries: list[MagicTableMetadataEntry]
329
+
241
330
  @classmethod
242
331
  async def set_cell(
243
332
  cls,
@@ -435,3 +524,143 @@ class AgenticTable(APIResource["AgenticTable"]):
435
524
  params,
436
525
  ),
437
526
  )
527
+
528
+ @classmethod
529
+ async def create_sheet(
530
+ cls,
531
+ user_id: str,
532
+ company_id: str,
533
+ **params: Unpack["AgenticTable.CreateSheet"],
534
+ ) -> CreatedAgenticTableSheet:
535
+ """Create a new Agentic Table sheet in a space (`POST /magic-table`).
536
+
537
+ The caller must have access to the space (``assistantId``). The response
538
+ includes ``sheetId`` (use it as ``tableId`` for all subsequent calls) and
539
+ ``dueDiligenceId``.
540
+ """
541
+ return cast(
542
+ CreatedAgenticTableSheet,
543
+ await cls._static_request_async(
544
+ "post",
545
+ "/magic-table",
546
+ user_id,
547
+ company_id,
548
+ params,
549
+ ),
550
+ )
551
+
552
+ @classmethod
553
+ async def add_metadata(
554
+ cls,
555
+ user_id: str,
556
+ company_id: str,
557
+ **params: Unpack["AgenticTable.AddMetaData"],
558
+ ) -> MagicTableActionResult:
559
+ """Import question files/texts and source files (`POST /magic-table/{tableId}/metadata`).
560
+
561
+ Triggers the agent when new questions are included. Rejected with 422 while
562
+ the sheet is ``PROCESSING``.
563
+ """
564
+ url = f"/magic-table/{params['tableId']}/metadata"
565
+ params.pop("tableId")
566
+ return cast(
567
+ MagicTableActionResult,
568
+ await cls._static_request_async(
569
+ "post",
570
+ url,
571
+ user_id,
572
+ company_id,
573
+ params,
574
+ ),
575
+ )
576
+
577
+ @classmethod
578
+ async def generate_artifact(
579
+ cls,
580
+ user_id: str,
581
+ company_id: str,
582
+ **params: Unpack["AgenticTable.GenerateArtifact"],
583
+ ) -> MagicTableActionResult:
584
+ """Trigger export generation (`POST /magic-table/{tableId}/generate-artifact`).
585
+
586
+ Generation is asynchronous: poll `list_artifacts` until the artifact of the
587
+ requested type reaches state ``DONE``, then download its ``contentId`` via
588
+ the Content API.
589
+ """
590
+ url = f"/magic-table/{params['tableId']}/generate-artifact"
591
+ params.pop("tableId")
592
+ return cast(
593
+ MagicTableActionResult,
594
+ await cls._static_request_async(
595
+ "post",
596
+ url,
597
+ user_id,
598
+ company_id,
599
+ params,
600
+ ),
601
+ )
602
+
603
+ @classmethod
604
+ async def list_artifacts(
605
+ cls,
606
+ user_id: str,
607
+ company_id: str,
608
+ tableId: str,
609
+ ) -> list[MagicTableArtifact]:
610
+ """List export artifacts of a sheet (`GET /magic-table/{tableId}/artifacts`).
611
+
612
+ The route responds with a ``{object: "list", data: [...]}`` envelope; this
613
+ method unwraps it and returns the artifact entries.
614
+ """
615
+ url = f"/magic-table/{tableId}/artifacts"
616
+ response = cast(
617
+ MagicTableArtifactList,
618
+ await cls._static_request_async(
619
+ "get",
620
+ url,
621
+ user_id,
622
+ company_id,
623
+ ),
624
+ )
625
+ return response["data"]
626
+
627
+ @classmethod
628
+ async def create_sheet_metadata(
629
+ cls,
630
+ user_id: str,
631
+ company_id: str,
632
+ **params: Unpack["AgenticTable.CreateSheetMetadata"],
633
+ ) -> MagicTableActionResult:
634
+ """Create sheet metadata entries (`POST /magic-table/{tableId}/sheet/metadata`)."""
635
+ url = f"/magic-table/{params['tableId']}/sheet/metadata"
636
+ params.pop("tableId")
637
+ return cast(
638
+ MagicTableActionResult,
639
+ await cls._static_request_async(
640
+ "post",
641
+ url,
642
+ user_id,
643
+ company_id,
644
+ params,
645
+ ),
646
+ )
647
+
648
+ @classmethod
649
+ async def delete_sheet_metadata(
650
+ cls,
651
+ user_id: str,
652
+ company_id: str,
653
+ tableId: str,
654
+ metadataId: str,
655
+ ) -> MagicTableActionResult:
656
+ """Delete a sheet metadata entry (`DELETE /magic-table/{tableId}/sheet/metadata/{metadataId}`)."""
657
+ url = f"/magic-table/{tableId}/sheet/metadata/{metadataId}"
658
+ return cast(
659
+ MagicTableActionResult,
660
+ await cls._static_request_async(
661
+ "delete",
662
+ url,
663
+ user_id,
664
+ company_id,
665
+ ),
666
+ )
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import warnings
3
5
  from collections.abc import Awaitable, Callable, Sequence
@@ -11,6 +13,17 @@ from unique_sdk.utils.file_io import (
11
13
  )
12
14
 
13
15
 
16
+ def get_message_invocations(message: Space.Message) -> list[dict[str, Any]]:
17
+ """Per-LLM-invocation stats for a completed Space message, read from
18
+ `debugInfo.llm_invocations` (a plain list, one entry per LLM call).
19
+
20
+ Each entry has `modelName`, `tokenUsage`, and `source` keys.
21
+ """
22
+ debug_info = message.get("debugInfo") or {}
23
+ llm_invocations = debug_info.get("llm_invocations")
24
+ return llm_invocations if isinstance(llm_invocations, list) else []
25
+
26
+
14
27
  async def send_message_and_wait_for_completion(
15
28
  user_id: str,
16
29
  company_id: str,
@@ -23,10 +36,10 @@ async def send_message_and_wait_for_completion(
23
36
  poll_interval: float = 1.0,
24
37
  max_wait: float = 60.0,
25
38
  stop_condition: Literal["stoppedStreamingAt", "completedAt"] = "stoppedStreamingAt",
26
- correlation: "Space.Correlation | None" = None,
39
+ correlation: Space.Correlation | None = None,
27
40
  auto_approve_elicitation: bool | None = None,
28
- on_message_update: Callable[["Space.Message"], Awaitable[None]] | None = None,
29
- ) -> "Space.Message":
41
+ on_message_update: Callable[[Space.Message], Awaitable[None]] | None = None,
42
+ ) -> Space.Message:
30
43
  """
31
44
  Sends a prompt asynchronously and polls for completion. (until stoppedStreamingAt is not None)
32
45
 
@@ -42,6 +55,13 @@ async def send_message_and_wait_for_completion(
42
55
  poll_interval: Seconds between polls.
43
56
  max_wait: Maximum seconds to wait for completion.
44
57
  stop_condition: Defines when to expect a response back, when the assistant stop streaming or when it completes the message. (default: "stoppedStreamingAt")
58
+ Note: `debugInfo.llm_invocations` (see `get_message_invocations`) is written
59
+ near the very end of the orchestrator's run -- after postprocessors and
60
+ evaluations, which happen after the visible text stream ends. With the default
61
+ `"stoppedStreamingAt"`, or even `"completedAt"` (set slightly before the debugInfo
62
+ write completes), the returned message's invocations can still be incomplete
63
+ immediately after this function returns. Re-fetch the message after a short delay
64
+ if you need the full invocation list.
45
65
  correlation: Optional correlation data to link this message to a parent message in another chat.
46
66
  Should contain: parentMessageId, parentChatId, parentAssistantId.
47
67
  auto_approve_elicitation: When True, automatically approves elicitation requests during
@@ -91,6 +111,11 @@ async def send_message_and_wait_for_completion(
91
111
  last_update_signature = update_signature
92
112
  if answer.get(stop_condition) is not None:
93
113
  try:
114
+ # debugInfo/llm_invocations is written via update_debug_info_async(),
115
+ # which always targets the original USER message, not the
116
+ # assistant reply (assistant=False in
117
+ # ChatService._construct_message_modify_params) — re-fetch by
118
+ # `message_id`, not the polled assistant message's own id.
94
119
  user_message = await Message.retrieve_async(
95
120
  user_id, company_id, message_id, chatId=chat_id
96
121
  )
@@ -116,7 +141,7 @@ async def chat_against_file(
116
141
  poll_interval: float = 1.0,
117
142
  max_wait: float = 60.0,
118
143
  should_delete_chat: bool = True,
119
- ) -> "Space.Message":
144
+ ) -> Space.Message:
120
145
  """
121
146
  Chat against a file by uploading it, sending a message and waiting for a reply.
122
147
  Args: