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
sdk/resources/feeds.py ADDED
@@ -0,0 +1,246 @@
1
+ """Feed resource mixin for the Knowledge2 SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from sdk._paging import Page
8
+ from sdk._preview import preview_resource
9
+ from sdk._request_options import RequestOptions
10
+ from sdk._validation import require_str
11
+ from sdk.resources._mixin_base import RequesterMixin
12
+
13
+
14
+ @preview_resource
15
+ class FeedsMixin(RequesterMixin):
16
+ def create_feed(
17
+ self,
18
+ *,
19
+ project_id: str,
20
+ name: str,
21
+ query: str = "",
22
+ persistent: bool = False,
23
+ target_corpus: dict[str, Any] | None = None,
24
+ reactive: bool = False,
25
+ agent_id: str | None = None,
26
+ schedule_interval: str | None = None,
27
+ schedule_hour: int | None = None,
28
+ start_from: str | None = None,
29
+ top_k: int | None = None,
30
+ metadata_filters: dict[str, Any] | None = None,
31
+ request_options: RequestOptions | None = None,
32
+ ) -> dict[str, Any]:
33
+ """Create a new knowledge feed.
34
+
35
+ Args:
36
+ project_id: ID of the project that will own the feed.
37
+ name: Human-readable name for the feed.
38
+ query: Query string for the feed (defaults to empty string).
39
+ persistent: Whether the feed persists results to storage.
40
+ target_corpus: Optional target corpus configuration dict. Pass
41
+ ``{"existing": "<corpus-id>"}`` to write into an existing corpus,
42
+ or ``{"create_new": True}`` to have the API create a new one.
43
+ reactive: Whether the feed triggers on new data automatically.
44
+ agent_id: Optional agent ID to associate with the feed.
45
+ schedule_interval: Optional cron-style schedule interval.
46
+ schedule_hour: UTC hour (0-23) for daily scheduled feeds. Only
47
+ valid when schedule_interval is ``'1d'``.
48
+ start_from: Optional ISO-8601 timestamp to start processing from.
49
+ top_k: Optional number of top results to return per run.
50
+ metadata_filters: Optional metadata filter dictionary.
51
+
52
+ Returns:
53
+ The newly created feed record.
54
+
55
+ Raises:
56
+ Knowledge2Error: If the API request fails.
57
+ """
58
+ project_id = require_str(project_id, "project_id")
59
+ payload: dict[str, Any] = {
60
+ "project_id": project_id,
61
+ "name": name,
62
+ "query": query,
63
+ "persistent": persistent,
64
+ "reactive": reactive,
65
+ }
66
+ if target_corpus is not None:
67
+ payload["target_corpus"] = target_corpus
68
+ if agent_id is not None:
69
+ payload["agent_id"] = agent_id
70
+ if schedule_interval is not None:
71
+ payload["schedule_interval"] = schedule_interval
72
+ if schedule_hour is not None:
73
+ payload["schedule_hour"] = schedule_hour
74
+ if start_from is not None:
75
+ payload["start_from"] = start_from
76
+ if top_k is not None:
77
+ payload["top_k"] = top_k
78
+ if metadata_filters is not None:
79
+ payload["metadata_filters"] = metadata_filters
80
+ data = self._request("POST", "/v1/feeds", json=payload, request_options=request_options)
81
+ return self._maybe_validate(data, "FeedResponse")
82
+
83
+ def list_feeds(
84
+ self,
85
+ *,
86
+ project_id: str | None = None,
87
+ limit: int = 100,
88
+ offset: int = 0,
89
+ request_options: RequestOptions | None = None,
90
+ ) -> Page[dict[str, Any]]:
91
+ """List feeds accessible to the current credentials.
92
+
93
+ Args:
94
+ project_id: Optional project ID to filter feeds by.
95
+ limit: Maximum number of feeds to return per page.
96
+ offset: Number of feeds to skip for pagination.
97
+
98
+ Returns:
99
+ A Page containing feed records with pagination metadata.
100
+
101
+ Raises:
102
+ Knowledge2Error: If the API request fails.
103
+ """
104
+ params: dict[str, Any] = {}
105
+ if project_id is not None:
106
+ params["project_id"] = project_id
107
+ return self._list_page(
108
+ "GET",
109
+ "/v1/feeds",
110
+ items_key="feeds",
111
+ params=params or None,
112
+ limit=limit,
113
+ offset=offset,
114
+ )
115
+
116
+ def get_feed(
117
+ self,
118
+ feed_id: str,
119
+ request_options: RequestOptions | None = None,
120
+ ) -> dict[str, Any]:
121
+ """Retrieve a single feed by ID.
122
+
123
+ Args:
124
+ feed_id: Unique identifier of the feed.
125
+
126
+ Returns:
127
+ The feed record.
128
+
129
+ Raises:
130
+ NotFoundError: If the feed does not exist.
131
+ Knowledge2Error: If the API request fails.
132
+ """
133
+ feed_id = require_str(feed_id, "feed_id")
134
+ data = self._request("GET", f"/v1/feeds/{feed_id}", request_options=request_options)
135
+ return self._maybe_validate(data, "FeedResponse")
136
+
137
+ def update_feed(
138
+ self,
139
+ feed_id: str,
140
+ *,
141
+ name: str | None = None,
142
+ query: str | None = None,
143
+ top_k: int | None = None,
144
+ metadata_filters: dict[str, Any] | None = None,
145
+ reactive: bool | None = None,
146
+ schedule_interval: str | None = None,
147
+ schedule_hour: int | None = None,
148
+ request_options: RequestOptions | None = None,
149
+ ) -> dict[str, Any]:
150
+ """Update feed settings.
151
+
152
+ Args:
153
+ feed_id: ID of the feed to update.
154
+ name: New name for the feed.
155
+ query: New query string for the feed.
156
+ top_k: New number of top results to return per run.
157
+ metadata_filters: New metadata filter dictionary.
158
+ reactive: Whether the feed triggers on new data automatically.
159
+ schedule_interval: New cron-style schedule interval.
160
+ schedule_hour: UTC hour (0-23) for daily scheduled feeds. Only
161
+ valid when schedule_interval is ``'1d'``.
162
+
163
+ Returns:
164
+ Updated feed record.
165
+
166
+ Raises:
167
+ NotFoundError: If the feed does not exist.
168
+ Knowledge2Error: If the API request fails.
169
+ """
170
+ feed_id = require_str(feed_id, "feed_id")
171
+ payload: dict[str, Any] = {}
172
+ if name is not None:
173
+ payload["name"] = name
174
+ if query is not None:
175
+ payload["query"] = query
176
+ if top_k is not None:
177
+ payload["top_k"] = top_k
178
+ if metadata_filters is not None:
179
+ payload["metadata_filters"] = metadata_filters
180
+ if reactive is not None:
181
+ payload["reactive"] = reactive
182
+ if schedule_interval is not None:
183
+ payload["schedule_interval"] = schedule_interval
184
+ if schedule_hour is not None:
185
+ payload["schedule_hour"] = schedule_hour
186
+ data = self._request(
187
+ "PATCH", f"/v1/feeds/{feed_id}", json=payload, request_options=request_options
188
+ )
189
+ return self._maybe_validate(data, "FeedResponse")
190
+
191
+ def delete_feed(
192
+ self,
193
+ feed_id: str,
194
+ request_options: RequestOptions | None = None,
195
+ ) -> None:
196
+ """Delete a feed.
197
+
198
+ Args:
199
+ feed_id: Unique identifier of the feed to delete.
200
+
201
+ Returns:
202
+ None (the API returns 204 No Content on success).
203
+
204
+ Raises:
205
+ NotFoundError: If the feed does not exist.
206
+ Knowledge2Error: If the API request fails.
207
+ """
208
+ feed_id = require_str(feed_id, "feed_id")
209
+ self._request("DELETE", f"/v1/feeds/{feed_id}", request_options=request_options)
210
+
211
+ def run_feed(
212
+ self,
213
+ feed_id: str,
214
+ *,
215
+ return_results: bool | None = None,
216
+ dry_run: bool = False,
217
+ request_options: RequestOptions | None = None,
218
+ ) -> dict[str, Any]:
219
+ """Trigger a feed run.
220
+
221
+ Args:
222
+ feed_id: Unique identifier of the feed to run.
223
+ return_results: Whether to include results in the response.
224
+ When ``None`` (default), the API applies a smart default
225
+ based on feed type (``True`` for standard, ``False`` for
226
+ persistent feeds).
227
+ dry_run: If ``True``, simulate the run without persisting results.
228
+
229
+ Returns:
230
+ A dict containing the feed run results and metadata.
231
+
232
+ Raises:
233
+ NotFoundError: If the feed does not exist.
234
+ Knowledge2Error: If the API request fails.
235
+ """
236
+ feed_id = require_str(feed_id, "feed_id")
237
+ params: dict[str, Any] = {"dry_run": dry_run}
238
+ if return_results is not None:
239
+ params["return_results"] = return_results
240
+ data = self._request(
241
+ "POST",
242
+ f"/v1/feeds/{feed_id}/run",
243
+ params=params,
244
+ request_options=request_options,
245
+ )
246
+ return self._maybe_validate(data, "FeedRunResponse")
@@ -0,0 +1,210 @@
1
+ """Index management resource mixin for the Knowledge2 SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import cast
6
+
7
+ from sdk._request_options import RequestOptions
8
+ from sdk._validation import require_str
9
+ from sdk.resources._mixin_base import RequesterMixin
10
+ from sdk.types import IndexBuildResponse, IndexCompactResponse, IndexStatusResponse
11
+
12
+
13
+ class IndexesMixin(RequesterMixin):
14
+ def sync_indexes(
15
+ self,
16
+ corpus_id: str,
17
+ *,
18
+ wait: bool = True,
19
+ poll_s: int = 5,
20
+ idempotency_key: str | None = None,
21
+ ) -> IndexBuildResponse:
22
+ """Synchronize the default core retrieval indexes for a corpus.
23
+
24
+ This is the simplest public indexing entrypoint. It queues the platform's
25
+ synchronized core retrieval snapshot using the default automatic
26
+ incremental behavior and does not request GraphRAG artifacts.
27
+ """
28
+ corpus_id = require_str(corpus_id, "corpus_id")
29
+ headers = self._idempotency_headers(idempotency_key)
30
+ data = self._request(
31
+ "POST",
32
+ f"/v1/corpora/{corpus_id}/indexes:sync",
33
+ headers=headers,
34
+ )
35
+ if wait:
36
+ job_id = data.get("job_id")
37
+ if job_id:
38
+ self._wait_for_job(job_id, poll_s=poll_s)
39
+ return cast("IndexBuildResponse", data)
40
+
41
+ def build_indexes(
42
+ self,
43
+ corpus_id: str,
44
+ dense: bool = True,
45
+ sparse: bool = True,
46
+ sparse_metadata: bool | None = None,
47
+ mode: str = "incremental",
48
+ idempotency_key: str | None = None,
49
+ *,
50
+ sparse_metadata_required: bool = False,
51
+ wait: bool = True,
52
+ poll_s: int = 5,
53
+ request_options: RequestOptions | None = None,
54
+ ) -> IndexBuildResponse:
55
+ """Advanced index build entrypoint for a corpus.
56
+
57
+ Args:
58
+ corpus_id: The corpus to build indexes for.
59
+ dense: Whether to build a dense (vector) index.
60
+ sparse: Whether to build a sparse (BM25) index.
61
+ sparse_metadata: Whether to build the metadata sparse (BM25)
62
+ index. ``None`` uses the server default.
63
+ sparse_metadata_required: Whether metadata sparse is required for
64
+ successful completion. When ``True``, a metadata-sparse skip is
65
+ treated as a build failure.
66
+ mode: Build mode — ``"incremental"`` is the default synchronized
67
+ snapshot path, while ``"full"`` forces a rebuild from scratch.
68
+ idempotency_key: Optional idempotency key to prevent
69
+ duplicate builds.
70
+ wait: If ``True`` (default), block until the index build
71
+ job completes.
72
+ poll_s: Polling interval in seconds when *wait* is ``True``.
73
+
74
+ For most callers, :meth:`sync_indexes` is the simpler default.
75
+
76
+ Returns:
77
+ The index build response, including the background job ID.
78
+
79
+ Raises:
80
+ NotFoundError: If the corpus does not exist.
81
+ ConflictError: If a duplicate idempotency key is detected.
82
+ Knowledge2Error: If the API request fails.
83
+ """
84
+ corpus_id = require_str(corpus_id, "corpus_id")
85
+ payload = {"dense": dense, "sparse": sparse, "mode": mode}
86
+ if sparse_metadata is not None:
87
+ payload["sparse_metadata"] = bool(sparse_metadata)
88
+ if sparse_metadata_required:
89
+ payload["sparse_metadata_required"] = True
90
+ headers = self._idempotency_headers(idempotency_key)
91
+ data = self._request(
92
+ "POST",
93
+ f"/v1/corpora/{corpus_id}/indexes:build",
94
+ json=payload,
95
+ headers=headers,
96
+ request_options=request_options,
97
+ )
98
+ if wait:
99
+ job_id = data.get("job_id")
100
+ if job_id:
101
+ self._wait_for_job(job_id, poll_s=poll_s)
102
+ return self._maybe_validate(data, "IndexBuildResponse")
103
+
104
+ def index_status(
105
+ self,
106
+ corpus_id: str,
107
+ request_options: RequestOptions | None = None,
108
+ ) -> IndexStatusResponse:
109
+ """Retrieve the current index status for a corpus.
110
+
111
+ Args:
112
+ corpus_id: The corpus to check.
113
+
114
+ Returns:
115
+ Index status including a top-level retrieval summary plus per-family details.
116
+
117
+ Raises:
118
+ NotFoundError: If the corpus does not exist.
119
+ Knowledge2Error: If the API request fails.
120
+ """
121
+ corpus_id = require_str(corpus_id, "corpus_id")
122
+ data = self._request(
123
+ "GET", f"/v1/corpora/{corpus_id}/indexes/status", request_options=request_options
124
+ )
125
+ return self._maybe_validate(data, "IndexStatusResponse")
126
+
127
+ def rebuild_indexes(
128
+ self,
129
+ corpus_id: str,
130
+ dense: bool = True,
131
+ sparse: bool = True,
132
+ sparse_metadata: bool | None = None,
133
+ idempotency_key: str | None = None,
134
+ *,
135
+ sparse_metadata_required: bool = False,
136
+ request_options: RequestOptions | None = None,
137
+ ) -> IndexBuildResponse:
138
+ """Force a full rebuild of indexes for a corpus.
139
+
140
+ Unlike :meth:`build_indexes`, this always performs a full rebuild
141
+ regardless of existing index state.
142
+
143
+ Args:
144
+ corpus_id: The corpus to rebuild indexes for.
145
+ dense: Whether to rebuild the dense (vector) index.
146
+ sparse: Whether to rebuild the sparse (BM25) index.
147
+ sparse_metadata: Whether to rebuild the metadata sparse
148
+ (BM25) index. ``None`` uses the server default.
149
+ sparse_metadata_required: Whether metadata sparse is required for
150
+ successful completion. When ``True``, a metadata-sparse skip is
151
+ treated as a build failure.
152
+ idempotency_key: Optional idempotency key to prevent
153
+ duplicate rebuilds.
154
+
155
+ Returns:
156
+ The index build response, including the background job ID.
157
+
158
+ Raises:
159
+ NotFoundError: If the corpus does not exist.
160
+ ConflictError: If a duplicate idempotency key is detected.
161
+ Knowledge2Error: If the API request fails.
162
+ """
163
+ corpus_id = require_str(corpus_id, "corpus_id")
164
+ payload = {"dense": dense, "sparse": sparse, "mode": "full"}
165
+ if sparse_metadata is not None:
166
+ payload["sparse_metadata"] = bool(sparse_metadata)
167
+ if sparse_metadata_required:
168
+ payload["sparse_metadata_required"] = True
169
+ headers = self._idempotency_headers(idempotency_key)
170
+ data = self._request(
171
+ "POST",
172
+ f"/v1/corpora/{corpus_id}/indexes:rebuild",
173
+ json=payload,
174
+ headers=headers,
175
+ request_options=request_options,
176
+ )
177
+ return self._maybe_validate(data, "IndexBuildResponse")
178
+
179
+ def compact_indexes(
180
+ self,
181
+ corpus_id: str,
182
+ *,
183
+ dense: bool = True,
184
+ sparse: bool = True,
185
+ keep: int = 1,
186
+ request_options: RequestOptions | None = None,
187
+ ) -> IndexCompactResponse:
188
+ """Compact index artifacts for a corpus, removing old generations.
189
+
190
+ Args:
191
+ corpus_id: The corpus whose indexes to compact.
192
+ dense: Whether to compact dense index artifacts.
193
+ sparse: Whether to compact sparse index artifacts.
194
+ keep: Number of recent index generations to retain.
195
+
196
+ Returns:
197
+ A summary of compacted artifacts.
198
+
199
+ Raises:
200
+ NotFoundError: If the corpus does not exist.
201
+ Knowledge2Error: If the API request fails.
202
+ """
203
+ corpus_id = require_str(corpus_id, "corpus_id")
204
+ data = self._request(
205
+ "POST",
206
+ f"/v1/corpora/{corpus_id}/indexes:compact",
207
+ params={"dense": dense, "sparse": sparse, "keep": keep},
208
+ request_options=request_options,
209
+ )
210
+ return self._maybe_validate(data, "IndexCompactResponse")
sdk/resources/jobs.py ADDED
@@ -0,0 +1,164 @@
1
+ """Job management resource mixin for the Knowledge2 SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from sdk._paging import Page, SyncPager
8
+ from sdk._request_options import RequestOptions
9
+ from sdk._validation import require_str
10
+ from sdk.resources._mixin_base import RequesterMixin
11
+ from sdk.types import JobResponse, JobStatusResponse, ReconcileJobsResponse
12
+
13
+
14
+ class JobsMixin(RequesterMixin):
15
+ def get_job(
16
+ self,
17
+ job_id: str,
18
+ request_options: RequestOptions | None = None,
19
+ ) -> JobResponse:
20
+ """Retrieve details of a single job.
21
+
22
+ Args:
23
+ job_id: Unique identifier of the job.
24
+
25
+ Returns:
26
+ The job record including type, status, and metadata.
27
+
28
+ Raises:
29
+ NotFoundError: If the job does not exist.
30
+ Knowledge2Error: If the API request fails.
31
+ """
32
+ job_id = require_str(job_id, "job_id")
33
+ data = self._request("GET", f"/v1/jobs/{job_id}", request_options=request_options)
34
+ return self._maybe_validate(data, "JobResponse")
35
+
36
+ def list_jobs(
37
+ self,
38
+ *,
39
+ corpus_id: str | None = None,
40
+ job_type: str | None = None,
41
+ status: str | None = None,
42
+ limit: int = 100,
43
+ offset: int = 0,
44
+ request_options: RequestOptions | None = None,
45
+ ) -> Page[dict[str, Any]]:
46
+ """List jobs with optional filters.
47
+
48
+ Args:
49
+ corpus_id: Filter to jobs belonging to this corpus.
50
+ job_type: Filter by job type (e.g. ``"ingest"``, ``"index"``,
51
+ ``"train"``).
52
+ status: Filter by job status (e.g. ``"pending"``,
53
+ ``"running"``, ``"completed"``, ``"failed"``).
54
+ limit: Maximum number of jobs to return per page.
55
+ offset: Number of jobs to skip for pagination.
56
+
57
+ Returns:
58
+ A Page containing job records with pagination metadata.
59
+
60
+ Raises:
61
+ Knowledge2Error: If the API request fails.
62
+ """
63
+ params: dict[str, Any] = {}
64
+ if corpus_id:
65
+ params["corpus_id"] = corpus_id
66
+ if job_type:
67
+ params["job_type"] = job_type
68
+ if status:
69
+ params["status"] = status
70
+ return self._list_page(
71
+ "GET", "/v1/jobs", items_key="jobs", params=params or None, limit=limit, offset=offset
72
+ )
73
+
74
+ def iter_jobs(
75
+ self,
76
+ *,
77
+ corpus_id: str | None = None,
78
+ job_type: str | None = None,
79
+ status: str | None = None,
80
+ limit: int = 100,
81
+ request_options: RequestOptions | None = None,
82
+ ) -> SyncPager[dict[str, Any]]:
83
+ """Iterate over jobs, automatically paginating.
84
+
85
+ Args:
86
+ corpus_id: Filter to jobs belonging to this corpus.
87
+ job_type: Filter by job type.
88
+ status: Filter by job status.
89
+ limit: Page size used for each underlying API request.
90
+
91
+ Yields:
92
+ Individual job dicts.
93
+
94
+ Raises:
95
+ Knowledge2Error: If any underlying API request fails.
96
+ """
97
+ params: dict[str, Any] = {}
98
+ if corpus_id:
99
+ params["corpus_id"] = corpus_id
100
+ if job_type:
101
+ params["job_type"] = job_type
102
+ if status:
103
+ params["status"] = status
104
+ return self._paginate(
105
+ "GET", "/v1/jobs", items_key="jobs", params=params or None, limit=limit
106
+ )
107
+
108
+ def cancel_job(
109
+ self,
110
+ job_id: str,
111
+ request_options: RequestOptions | None = None,
112
+ ) -> JobStatusResponse:
113
+ """Cancel a running or pending job.
114
+
115
+ Args:
116
+ job_id: Unique identifier of the job to cancel.
117
+
118
+ Returns:
119
+ The updated job status after cancellation.
120
+
121
+ Raises:
122
+ NotFoundError: If the job does not exist.
123
+ Knowledge2Error: If the API request fails.
124
+ """
125
+ job_id = require_str(job_id, "job_id")
126
+ data = self._request("POST", f"/v1/jobs/{job_id}:cancel", request_options=request_options)
127
+ return self._maybe_validate(data, "JobStatusResponse")
128
+
129
+ def retry_job(
130
+ self,
131
+ job_id: str,
132
+ request_options: RequestOptions | None = None,
133
+ ) -> JobStatusResponse:
134
+ """Retry a failed job.
135
+
136
+ Args:
137
+ job_id: Unique identifier of the job to retry.
138
+
139
+ Returns:
140
+ The updated job status after scheduling the retry.
141
+
142
+ Raises:
143
+ NotFoundError: If the job does not exist.
144
+ Knowledge2Error: If the API request fails.
145
+ """
146
+ job_id = require_str(job_id, "job_id")
147
+ data = self._request("POST", f"/v1/jobs/{job_id}:retry", request_options=request_options)
148
+ return self._maybe_validate(data, "JobStatusResponse")
149
+
150
+ def reconcile_jobs(
151
+ self,
152
+ request_options: RequestOptions | None = None,
153
+ ) -> ReconcileJobsResponse:
154
+ """Reconcile stale or stuck jobs across the platform.
155
+
156
+ Returns:
157
+ A summary of reconciled jobs (e.g. counts of requeued or
158
+ cancelled jobs).
159
+
160
+ Raises:
161
+ Knowledge2Error: If the API request fails.
162
+ """
163
+ data = self._request("POST", "/v1/jobs:reconcile", request_options=request_options)
164
+ return self._maybe_validate(data, "ReconcileJobsResponse")
@@ -0,0 +1,53 @@
1
+ """Metadata discovery 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._validation import require_str
9
+ from sdk.resources._mixin_base import RequesterMixin
10
+
11
+
12
+ class MetadataMixin(RequesterMixin):
13
+ """Metadata discovery operations."""
14
+
15
+ def discover_metadata(
16
+ self,
17
+ corpus_id: str,
18
+ *,
19
+ refresh: bool = False,
20
+ request_options: RequestOptions | None = None,
21
+ ) -> dict[str, Any]:
22
+ """Discover metadata fields available in a corpus.
23
+
24
+ Returns distinct metadata keys with inferred types, value
25
+ distributions, and basic statistics.
26
+
27
+ Args:
28
+ corpus_id: Corpus ID to discover metadata for.
29
+ refresh: Force cache refresh (bypass cached results).
30
+
31
+ Returns:
32
+ Discovery response with fields, types, and stats.
33
+
34
+ Raises:
35
+ NotFoundError: If the corpus does not exist.
36
+ Knowledge2Error: If the API request fails.
37
+
38
+ Example:
39
+ >>> info = client.discover_metadata("corpus-123")
40
+ >>> for field in info["fields"]:
41
+ ... print(field["key"], field["type"], field["count"])
42
+ """
43
+ corpus_id = require_str(corpus_id, "corpus_id")
44
+ params: dict[str, Any] = {}
45
+ if refresh:
46
+ params["refresh"] = "true"
47
+ data = self._request(
48
+ "GET",
49
+ f"/v1/corpora/{corpus_id}/metadata/discover",
50
+ params=params,
51
+ request_options=request_options,
52
+ )
53
+ return data