knowledge2 0.4.0__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.
Files changed (139) hide show
  1. knowledge2-0.4.0.dist-info/METADATA +556 -0
  2. knowledge2-0.4.0.dist-info/RECORD +139 -0
  3. knowledge2-0.4.0.dist-info/WHEEL +5 -0
  4. knowledge2-0.4.0.dist-info/top_level.txt +1 -0
  5. sdk/__init__.py +70 -0
  6. sdk/_async_base.py +525 -0
  7. sdk/_async_paging.py +57 -0
  8. sdk/_base.py +541 -0
  9. sdk/_logging.py +41 -0
  10. sdk/_paging.py +73 -0
  11. sdk/_preview.py +70 -0
  12. sdk/_raw_response.py +25 -0
  13. sdk/_request_options.py +51 -0
  14. sdk/_transport.py +144 -0
  15. sdk/_validation.py +25 -0
  16. sdk/_validation_response.py +36 -0
  17. sdk/_version.py +3 -0
  18. sdk/async_client.py +320 -0
  19. sdk/async_resources/__init__.py +45 -0
  20. sdk/async_resources/_mixin_base.py +42 -0
  21. sdk/async_resources/a2a.py +230 -0
  22. sdk/async_resources/agents.py +489 -0
  23. sdk/async_resources/audit.py +145 -0
  24. sdk/async_resources/auth.py +133 -0
  25. sdk/async_resources/console.py +409 -0
  26. sdk/async_resources/corpora.py +276 -0
  27. sdk/async_resources/deployments.py +106 -0
  28. sdk/async_resources/documents.py +592 -0
  29. sdk/async_resources/feeds.py +248 -0
  30. sdk/async_resources/indexes.py +208 -0
  31. sdk/async_resources/jobs.py +165 -0
  32. sdk/async_resources/metadata.py +48 -0
  33. sdk/async_resources/models.py +102 -0
  34. sdk/async_resources/onboarding.py +538 -0
  35. sdk/async_resources/orgs.py +37 -0
  36. sdk/async_resources/pipelines.py +523 -0
  37. sdk/async_resources/projects.py +90 -0
  38. sdk/async_resources/search.py +262 -0
  39. sdk/async_resources/training.py +357 -0
  40. sdk/async_resources/usage.py +91 -0
  41. sdk/client.py +417 -0
  42. sdk/config.py +182 -0
  43. sdk/errors.py +178 -0
  44. sdk/examples/auth_factory.py +34 -0
  45. sdk/examples/batch_operations.py +57 -0
  46. sdk/examples/document_upload.py +56 -0
  47. sdk/examples/e2e_lifecycle.py +213 -0
  48. sdk/examples/error_handling.py +61 -0
  49. sdk/examples/pagination.py +64 -0
  50. sdk/examples/quickstart.py +36 -0
  51. sdk/examples/request_options.py +44 -0
  52. sdk/examples/search.py +64 -0
  53. sdk/integrations/__init__.py +57 -0
  54. sdk/integrations/_client.py +101 -0
  55. sdk/integrations/langchain/__init__.py +6 -0
  56. sdk/integrations/langchain/retriever.py +166 -0
  57. sdk/integrations/langchain/tools.py +108 -0
  58. sdk/integrations/llamaindex/__init__.py +11 -0
  59. sdk/integrations/llamaindex/filters.py +78 -0
  60. sdk/integrations/llamaindex/retriever.py +162 -0
  61. sdk/integrations/llamaindex/tools.py +109 -0
  62. sdk/integrations/llamaindex/vector_store.py +320 -0
  63. sdk/models/__init__.py +18 -0
  64. sdk/models/_base.py +24 -0
  65. sdk/models/_registry.py +457 -0
  66. sdk/models/a2a.py +92 -0
  67. sdk/models/agents.py +109 -0
  68. sdk/models/audit.py +28 -0
  69. sdk/models/auth.py +49 -0
  70. sdk/models/chunks.py +20 -0
  71. sdk/models/common.py +14 -0
  72. sdk/models/console.py +103 -0
  73. sdk/models/corpora.py +48 -0
  74. sdk/models/deployments.py +13 -0
  75. sdk/models/documents.py +126 -0
  76. sdk/models/embeddings.py +24 -0
  77. sdk/models/evaluation.py +17 -0
  78. sdk/models/feedback.py +9 -0
  79. sdk/models/feeds.py +57 -0
  80. sdk/models/indexes.py +36 -0
  81. sdk/models/jobs.py +52 -0
  82. sdk/models/models.py +26 -0
  83. sdk/models/onboarding.py +323 -0
  84. sdk/models/orgs.py +11 -0
  85. sdk/models/pipelines.py +147 -0
  86. sdk/models/projects.py +19 -0
  87. sdk/models/search.py +149 -0
  88. sdk/models/training.py +57 -0
  89. sdk/models/usage.py +39 -0
  90. sdk/namespaces.py +386 -0
  91. sdk/py.typed +0 -0
  92. sdk/resources/__init__.py +45 -0
  93. sdk/resources/_mixin_base.py +40 -0
  94. sdk/resources/a2a.py +230 -0
  95. sdk/resources/agents.py +487 -0
  96. sdk/resources/audit.py +144 -0
  97. sdk/resources/auth.py +138 -0
  98. sdk/resources/console.py +411 -0
  99. sdk/resources/corpora.py +269 -0
  100. sdk/resources/deployments.py +105 -0
  101. sdk/resources/documents.py +597 -0
  102. sdk/resources/feeds.py +246 -0
  103. sdk/resources/indexes.py +210 -0
  104. sdk/resources/jobs.py +164 -0
  105. sdk/resources/metadata.py +53 -0
  106. sdk/resources/models.py +99 -0
  107. sdk/resources/onboarding.py +542 -0
  108. sdk/resources/orgs.py +35 -0
  109. sdk/resources/pipeline_builder.py +257 -0
  110. sdk/resources/pipelines.py +520 -0
  111. sdk/resources/projects.py +87 -0
  112. sdk/resources/search.py +277 -0
  113. sdk/resources/training.py +358 -0
  114. sdk/resources/usage.py +92 -0
  115. sdk/types/__init__.py +366 -0
  116. sdk/types/a2a.py +88 -0
  117. sdk/types/agents.py +133 -0
  118. sdk/types/audit.py +26 -0
  119. sdk/types/auth.py +45 -0
  120. sdk/types/chunks.py +18 -0
  121. sdk/types/common.py +10 -0
  122. sdk/types/console.py +99 -0
  123. sdk/types/corpora.py +42 -0
  124. sdk/types/deployments.py +11 -0
  125. sdk/types/documents.py +104 -0
  126. sdk/types/embeddings.py +22 -0
  127. sdk/types/evaluation.py +15 -0
  128. sdk/types/feedback.py +7 -0
  129. sdk/types/feeds.py +61 -0
  130. sdk/types/indexes.py +30 -0
  131. sdk/types/jobs.py +50 -0
  132. sdk/types/models.py +22 -0
  133. sdk/types/onboarding.py +395 -0
  134. sdk/types/orgs.py +9 -0
  135. sdk/types/pipelines.py +177 -0
  136. sdk/types/projects.py +14 -0
  137. sdk/types/search.py +116 -0
  138. sdk/types/training.py +55 -0
  139. sdk/types/usage.py +37 -0
