openrag-sdk 0.3.1__tar.gz → 0.7.0.dev1__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.
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/PKG-INFO +62 -2
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/README.md +60 -1
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/__init__.py +10 -1
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/documents.py +126 -9
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/models.py +57 -2
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk.egg-info/PKG-INFO +62 -2
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk.egg-info/requires.txt +1 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/pyproject.toml +2 -1
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/chat.py +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/client.py +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/exceptions.py +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/knowledge_filters.py +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk/search.py +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk.egg-info/SOURCES.txt +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk.egg-info/dependency_links.txt +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/openrag_sdk.egg-info/top_level.txt +0 -0
- {openrag_sdk-0.3.1 → openrag_sdk-0.7.0.dev1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrag-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0.dev1
|
|
4
4
|
Summary: Official Python SDK for OpenRAG API
|
|
5
5
|
Author: OpenRAG Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,6 +24,7 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
Requires-Dist: httpx>=0.25.0
|
|
26
26
|
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Requires-Dist: idna>=3.15
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
30
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -102,7 +103,8 @@ async for event in await client.chat.create(message="Explain RAG", stream=True):
|
|
|
102
103
|
print(event.delta, end="", flush=True)
|
|
103
104
|
elif event.type == "sources":
|
|
104
105
|
for source in event.sources:
|
|
105
|
-
|
|
106
|
+
page_info = f" (page {source.page})" if source.page else ""
|
|
107
|
+
print(f"\nSource: {source.filename}{page_info}")
|
|
106
108
|
elif event.type == "done":
|
|
107
109
|
chat_id = event.chat_id
|
|
108
110
|
```
|
|
@@ -200,6 +202,64 @@ result = await client.documents.delete("report.pdf")
|
|
|
200
202
|
print(f"Success: {result.success}")
|
|
201
203
|
```
|
|
202
204
|
|
|
205
|
+
## Listing Files
|
|
206
|
+
|
|
207
|
+
`client.documents.list_files()` inventories everything in the knowledge base and
|
|
208
|
+
returns the metadata needed to drive knowledge filters and search.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import json
|
|
212
|
+
|
|
213
|
+
# List the first page of files
|
|
214
|
+
page = await client.documents.list_files(page_size=50)
|
|
215
|
+
for f in page.files:
|
|
216
|
+
print(f"{f.filename} ({f.mimetype}, {f.chunk_count} chunks)")
|
|
217
|
+
|
|
218
|
+
# Cursor-paginate through all files
|
|
219
|
+
after_key = None
|
|
220
|
+
while True:
|
|
221
|
+
page = await client.documents.list_files(page_size=100, after_key=after_key)
|
|
222
|
+
for f in page.files:
|
|
223
|
+
print(f.filename)
|
|
224
|
+
if page.after_key is None:
|
|
225
|
+
break
|
|
226
|
+
after_key = json.dumps(page.after_key)
|
|
227
|
+
|
|
228
|
+
# Filter and sort
|
|
229
|
+
page = await client.documents.list_files(
|
|
230
|
+
connector_type="sharepoint",
|
|
231
|
+
sort_by="indexed_time",
|
|
232
|
+
sort_order="desc",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# List → create knowledge filter workflow
|
|
236
|
+
page = await client.documents.list_files(connector_type="sharepoint")
|
|
237
|
+
filenames = [f.filename for f in page.files]
|
|
238
|
+
result = await client.knowledge_filters.create({
|
|
239
|
+
"name": "SharePoint docs",
|
|
240
|
+
"queryData": {"filters": {"data_sources": filenames}},
|
|
241
|
+
})
|
|
242
|
+
filter_id = result.id
|
|
243
|
+
|
|
244
|
+
# Use the filter in search and chat
|
|
245
|
+
results = await client.search.query("quarterly report", filter_id=filter_id)
|
|
246
|
+
response = await client.chat.create(message="Summarise Q3", filter_id=filter_id)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
For a one-shot listing without managing a cursor, `client.documents.get_all_files()`
|
|
250
|
+
returns all files in a single call — no parameters needed.
|
|
251
|
+
|
|
252
|
+
> **Note:** `get_all_files()` returns at most **500 files**. If your knowledge
|
|
253
|
+
> base contains more than 500 files, use `list_files()` with cursor pagination
|
|
254
|
+
> (`after_key`) to page through the full set.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
# Get all files in a single call (no cursor to track)
|
|
258
|
+
page = await client.documents.get_all_files()
|
|
259
|
+
for f in page.files:
|
|
260
|
+
print(f.filename)
|
|
261
|
+
```
|
|
262
|
+
|
|
203
263
|
## Settings
|
|
204
264
|
|
|
205
265
|
```python
|
|
@@ -72,7 +72,8 @@ async for event in await client.chat.create(message="Explain RAG", stream=True):
|
|
|
72
72
|
print(event.delta, end="", flush=True)
|
|
73
73
|
elif event.type == "sources":
|
|
74
74
|
for source in event.sources:
|
|
75
|
-
|
|
75
|
+
page_info = f" (page {source.page})" if source.page else ""
|
|
76
|
+
print(f"\nSource: {source.filename}{page_info}")
|
|
76
77
|
elif event.type == "done":
|
|
77
78
|
chat_id = event.chat_id
|
|
78
79
|
```
|
|
@@ -170,6 +171,64 @@ result = await client.documents.delete("report.pdf")
|
|
|
170
171
|
print(f"Success: {result.success}")
|
|
171
172
|
```
|
|
172
173
|
|
|
174
|
+
## Listing Files
|
|
175
|
+
|
|
176
|
+
`client.documents.list_files()` inventories everything in the knowledge base and
|
|
177
|
+
returns the metadata needed to drive knowledge filters and search.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
import json
|
|
181
|
+
|
|
182
|
+
# List the first page of files
|
|
183
|
+
page = await client.documents.list_files(page_size=50)
|
|
184
|
+
for f in page.files:
|
|
185
|
+
print(f"{f.filename} ({f.mimetype}, {f.chunk_count} chunks)")
|
|
186
|
+
|
|
187
|
+
# Cursor-paginate through all files
|
|
188
|
+
after_key = None
|
|
189
|
+
while True:
|
|
190
|
+
page = await client.documents.list_files(page_size=100, after_key=after_key)
|
|
191
|
+
for f in page.files:
|
|
192
|
+
print(f.filename)
|
|
193
|
+
if page.after_key is None:
|
|
194
|
+
break
|
|
195
|
+
after_key = json.dumps(page.after_key)
|
|
196
|
+
|
|
197
|
+
# Filter and sort
|
|
198
|
+
page = await client.documents.list_files(
|
|
199
|
+
connector_type="sharepoint",
|
|
200
|
+
sort_by="indexed_time",
|
|
201
|
+
sort_order="desc",
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
# List → create knowledge filter workflow
|
|
205
|
+
page = await client.documents.list_files(connector_type="sharepoint")
|
|
206
|
+
filenames = [f.filename for f in page.files]
|
|
207
|
+
result = await client.knowledge_filters.create({
|
|
208
|
+
"name": "SharePoint docs",
|
|
209
|
+
"queryData": {"filters": {"data_sources": filenames}},
|
|
210
|
+
})
|
|
211
|
+
filter_id = result.id
|
|
212
|
+
|
|
213
|
+
# Use the filter in search and chat
|
|
214
|
+
results = await client.search.query("quarterly report", filter_id=filter_id)
|
|
215
|
+
response = await client.chat.create(message="Summarise Q3", filter_id=filter_id)
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
For a one-shot listing without managing a cursor, `client.documents.get_all_files()`
|
|
219
|
+
returns all files in a single call — no parameters needed.
|
|
220
|
+
|
|
221
|
+
> **Note:** `get_all_files()` returns at most **500 files**. If your knowledge
|
|
222
|
+
> base contains more than 500 files, use `list_files()` with cursor pagination
|
|
223
|
+
> (`after_key`) to page through the full set.
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
# Get all files in a single call (no cursor to track)
|
|
227
|
+
page = await client.documents.get_all_files()
|
|
228
|
+
for f in page.files:
|
|
229
|
+
print(f.filename)
|
|
230
|
+
```
|
|
231
|
+
|
|
173
232
|
## Settings
|
|
174
233
|
|
|
175
234
|
```python
|
|
@@ -49,13 +49,17 @@ from .models import (
|
|
|
49
49
|
DeleteDocumentResponse,
|
|
50
50
|
DeleteKnowledgeFilterResponse,
|
|
51
51
|
DoneEvent,
|
|
52
|
+
FileRecord,
|
|
53
|
+
GetAllFilesResponse,
|
|
52
54
|
GetKnowledgeFilterResponse,
|
|
53
55
|
IngestResponse,
|
|
54
56
|
KnowledgeFilter,
|
|
55
57
|
KnowledgeFilterQueryData,
|
|
56
58
|
KnowledgeFilterSearchResponse,
|
|
57
59
|
KnowledgeSettings,
|
|
60
|
+
ListFilesResponse,
|
|
58
61
|
Message,
|
|
62
|
+
PrincipalLabel,
|
|
59
63
|
SearchFilters,
|
|
60
64
|
SearchResponse,
|
|
61
65
|
SearchResult,
|
|
@@ -68,7 +72,7 @@ from .models import (
|
|
|
68
72
|
UpdateKnowledgeFilterOptions,
|
|
69
73
|
)
|
|
70
74
|
|
|
71
|
-
__version__ = "0.
|
|
75
|
+
__version__ = "0.7.0"
|
|
72
76
|
|
|
73
77
|
__all__ = [
|
|
74
78
|
# Main client
|
|
@@ -82,6 +86,11 @@ __all__ = [
|
|
|
82
86
|
"NotFoundError",
|
|
83
87
|
"ValidationError",
|
|
84
88
|
"ServerError",
|
|
89
|
+
# File models
|
|
90
|
+
"FileRecord",
|
|
91
|
+
"GetAllFilesResponse",
|
|
92
|
+
"ListFilesResponse",
|
|
93
|
+
"PrincipalLabel",
|
|
85
94
|
# Models
|
|
86
95
|
"ChatResponse",
|
|
87
96
|
"ContentEvent",
|
|
@@ -5,7 +5,14 @@ from pathlib import Path
|
|
|
5
5
|
from typing import TYPE_CHECKING, BinaryIO
|
|
6
6
|
|
|
7
7
|
from .exceptions import NotFoundError
|
|
8
|
-
from .models import
|
|
8
|
+
from .models import (
|
|
9
|
+
DeleteDocumentResponse,
|
|
10
|
+
FileRecord,
|
|
11
|
+
GetAllFilesResponse,
|
|
12
|
+
IngestResponse,
|
|
13
|
+
IngestTaskStatus,
|
|
14
|
+
ListFilesResponse,
|
|
15
|
+
)
|
|
9
16
|
|
|
10
17
|
if TYPE_CHECKING:
|
|
11
18
|
from .client import OpenRAGClient
|
|
@@ -125,27 +132,46 @@ class DocumentsClient:
|
|
|
125
132
|
await asyncio.sleep(poll_interval)
|
|
126
133
|
elapsed += poll_interval
|
|
127
134
|
|
|
128
|
-
raise TimeoutError(
|
|
135
|
+
raise TimeoutError(
|
|
136
|
+
f"Ingestion task {task_id} did not complete within {timeout}s"
|
|
137
|
+
)
|
|
129
138
|
|
|
130
|
-
async def delete(
|
|
139
|
+
async def delete(
|
|
140
|
+
self,
|
|
141
|
+
filename: str | None = None,
|
|
142
|
+
*,
|
|
143
|
+
filter_id: str | None = None,
|
|
144
|
+
) -> DeleteDocumentResponse:
|
|
131
145
|
"""
|
|
132
|
-
Delete
|
|
146
|
+
Delete document(s) from the knowledge base.
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
filename:
|
|
148
|
+
Provide exactly one of:
|
|
149
|
+
filename: delete all chunks for that filename.
|
|
150
|
+
filter_id: delete chunks for each filename in the filter's data_sources.
|
|
136
151
|
|
|
137
152
|
Returns:
|
|
138
153
|
DeleteDocumentResponse with deleted chunk count.
|
|
139
154
|
"""
|
|
155
|
+
if bool(filename) == bool(filter_id):
|
|
156
|
+
raise ValueError("Provide exactly one of `filename` or `filter_id`")
|
|
157
|
+
|
|
158
|
+
body: dict[str, str] = {}
|
|
159
|
+
if filename is not None:
|
|
160
|
+
body["filename"] = filename
|
|
161
|
+
if filter_id is not None:
|
|
162
|
+
body["filter_id"] = filter_id
|
|
163
|
+
|
|
140
164
|
try:
|
|
141
165
|
response = await self._client._request(
|
|
142
166
|
"DELETE",
|
|
143
167
|
"/api/v1/documents",
|
|
144
|
-
json=
|
|
168
|
+
json=body,
|
|
145
169
|
)
|
|
146
170
|
except NotFoundError as e:
|
|
147
|
-
# Keep delete idempotent for SDK callers: a missing document is not
|
|
148
|
-
|
|
171
|
+
# Keep delete idempotent for SDK callers: a missing document is not
|
|
172
|
+
# an exception.
|
|
173
|
+
# Filter-not-found 404s still raise because the filter_id is caller input.
|
|
174
|
+
if filename is not None and getattr(e, "status_code", None) == 404:
|
|
149
175
|
return DeleteDocumentResponse(
|
|
150
176
|
success=False,
|
|
151
177
|
deleted_chunks=0,
|
|
@@ -157,3 +183,94 @@ class DocumentsClient:
|
|
|
157
183
|
|
|
158
184
|
data = response.json()
|
|
159
185
|
return DeleteDocumentResponse(**data)
|
|
186
|
+
|
|
187
|
+
async def list_files(
|
|
188
|
+
self,
|
|
189
|
+
*,
|
|
190
|
+
page: int = 1,
|
|
191
|
+
page_size: int = 25,
|
|
192
|
+
sort_by: str = "filename",
|
|
193
|
+
sort_order: str = "asc",
|
|
194
|
+
connector_type: str | None = None,
|
|
195
|
+
mimetype: str | None = None,
|
|
196
|
+
owner: str | None = None,
|
|
197
|
+
search: str | None = None,
|
|
198
|
+
after_key: str | None = None,
|
|
199
|
+
) -> ListFilesResponse:
|
|
200
|
+
"""
|
|
201
|
+
List ingested files with cursor-based composite-aggregation pagination (v2).
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
page: Page number (display only; use after_key for cursor navigation).
|
|
205
|
+
page_size: Number of files per page (1–500, default 25).
|
|
206
|
+
sort_by: Field to sort by. One of: filename, file_size, mimetype,
|
|
207
|
+
indexed_time, connector_type, chunk_count, owner.
|
|
208
|
+
sort_order: "asc" or "desc".
|
|
209
|
+
connector_type: Filter to files from a specific connector type.
|
|
210
|
+
mimetype: Filter to files with a specific MIME type.
|
|
211
|
+
owner: Filter to files owned by a specific user ID.
|
|
212
|
+
search: Substring/prefix match against filename.
|
|
213
|
+
after_key: JSON-encoded composite cursor from a previous response's
|
|
214
|
+
after_key field. Pass to fetch the next page.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
ListFilesResponse with files list, approximate total, and next
|
|
218
|
+
after_key cursor.
|
|
219
|
+
"""
|
|
220
|
+
params: dict[str, str | int] = {
|
|
221
|
+
"page": page,
|
|
222
|
+
"page_size": page_size,
|
|
223
|
+
"sort_by": sort_by,
|
|
224
|
+
"sort_order": sort_order,
|
|
225
|
+
}
|
|
226
|
+
if connector_type is not None:
|
|
227
|
+
params["connector_type"] = connector_type
|
|
228
|
+
if mimetype is not None:
|
|
229
|
+
params["mimetype"] = mimetype
|
|
230
|
+
if owner is not None:
|
|
231
|
+
params["owner"] = owner
|
|
232
|
+
if search is not None:
|
|
233
|
+
params["search"] = search
|
|
234
|
+
if after_key is not None:
|
|
235
|
+
params["after_key"] = after_key
|
|
236
|
+
|
|
237
|
+
response = await self._client._request(
|
|
238
|
+
"GET",
|
|
239
|
+
"/api/v2/files",
|
|
240
|
+
params=params,
|
|
241
|
+
)
|
|
242
|
+
data = response.json()
|
|
243
|
+
return ListFilesResponse(
|
|
244
|
+
files=[FileRecord(**f) for f in data.get("files", [])],
|
|
245
|
+
total=data.get("total", 0),
|
|
246
|
+
is_approximate=data.get("is_approximate", True),
|
|
247
|
+
page=data.get("page", page),
|
|
248
|
+
page_size=data.get("page_size", page_size),
|
|
249
|
+
after_key=data.get("after_key"),
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
async def get_all_files(self) -> GetAllFilesResponse:
|
|
253
|
+
"""
|
|
254
|
+
Return all ingested files (v1).
|
|
255
|
+
|
|
256
|
+
No parameters — just returns everything in the knowledge base.
|
|
257
|
+
|
|
258
|
+
Note:
|
|
259
|
+
Returns at most 500 files. If your knowledge base contains more
|
|
260
|
+
than 500 files, use ``list_files()`` with cursor pagination
|
|
261
|
+
(``after_key``) to page through the full set.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
GetAllFilesResponse with files list, total count, page, and page_size.
|
|
265
|
+
"""
|
|
266
|
+
response = await self._client._request(
|
|
267
|
+
"GET",
|
|
268
|
+
"/api/v1/files/get_all",
|
|
269
|
+
)
|
|
270
|
+
data = response.json()
|
|
271
|
+
return GetAllFilesResponse(
|
|
272
|
+
files=[FileRecord(**f) for f in data.get("files", [])],
|
|
273
|
+
total=data.get("total", 0),
|
|
274
|
+
page=data.get("page", 1),
|
|
275
|
+
page_size=data.get("page_size", 500),
|
|
276
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""OpenRAG SDK data models."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from typing import Any, Literal
|
|
3
|
+
from typing import Literal
|
|
5
4
|
|
|
6
5
|
from pydantic import BaseModel, Field
|
|
7
6
|
|
|
@@ -98,6 +97,10 @@ class DeleteDocumentResponse(BaseModel):
|
|
|
98
97
|
filename: str | None = None
|
|
99
98
|
message: str | None = None
|
|
100
99
|
error: str | None = None
|
|
100
|
+
# Populated when deleting by filter_id — one entry per resolved data_source.
|
|
101
|
+
filenames: list[str] | None = None
|
|
102
|
+
filter_id: str | None = None
|
|
103
|
+
per_file: list[dict] | None = None
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
# Chat history models
|
|
@@ -281,3 +284,55 @@ class DeleteKnowledgeFilterResponse(BaseModel):
|
|
|
281
284
|
|
|
282
285
|
success: bool
|
|
283
286
|
error: str | None = None
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class PrincipalLabel(BaseModel):
|
|
290
|
+
"""A principal label entry in a file's ACL (connector-ingested files only)."""
|
|
291
|
+
|
|
292
|
+
principal: str
|
|
293
|
+
kind: str
|
|
294
|
+
provider: str
|
|
295
|
+
display_name: str = ""
|
|
296
|
+
email: str = ""
|
|
297
|
+
external_id: str = ""
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class FileRecord(BaseModel):
|
|
301
|
+
"""A single ingested file record returned by the list-files endpoint."""
|
|
302
|
+
|
|
303
|
+
filename: str
|
|
304
|
+
document_id: str
|
|
305
|
+
mimetype: str
|
|
306
|
+
file_size: int
|
|
307
|
+
source_url: str
|
|
308
|
+
owner: str
|
|
309
|
+
owner_name: str
|
|
310
|
+
owner_email: str
|
|
311
|
+
connector_type: str
|
|
312
|
+
embedding_model: str
|
|
313
|
+
embedding_dimensions: int | None = None
|
|
314
|
+
indexed_time: str
|
|
315
|
+
chunk_count: int
|
|
316
|
+
allowed_users: list[str] = Field(default_factory=list)
|
|
317
|
+
allowed_groups: list[str] = Field(default_factory=list)
|
|
318
|
+
allowed_principal_labels: list[PrincipalLabel] = Field(default_factory=list)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
class GetAllFilesResponse(BaseModel):
|
|
322
|
+
"""Response from GET /v1/files/get_all — offset pagination, no cursor."""
|
|
323
|
+
|
|
324
|
+
files: list[FileRecord]
|
|
325
|
+
total: int
|
|
326
|
+
page: int
|
|
327
|
+
page_size: int
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class ListFilesResponse(BaseModel):
|
|
331
|
+
"""Response from GET /v2/files — cursor-based composite-aggregation pagination."""
|
|
332
|
+
|
|
333
|
+
files: list[FileRecord]
|
|
334
|
+
total: int
|
|
335
|
+
is_approximate: bool
|
|
336
|
+
page: int
|
|
337
|
+
page_size: int
|
|
338
|
+
after_key: dict | None = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openrag-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0.dev1
|
|
4
4
|
Summary: Official Python SDK for OpenRAG API
|
|
5
5
|
Author: OpenRAG Team
|
|
6
6
|
License-Expression: MIT
|
|
@@ -24,6 +24,7 @@ Requires-Python: >=3.10
|
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
Requires-Dist: httpx>=0.25.0
|
|
26
26
|
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Requires-Dist: idna>=3.15
|
|
27
28
|
Provides-Extra: dev
|
|
28
29
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
30
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -102,7 +103,8 @@ async for event in await client.chat.create(message="Explain RAG", stream=True):
|
|
|
102
103
|
print(event.delta, end="", flush=True)
|
|
103
104
|
elif event.type == "sources":
|
|
104
105
|
for source in event.sources:
|
|
105
|
-
|
|
106
|
+
page_info = f" (page {source.page})" if source.page else ""
|
|
107
|
+
print(f"\nSource: {source.filename}{page_info}")
|
|
106
108
|
elif event.type == "done":
|
|
107
109
|
chat_id = event.chat_id
|
|
108
110
|
```
|
|
@@ -200,6 +202,64 @@ result = await client.documents.delete("report.pdf")
|
|
|
200
202
|
print(f"Success: {result.success}")
|
|
201
203
|
```
|
|
202
204
|
|
|
205
|
+
## Listing Files
|
|
206
|
+
|
|
207
|
+
`client.documents.list_files()` inventories everything in the knowledge base and
|
|
208
|
+
returns the metadata needed to drive knowledge filters and search.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import json
|
|
212
|
+
|
|
213
|
+
# List the first page of files
|
|
214
|
+
page = await client.documents.list_files(page_size=50)
|
|
215
|
+
for f in page.files:
|
|
216
|
+
print(f"{f.filename} ({f.mimetype}, {f.chunk_count} chunks)")
|
|
217
|
+
|
|
218
|
+
# Cursor-paginate through all files
|
|
219
|
+
after_key = None
|
|
220
|
+
while True:
|
|
221
|
+
page = await client.documents.list_files(page_size=100, after_key=after_key)
|
|
222
|
+
for f in page.files:
|
|
223
|
+
print(f.filename)
|
|
224
|
+
if page.after_key is None:
|
|
225
|
+
break
|
|
226
|
+
after_key = json.dumps(page.after_key)
|
|
227
|
+
|
|
228
|
+
# Filter and sort
|
|
229
|
+
page = await client.documents.list_files(
|
|
230
|
+
connector_type="sharepoint",
|
|
231
|
+
sort_by="indexed_time",
|
|
232
|
+
sort_order="desc",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# List → create knowledge filter workflow
|
|
236
|
+
page = await client.documents.list_files(connector_type="sharepoint")
|
|
237
|
+
filenames = [f.filename for f in page.files]
|
|
238
|
+
result = await client.knowledge_filters.create({
|
|
239
|
+
"name": "SharePoint docs",
|
|
240
|
+
"queryData": {"filters": {"data_sources": filenames}},
|
|
241
|
+
})
|
|
242
|
+
filter_id = result.id
|
|
243
|
+
|
|
244
|
+
# Use the filter in search and chat
|
|
245
|
+
results = await client.search.query("quarterly report", filter_id=filter_id)
|
|
246
|
+
response = await client.chat.create(message="Summarise Q3", filter_id=filter_id)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
For a one-shot listing without managing a cursor, `client.documents.get_all_files()`
|
|
250
|
+
returns all files in a single call — no parameters needed.
|
|
251
|
+
|
|
252
|
+
> **Note:** `get_all_files()` returns at most **500 files**. If your knowledge
|
|
253
|
+
> base contains more than 500 files, use `list_files()` with cursor pagination
|
|
254
|
+
> (`after_key`) to page through the full set.
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
# Get all files in a single call (no cursor to track)
|
|
258
|
+
page = await client.documents.get_all_files()
|
|
259
|
+
for f in page.files:
|
|
260
|
+
print(f.filename)
|
|
261
|
+
```
|
|
262
|
+
|
|
203
263
|
## Settings
|
|
204
264
|
|
|
205
265
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openrag-sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.7.0.dev1"
|
|
8
8
|
description = "Official Python SDK for OpenRAG API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -29,6 +29,7 @@ classifiers = [
|
|
|
29
29
|
dependencies = [
|
|
30
30
|
"httpx>=0.25.0",
|
|
31
31
|
"pydantic>=2.0.0",
|
|
32
|
+
"idna>=3.15",
|
|
32
33
|
]
|
|
33
34
|
|
|
34
35
|
[project.optional-dependencies]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|