openrag-sdk 0.4.0.dev0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openrag-sdk
3
- Version: 0.4.0.dev0
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
- print(f"\nSource: {source.filename}")
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
- print(f"\nSource: {source.filename}")
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.4.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 DeleteDocumentResponse, IngestResponse, IngestTaskStatus
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
@@ -176,3 +183,94 @@ class DocumentsClient:
176
183
 
177
184
  data = response.json()
178
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
+ )
@@ -284,3 +284,55 @@ class DeleteKnowledgeFilterResponse(BaseModel):
284
284
 
285
285
  success: bool
286
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.4.0.dev0
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
- print(f"\nSource: {source.filename}")
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
@@ -1,5 +1,6 @@
1
1
  httpx>=0.25.0
2
2
  pydantic>=2.0.0
3
+ idna>=3.15
3
4
 
4
5
  [dev]
5
6
  pytest>=7.0.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openrag-sdk"
7
- version = "0.4.0.dev0"
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]