@@ -0,0 +1,102 @@
1
+ """Async model resource mixin for the Knowledge2 SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from sdk._async_paging import AsyncPager
8
+ from sdk._paging import Page
9
+ from sdk._request_options import RequestOptions
10
+ from sdk._validation import require_str
11
+ from sdk.async_resources._mixin_base import AsyncRequesterMixin
12
+ from sdk.errors import ConfirmationRequiredError
13
+ from sdk.types import ModelDeleteResponse
14
+
15
+
16
+ class AsyncModelsMixin(AsyncRequesterMixin):
17
+ async def list_models(
18
+ self,
19
+ limit: int = 100,
20
+ offset: int = 0,
21
+ request_options: RequestOptions | None = None,
22
+ ) -> Page[dict[str, Any]]:
23
+ """List models accessible to the current credentials.
24
+
25
+ Args:
26
+ limit: Maximum number of models to return per page.
27
+ offset: Number of models to skip for pagination.
28
+
29
+ Returns:
30
+ A Page containing model records with pagination metadata.
31
+
32
+ Raises:
33
+ Knowledge2Error: If the API request fails.
34
+ """
35
+ return await self._list_page(
36
+ "GET", "/v1/models", items_key="models", limit=limit, offset=offset
37
+ )
38
+
39
+ def iter_models(
40
+ self,
41
+ *,
42
+ limit: int = 100,
43
+ request_options: RequestOptions | None = None,
44
+ ) -> AsyncPager[dict[str, Any]]:
45
+ """Lazily paginate models, yielding individual model items.
46
+
47
+ Args:
48
+ limit: Page size used for each underlying API request.
49
+
50
+ Yields:
51
+ Individual model dicts.
52
+
53
+ Raises:
54
+ Knowledge2Error: If any underlying API request fails.
55
+ """
56
+ return self._paginate(
57
+ "GET",
58
+ "/v1/models",
59
+ items_key="models",
60
+ limit=limit,
61
+ )
62
+
63
+ async def delete_model(
64
+ self,
65
+ model_id: str,
66
+ *,
67
+ confirm: bool = False,
68
+ force: bool = False,
69
+ request_options: RequestOptions | None = None,
70
+ ) -> ModelDeleteResponse:
71
+ """Delete a model and its associated artifacts.
72
+
73
+ This is an irreversible operation. You must pass ``confirm=True``
74
+ to acknowledge this and proceed.
75
+
76
+ Args:
77
+ model_id: Unique identifier of the model to delete.
78
+ confirm: Safety guard -- must be ``True`` to execute the
79
+ deletion. Raises ``ConfirmationRequiredError`` when ``False``.
80
+ force: If ``True``, delete even if the model is currently
81
+ deployed.
82
+
83
+ Returns:
84
+ Confirmation of the deletion.
85
+
86
+ Raises:
87
+ ConfirmationRequiredError: If *confirm* is not ``True``.
88
+ NotFoundError: If the model does not exist.
89
+ ConflictError: If *force* is ``False`` and the model has
90
+ active deployments.
91
+ Knowledge2Error: If the API request fails.
92
+ """
93
+ model_id = require_str(model_id, "model_id")
94
+ if not confirm:
95
+ raise ConfirmationRequiredError("model", model_id)
96
+ data = await self._request(
97
+ "DELETE",
98
+ f"/v1/models/{model_id}",
99
+ params={"force": force},
100
+ request_options=request_options,
101
+ )
102
+ return self._maybe_validate(data, "ModelDeleteResponse")
@@ -0,0 +1,538 @@
1
+ """Async SDK resource for dataset onboarding operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from sdk._async_paging import AsyncPager
9
+ from sdk._paging import Page
10
+ from sdk._request_options import RequestOptions
11
+ from sdk._validation import require_str
12
+ from sdk.async_resources._mixin_base import AsyncRequesterMixin
13
+ from sdk.types import (
14
+ DatasetAnalysisDetails,
15
+ DatasetAnalysisResponse,
16
+ DocumentSummaryResponse,
17
+ EvaluationDetails,
18
+ EvaluationListResponse,
19
+ EvaluationReportResponse,
20
+ EvaluationResponse,
21
+ GoldLabelEntry,
22
+ GoldLabelsUploadResponse,
23
+ OnboardingStatusResponse,
24
+ SummarizationResponse,
25
+ SummarizationStatusResponse,
26
+ SyntheticQueryBatchDetails,
27
+ SyntheticQueryBatchListResponse,
28
+ SyntheticQueryBatchResponse,
29
+ )
30
+
31
+
32
+ class AsyncOnboardingMixin(AsyncRequesterMixin):
33
+ """Async mixin providing dataset onboarding operations."""
34
+
35
+ # =========================================================================
36
+ # Dataset Analysis
37
+ # =========================================================================
38
+
39
+ async def start_analysis(
40
+ self,
41
+ corpus_id: str,
42
+ *,
43
+ description: str | None = None,
44
+ auto_bootstrap: bool = True,
45
+ bootstrap_num_samples: int | None = None,
46
+ queries_per_chunk: int | None = None,
47
+ request_options: RequestOptions | None = None,
48
+ ) -> DatasetAnalysisResponse:
49
+ """Start the dataset analysis pipeline for a corpus.
50
+
51
+ Args:
52
+ corpus_id: ID of the corpus to analyze
53
+ description: Optional dataset description for analysis context
54
+ auto_bootstrap: Automatically bootstrap if no gold labels exist
55
+ bootstrap_num_samples: Override bootstrap sample count
56
+ queries_per_chunk: Override queries per chunk
57
+
58
+ Returns:
59
+ DatasetAnalysisResponse with analysis_id and job_id
60
+ """
61
+ corpus_id = require_str(corpus_id, "corpus_id")
62
+ payload: dict[str, Any] = {"auto_bootstrap": auto_bootstrap}
63
+ if description is not None:
64
+ payload["description"] = description
65
+ if bootstrap_num_samples is not None:
66
+ payload["bootstrap_num_samples"] = bootstrap_num_samples
67
+ if queries_per_chunk is not None:
68
+ payload["queries_per_chunk"] = queries_per_chunk
69
+
70
+ data = await self._request(
71
+ "POST",
72
+ f"/v1/corpora/{corpus_id}/onboard:analyze",
73
+ json=payload,
74
+ request_options=request_options,
75
+ )
76
+ return self._maybe_validate(data, "DatasetAnalysisResponse")
77
+
78
+ async def get_onboarding_status(
79
+ self,
80
+ corpus_id: str,
81
+ request_options: RequestOptions | None = None,
82
+ ) -> OnboardingStatusResponse:
83
+ """Get current onboarding status for a corpus.
84
+
85
+ Args:
86
+ corpus_id: ID of the corpus
87
+
88
+ Returns:
89
+ OnboardingStatusResponse with latest analysis and counts
90
+ """
91
+ corpus_id = require_str(corpus_id, "corpus_id")
92
+ data = await self._request(
93
+ "GET", f"/v1/corpora/{corpus_id}/onboard/status", request_options=request_options
94
+ )
95
+ return self._maybe_validate(data, "OnboardingStatusResponse")
96
+
97
+ async def get_analysis(
98
+ self,
99
+ corpus_id: str,
100
+ analysis_id: str,
101
+ request_options: RequestOptions | None = None,
102
+ ) -> DatasetAnalysisDetails:
103
+ """Get detailed results of an analysis run.
104
+
105
+ Args:
106
+ corpus_id: ID of the corpus
107
+ analysis_id: ID of the analysis run
108
+
109
+ Returns:
110
+ DatasetAnalysisDetails with full analysis results
111
+ """
112
+ corpus_id = require_str(corpus_id, "corpus_id")
113
+ analysis_id = require_str(analysis_id, "analysis_id")
114
+ data = await self._request(
115
+ "GET",
116
+ f"/v1/corpora/{corpus_id}/onboard/analysis/{analysis_id}",
117
+ request_options=request_options,
118
+ )
119
+ return self._maybe_validate(data, "DatasetAnalysisDetails")
120
+
121
+ # =========================================================================
122
+ # Gold Labels
123
+ # =========================================================================
124
+
125
+ async def upload_gold_labels(
126
+ self,
127
+ corpus_id: str,
128
+ labels: list[GoldLabelEntry],
129
+ *,
130
+ description: str | None = None,
131
+ request_options: RequestOptions | None = None,
132
+ ) -> GoldLabelsUploadResponse:
133
+ """Upload gold labels for a corpus.
134
+
135
+ Args:
136
+ corpus_id: ID of the corpus
137
+ labels: List of gold label entries (query-chunk pairs)
138
+ description: Optional description of the labels source
139
+
140
+ Returns:
141
+ GoldLabelsUploadResponse with resolution results
142
+ """
143
+ corpus_id = require_str(corpus_id, "corpus_id")
144
+ payload: dict[str, Any] = {"labels": labels}
145
+ if description is not None:
146
+ payload["description"] = description
147
+
148
+ data = await self._request(
149
+ "POST",
150
+ f"/v1/corpora/{corpus_id}/onboard:upload-labels",
151
+ json=payload,
152
+ request_options=request_options,
153
+ )
154
+ return self._maybe_validate(data, "GoldLabelsUploadResponse")
155
+
156
+ async def upload_gold_labels_file(
157
+ self,
158
+ corpus_id: str,
159
+ file_path: str | Path,
160
+ *,
161
+ description: str | None = None,
162
+ request_options: RequestOptions | None = None,
163
+ ) -> GoldLabelsUploadResponse:
164
+ """Upload gold labels from a JSONL file.
165
+
166
+ Args:
167
+ corpus_id: ID of the corpus
168
+ file_path: Path to JSONL file with gold labels
169
+ description: Optional description of the labels source
170
+
171
+ Returns:
172
+ GoldLabelsUploadResponse with resolution results
173
+ """
174
+ corpus_id = require_str(corpus_id, "corpus_id")
175
+ import json
176
+
177
+ labels: list[GoldLabelEntry] = []
178
+ with open(file_path, "r") as f:
179
+ for line in f:
180
+ if line.strip():
181
+ labels.append(json.loads(line))
182
+
183
+ return await self.upload_gold_labels(
184
+ corpus_id, labels, description=description, request_options=request_options
185
+ )
186
+
187
+ async def list_gold_labels(
188
+ self,
189
+ corpus_id: str,
190
+ *,
191
+ limit: int = 100,
192
+ offset: int = 0,
193
+ request_options: RequestOptions | None = None,
194
+ ) -> Page[dict[str, Any]]:
195
+ """List gold labels for a corpus.
196
+
197
+ Args:
198
+ corpus_id: ID of the corpus
199
+ limit: Maximum number of labels to return
200
+ offset: Offset for pagination
201
+
202
+ Returns:
203
+ A Page containing gold label entries with pagination metadata.
204
+ """
205
+ corpus_id = require_str(corpus_id, "corpus_id")
206
+ return await self._list_page(
207
+ "GET",
208
+ f"/v1/corpora/{corpus_id}/gold-labels",
209
+ items_key="labels",
210
+ limit=limit,
211
+ offset=offset,
212
+ )
213
+
214
+ def iter_gold_labels(
215
+ self,
216
+ corpus_id: str,
217
+ *,
218
+ limit: int = 100,
219
+ request_options: RequestOptions | None = None,
220
+ ) -> AsyncPager[dict[str, Any]]:
221
+ """Iterate over gold labels, automatically paginating."""
222
+ corpus_id = require_str(corpus_id, "corpus_id")
223
+ return self._paginate(
224
+ "GET",
225
+ f"/v1/corpora/{corpus_id}/gold-labels",
226
+ items_key="labels",
227
+ limit=limit,
228
+ )
229
+
230
+ # =========================================================================
231
+ # Synthetic Query Generation
232
+ # =========================================================================
233
+
234
+ async def generate_synthetic_queries(
235
+ self,
236
+ corpus_id: str,
237
+ analysis_id: str,
238
+ *,
239
+ sample_size: int = 0,
240
+ queries_per_chunk: int = 3,
241
+ use_document_context: bool = True,
242
+ eval_sample_size: int | None = None,
243
+ request_options: RequestOptions | None = None,
244
+ ) -> SyntheticQueryBatchResponse:
245
+ """Start synthetic query generation.
246
+
247
+ Args:
248
+ corpus_id: ID of the corpus
249
+ analysis_id: ID of the analysis run to use
250
+ sample_size: Chunks to sample (0 = automatic bounded sample)
251
+ queries_per_chunk: Queries to generate per chunk
252
+ use_document_context: Inject document summaries into prompts
253
+ eval_sample_size: Override eval sample size (None = use default)
254
+
255
+ Returns:
256
+ SyntheticQueryBatchResponse with batch_id and job_id
257
+ """
258
+ corpus_id = require_str(corpus_id, "corpus_id")
259
+ analysis_id = require_str(analysis_id, "analysis_id")
260
+ payload: dict[str, Any] = {
261
+ "analysis_id": analysis_id,
262
+ "sample_size": sample_size,
263
+ "queries_per_chunk": queries_per_chunk,
264
+ "use_document_context": use_document_context,
265
+ }
266
+ if eval_sample_size is not None:
267
+ payload["eval_sample_size"] = eval_sample_size
268
+
269
+ data = await self._request(
270
+ "POST",
271
+ f"/v1/corpora/{corpus_id}/synthetic-queries:generate",
272
+ json=payload,
273
+ request_options=request_options,
274
+ )
275
+ return self._maybe_validate(data, "SyntheticQueryBatchResponse")
276
+
277
+ async def list_synthetic_batches(
278
+ self,
279
+ corpus_id: str,
280
+ request_options: RequestOptions | None = None,
281
+ ) -> SyntheticQueryBatchListResponse:
282
+ """List synthetic query batches for a corpus.
283
+
284
+ Args:
285
+ corpus_id: ID of the corpus
286
+
287
+ Returns:
288
+ SyntheticQueryBatchListResponse with batches list
289
+ """
290
+ corpus_id = require_str(corpus_id, "corpus_id")
291
+ data = await self._request(
292
+ "GET",
293
+ f"/v1/corpora/{corpus_id}/synthetic-queries/batches",
294
+ request_options=request_options,
295
+ )
296
+ return self._maybe_validate(data, "SyntheticQueryBatchListResponse")
297
+
298
+ async def get_synthetic_batch(
299
+ self,
300
+ corpus_id: str,
301
+ batch_id: str,
302
+ request_options: RequestOptions | None = None,
303
+ ) -> SyntheticQueryBatchDetails:
304
+ """Get details of a synthetic query batch.
305
+
306
+ Args:
307
+ corpus_id: ID of the corpus
308
+ batch_id: ID of the batch
309
+
310
+ Returns:
311
+ SyntheticQueryBatchDetails with full batch info
312
+ """
313
+ corpus_id = require_str(corpus_id, "corpus_id")
314
+ batch_id = require_str(batch_id, "batch_id")
315
+ data = await self._request(
316
+ "GET",
317
+ f"/v1/corpora/{corpus_id}/synthetic-queries/batches/{batch_id}",
318
+ request_options=request_options,
319
+ )
320
+ return self._maybe_validate(data, "SyntheticQueryBatchDetails")
321
+
322
+ async def download_synthetic_queries(
323
+ self,
324
+ corpus_id: str,
325
+ batch_id: str,
326
+ output_path: str | Path,
327
+ request_options: RequestOptions | None = None,
328
+ ) -> str:
329
+ """Download synthetic queries from a batch.
330
+
331
+ Args:
332
+ corpus_id: ID of the corpus
333
+ batch_id: ID of the batch
334
+ output_path: Path to save downloaded queries
335
+
336
+ Returns:
337
+ Path to the downloaded file
338
+ """
339
+ corpus_id = require_str(corpus_id, "corpus_id")
340
+ batch_id = require_str(batch_id, "batch_id")
341
+ batch = await self.get_synthetic_batch(corpus_id, batch_id, request_options=request_options)
342
+
343
+ artifact_uri = (
344
+ batch.get("artifact_uri")
345
+ if isinstance(batch, dict)
346
+ else getattr(batch, "artifact_uri", None)
347
+ )
348
+ if not artifact_uri:
349
+ raise ValueError("Batch does not have an artifact URI")
350
+
351
+ return str(artifact_uri)
352
+
353
+ # =========================================================================
354
+ # Evaluation
355
+ # =========================================================================
356
+
357
+ async def evaluate_synthetic_queries(
358
+ self,
359
+ corpus_id: str,
360
+ batch_id: str,
361
+ *,
362
+ sample_size: int | None = None,
363
+ generate_report: bool = True,
364
+ report_formats: list[str] | None = None,
365
+ request_options: RequestOptions | None = None,
366
+ ) -> EvaluationResponse:
367
+ """Start evaluation of synthetic queries.
368
+
369
+ Args:
370
+ corpus_id: ID of the corpus
371
+ batch_id: ID of the synthetic query batch
372
+ sample_size: Sample for evaluation (None or 0 = automatic bounded sample)
373
+ generate_report: Generate HTML/JSON report
374
+ report_formats: Report formats to generate
375
+
376
+ Returns:
377
+ EvaluationResponse with eval_id and job_id
378
+ """
379
+ corpus_id = require_str(corpus_id, "corpus_id")
380
+ batch_id = require_str(batch_id, "batch_id")
381
+ payload: dict[str, Any] = {
382
+ "batch_id": batch_id,
383
+ "generate_report": generate_report,
384
+ }
385
+ if sample_size is not None:
386
+ payload["sample_size"] = sample_size
387
+ if report_formats is not None:
388
+ payload["report_formats"] = report_formats
389
+
390
+ data = await self._request(
391
+ "POST",
392
+ f"/v1/corpora/{corpus_id}/synthetic-queries:evaluate",
393
+ json=payload,
394
+ request_options=request_options,
395
+ )
396
+ return self._maybe_validate(data, "EvaluationResponse")
397
+
398
+ async def list_evaluations(
399
+ self,
400
+ corpus_id: str,
401
+ request_options: RequestOptions | None = None,
402
+ ) -> EvaluationListResponse:
403
+ """List evaluations for a corpus.
404
+
405
+ Args:
406
+ corpus_id: ID of the corpus
407
+
408
+ Returns:
409
+ EvaluationListResponse with evaluations list
410
+ """
411
+ corpus_id = require_str(corpus_id, "corpus_id")
412
+ data = await self._request(
413
+ "GET", f"/v1/corpora/{corpus_id}/evaluations", request_options=request_options
414
+ )
415
+ return self._maybe_validate(data, "EvaluationListResponse")
416
+
417
+ async def get_evaluation(
418
+ self,
419
+ corpus_id: str,
420
+ eval_id: str,
421
+ request_options: RequestOptions | None = None,
422
+ ) -> EvaluationDetails:
423
+ """Get details of an evaluation.
424
+
425
+ Args:
426
+ corpus_id: ID of the corpus
427
+ eval_id: ID of the evaluation
428
+
429
+ Returns:
430
+ EvaluationDetails with full evaluation results
431
+ """
432
+ corpus_id = require_str(corpus_id, "corpus_id")
433
+ eval_id = require_str(eval_id, "eval_id")
434
+ data = await self._request(
435
+ "GET", f"/v1/corpora/{corpus_id}/evaluations/{eval_id}", request_options=request_options
436
+ )
437
+ return self._maybe_validate(data, "EvaluationDetails")
438
+
439
+ async def get_evaluation_report(
440
+ self,
441
+ corpus_id: str,
442
+ eval_id: str,
443
+ *,
444
+ format: str = "json",
445
+ request_options: RequestOptions | None = None,
446
+ ) -> EvaluationReportResponse:
447
+ """Get evaluation report.
448
+
449
+ Args:
450
+ corpus_id: ID of the corpus
451
+ eval_id: ID of the evaluation
452
+ format: Report format ("json" or "html")
453
+
454
+ Returns:
455
+ EvaluationReportResponse with report URI and metrics
456
+ """
457
+ corpus_id = require_str(corpus_id, "corpus_id")
458
+ eval_id = require_str(eval_id, "eval_id")
459
+ data = await self._request(
460
+ "GET",
461
+ f"/v1/corpora/{corpus_id}/evaluations/{eval_id}/report",
462
+ params={"format": format},
463
+ request_options=request_options,
464
+ )
465
+ return self._maybe_validate(data, "EvaluationReportResponse")
466
+
467
+ # =========================================================================
468
+ # Document Summarization
469
+ # =========================================================================
470
+
471
+ async def summarize_documents(
472
+ self,
473
+ corpus_id: str,
474
+ *,
475
+ force_regenerate: bool = False,
476
+ request_options: RequestOptions | None = None,
477
+ ) -> SummarizationResponse:
478
+ """Start document summarization.
479
+
480
+ Args:
481
+ corpus_id: ID of the corpus
482
+ force_regenerate: Regenerate summaries for all documents
483
+
484
+ Returns:
485
+ SummarizationResponse with job_id and stats
486
+ """
487
+ corpus_id = require_str(corpus_id, "corpus_id")
488
+ payload: dict[str, Any] = {"force_regenerate": force_regenerate}
489
+ data = await self._request(
490
+ "POST",
491
+ f"/v1/corpora/{corpus_id}/documents:summarize",
492
+ json=payload,
493
+ request_options=request_options,
494
+ )
495
+ return self._maybe_validate(data, "SummarizationResponse")
496
+
497
+ async def get_summarization_status(
498
+ self,
499
+ corpus_id: str,
500
+ request_options: RequestOptions | None = None,
501
+ ) -> SummarizationStatusResponse:
502
+ """Get summarization status for a corpus.
503
+
504
+ Args:
505
+ corpus_id: ID of the corpus
506
+
507
+ Returns:
508
+ SummarizationStatusResponse with coverage stats
509
+ """
510
+ corpus_id = require_str(corpus_id, "corpus_id")
511
+ data = await self._request(
512
+ "GET", f"/v1/corpora/{corpus_id}/summaries/status", request_options=request_options
513
+ )
514
+ return self._maybe_validate(data, "SummarizationStatusResponse")
515
+
516
+ async def get_document_summary(
517
+ self,
518
+ corpus_id: str,
519
+ doc_id: str,
520
+ request_options: RequestOptions | None = None,
521
+ ) -> DocumentSummaryResponse:
522
+ """Get summary for a specific document.
523
+
524
+ Args:
525
+ corpus_id: ID of the corpus
526
+ doc_id: ID of the document
527
+
528
+ Returns:
529
+ DocumentSummaryResponse with summary and entities
530
+ """
531
+ corpus_id = require_str(corpus_id, "corpus_id")
532
+ doc_id = require_str(doc_id, "doc_id")
533
+ data = await self._request(
534
+ "GET",
535
+ f"/v1/corpora/{corpus_id}/documents/{doc_id}/summary",
536
+ request_options=request_options,
537
+ )
538
+ return self._maybe_validate(data, "DocumentSummaryResponse")
@@ -0,0 +1,37 @@
1
+ """Async organisation resource mixin for the Knowledge2 SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from sdk._request_options import RequestOptions
8
+ from sdk.async_resources._mixin_base import AsyncRequesterMixin
9
+ from sdk.types import OrgResponse
10
+
11
+
12
+ class AsyncOrgsMixin(AsyncRequesterMixin):
13
+ async def create_org(
14
+ self,
15
+ name: str,
16
+ contact_email: str | None = None,
17
+ request_options: RequestOptions | None = None,
18
+ ) -> OrgResponse:
19
+ """Create a new organisation.
20
+
21
+ Args:
22
+ name: Display name for the organisation.
23
+ contact_email: Optional contact email address for the org.
24
+
25
+ Returns:
26
+ The newly created organisation record.
27
+
28
+ Raises:
29
+ Knowledge2Error: If the API request fails.
30
+ """
31
+ payload: dict[str, Any] = {"name": name}
32
+ if contact_email is not None:
33
+ payload["contact_email"] = contact_email
34
+ data = await self._request(
35
+ "POST", "/v1/orgs", json=payload, request_options=request_options
36
+ )
37
+ return self._maybe_validate(data, "OrgResponse")