knowledge2 0.5.0__tar.gz → 0.7.0__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.
- {knowledge2-0.5.0 → knowledge2-0.7.0}/CHANGELOG.md +67 -2
- {knowledge2-0.5.0/knowledge2.egg-info → knowledge2-0.7.0}/PKG-INFO +115 -14
- knowledge2-0.7.0/README.md +289 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_async_base.py +48 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_base.py +48 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_validation_response.py +2 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_version.py +1 -1
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_client.py +10 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/agents.py +44 -3
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/documents.py +354 -9
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/feeds.py +138 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/indexes.py +11 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/client.py +10 -3
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/batch_operations.py +4 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/document_upload.py +3 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/e2e_lifecycle.py +6 -32
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/retrieval_quickstart.py +2 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/__init__.py +6 -6
- {knowledge2-0.5.0 → knowledge2-0.7.0/knowledge2.egg-info}/PKG-INFO +115 -14
- {knowledge2-0.5.0 → knowledge2-0.7.0}/knowledge2.egg-info/requires.txt +2 -1
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/__init__.py +4 -6
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/_registry.py +8 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/agents.py +4 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/documents.py +26 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/feeds.py +31 -3
- {knowledge2-0.5.0 → knowledge2-0.7.0}/namespaces.py +25 -1
- {knowledge2-0.5.0 → knowledge2-0.7.0}/pyproject.toml +4 -3
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/agents.py +44 -3
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/documents.py +356 -9
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/feeds.py +193 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/indexes.py +11 -2
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/__init__.py +10 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/agents.py +10 -1
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/documents.py +26 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/feeds.py +27 -0
- knowledge2-0.5.0/README.md +0 -189
- {knowledge2-0.5.0 → knowledge2-0.7.0}/.github/workflows/pypi-release.yml +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/MANIFEST.in +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/__init__.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_async_paging.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_logging.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_paging.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_preview.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_raw_response.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_request_options.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_transport.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/_validation.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/__init__.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/_mixin_base.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/a2a.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/audit.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/auth.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/console.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/corpora.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/generation_models.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/jobs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/metadata.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/onboarding.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/orgs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/pipelines.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/projects.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/search.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/async_resources/usage.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/config.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/errors.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/auth_factory.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/error_handling.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/pagination.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/quickstart.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/request_options.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/examples/search.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/_client.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/langchain/__init__.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/langchain/retriever.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/langchain/tools.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/llamaindex/__init__.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/llamaindex/filters.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/llamaindex/retriever.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/llamaindex/tools.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/integrations/llamaindex/vector_store.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/knowledge2.egg-info/SOURCES.txt +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/knowledge2.egg-info/dependency_links.txt +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/knowledge2.egg-info/top_level.txt +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/_base.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/a2a.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/audit.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/auth.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/chunks.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/common.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/console.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/corpora.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/embeddings.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/feedback.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/generation_models.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/indexes.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/jobs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/onboarding.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/orgs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/pipelines.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/projects.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/search.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/models/usage.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/py.typed +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/__init__.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/_mixin_base.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/a2a.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/audit.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/auth.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/console.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/corpora.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/generation_models.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/jobs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/metadata.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/onboarding.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/orgs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/pipeline_builder.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/pipelines.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/projects.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/search.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/resources/usage.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/setup.cfg +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/a2a.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/audit.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/auth.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/chunks.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/common.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/console.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/corpora.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/embeddings.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/feedback.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/generation_models.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/indexes.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/jobs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/onboarding.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/orgs.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/pipelines.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/projects.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/search.py +0 -0
- {knowledge2-0.5.0 → knowledge2-0.7.0}/types/usage.py +0 -0
|
@@ -1,10 +1,75 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to the
|
|
3
|
+
All notable changes to the Knowledge² Python SDK will be documented in this file.
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-04-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Feed draft lifecycle on both `Knowledge2` and `AsyncKnowledge2`:
|
|
13
|
+
`create_feed_draft`, `get_feed_draft`, `activate_feed_draft`,
|
|
14
|
+
`discard_feed_draft`. Activation returns the updated **parent** feed;
|
|
15
|
+
the draft is deleted server-side.
|
|
16
|
+
- `list_feed_subscriptions(feed_id)` — read-only view of the subscriptions
|
|
17
|
+
embedded on the feed record. Handles validated and raw-response modes,
|
|
18
|
+
passing `with_raw_response` errors through unchanged and re-wrapping
|
|
19
|
+
successful responses so status/headers survive.
|
|
20
|
+
- Feed feedback helpers: `submit_feed_feedback(feed_id, *, rating,
|
|
21
|
+
chunk_id, feed_run_id)` and `get_feed_feedback_stats(feed_id, *,
|
|
22
|
+
feed_run_id=None)`. `rating` is validated client-side to be `0` or `1`.
|
|
23
|
+
- New response types `FeedFeedbackSubmitResponse` and
|
|
24
|
+
`FeedFeedbackStatsResponse` exported from `sdk.types`, with matching
|
|
25
|
+
strict Pydantic models registered for `validate_responses=True`.
|
|
26
|
+
- `FeedSubscriptionResponse` (and its Pydantic model) now include
|
|
27
|
+
`match_spec` and `match_spec_description` — the routing metadata
|
|
28
|
+
returned by the feed-side subscription schema for `explicit` and
|
|
29
|
+
`nl_semantic` subscriptions.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- `FeedResponseModel.subscriptions` now always validates to a list
|
|
34
|
+
(previously `list | None = None`). The server contract guarantees a
|
|
35
|
+
list — empty by default — so validated clients can iterate without a
|
|
36
|
+
None check. A legacy response containing `null` for `subscriptions`
|
|
37
|
+
will now raise `ValidationError` instead of producing a `None` field.
|
|
38
|
+
- `FeedSubscriptionResponseModel.agent_id` and `role` are now required
|
|
39
|
+
(previously `str | None = None`) to match the server schema and catch
|
|
40
|
+
malformed subscription payloads in validated clients.
|
|
41
|
+
|
|
42
|
+
## [0.6.1] - 2026-04-17
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- `upload_documents_batch_and_wait(...)` as the canonical blocking helper for raw-text batch ingestion
|
|
47
|
+
- `wait_for_document_batch(...)` for callers that intentionally enqueue first and resolve the batch later
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- base installs now include `pydantic`, so `pip install knowledge2` is import-clean and supports validated response models without an extra
|
|
52
|
+
- `knowledge2[pydantic]` remains accepted as a compatibility alias for older install commands
|
|
53
|
+
- batch wait helpers now enforce a single end-to-end timeout budget and support `with_raw_response` correctly
|
|
54
|
+
- `get_document_batch(...)` now surfaces stable `doc_ids` and live counters once a batch is visible, instead of waiting for terminal aggregation
|
|
55
|
+
- refreshed the primary onboarding docs and examples to prefer the blocking batch helper and the released public contract
|
|
56
|
+
- clarified early auth and quota guidance for the supported retrieval workflow
|
|
57
|
+
|
|
58
|
+
## [0.6.0] - 2026-04-16
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- `upload_documents_batch(..., wait=False)` returns an enqueue handle with `job_id`, `batch_id`, and `count`
|
|
63
|
+
- `get_document_batch(...)` is the supported batch-status lookup for raw-text uploads, including final `doc_ids` and per-item errors
|
|
64
|
+
- refreshed public SDK release notes to align with the current batch-upload surface
|
|
65
|
+
|
|
66
|
+
## [0.5.0] - 2026-04-13
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- aligned the published Python SDK support matrix with the public customer-facing surface
|
|
71
|
+
- refreshed README guidance, examples, and optimize-related test coverage for the current SDK capabilities
|
|
72
|
+
|
|
8
73
|
## [0.4.1] - 2026-04-03
|
|
9
74
|
|
|
10
75
|
### Changed
|
|
@@ -43,7 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
43
108
|
- **`K2Config` frozen config** (#720): Pydantic-settings based configuration from environment variables (`K2_*` prefix), JSON/YAML files, or named profiles.
|
|
44
109
|
- **Sub-client namespaces** (#721): `client.documents.*`, `client.corpora.*`, `client.search_ns.*`, etc. Lightweight cached proxies — flat methods still work.
|
|
45
110
|
- **Parallel uploads** (#722): `upload_documents_parallel()` using `ThreadPoolExecutor` for concurrent document ingestion.
|
|
46
|
-
- **Pydantic response validation** (#723): Optional `validate_responses=True` or `Knowledge2Validated` class.
|
|
111
|
+
- **Pydantic response validation** (#723): Optional `validate_responses=True` or `Knowledge2Validated` class. Base installs now include the Pydantic model dependency.
|
|
47
112
|
- **`AsyncKnowledge2` client** (#724): Native async client using `httpx.AsyncClient`. `AsyncPager[T]` for async pagination. Factory methods: `create()`, `from_env()`, `from_file()`, `from_profile()`.
|
|
48
113
|
- **`RequestOptions`** (#732): Per-call overrides for timeout, retries, and passthrough headers. `request_options` parameter on all public methods.
|
|
49
114
|
- **`with_raw_response`** (#733): `client.with_raw_response.<method>(...)` returns `RawResponse[T]` with `status_code`, `headers`, and `parsed` body. Thread-safe (sync) and task-safe (async).
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: knowledge2
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Python SDK for the
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Python SDK for the Knowledge² retrieval platform
|
|
5
5
|
Author-email: Knowledge2 <contact@knowledge2.ai>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://knowledge2.ai
|
|
@@ -19,20 +19,21 @@ Classifier: Typing :: Typed
|
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: httpx>=0.27
|
|
22
|
+
Requires-Dist: pydantic<3,>=2
|
|
22
23
|
Provides-Extra: config
|
|
23
24
|
Requires-Dist: pydantic-settings>=2.0; extra == "config"
|
|
24
25
|
Provides-Extra: pydantic
|
|
25
|
-
Requires-Dist: pydantic
|
|
26
|
+
Requires-Dist: pydantic<3,>=2; extra == "pydantic"
|
|
26
27
|
Provides-Extra: yaml
|
|
27
28
|
Requires-Dist: pyyaml>=6.0; extra == "yaml"
|
|
28
29
|
|
|
29
|
-
#
|
|
30
|
+
# Knowledge² Python SDK
|
|
30
31
|
|
|
31
32
|
[](https://pypi.org/project/knowledge2/)
|
|
32
33
|
[](https://www.python.org/downloads/)
|
|
33
34
|
[](https://opensource.org/licenses/MIT)
|
|
34
35
|
|
|
35
|
-
Official Python client for the
|
|
36
|
+
Official Python client for the Knowledge² retrieval platform. The supported customer journey is:
|
|
36
37
|
|
|
37
38
|
`create corpus -> ingest documents -> build indexes -> search -> optimize retrieval`
|
|
38
39
|
|
|
@@ -43,7 +44,6 @@ From PyPI:
|
|
|
43
44
|
```bash
|
|
44
45
|
pip install knowledge2
|
|
45
46
|
pip install "knowledge2[config]"
|
|
46
|
-
pip install "knowledge2[pydantic]"
|
|
47
47
|
pip install "knowledge2[yaml]"
|
|
48
48
|
```
|
|
49
49
|
|
|
@@ -52,10 +52,21 @@ From source:
|
|
|
52
52
|
```bash
|
|
53
53
|
pip install -e .
|
|
54
54
|
pip install -e ".[config]"
|
|
55
|
-
pip install -e ".[pydantic]"
|
|
56
55
|
pip install -e ".[yaml]"
|
|
57
56
|
```
|
|
58
57
|
|
|
58
|
+
`pip install knowledge2` now includes the typed response model dependency
|
|
59
|
+
(`pydantic`) out of the box. Install `knowledge2[config]` only if you want
|
|
60
|
+
`K2Config` environment/file loading via `pydantic-settings`.
|
|
61
|
+
|
|
62
|
+
## Before You Start
|
|
63
|
+
|
|
64
|
+
- Use a normal org-scoped API key for the standard retrieval workflow:
|
|
65
|
+
projects, corpora, documents, indexes, search, and optimize.
|
|
66
|
+
- `optimize_indexes()` and some enterprise/preview surfaces can return
|
|
67
|
+
feature-flag or quota errors (`403`, `409`, `429`) even when the payload is
|
|
68
|
+
correct. Check environment entitlements early.
|
|
69
|
+
|
|
59
70
|
## Surface Categories
|
|
60
71
|
|
|
61
72
|
| Category | Surface |
|
|
@@ -75,12 +86,12 @@ client = Knowledge2(api_key="k2_...")
|
|
|
75
86
|
project = client.create_project("My Project")
|
|
76
87
|
corpus = client.create_corpus(project["id"], "My Corpus")
|
|
77
88
|
|
|
78
|
-
client.
|
|
89
|
+
batch = client.upload_documents_batch_and_wait(
|
|
79
90
|
corpus["id"],
|
|
80
91
|
[
|
|
81
92
|
{
|
|
82
93
|
"source_uri": "doc://overview",
|
|
83
|
-
"raw_text": "
|
|
94
|
+
"raw_text": "Knowledge² builds dense and sparse indexes for hybrid retrieval.",
|
|
84
95
|
"metadata": {"topic": "overview"},
|
|
85
96
|
},
|
|
86
97
|
{
|
|
@@ -89,7 +100,6 @@ client.upload_documents_batch(
|
|
|
89
100
|
"metadata": {"topic": "search"},
|
|
90
101
|
},
|
|
91
102
|
],
|
|
92
|
-
wait=True,
|
|
93
103
|
auto_index=False,
|
|
94
104
|
)
|
|
95
105
|
client.sync_indexes(corpus["id"], wait=True)
|
|
@@ -105,6 +115,33 @@ for hit in results["results"]:
|
|
|
105
115
|
print(hit["score"], hit.get("text", "")[:80])
|
|
106
116
|
```
|
|
107
117
|
|
|
118
|
+
`upload_documents_batch_and_wait(...)` is the canonical onboarding helper for
|
|
119
|
+
raw-text JSON batch ingestion. It blocks until the batch finishes and returns
|
|
120
|
+
the final batch payload, including `doc_ids`.
|
|
121
|
+
|
|
122
|
+
If you intentionally want enqueue-first control, use `wait=False` and then
|
|
123
|
+
resolve the batch with `wait_for_document_batch(...)`:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
docs = [
|
|
127
|
+
{
|
|
128
|
+
"source_uri": "doc://overview",
|
|
129
|
+
"raw_text": "Knowledge² builds dense and sparse indexes for hybrid retrieval.",
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
enqueue = client.upload_documents_batch(corpus["id"], docs, wait=False)
|
|
134
|
+
batch = client.wait_for_document_batch(corpus["id"], enqueue["batch_id"])
|
|
135
|
+
print(batch["status"], batch["doc_ids"])
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For large in-flight imports, `get_document_batch(...)` and
|
|
139
|
+
`wait_for_document_batch(...)` are the canonical batch APIs. Once the batch is
|
|
140
|
+
visible they return stable `doc_ids`, terminal resolution, and live batch
|
|
141
|
+
counters that track admitted documents as processing advances. For broader
|
|
142
|
+
operational context during a large import, you can still pair them with
|
|
143
|
+
`get_corpus_status(...)`, `get_job(...)`, or document-level status checks.
|
|
144
|
+
|
|
108
145
|
## Improve Retrieval Quality
|
|
109
146
|
|
|
110
147
|
```python
|
|
@@ -121,7 +158,7 @@ job = client.optimize_indexes(
|
|
|
121
158
|
query_count=25,
|
|
122
159
|
top_k=10,
|
|
123
160
|
metric="ndcg",
|
|
124
|
-
wait=
|
|
161
|
+
wait=True,
|
|
125
162
|
)
|
|
126
163
|
print(job["job_id"], job["job_type"])
|
|
127
164
|
```
|
|
@@ -144,9 +181,8 @@ python sdk/examples/e2e_lifecycle.py
|
|
|
144
181
|
|
|
145
182
|
| Method | Header | Typical use |
|
|
146
183
|
|---|---|---|
|
|
147
|
-
| API key | `X-API-Key` | primary programmatic access |
|
|
184
|
+
| API key | `X-API-Key` | primary programmatic access for retrieval workflows |
|
|
148
185
|
| Bearer token | `Authorization: Bearer <token>` | console / Auth0 session |
|
|
149
|
-
| Admin token | `X-Admin-Token` | bootstrap and admin operations |
|
|
150
186
|
|
|
151
187
|
```python
|
|
152
188
|
client = Knowledge2(api_key="k2_...")
|
|
@@ -178,14 +214,19 @@ client = Knowledge2(
|
|
|
178
214
|
|
|
179
215
|
## Namespaces
|
|
180
216
|
|
|
181
|
-
The flat client API is canonical.
|
|
217
|
+
The flat client API is canonical. The sync client also exposes namespace helpers
|
|
218
|
+
that group the same methods without changing behavior:
|
|
182
219
|
|
|
183
220
|
- `client.documents.*`
|
|
221
|
+
- `client.documents.upload_batch_and_wait(...)`
|
|
222
|
+
- `client.documents.wait_for_batch(...)`
|
|
184
223
|
- `client.corpora.*`
|
|
185
224
|
- `client.search_ns.*`
|
|
186
225
|
- `client.jobs.*`
|
|
187
226
|
- `client.auth.*`
|
|
188
227
|
|
|
228
|
+
`AsyncKnowledge2` currently stays flat-only.
|
|
229
|
+
|
|
189
230
|
## Framework Integrations
|
|
190
231
|
|
|
191
232
|
The SDK ships LangChain and LlamaIndex integration modules in-package. Install the framework dependency separately, then import the adapter:
|
|
@@ -199,6 +240,66 @@ from sdk.integrations.llamaindex import K2LlamaIndexRetriever
|
|
|
199
240
|
|
|
200
241
|
Agents, feeds, pipelines, and A2A are available for enterprise deployments. Keep the primary examples focused on the core retrieval flow.
|
|
201
242
|
|
|
243
|
+
### Subscription Modes (Preview)
|
|
244
|
+
|
|
245
|
+
Agent-feed subscriptions support three authoring modes on `create_subscription`, gated behind the `knowledge_agents_enabled` feature flag:
|
|
246
|
+
|
|
247
|
+
| Mode | Use | Required fields |
|
|
248
|
+
|------|-----|-----------------|
|
|
249
|
+
| `always` | Route every envelope from the feed | `feed_id`, `role` |
|
|
250
|
+
| `explicit` | Evaluate a predicate DSL against the envelope | `feed_id`, `role`, `match_spec` |
|
|
251
|
+
| `nl_semantic` | Describe the match in plain English; compiled server-side into a `semantic_like` predicate against `content` | `feed_id`, `role`, `match_spec_description` (10-500 chars); optional `threshold` (default 0.75) |
|
|
252
|
+
|
|
253
|
+
The create response echoes the compiled `match_spec` and the raw `match_spec_description`, so no separate `/preview` endpoint is required:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
sub = client.create_subscription(
|
|
257
|
+
agent_id,
|
|
258
|
+
feed_id=feed_id,
|
|
259
|
+
role="input",
|
|
260
|
+
mode="nl_semantic",
|
|
261
|
+
match_spec_description="documents about security incidents",
|
|
262
|
+
)
|
|
263
|
+
print(sub["match_spec"]) # compiled semantic_like predicate
|
|
264
|
+
print(sub["match_spec_description"]) # raw NL description (echoed)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Feed Drafts, Subscriptions, and Feedback (Preview)
|
|
268
|
+
|
|
269
|
+
In addition to CRUD and `run_feed`, the `Knowledge2` client exposes the full
|
|
270
|
+
editing and feedback surface of the Feeds API as flat methods on `client`
|
|
271
|
+
(the same mixin-based pattern used by every other resource).
|
|
272
|
+
|
|
273
|
+
| Method | Endpoint | Notes |
|
|
274
|
+
|--------|----------|-------|
|
|
275
|
+
| `create_feed_draft(feed_id)` | `POST /v1/feeds/{id}/draft` | Returns a draft feed with `parent_feed_id` set |
|
|
276
|
+
| `get_feed_draft(feed_id)` | `GET /v1/feeds/{id}/draft` | 404 when no draft exists |
|
|
277
|
+
| `activate_feed_draft(feed_id)` | `POST /v1/feeds/{id}/draft/activate` | Returns the updated **parent** feed (draft is deleted) |
|
|
278
|
+
| `discard_feed_draft(feed_id)` | `DELETE /v1/feeds/{id}/draft` | Returns `None` |
|
|
279
|
+
| `list_feed_subscriptions(feed_id)` | Read-only view | Returns subscriptions embedded on the feed record; use `create_subscription` on the Agents mixin to attach new ones |
|
|
280
|
+
| `submit_feed_feedback(feed_id, *, rating, chunk_id, feed_run_id)` | `POST /v1/feeds/{id}/feedback` | `rating` is `1` (thumbs up) or `0` (thumbs down) |
|
|
281
|
+
| `get_feed_feedback_stats(feed_id, *, feed_run_id=None)` | `GET /v1/feeds/{id}/feedback` | Optional `feed_run_id` scopes stats to a single run |
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
draft = client.create_feed_draft(feed_id)
|
|
285
|
+
client.update_feed(draft["id"], name="new name")
|
|
286
|
+
client.activate_feed_draft(feed_id) # applies the draft; returns the parent
|
|
287
|
+
|
|
288
|
+
run = client.run_feed(feed_id, return_results=True)
|
|
289
|
+
# `results` is only populated for non-persistent feeds run with
|
|
290
|
+
# `return_results=True`; guard the example for safe use.
|
|
291
|
+
if run.get("results"):
|
|
292
|
+
client.submit_feed_feedback(
|
|
293
|
+
feed_id,
|
|
294
|
+
rating=1,
|
|
295
|
+
chunk_id=run["results"][0]["chunk_id"],
|
|
296
|
+
feed_run_id=run["feed_run_id"],
|
|
297
|
+
)
|
|
298
|
+
stats = client.get_feed_feedback_stats(feed_id) # org-wide for this feed
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
All three areas are fully mirrored on `AsyncKnowledge2` under the same names.
|
|
302
|
+
|
|
202
303
|
## Error Handling
|
|
203
304
|
|
|
204
305
|
All SDK exceptions inherit from `Knowledge2Error`.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Knowledge² Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/knowledge2/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Official Python client for the Knowledge² retrieval platform. The supported customer journey is:
|
|
8
|
+
|
|
9
|
+
`create corpus -> ingest documents -> build indexes -> search -> optimize retrieval`
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
From PyPI:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install knowledge2
|
|
17
|
+
pip install "knowledge2[config]"
|
|
18
|
+
pip install "knowledge2[yaml]"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
From source:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -e .
|
|
25
|
+
pip install -e ".[config]"
|
|
26
|
+
pip install -e ".[yaml]"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`pip install knowledge2` now includes the typed response model dependency
|
|
30
|
+
(`pydantic`) out of the box. Install `knowledge2[config]` only if you want
|
|
31
|
+
`K2Config` environment/file loading via `pydantic-settings`.
|
|
32
|
+
|
|
33
|
+
## Before You Start
|
|
34
|
+
|
|
35
|
+
- Use a normal org-scoped API key for the standard retrieval workflow:
|
|
36
|
+
projects, corpora, documents, indexes, search, and optimize.
|
|
37
|
+
- `optimize_indexes()` and some enterprise/preview surfaces can return
|
|
38
|
+
feature-flag or quota errors (`403`, `409`, `429`) even when the payload is
|
|
39
|
+
correct. Check environment entitlements early.
|
|
40
|
+
|
|
41
|
+
## Surface Categories
|
|
42
|
+
|
|
43
|
+
| Category | Surface |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Core retrieval workflow | orgs, auth, projects, corpora, documents, indexes, search, jobs, metadata, onboarding, audit, usage, console, generation models |
|
|
46
|
+
| Enterprise capabilities | agents, feeds, pipelines, A2A |
|
|
47
|
+
|
|
48
|
+
The main docs and examples below focus on the core retrieval workflow.
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from sdk import Knowledge2
|
|
54
|
+
|
|
55
|
+
client = Knowledge2(api_key="k2_...")
|
|
56
|
+
|
|
57
|
+
project = client.create_project("My Project")
|
|
58
|
+
corpus = client.create_corpus(project["id"], "My Corpus")
|
|
59
|
+
|
|
60
|
+
batch = client.upload_documents_batch_and_wait(
|
|
61
|
+
corpus["id"],
|
|
62
|
+
[
|
|
63
|
+
{
|
|
64
|
+
"source_uri": "doc://overview",
|
|
65
|
+
"raw_text": "Knowledge² builds dense and sparse indexes for hybrid retrieval.",
|
|
66
|
+
"metadata": {"topic": "overview"},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"source_uri": "doc://search",
|
|
70
|
+
"raw_text": "Hybrid retrieval combines semantic similarity with exact keyword matching.",
|
|
71
|
+
"metadata": {"topic": "search"},
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
auto_index=False,
|
|
75
|
+
)
|
|
76
|
+
client.sync_indexes(corpus["id"], wait=True)
|
|
77
|
+
|
|
78
|
+
results = client.search(
|
|
79
|
+
corpus["id"],
|
|
80
|
+
"what is hybrid retrieval",
|
|
81
|
+
top_k=3,
|
|
82
|
+
return_config={"include_text": True, "include_scores": True},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
for hit in results["results"]:
|
|
86
|
+
print(hit["score"], hit.get("text", "")[:80])
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`upload_documents_batch_and_wait(...)` is the canonical onboarding helper for
|
|
90
|
+
raw-text JSON batch ingestion. It blocks until the batch finishes and returns
|
|
91
|
+
the final batch payload, including `doc_ids`.
|
|
92
|
+
|
|
93
|
+
If you intentionally want enqueue-first control, use `wait=False` and then
|
|
94
|
+
resolve the batch with `wait_for_document_batch(...)`:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
docs = [
|
|
98
|
+
{
|
|
99
|
+
"source_uri": "doc://overview",
|
|
100
|
+
"raw_text": "Knowledge² builds dense and sparse indexes for hybrid retrieval.",
|
|
101
|
+
},
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
enqueue = client.upload_documents_batch(corpus["id"], docs, wait=False)
|
|
105
|
+
batch = client.wait_for_document_batch(corpus["id"], enqueue["batch_id"])
|
|
106
|
+
print(batch["status"], batch["doc_ids"])
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
For large in-flight imports, `get_document_batch(...)` and
|
|
110
|
+
`wait_for_document_batch(...)` are the canonical batch APIs. Once the batch is
|
|
111
|
+
visible they return stable `doc_ids`, terminal resolution, and live batch
|
|
112
|
+
counters that track admitted documents as processing advances. For broader
|
|
113
|
+
operational context during a large import, you can still pair them with
|
|
114
|
+
`get_corpus_status(...)`, `get_job(...)`, or document-level status checks.
|
|
115
|
+
|
|
116
|
+
## Improve Retrieval Quality
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
profile = client.get_query_profile(corpus["id"])
|
|
120
|
+
print(profile["example_queries"])
|
|
121
|
+
|
|
122
|
+
job = client.optimize_indexes(
|
|
123
|
+
corpus["id"],
|
|
124
|
+
example_queries=[
|
|
125
|
+
"how does hybrid retrieval work",
|
|
126
|
+
"what is bm25 tuning",
|
|
127
|
+
"how does rrf combine dense and sparse search",
|
|
128
|
+
],
|
|
129
|
+
query_count=25,
|
|
130
|
+
top_k=10,
|
|
131
|
+
metric="ndcg",
|
|
132
|
+
wait=True,
|
|
133
|
+
)
|
|
134
|
+
print(job["job_id"], job["job_type"])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Examples
|
|
138
|
+
|
|
139
|
+
- `sdk/examples/retrieval_quickstart.py`: minimal happy path from empty corpus to working hybrid search
|
|
140
|
+
- `sdk/examples/e2e_lifecycle.py`: full retrieval-quality workflow with query profile inspection and `indexes:optimize`
|
|
141
|
+
|
|
142
|
+
Run either example with:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
export K2_BASE_URL=https://api.knowledge2.ai
|
|
146
|
+
export K2_API_KEY=<api-key>
|
|
147
|
+
python sdk/examples/retrieval_quickstart.py
|
|
148
|
+
python sdk/examples/e2e_lifecycle.py
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Authentication
|
|
152
|
+
|
|
153
|
+
| Method | Header | Typical use |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| API key | `X-API-Key` | primary programmatic access for retrieval workflows |
|
|
156
|
+
| Bearer token | `Authorization: Bearer <token>` | console / Auth0 session |
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
client = Knowledge2(api_key="k2_...")
|
|
160
|
+
client = Knowledge2.from_env()
|
|
161
|
+
client = Knowledge2(bearer_token="...")
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Configuration
|
|
165
|
+
|
|
166
|
+
Important constructor knobs:
|
|
167
|
+
|
|
168
|
+
- `api_host`: defaults to `https://api.knowledge2.ai`
|
|
169
|
+
- `api_key`: API key for programmatic access
|
|
170
|
+
- `org_id`: auto-detected from `GET /v1/auth/whoami` when omitted
|
|
171
|
+
- `timeout`: float or `ClientTimeouts`
|
|
172
|
+
- `limits`: connection-pool settings via `ClientLimits`
|
|
173
|
+
- `max_retries`: transient retry budget
|
|
174
|
+
- `validate_responses`: enable Pydantic response validation
|
|
175
|
+
- `http_client`: bring your own `httpx.Client`
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from sdk import ClientTimeouts, Knowledge2
|
|
179
|
+
|
|
180
|
+
client = Knowledge2(
|
|
181
|
+
api_key="k2_...",
|
|
182
|
+
timeout=ClientTimeouts(connect=5, read=120, write=30, pool=10),
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Namespaces
|
|
187
|
+
|
|
188
|
+
The flat client API is canonical. The sync client also exposes namespace helpers
|
|
189
|
+
that group the same methods without changing behavior:
|
|
190
|
+
|
|
191
|
+
- `client.documents.*`
|
|
192
|
+
- `client.documents.upload_batch_and_wait(...)`
|
|
193
|
+
- `client.documents.wait_for_batch(...)`
|
|
194
|
+
- `client.corpora.*`
|
|
195
|
+
- `client.search_ns.*`
|
|
196
|
+
- `client.jobs.*`
|
|
197
|
+
- `client.auth.*`
|
|
198
|
+
|
|
199
|
+
`AsyncKnowledge2` currently stays flat-only.
|
|
200
|
+
|
|
201
|
+
## Framework Integrations
|
|
202
|
+
|
|
203
|
+
The SDK ships LangChain and LlamaIndex integration modules in-package. Install the framework dependency separately, then import the adapter:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from sdk.integrations.langchain import K2LangChainRetriever
|
|
207
|
+
from sdk.integrations.llamaindex import K2LlamaIndexRetriever
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Enterprise Capabilities
|
|
211
|
+
|
|
212
|
+
Agents, feeds, pipelines, and A2A are available for enterprise deployments. Keep the primary examples focused on the core retrieval flow.
|
|
213
|
+
|
|
214
|
+
### Subscription Modes (Preview)
|
|
215
|
+
|
|
216
|
+
Agent-feed subscriptions support three authoring modes on `create_subscription`, gated behind the `knowledge_agents_enabled` feature flag:
|
|
217
|
+
|
|
218
|
+
| Mode | Use | Required fields |
|
|
219
|
+
|------|-----|-----------------|
|
|
220
|
+
| `always` | Route every envelope from the feed | `feed_id`, `role` |
|
|
221
|
+
| `explicit` | Evaluate a predicate DSL against the envelope | `feed_id`, `role`, `match_spec` |
|
|
222
|
+
| `nl_semantic` | Describe the match in plain English; compiled server-side into a `semantic_like` predicate against `content` | `feed_id`, `role`, `match_spec_description` (10-500 chars); optional `threshold` (default 0.75) |
|
|
223
|
+
|
|
224
|
+
The create response echoes the compiled `match_spec` and the raw `match_spec_description`, so no separate `/preview` endpoint is required:
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
sub = client.create_subscription(
|
|
228
|
+
agent_id,
|
|
229
|
+
feed_id=feed_id,
|
|
230
|
+
role="input",
|
|
231
|
+
mode="nl_semantic",
|
|
232
|
+
match_spec_description="documents about security incidents",
|
|
233
|
+
)
|
|
234
|
+
print(sub["match_spec"]) # compiled semantic_like predicate
|
|
235
|
+
print(sub["match_spec_description"]) # raw NL description (echoed)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Feed Drafts, Subscriptions, and Feedback (Preview)
|
|
239
|
+
|
|
240
|
+
In addition to CRUD and `run_feed`, the `Knowledge2` client exposes the full
|
|
241
|
+
editing and feedback surface of the Feeds API as flat methods on `client`
|
|
242
|
+
(the same mixin-based pattern used by every other resource).
|
|
243
|
+
|
|
244
|
+
| Method | Endpoint | Notes |
|
|
245
|
+
|--------|----------|-------|
|
|
246
|
+
| `create_feed_draft(feed_id)` | `POST /v1/feeds/{id}/draft` | Returns a draft feed with `parent_feed_id` set |
|
|
247
|
+
| `get_feed_draft(feed_id)` | `GET /v1/feeds/{id}/draft` | 404 when no draft exists |
|
|
248
|
+
| `activate_feed_draft(feed_id)` | `POST /v1/feeds/{id}/draft/activate` | Returns the updated **parent** feed (draft is deleted) |
|
|
249
|
+
| `discard_feed_draft(feed_id)` | `DELETE /v1/feeds/{id}/draft` | Returns `None` |
|
|
250
|
+
| `list_feed_subscriptions(feed_id)` | Read-only view | Returns subscriptions embedded on the feed record; use `create_subscription` on the Agents mixin to attach new ones |
|
|
251
|
+
| `submit_feed_feedback(feed_id, *, rating, chunk_id, feed_run_id)` | `POST /v1/feeds/{id}/feedback` | `rating` is `1` (thumbs up) or `0` (thumbs down) |
|
|
252
|
+
| `get_feed_feedback_stats(feed_id, *, feed_run_id=None)` | `GET /v1/feeds/{id}/feedback` | Optional `feed_run_id` scopes stats to a single run |
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
draft = client.create_feed_draft(feed_id)
|
|
256
|
+
client.update_feed(draft["id"], name="new name")
|
|
257
|
+
client.activate_feed_draft(feed_id) # applies the draft; returns the parent
|
|
258
|
+
|
|
259
|
+
run = client.run_feed(feed_id, return_results=True)
|
|
260
|
+
# `results` is only populated for non-persistent feeds run with
|
|
261
|
+
# `return_results=True`; guard the example for safe use.
|
|
262
|
+
if run.get("results"):
|
|
263
|
+
client.submit_feed_feedback(
|
|
264
|
+
feed_id,
|
|
265
|
+
rating=1,
|
|
266
|
+
chunk_id=run["results"][0]["chunk_id"],
|
|
267
|
+
feed_run_id=run["feed_run_id"],
|
|
268
|
+
)
|
|
269
|
+
stats = client.get_feed_feedback_stats(feed_id) # org-wide for this feed
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
All three areas are fully mirrored on `AsyncKnowledge2` under the same names.
|
|
273
|
+
|
|
274
|
+
## Error Handling
|
|
275
|
+
|
|
276
|
+
All SDK exceptions inherit from `Knowledge2Error`.
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from sdk.errors import Knowledge2Error, NotFoundError, RateLimitError
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
client.get_corpus("missing")
|
|
283
|
+
except NotFoundError:
|
|
284
|
+
...
|
|
285
|
+
except RateLimitError as exc:
|
|
286
|
+
print(exc.retry_after)
|
|
287
|
+
except Knowledge2Error as exc:
|
|
288
|
+
print(exc)
|
|
289
|
+
```
|
|
@@ -41,6 +41,34 @@ except ImportError: # pragma: no cover - Python < 3.11
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class AsyncBaseClient:
|
|
44
|
+
@staticmethod
|
|
45
|
+
def _tightest_timeout(current: float | None, budget: float | None) -> float | None:
|
|
46
|
+
if budget is None:
|
|
47
|
+
return current
|
|
48
|
+
if current is None:
|
|
49
|
+
return budget
|
|
50
|
+
if current <= budget:
|
|
51
|
+
return current
|
|
52
|
+
return budget
|
|
53
|
+
|
|
54
|
+
def _job_poll_timeout(self, budget_s: float | None) -> httpx.Timeout | None:
|
|
55
|
+
if budget_s is None:
|
|
56
|
+
return None
|
|
57
|
+
client_timeout = getattr(self._client, "timeout", None)
|
|
58
|
+
if isinstance(client_timeout, httpx.Timeout):
|
|
59
|
+
return httpx.Timeout(
|
|
60
|
+
connect=self._tightest_timeout(client_timeout.connect, budget_s),
|
|
61
|
+
read=self._tightest_timeout(client_timeout.read, budget_s),
|
|
62
|
+
write=self._tightest_timeout(client_timeout.write, budget_s),
|
|
63
|
+
pool=self._tightest_timeout(client_timeout.pool, budget_s),
|
|
64
|
+
)
|
|
65
|
+
return httpx.Timeout(
|
|
66
|
+
connect=budget_s,
|
|
67
|
+
read=budget_s,
|
|
68
|
+
write=budget_s,
|
|
69
|
+
pool=budget_s,
|
|
70
|
+
)
|
|
71
|
+
|
|
44
72
|
@staticmethod
|
|
45
73
|
def _normalize_base_url(base_url: str) -> str:
|
|
46
74
|
"""Normalize and validate base URL input before constructing httpx.AsyncClient."""
|
|
@@ -421,7 +449,19 @@ class AsyncBaseClient:
|
|
|
421
449
|
) -> dict[str, Any]:
|
|
422
450
|
start = time.monotonic()
|
|
423
451
|
while True:
|
|
424
|
-
|
|
452
|
+
remaining_timeout = (
|
|
453
|
+
max(0.0, timeout_s - (time.monotonic() - start)) if timeout_s is not None else None
|
|
454
|
+
)
|
|
455
|
+
if timeout_s is not None and remaining_timeout == 0:
|
|
456
|
+
raise TimeoutError(f"Timed out waiting for job {job_id}")
|
|
457
|
+
try:
|
|
458
|
+
job = await self._request(
|
|
459
|
+
"GET",
|
|
460
|
+
f"/v1/jobs/{job_id}",
|
|
461
|
+
timeout=self._job_poll_timeout(remaining_timeout),
|
|
462
|
+
)
|
|
463
|
+
except APITimeoutError as exc:
|
|
464
|
+
raise TimeoutError(f"Timed out waiting for job {job_id}") from exc
|
|
425
465
|
if not isinstance(job, dict):
|
|
426
466
|
raise RuntimeError(
|
|
427
467
|
f"Unexpected response polling job {job_id}: {type(job).__name__}"
|
|
@@ -434,7 +474,13 @@ class AsyncBaseClient:
|
|
|
434
474
|
return job
|
|
435
475
|
if timeout_s is not None and (time.monotonic() - start) > timeout_s:
|
|
436
476
|
raise TimeoutError(f"Timed out waiting for job {job_id}")
|
|
437
|
-
|
|
477
|
+
if timeout_s is not None:
|
|
478
|
+
remaining_timeout = max(0.0, timeout_s - (time.monotonic() - start))
|
|
479
|
+
if remaining_timeout == 0:
|
|
480
|
+
raise TimeoutError(f"Timed out waiting for job {job_id}")
|
|
481
|
+
await asyncio.sleep(min(poll_s, remaining_timeout))
|
|
482
|
+
else:
|
|
483
|
+
await asyncio.sleep(poll_s)
|
|
438
484
|
|
|
439
485
|
# ------------------------------------------------------------------
|
|
440
486
|
# Pagination
|