docforge-sdk 0.28.0__tar.gz → 0.30.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.
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/PKG-INFO +1 -1
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/__init__.py +13 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_version.py +1 -1
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/client.py +10 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/__init__.py +29 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/_shared.py +29 -2
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/auth.py +11 -4
- docforge_sdk-0.30.0/docforge_sdk/models/collection_aliases.py +43 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/collections.py +32 -3
- docforge_sdk-0.30.0/docforge_sdk/models/config_versions.py +83 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/corpus.py +4 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/jobs.py +6 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/__init__.py +5 -0
- docforge_sdk-0.30.0/docforge_sdk/resources/collection_aliases.py +91 -0
- docforge_sdk-0.30.0/docforge_sdk/resources/config_versions.py +148 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/corpus.py +59 -9
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/documents.py +19 -10
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/openapi_snapshot.json +1328 -342
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/parity_map.py +26 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/route_map.py +9 -0
- docforge_sdk-0.30.0/tests/unit/test_collection_aliases.py +58 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/.gitignore +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/.python-version +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/LICENSE +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/README.md +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_exceptions.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_requestspec.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_transport_async.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_transport_base.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/_transport_sync.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/audit.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/blobs.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/capabilities.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/documents.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/estimate.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/explorer.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/field_spec.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/health.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/index_rebuild.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/ir.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/pipelines.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/preview.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/reingest.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/schema_ops.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/search.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/snippets.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/storage.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/trace.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/models/transfers.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/py.typed +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/_base.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/audit.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/auth.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/blobs.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/capabilities.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/collections.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/explorer.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/health.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/jobs.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/pipelines.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/search.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/snippets.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/docforge_sdk/resources/transfers.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/pyproject.toml +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/__init__.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/check_schema_drift.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/live/__init__.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/live/test_openapi_parity_live.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/__init__.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_audit.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_auth.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_blobs.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_collections.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_corpus.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_documents.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_explorer.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_health.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_jobs.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_models_offline_parity.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_pipelines.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_resource_parity.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_route_coverage.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_search.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_snippets.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_transfers.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/tests/unit/test_transport.py +0 -0
- {docforge_sdk-0.28.0 → docforge_sdk-0.30.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: docforge-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.0
|
|
4
4
|
Summary: Typed async + sync Python client for the DocForge REST API (standalone, zero server deps).
|
|
5
5
|
Project-URL: Homepage, https://github.com/Florian-BARRE/docforge
|
|
6
6
|
Project-URL: Repository, https://github.com/Florian-BARRE/docforge
|
|
@@ -24,6 +24,7 @@ from .models._shared import (
|
|
|
24
24
|
FieldScope,
|
|
25
25
|
FieldType,
|
|
26
26
|
KeyPermissions,
|
|
27
|
+
KeyProfile,
|
|
27
28
|
SourceKind,
|
|
28
29
|
)
|
|
29
30
|
|
|
@@ -39,6 +40,13 @@ from .models.blobs import BlobContent
|
|
|
39
40
|
# ------------------- Capabilities models ------------------- #
|
|
40
41
|
from .models.capabilities import CapabilitiesResponse, CapabilityMatrix, ServiceInfo
|
|
41
42
|
|
|
43
|
+
# ------------------- Collection alias models ------------------- #
|
|
44
|
+
from .models.collection_aliases import (
|
|
45
|
+
CollectionAliasModel,
|
|
46
|
+
SetCollectionAliasRequest,
|
|
47
|
+
SetCollectionAliasResponse,
|
|
48
|
+
)
|
|
49
|
+
|
|
42
50
|
# ------------------- Collections models ------------------- #
|
|
43
51
|
from .models.collections import (
|
|
44
52
|
BulkReingestAccepted,
|
|
@@ -189,6 +197,7 @@ __all__ = [
|
|
|
189
197
|
# Shared vocabulary
|
|
190
198
|
"Capability",
|
|
191
199
|
"KeyPermissions",
|
|
200
|
+
"KeyProfile",
|
|
192
201
|
"FieldType",
|
|
193
202
|
"FieldOrigin",
|
|
194
203
|
"FieldScope",
|
|
@@ -206,6 +215,10 @@ __all__ = [
|
|
|
206
215
|
"KeyInfo",
|
|
207
216
|
# Health
|
|
208
217
|
"HealthStatus",
|
|
218
|
+
# Collection aliases
|
|
219
|
+
"CollectionAliasModel",
|
|
220
|
+
"SetCollectionAliasRequest",
|
|
221
|
+
"SetCollectionAliasResponse",
|
|
209
222
|
# Collections
|
|
210
223
|
"FieldSpec",
|
|
211
224
|
"CollectionModel",
|
|
@@ -13,7 +13,9 @@ from .resources.audit import AsyncAudit, SyncAudit
|
|
|
13
13
|
from .resources.auth import AsyncAuth, SyncAuth
|
|
14
14
|
from .resources.blobs import AsyncBlobs, SyncBlobs
|
|
15
15
|
from .resources.capabilities import AsyncCapabilities, SyncCapabilities
|
|
16
|
+
from .resources.collection_aliases import AsyncCollectionAliases, SyncCollectionAliases
|
|
16
17
|
from .resources.collections import AsyncCollections, SyncCollections
|
|
18
|
+
from .resources.config_versions import AsyncConfigVersions, SyncConfigVersions
|
|
17
19
|
from .resources.corpus import AsyncCorpus, SyncCorpus
|
|
18
20
|
from .resources.documents import AsyncDocuments, SyncDocuments
|
|
19
21
|
from .resources.explorer import AsyncExplorer, SyncExplorer
|
|
@@ -42,6 +44,8 @@ class AsyncClient:
|
|
|
42
44
|
pipelines (AsyncPipelines): Pipeline discovery + design.
|
|
43
45
|
transfers (AsyncTransfers): Collection export/import.
|
|
44
46
|
snippets (AsyncSnippets): Granular collection-config snippet export/apply.
|
|
47
|
+
config_versions (AsyncConfigVersions): Collection config history (list/get/diff/restore).
|
|
48
|
+
collection_aliases (AsyncCollectionAliases): Stable collection names (list/set/delete).
|
|
45
49
|
audit (AsyncAudit): Root-only audit-trail reads.
|
|
46
50
|
corpus (AsyncCorpus): Server-side document grid — query + bulk delete/enable/reingest.
|
|
47
51
|
"""
|
|
@@ -69,6 +73,8 @@ class AsyncClient:
|
|
|
69
73
|
self.pipelines = AsyncPipelines(self._transport)
|
|
70
74
|
self.transfers = AsyncTransfers(self._transport)
|
|
71
75
|
self.snippets = AsyncSnippets(self._transport)
|
|
76
|
+
self.config_versions = AsyncConfigVersions(self._transport)
|
|
77
|
+
self.collection_aliases = AsyncCollectionAliases(self._transport)
|
|
72
78
|
self.corpus = AsyncCorpus(self._transport)
|
|
73
79
|
|
|
74
80
|
async def __aenter__(self) -> "AsyncClient":
|
|
@@ -106,6 +112,8 @@ class Client:
|
|
|
106
112
|
pipelines (SyncPipelines): Pipeline discovery + design.
|
|
107
113
|
transfers (SyncTransfers): Collection export/import.
|
|
108
114
|
snippets (SyncSnippets): Granular collection-config snippet export/apply.
|
|
115
|
+
config_versions (SyncConfigVersions): Collection config history (list/get/diff/restore).
|
|
116
|
+
collection_aliases (SyncCollectionAliases): Stable collection names (list/set/delete).
|
|
109
117
|
audit (SyncAudit): Root-only audit-trail reads.
|
|
110
118
|
corpus (SyncCorpus): Server-side document grid — query + bulk delete/enable/reingest.
|
|
111
119
|
"""
|
|
@@ -133,6 +141,8 @@ class Client:
|
|
|
133
141
|
self.pipelines = SyncPipelines(self._transport)
|
|
134
142
|
self.transfers = SyncTransfers(self._transport)
|
|
135
143
|
self.snippets = SyncSnippets(self._transport)
|
|
144
|
+
self.config_versions = SyncConfigVersions(self._transport)
|
|
145
|
+
self.collection_aliases = SyncCollectionAliases(self._transport)
|
|
136
146
|
self.corpus = SyncCorpus(self._transport)
|
|
137
147
|
|
|
138
148
|
def __enter__(self) -> "Client":
|
|
@@ -8,6 +8,7 @@ from ._shared import (
|
|
|
8
8
|
FieldScope,
|
|
9
9
|
FieldType,
|
|
10
10
|
KeyPermissions,
|
|
11
|
+
KeyProfile,
|
|
11
12
|
SourceKind,
|
|
12
13
|
)
|
|
13
14
|
|
|
@@ -23,6 +24,13 @@ from .blobs import BlobContent
|
|
|
23
24
|
# ------------------- Capabilities models ------------------- #
|
|
24
25
|
from .capabilities import CapabilitiesResponse, CapabilityMatrix, ServiceInfo
|
|
25
26
|
|
|
27
|
+
# ------------------- Collection alias models ------------------- #
|
|
28
|
+
from .collection_aliases import (
|
|
29
|
+
CollectionAliasModel,
|
|
30
|
+
SetCollectionAliasRequest,
|
|
31
|
+
SetCollectionAliasResponse,
|
|
32
|
+
)
|
|
33
|
+
|
|
26
34
|
# ------------------- Collections models ------------------- #
|
|
27
35
|
from .collections import (
|
|
28
36
|
BulkReingestAccepted,
|
|
@@ -40,6 +48,16 @@ from .collections import (
|
|
|
40
48
|
UpdateCollectionResponse,
|
|
41
49
|
)
|
|
42
50
|
|
|
51
|
+
# ------------------- Config history models ------------------- #
|
|
52
|
+
from .config_versions import (
|
|
53
|
+
ConfigDiffEntry,
|
|
54
|
+
ConfigVersionDetail,
|
|
55
|
+
ConfigVersionDiffResponse,
|
|
56
|
+
ConfigVersionListResponse,
|
|
57
|
+
ConfigVersionRestoreResponse,
|
|
58
|
+
ConfigVersionSummary,
|
|
59
|
+
)
|
|
60
|
+
|
|
43
61
|
# ------------------- Corpus filter + grid models ------------------- #
|
|
44
62
|
from .corpus import (
|
|
45
63
|
BulkDeleteResponse,
|
|
@@ -225,6 +243,7 @@ __all__ = [
|
|
|
225
243
|
# Shared vocabulary
|
|
226
244
|
"Capability",
|
|
227
245
|
"KeyPermissions",
|
|
246
|
+
"KeyProfile",
|
|
228
247
|
"FieldType",
|
|
229
248
|
"FieldOrigin",
|
|
230
249
|
"FieldScope",
|
|
@@ -277,6 +296,16 @@ __all__ = [
|
|
|
277
296
|
"MetadataValuesPatch",
|
|
278
297
|
"MetadataUpdateResponse",
|
|
279
298
|
"DocumentView",
|
|
299
|
+
# Config history
|
|
300
|
+
"ConfigDiffEntry",
|
|
301
|
+
"CollectionAliasModel",
|
|
302
|
+
"SetCollectionAliasRequest",
|
|
303
|
+
"SetCollectionAliasResponse",
|
|
304
|
+
"ConfigVersionDetail",
|
|
305
|
+
"ConfigVersionDiffResponse",
|
|
306
|
+
"ConfigVersionListResponse",
|
|
307
|
+
"ConfigVersionRestoreResponse",
|
|
308
|
+
"ConfigVersionSummary",
|
|
280
309
|
# Snippets
|
|
281
310
|
"SNIPPET_FILE_EXTENSION",
|
|
282
311
|
"SnippetKind",
|
|
@@ -12,15 +12,31 @@ from pydantic import BaseModel, Field
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class Capability(StrEnum):
|
|
15
|
-
"""
|
|
15
|
+
"""
|
|
16
|
+
A coarse action class an endpoint requires of the calling key.
|
|
17
|
+
|
|
18
|
+
``READ`` is the legacy pre-split alias: the server normalizes it to ``READ_TEXT`` +
|
|
19
|
+
``READ_TECHNICAL`` and never returns it.
|
|
20
|
+
"""
|
|
16
21
|
|
|
17
22
|
READ = "read"
|
|
23
|
+
READ_TEXT = "read_text"
|
|
24
|
+
READ_TECHNICAL = "read_technical"
|
|
18
25
|
WRITE = "write"
|
|
19
26
|
SEARCH = "search"
|
|
20
27
|
CREATE = "create"
|
|
21
28
|
ADMIN = "admin"
|
|
22
29
|
|
|
23
30
|
|
|
31
|
+
class KeyProfile(StrEnum):
|
|
32
|
+
"""A named capability preset offered at key creation."""
|
|
33
|
+
|
|
34
|
+
AGENT_READER = "agent_reader"
|
|
35
|
+
AGENT_SEARCHER = "agent_searcher"
|
|
36
|
+
OPERATOR = "operator"
|
|
37
|
+
ADMIN = "admin"
|
|
38
|
+
|
|
39
|
+
|
|
24
40
|
class FieldType(StrEnum):
|
|
25
41
|
"""Data type of a metadata field — drives validation and storage."""
|
|
26
42
|
|
|
@@ -99,18 +115,29 @@ class KeyPermissions(BaseModel):
|
|
|
99
115
|
that can authenticate but is authorized for nothing).
|
|
100
116
|
collections (list[str]): Either ``["*"]`` (every collection) or an explicit list of
|
|
101
117
|
collection UUID strings the key is scoped to.
|
|
118
|
+
profile (KeyProfile | None): A named preset — give it INSTEAD of ``capabilities`` and the
|
|
119
|
+
server expands it to the preset's explicit list (stored for display).
|
|
102
120
|
"""
|
|
103
121
|
|
|
104
122
|
capabilities: list[Capability] = Field(
|
|
105
|
-
|
|
123
|
+
default_factory=list,
|
|
124
|
+
description="The action classes this key grants (read_text / read_technical / write / "
|
|
125
|
+
"search / create / admin; the legacy 'read' = read_text + read_technical). May be omitted "
|
|
126
|
+
"when 'profile' is given.",
|
|
106
127
|
)
|
|
107
128
|
collections: list[str] = Field(
|
|
108
129
|
description="Collection scope: ['*'] for all, else explicit collection UUID strings."
|
|
109
130
|
)
|
|
131
|
+
profile: KeyProfile | None = Field(
|
|
132
|
+
default=None,
|
|
133
|
+
description="Named capability preset (agent_reader / agent_searcher / operator / admin). "
|
|
134
|
+
"Given instead of 'capabilities', it expands to the preset; stored for display only.",
|
|
135
|
+
)
|
|
110
136
|
|
|
111
137
|
|
|
112
138
|
__all__ = [
|
|
113
139
|
"Capability",
|
|
140
|
+
"KeyProfile",
|
|
114
141
|
"FieldType",
|
|
115
142
|
"FieldOrigin",
|
|
116
143
|
"FieldScope",
|
|
@@ -11,7 +11,7 @@ from typing import Any
|
|
|
11
11
|
from pydantic import BaseModel, Field
|
|
12
12
|
|
|
13
13
|
# ====== Local Project Imports ======
|
|
14
|
-
from ._shared import KeyPermissions
|
|
14
|
+
from ._shared import KeyPermissions, KeyProfile
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class CreateKeyRequest(BaseModel):
|
|
@@ -28,7 +28,8 @@ class CreateKeyRequest(BaseModel):
|
|
|
28
28
|
name: str = Field(min_length=1, description="Human-readable label for the key.")
|
|
29
29
|
permissions: KeyPermissions | None = Field(
|
|
30
30
|
default=None,
|
|
31
|
-
description="Per-key capability + collection scope; null = full access (root)."
|
|
31
|
+
description="Per-key capability + collection scope; null = full access (root). Give "
|
|
32
|
+
"'profile' instead of 'capabilities' to apply a named preset.",
|
|
32
33
|
)
|
|
33
34
|
expires_at: datetime | None = Field(
|
|
34
35
|
default=None,
|
|
@@ -128,16 +129,22 @@ class KeyInfo(BaseModel):
|
|
|
128
129
|
|
|
129
130
|
|
|
130
131
|
class WhoAmI(BaseModel):
|
|
131
|
-
"""The calling token's own access — capabilities
|
|
132
|
+
"""The calling token's own access — capabilities, collection scope and usage profile."""
|
|
132
133
|
|
|
133
134
|
authenticated: bool = Field(description="Always true for a resolved principal.")
|
|
134
135
|
root: bool = Field(description="Full, unscoped access (auth off, or a NULL-permissions key).")
|
|
135
136
|
capabilities: list[str] = Field(
|
|
136
|
-
description="Action classes this token grants (
|
|
137
|
+
description="Action classes this token grants (read_text / read_technical / write / search "
|
|
138
|
+
"/ create / admin)."
|
|
137
139
|
)
|
|
138
140
|
collections: list[str] = Field(
|
|
139
141
|
description="Collection scope: ['*'] for all, else explicit collection UUID strings."
|
|
140
142
|
)
|
|
143
|
+
profile: KeyProfile | None = Field(
|
|
144
|
+
default=None,
|
|
145
|
+
description="Named preset matching the capabilities (agent_reader / agent_searcher / "
|
|
146
|
+
"operator / admin); null for root or a custom capability set.",
|
|
147
|
+
)
|
|
141
148
|
|
|
142
149
|
|
|
143
150
|
__all__ = ["CreateKeyRequest", "RotateKeyRequest", "CreatedKey", "KeyInfo", "WhoAmI"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ====== Code Summary ======
|
|
2
|
+
# Models of the collection-alias surface, mirrored field-for-field from the DocForge backend
|
|
3
|
+
# (app/backend/routers/collection_aliases/models.py): an alias and its current target, the PUT body
|
|
4
|
+
# (the target collection), and the PUT outcome (the alias now + where it pointed before). A collection
|
|
5
|
+
# alias is a stable deployment-level name for one collection, usable wherever a collection id is taken.
|
|
6
|
+
|
|
7
|
+
# ====== Standard Library Imports ======
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
# ====== Third-Party Library Imports ======
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CollectionAliasModel(BaseModel):
|
|
15
|
+
"""One collection alias and the collection it currently targets."""
|
|
16
|
+
|
|
17
|
+
name: str = Field(description="The alias name (a slug: lowercase, digits, '-', '_').")
|
|
18
|
+
collection_id: str = Field(description="The UUID of the collection it points at.")
|
|
19
|
+
collection_name: str = Field(description="The name of the collection it points at.")
|
|
20
|
+
created_at: datetime = Field(description="When the alias was created.")
|
|
21
|
+
updated_at: datetime = Field(description="When the alias was last (re-)pointed.")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SetCollectionAliasRequest(BaseModel):
|
|
25
|
+
"""The body of PUT /collection-aliases/{name}: the collection the alias must point at."""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(extra="forbid")
|
|
28
|
+
|
|
29
|
+
collection_id: str = Field(description="The UUID of the target collection.")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SetCollectionAliasResponse(CollectionAliasModel):
|
|
33
|
+
"""The alias after a create / re-point, plus where it pointed before."""
|
|
34
|
+
|
|
35
|
+
previous_collection_id: str | None = Field(
|
|
36
|
+
default=None,
|
|
37
|
+
description="The collection the alias pointed at before this call (null = it was created; "
|
|
38
|
+
"equal to collection_id = a no-op re-point).",
|
|
39
|
+
)
|
|
40
|
+
created: bool = Field(description="True when this call created the alias.")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = ["CollectionAliasModel", "SetCollectionAliasRequest", "SetCollectionAliasResponse"]
|
|
@@ -33,6 +33,7 @@ class CollectionModel(BaseModel):
|
|
|
33
33
|
needs_reindex (bool): True when a config change requires reindexing.
|
|
34
34
|
missing_vectors (list[str] | None): Named vectors the schema's semantic/lexical fields need
|
|
35
35
|
but the vector store does not declare (an index rebuild is required); None on list rows.
|
|
36
|
+
aliases (list[str] | None): The collection aliases targeting it (single read only).
|
|
36
37
|
created_at (datetime | None): Creation timestamp.
|
|
37
38
|
title_field (str | None): Document-scope field used as the display title (None = parsed).
|
|
38
39
|
pipeline (dict[str, Any]): The ingestion pipeline blob (the graph).
|
|
@@ -76,15 +77,25 @@ class CollectionModel(BaseModel):
|
|
|
76
77
|
"on the single-collection read and the PATCH response ([] = aligned or never ingested); "
|
|
77
78
|
"null on the fleet list, which never reads the vector store.",
|
|
78
79
|
)
|
|
80
|
+
aliases: list[str] | None = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="The collection aliases pointing at this collection (each usable in place of the "
|
|
83
|
+
"UUID in every collection route and as an 'alias:<name>' key scope). Computed on the "
|
|
84
|
+
"single-collection read (GET /collections/{id}); null on the other payloads.",
|
|
85
|
+
)
|
|
79
86
|
created_at: datetime | None = Field(default=None, description="Creation timestamp.")
|
|
80
87
|
title_field: str | None = Field(
|
|
81
88
|
default=None,
|
|
82
89
|
description="Document-scope field whose value is each document's display title (None = the "
|
|
83
90
|
"parser-derived title).",
|
|
84
91
|
)
|
|
85
|
-
pipeline: dict[str, Any] = Field(
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
pipeline: dict[str, Any] | None = Field(
|
|
93
|
+
description="The ingestion pipeline blob (the graph); null when the calling key lacks the "
|
|
94
|
+
"read_technical capability (withheld, not refused)."
|
|
95
|
+
)
|
|
96
|
+
search: dict[str, Any] | None = Field(
|
|
97
|
+
description="The search pipeline graph blob ({} = use the stock default); null when the "
|
|
98
|
+
"calling key lacks the read_technical capability (withheld, not refused)."
|
|
88
99
|
)
|
|
89
100
|
fields: list[FieldSpec] = Field(default_factory=list, description="The metadata schema.")
|
|
90
101
|
estimate_overrides: EstimateOverrides | None = Field(
|
|
@@ -281,6 +292,8 @@ class BulkReingestRequest(BaseModel):
|
|
|
281
292
|
in the collection. An empty list is rejected by the API (an ambiguous no-op).
|
|
282
293
|
force (bool): Bypass the stage cache and recompute every stage from scratch (no cache
|
|
283
294
|
read/write). Use to rebuild after a code change that did not bump a node's CACHE_VERSION.
|
|
295
|
+
replay_from (str | None): Replay from this post-IR stage (no re-parse); None = full re-run.
|
|
296
|
+
confirm_estimate (bool): Acknowledge the estimate of a reingest above the confirm threshold.
|
|
284
297
|
"""
|
|
285
298
|
|
|
286
299
|
document_ids: list[str] | None = Field(
|
|
@@ -292,6 +305,18 @@ class BulkReingestRequest(BaseModel):
|
|
|
292
305
|
description="Bypass the stage cache and recompute every stage from scratch (no cache "
|
|
293
306
|
"read/write). Use to rebuild after a code change that did not bump a node's CACHE_VERSION.",
|
|
294
307
|
)
|
|
308
|
+
replay_from: str | None = Field(
|
|
309
|
+
default=None,
|
|
310
|
+
description="Replay only this post-IR stage and its downstream from each document's PERSISTED "
|
|
311
|
+
"IR (no re-parse): one of enrich, chunk, metagen_chunk, metagen_document, embed — as the "
|
|
312
|
+
"collection's pipeline allows (422 lists the allowed stages). Omit for a full re-run.",
|
|
313
|
+
)
|
|
314
|
+
confirm_estimate: bool = Field(
|
|
315
|
+
default=False,
|
|
316
|
+
description="Acknowledge the cost estimate of a LARGE reingest (above the server's confirm "
|
|
317
|
+
"threshold). Without it such a call is refused 409 estimate_required carrying the estimate "
|
|
318
|
+
"summary; review it and resend with true.",
|
|
319
|
+
)
|
|
295
320
|
|
|
296
321
|
|
|
297
322
|
class BulkReingestAccepted(BaseModel):
|
|
@@ -324,6 +349,10 @@ class BulkReingestAccepted(BaseModel):
|
|
|
324
349
|
default=0,
|
|
325
350
|
description="Documents skipped because an ingestion job was already active for them.",
|
|
326
351
|
)
|
|
352
|
+
skipped_not_replayable: int = Field(
|
|
353
|
+
default=0,
|
|
354
|
+
description="Documents skipped by a replay_from run because they have no persisted IR.",
|
|
355
|
+
)
|
|
327
356
|
jobs: list[ReingestJobHandle] = Field(description="One handle per enqueued run.")
|
|
328
357
|
|
|
329
358
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# ====== Code Summary ======
|
|
2
|
+
# Response models of the versioned collection config history, mirrored field-for-field from the
|
|
3
|
+
# DocForge backend (app/backend/routers/config_history/models.py): the paginated version list (author +
|
|
4
|
+
# change summary), one masked snapshot, the structured masked diff, and a restore outcome.
|
|
5
|
+
|
|
6
|
+
# ====== Standard Library Imports ======
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
|
|
10
|
+
# ====== Third-Party Library Imports ======
|
|
11
|
+
from pydantic import BaseModel, Field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigVersionSummary(BaseModel):
|
|
15
|
+
"""One entry of a collection's config history (no config body)."""
|
|
16
|
+
|
|
17
|
+
version: int = Field(description="Per-collection version number (1 = creation).")
|
|
18
|
+
created_at: datetime = Field(description="When this version was written.")
|
|
19
|
+
note: str | None = Field(default=None, description="The note stored with the version.")
|
|
20
|
+
author_label: str | None = Field(
|
|
21
|
+
default=None,
|
|
22
|
+
description='Who wrote it: "<key name> (<key prefix>)" (the root token reads "root (df_…)"), '
|
|
23
|
+
'or "anonymous" (auth off); null = '
|
|
24
|
+
"unknown (written before authorship was recorded, or a system write such as an import).",
|
|
25
|
+
)
|
|
26
|
+
author_key_id: str | None = Field(
|
|
27
|
+
default=None, description="The authoring API key id (null once that key is deleted)."
|
|
28
|
+
)
|
|
29
|
+
changes: list[str] = Field(
|
|
30
|
+
default_factory=list,
|
|
31
|
+
description='What changed vs the previous version: "pipeline:<node id>", "pipeline:(graph)" '
|
|
32
|
+
'(transitions/bindings), "search". Empty for version 1 or a no-op write.',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigVersionListResponse(BaseModel):
|
|
37
|
+
"""One newest-first page of a collection's config history."""
|
|
38
|
+
|
|
39
|
+
collection_id: str
|
|
40
|
+
total: int = Field(description="Number of versions in the whole history.")
|
|
41
|
+
limit: int
|
|
42
|
+
offset: int
|
|
43
|
+
items: list[ConfigVersionSummary]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConfigVersionDetail(ConfigVersionSummary):
|
|
47
|
+
"""One config version with its {pipeline, search} snapshot — every provider secret masked."""
|
|
48
|
+
|
|
49
|
+
config: dict[str, Any] = Field(
|
|
50
|
+
description="The snapshot {pipeline, search}; secrets replaced by the mask marker."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ConfigDiffEntry(BaseModel):
|
|
55
|
+
"""One changed path between two versions (values masked)."""
|
|
56
|
+
|
|
57
|
+
path: str = Field(
|
|
58
|
+
description="JSON-pointer style path; graph nodes are keyed by id, e.g. "
|
|
59
|
+
'"/pipeline/nodes/parse/config/max_pages".',
|
|
60
|
+
)
|
|
61
|
+
op: Literal["added", "removed", "changed"]
|
|
62
|
+
before: Any = Field(default=None, description="The masked value in `from` (absent → null).")
|
|
63
|
+
after: Any = Field(default=None, description="The masked value in `to` (absent → null).")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ConfigVersionDiffResponse(BaseModel):
|
|
67
|
+
"""The structured diff from one config version to another (secrets always masked)."""
|
|
68
|
+
|
|
69
|
+
collection_id: str
|
|
70
|
+
from_version: int
|
|
71
|
+
to_version: int
|
|
72
|
+
changes: list[ConfigDiffEntry]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class ConfigVersionRestoreResponse(BaseModel):
|
|
76
|
+
"""The outcome of restoring a config version (always a NEW version, never a history rewrite)."""
|
|
77
|
+
|
|
78
|
+
collection_id: str
|
|
79
|
+
restored_from: int = Field(description="The version whose config was re-applied.")
|
|
80
|
+
version: int = Field(description='The new version written (note "restore of v{N}").')
|
|
81
|
+
needs_reindex: bool = Field(
|
|
82
|
+
description="The collection's derived reindex flag after the restore."
|
|
83
|
+
)
|
|
@@ -208,6 +208,10 @@ class BulkReingestResponse(BaseModel):
|
|
|
208
208
|
default=0,
|
|
209
209
|
description="Documents skipped because an ingestion job was already active for them.",
|
|
210
210
|
)
|
|
211
|
+
skipped_not_replayable: int = Field(
|
|
212
|
+
default=0,
|
|
213
|
+
description="Documents skipped by a replay_from run because they have no persisted IR.",
|
|
214
|
+
)
|
|
211
215
|
jobs: list[ReingestJobHandle] = Field(description="One handle per enqueued run.")
|
|
212
216
|
|
|
213
217
|
|
|
@@ -30,6 +30,7 @@ class JobStatus(BaseModel):
|
|
|
30
30
|
collection_name (str | None): The collection's name, joined at read (None if the
|
|
31
31
|
collection is gone).
|
|
32
32
|
status (str): queued / running / done / failed / cancelled.
|
|
33
|
+
replay_from (str | None): The stage an ingest job replays from (None = a full run).
|
|
33
34
|
cancel_requested (bool): A cooperative stop has been requested; the running job stops at
|
|
34
35
|
its next stage boundary (still 'running' until it does).
|
|
35
36
|
progress (int): 0–100 (completed pipeline nodes over total).
|
|
@@ -83,6 +84,11 @@ class JobStatus(BaseModel):
|
|
|
83
84
|
"'rebuild_index' (a collection-level vector-store rebuild — document_id is null). "
|
|
84
85
|
"Lets a client render a side-job distinctly from a full ingestion."
|
|
85
86
|
)
|
|
87
|
+
replay_from: str | None = Field(
|
|
88
|
+
default=None,
|
|
89
|
+
description="The stage an ingest job replays from on the persisted IR (no re-parse); null "
|
|
90
|
+
"for a full run.",
|
|
91
|
+
)
|
|
86
92
|
cancel_requested: bool = Field(
|
|
87
93
|
default=False,
|
|
88
94
|
description="A cooperative stop has been requested; the running job stops at its next "
|
|
@@ -13,6 +13,9 @@ from .blobs import AsyncBlobs, SyncBlobs
|
|
|
13
13
|
# ------------------- Capabilities resource ------------------- #
|
|
14
14
|
from .capabilities import AsyncCapabilities, SyncCapabilities
|
|
15
15
|
|
|
16
|
+
# ------------------- Collection aliases resource ------------------- #
|
|
17
|
+
from .collection_aliases import AsyncCollectionAliases, SyncCollectionAliases
|
|
18
|
+
|
|
16
19
|
# ------------------- Collections resource ------------------- #
|
|
17
20
|
from .collections import AsyncCollections, SyncCollections
|
|
18
21
|
|
|
@@ -47,6 +50,8 @@ __all__ = [
|
|
|
47
50
|
"SyncAuth",
|
|
48
51
|
"AsyncHealth",
|
|
49
52
|
"SyncHealth",
|
|
53
|
+
"AsyncCollectionAliases",
|
|
54
|
+
"SyncCollectionAliases",
|
|
50
55
|
"AsyncCollections",
|
|
51
56
|
"SyncCollections",
|
|
52
57
|
"AsyncDocuments",
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# ====== Code Summary ======
|
|
2
|
+
# The collection-alias resource — list the aliases (name → current target), create or atomically
|
|
3
|
+
# re-point one (the "switch": every key scoped `alias:<name>` and every client addressing the alias
|
|
4
|
+
# follow it on the next request), and delete one. URL logic lives once in the pure
|
|
5
|
+
# _CollectionAliasesSpecs mixin so the async/sync shells differ ONLY by ``await``.
|
|
6
|
+
|
|
7
|
+
# ====== Local Project Imports ======
|
|
8
|
+
from .._requestspec import RequestSpec
|
|
9
|
+
from ..models.collection_aliases import (
|
|
10
|
+
CollectionAliasModel,
|
|
11
|
+
SetCollectionAliasRequest,
|
|
12
|
+
SetCollectionAliasResponse,
|
|
13
|
+
)
|
|
14
|
+
from ._base import AsyncResource, SyncResource, _ResourceMixin
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _CollectionAliasesSpecs(_ResourceMixin):
|
|
18
|
+
"""Pure ``RequestSpec`` builders for the collection-alias endpoints."""
|
|
19
|
+
|
|
20
|
+
_BASE = "/collection-aliases"
|
|
21
|
+
|
|
22
|
+
def _list_spec(self) -> RequestSpec:
|
|
23
|
+
"""A GET of every alias the caller may see."""
|
|
24
|
+
return RequestSpec("GET", self._BASE)
|
|
25
|
+
|
|
26
|
+
def _set_spec(self, name: str, collection_id: str) -> RequestSpec:
|
|
27
|
+
"""A PUT creating / re-pointing ``name`` to ``collection_id``."""
|
|
28
|
+
body = SetCollectionAliasRequest(collection_id=collection_id)
|
|
29
|
+
return RequestSpec("PUT", f"{self._BASE}/{name}", json=body.model_dump(mode="json"))
|
|
30
|
+
|
|
31
|
+
def _delete_spec(self, name: str) -> RequestSpec:
|
|
32
|
+
"""A DELETE of one alias."""
|
|
33
|
+
return RequestSpec("DELETE", f"{self._BASE}/{name}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AsyncCollectionAliases(AsyncResource, _CollectionAliasesSpecs):
|
|
37
|
+
"""Asynchronous collection aliases."""
|
|
38
|
+
|
|
39
|
+
async def list(self) -> list[CollectionAliasModel]:
|
|
40
|
+
"""
|
|
41
|
+
List the collection aliases whose target the caller may see, by name.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
list[CollectionAliasModel]: Each alias with its current target (id + name).
|
|
45
|
+
"""
|
|
46
|
+
return await self._transport.request(self._list_spec(), list[CollectionAliasModel])
|
|
47
|
+
|
|
48
|
+
async def set(self, name: str, collection_id: str) -> SetCollectionAliasResponse:
|
|
49
|
+
"""
|
|
50
|
+
Create the alias, or atomically re-point it to another collection (the "switch").
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
name (str): The alias slug (lowercase letters, digits, '-', '_').
|
|
54
|
+
collection_id (str): The UUID of the collection the alias must target.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
SetCollectionAliasResponse: The alias now + its previous target (None = created).
|
|
58
|
+
"""
|
|
59
|
+
return await self._transport.request(
|
|
60
|
+
self._set_spec(name, collection_id), SetCollectionAliasResponse
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
async def delete(self, name: str) -> None:
|
|
64
|
+
"""
|
|
65
|
+
Delete a collection alias (keys scoped ``alias:<name>`` then grant nothing through it).
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
name (str): The alias name.
|
|
69
|
+
"""
|
|
70
|
+
await self._transport.request(self._delete_spec(name), type(None))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class SyncCollectionAliases(SyncResource, _CollectionAliasesSpecs):
|
|
74
|
+
"""Synchronous collection aliases."""
|
|
75
|
+
|
|
76
|
+
def list(self) -> list[CollectionAliasModel]:
|
|
77
|
+
"""List the collection aliases (see the async twin)."""
|
|
78
|
+
return self._transport.request(self._list_spec(), list[CollectionAliasModel])
|
|
79
|
+
|
|
80
|
+
def set(self, name: str, collection_id: str) -> SetCollectionAliasResponse:
|
|
81
|
+
"""Create or atomically re-point an alias (see the async twin)."""
|
|
82
|
+
return self._transport.request(
|
|
83
|
+
self._set_spec(name, collection_id), SetCollectionAliasResponse
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def delete(self, name: str) -> None:
|
|
87
|
+
"""Delete a collection alias (see the async twin)."""
|
|
88
|
+
self._transport.request(self._delete_spec(name), type(None))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
__all__ = ["AsyncCollectionAliases", "SyncCollectionAliases"]
|