nvisionx 0.1.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.
- nvisionx-0.1.0/.gitignore +13 -0
- nvisionx-0.1.0/CHANGELOG.md +6 -0
- nvisionx-0.1.0/LICENSE +21 -0
- nvisionx-0.1.0/PKG-INFO +349 -0
- nvisionx-0.1.0/README.md +321 -0
- nvisionx-0.1.0/pyproject.toml +77 -0
- nvisionx-0.1.0/src/nvisionx/__init__.py +51 -0
- nvisionx-0.1.0/src/nvisionx/_base.py +21 -0
- nvisionx-0.1.0/src/nvisionx/_base_client.py +149 -0
- nvisionx-0.1.0/src/nvisionx/_client.py +254 -0
- nvisionx-0.1.0/src/nvisionx/_errors.py +136 -0
- nvisionx-0.1.0/src/nvisionx/_files.py +49 -0
- nvisionx-0.1.0/src/nvisionx/_models.py +1060 -0
- nvisionx-0.1.0/src/nvisionx/_pagination.py +72 -0
- nvisionx-0.1.0/src/nvisionx/_streaming.py +156 -0
- nvisionx-0.1.0/src/nvisionx/_version.py +1 -0
- nvisionx-0.1.0/src/nvisionx/py.typed +0 -0
- nvisionx-0.1.0/src/nvisionx/resources/__init__.py +0 -0
- nvisionx-0.1.0/src/nvisionx/resources/_resource.py +34 -0
- nvisionx-0.1.0/src/nvisionx/resources/agents.py +55 -0
- nvisionx-0.1.0/src/nvisionx/resources/billing.py +16 -0
- nvisionx-0.1.0/src/nvisionx/resources/budgets.py +118 -0
- nvisionx-0.1.0/src/nvisionx/resources/chat.py +114 -0
- nvisionx-0.1.0/src/nvisionx/resources/documents.py +218 -0
- nvisionx-0.1.0/src/nvisionx/resources/jobs.py +64 -0
- nvisionx-0.1.0/src/nvisionx/resources/mcp.py +25 -0
- nvisionx-0.1.0/src/nvisionx/resources/search.py +57 -0
- nvisionx-0.1.0/src/nvisionx/resources/sources.py +288 -0
- nvisionx-0.1.0/src/nvisionx/types.py +186 -0
nvisionx-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NVISIONx
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
nvisionx-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nvisionx
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for the NVISIONx Nx Platform API: ingest, search, agents and chat over your business records.
|
|
5
|
+
Project-URL: Homepage, https://platform.us.nvisionx.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/Nvision-x/nx-saas-platform-api-python
|
|
7
|
+
Project-URL: Issues, https://github.com/Nvision-x/nx-saas-platform-api-python/issues
|
|
8
|
+
Author: NVISIONx
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: api,mcp,nvisionx,nx,rag,records,sdk,search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx<1,>=0.27
|
|
25
|
+
Requires-Dist: pydantic<3,>=2.7
|
|
26
|
+
Requires-Dist: typing-extensions>=4.7
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# NVISIONx Nx Platform Python library
|
|
30
|
+
|
|
31
|
+
[](https://github.com/Nvision-x/nx-saas-platform-api-python/actions/workflows/ci.yml)
|
|
32
|
+
|
|
33
|
+
The official Python client for the [NVISIONx Nx Platform](https://platform.us.nvisionx.ai) API
|
|
34
|
+
([API docs](https://platform.us.nvisionx.ai/docs)). Nx turns the documents in your data
|
|
35
|
+
estate into **business records** (record type, owner, sensitivity, retention), makes them searchable, and exposes them
|
|
36
|
+
to agents.
|
|
37
|
+
|
|
38
|
+
This library covers the data plane, which is everything an API token can call: ingest, documents, jobs, search,
|
|
39
|
+
hosted agents, chat, data sources, MCP settings, usage and budgets. It ships typed models, sync and async clients, automatic retries,
|
|
40
|
+
cursor pagination and streaming chat.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
pip install nvisionx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Python 3.10 or newer.
|
|
49
|
+
|
|
50
|
+
## Quickstart
|
|
51
|
+
|
|
52
|
+
Create an API token in the Nx console under **API tokens**. A token belongs to one workspace, and every call made with
|
|
53
|
+
it acts on that workspace.
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
export NX_API_KEY="nx_live_…"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from nvisionx import Nx
|
|
61
|
+
|
|
62
|
+
nx = Nx()
|
|
63
|
+
|
|
64
|
+
job = nx.documents.ingest(file="Northwind_MSA_2024.pdf", metadata={"department": "legal"})
|
|
65
|
+
job = nx.jobs.wait(job.id)
|
|
66
|
+
print(job.status, job.record_types)
|
|
67
|
+
|
|
68
|
+
results = nx.search("indemnification cap in vendor contracts", limit=5)
|
|
69
|
+
for result in results.results:
|
|
70
|
+
print(result.title, result.record_type, result.score)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuration
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from nvisionx import Nx
|
|
77
|
+
|
|
78
|
+
nx = Nx(
|
|
79
|
+
api_key="nx_live_…",
|
|
80
|
+
timeout=30.0,
|
|
81
|
+
max_retries=3,
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
| Option | Environment variable | Default |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `api_key` | `NX_API_KEY` | required |
|
|
88
|
+
| `base_url` | `NX_BASE_URL` | `https://platform.us.nvisionx.ai` |
|
|
89
|
+
| `timeout` | | 60 s; 300 s for agent runs and chat answers; no write timeout for uploads |
|
|
90
|
+
| `max_retries` | | 2 |
|
|
91
|
+
| `default_headers` | | |
|
|
92
|
+
| `http_client` | | your own `httpx.Client` / `httpx.AsyncClient` (proxies, transports, limits) |
|
|
93
|
+
|
|
94
|
+
`with_options` returns a copy that shares the connection pool:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
nx.with_options(timeout=120.0, max_retries=0).search("retention schedule")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Ingest
|
|
101
|
+
|
|
102
|
+
Pass `url=` for a document Nx should fetch, or `file=` to upload. `file=` accepts a path, bytes, an open binary file,
|
|
103
|
+
a `(filename, content)` or `(filename, content, content_type)` tuple, or a list of any of these.
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
job = nx.documents.ingest(url="https://files.example.com/signed/msa.pdf")
|
|
107
|
+
|
|
108
|
+
job = nx.documents.ingest(
|
|
109
|
+
file=["contracts/msa.pdf", ("notes.txt", b"Renewal due Q3", "text/plain")],
|
|
110
|
+
metadata={"department": "legal"},
|
|
111
|
+
classify=True,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
job = nx.jobs.wait(job.id, poll_interval=2.0, timeout=600.0)
|
|
115
|
+
if job.status == "completed":
|
|
116
|
+
for doc in job.classified or []:
|
|
117
|
+
print(doc)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`jobs.wait` returns the job once it is `completed` or `failed`, and raises `JobTimeoutError` if it is neither by the
|
|
121
|
+
timeout.
|
|
122
|
+
|
|
123
|
+
## Documents
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
for doc in nx.documents.list(source_id="src_123", q="msa"):
|
|
127
|
+
print(doc.name, doc.size, doc.ingest_status)
|
|
128
|
+
|
|
129
|
+
detail = nx.documents.get("doc_123")
|
|
130
|
+
parsed = nx.documents.parsed("doc_123")
|
|
131
|
+
print(parsed.parser, parsed.page_count, parsed.text[:200])
|
|
132
|
+
|
|
133
|
+
print(nx.documents.summary())
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`documents.parsed` raises `NotFoundError` until the document has been parsed.
|
|
137
|
+
|
|
138
|
+
Delete one document with `documents.delete`, or up to 200 at once by id. To delete by filter, run a dry run first,
|
|
139
|
+
then send its count back as `expected_count`. If the matching documents change in between, nothing is deleted and the
|
|
140
|
+
call raises `ConflictError`:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
nx.documents.delete_many(ids=["doc_1", "doc_2"])
|
|
144
|
+
|
|
145
|
+
preview = nx.documents.delete_many(filter={"source_id": "src_123", "q": "draft"}, dry_run=True)
|
|
146
|
+
nx.documents.delete_many(filter={"source_id": "src_123", "q": "draft"}, expected_count=preview.matched)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Search
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from nvisionx.types import SearchFilters
|
|
153
|
+
|
|
154
|
+
response = nx.search(
|
|
155
|
+
"termination for convenience",
|
|
156
|
+
mode="hybrid",
|
|
157
|
+
filters=SearchFilters(record_type=["contract"], sensitivity=["confidential"]),
|
|
158
|
+
limit=10,
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`filters` also accepts a plain `dict`.
|
|
163
|
+
|
|
164
|
+
## Chat
|
|
165
|
+
|
|
166
|
+
Conversations keep context across questions. Answers cite records with `[n]` markers that match
|
|
167
|
+
`citations[].marker`.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
conversation = nx.chat.conversations.create(title="Vendor risk")
|
|
171
|
+
|
|
172
|
+
answer = nx.chat.messages.create(conversation.id, content="Which vendor contracts cap indemnity below $2M?")
|
|
173
|
+
print(answer.content)
|
|
174
|
+
for citation in answer.citations or []:
|
|
175
|
+
print(citation.marker, citation.title)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Stream the answer as the agent works:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
with nx.chat.messages.stream(conversation.id, content="Summarize the Northwind MSA") as stream:
|
|
182
|
+
for text in stream.text_stream:
|
|
183
|
+
print(text, end="", flush=True)
|
|
184
|
+
message = stream.until_done()
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Iterate the stream itself to see every `ChatStreamEvent`: `status`, `step`, `delta` and `complete`. A failed answer
|
|
188
|
+
raises `StreamError`.
|
|
189
|
+
|
|
190
|
+
## Agents
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
run = nx.agents.run(
|
|
194
|
+
"Which vendor contracts cap indemnity below $2M?",
|
|
195
|
+
tools=["search_records", "get_record"],
|
|
196
|
+
max_tool_calls=10,
|
|
197
|
+
)
|
|
198
|
+
print(run.answer, run.citations, run.cost_usd)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## MCP
|
|
202
|
+
|
|
203
|
+
To connect an agent framework to Nx over the [Model Context Protocol](https://modelcontextprotocol.io), use the
|
|
204
|
+
official `mcp` package with the hosted endpoint and your token:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from mcp import ClientSession
|
|
208
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
209
|
+
|
|
210
|
+
async with streamablehttp_client(
|
|
211
|
+
"https://platform.us.nvisionx.ai/mcp", headers={"Authorization": f"Bearer {api_key}"}
|
|
212
|
+
) as (read, write, _):
|
|
213
|
+
async with ClientSession(read, write) as session:
|
|
214
|
+
await session.initialize()
|
|
215
|
+
tools = await session.list_tools()
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
This library manages the workspace's MCP server:
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
server = nx.mcp.get()
|
|
222
|
+
nx.mcp.update_tool("ingest_document", enabled=False)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Data sources
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
for source in nx.sources.list().data:
|
|
229
|
+
print(source.name, source.provider, source.status)
|
|
230
|
+
|
|
231
|
+
nx.sources.sync("src_123")
|
|
232
|
+
estate = nx.sources.estate("src_123")
|
|
233
|
+
for node in nx.sources.nodes("src_123", q="contracts"):
|
|
234
|
+
print(node)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Connecting a new source is an OAuth consent flow: `sources.authorizations.create`, send the person to the returned
|
|
238
|
+
URL, then `sources.authorizations.verify` with the callback parameters and `sources.create`.
|
|
239
|
+
|
|
240
|
+
## Usage and budgets
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
usage = nx.billing.usage(period="2026-10")
|
|
244
|
+
print(usage.total_usd, usage.forecast_usd)
|
|
245
|
+
|
|
246
|
+
nx.budgets.create(scope={"type": "workspace"}, type="soft", limit_usd=500, alert_thresholds=[50, 80, 100])
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Pagination
|
|
250
|
+
|
|
251
|
+
List methods return a page. Iterating a page fetches the following pages as needed:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
for job in nx.jobs.list(status="completed"):
|
|
255
|
+
print(job.id)
|
|
256
|
+
|
|
257
|
+
page = nx.jobs.list(limit=20)
|
|
258
|
+
print(page.data, page.next_cursor)
|
|
259
|
+
if page.has_next_page():
|
|
260
|
+
page = page.next_page()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Async
|
|
264
|
+
|
|
265
|
+
`AsyncNx` has the same surface:
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
import asyncio
|
|
269
|
+
from nvisionx import AsyncNx
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
async def main() -> None:
|
|
273
|
+
async with AsyncNx() as nx:
|
|
274
|
+
results = await nx.search("data processing agreement")
|
|
275
|
+
|
|
276
|
+
async for job in nx.jobs.list():
|
|
277
|
+
print(job.id)
|
|
278
|
+
|
|
279
|
+
page = await nx.jobs.list(limit=10)
|
|
280
|
+
|
|
281
|
+
stream = await nx.chat.messages.stream("conv_123", content="What changed in the MSA?")
|
|
282
|
+
async with stream:
|
|
283
|
+
async for text in stream.text_stream:
|
|
284
|
+
print(text, end="")
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
asyncio.run(main())
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Errors
|
|
291
|
+
|
|
292
|
+
Every non-2xx response raises a subclass of `APIStatusError`. Each one carries `status_code`, `type` (the API's error
|
|
293
|
+
type), `message`, `detail` (the full typed error body) and `response`.
|
|
294
|
+
|
|
295
|
+
| Status | Exception | Error types |
|
|
296
|
+
| --- | --- | --- |
|
|
297
|
+
| 400, 413 | `BadRequestError` | `invalid_request` |
|
|
298
|
+
| 401 | `AuthenticationError` | `unauthenticated` |
|
|
299
|
+
| 402 | `PaymentRequiredError` | `budget_exceeded`, `spend_limit_reached`, `insufficient_balance` |
|
|
300
|
+
| 403 | `PermissionDeniedError` | `insufficient_scope`, `permission_denied` |
|
|
301
|
+
| 404 | `NotFoundError` | `not_found` |
|
|
302
|
+
| 409 | `ConflictError` | `conflict` |
|
|
303
|
+
| 422 | `UnprocessableEntityError` | `unprocessable` |
|
|
304
|
+
| 429 | `RateLimitError` | `rate_limited` |
|
|
305
|
+
| 5xx | `InternalServerError` | `internal_error` |
|
|
306
|
+
|
|
307
|
+
Network failures raise `APIConnectionError` (or `APITimeoutError`). Everything derives from `NxError`.
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from nvisionx import PaymentRequiredError, PermissionDeniedError
|
|
311
|
+
|
|
312
|
+
try:
|
|
313
|
+
nx.search("board minutes")
|
|
314
|
+
except PermissionDeniedError as e:
|
|
315
|
+
print("token is missing scope", e.required_scope)
|
|
316
|
+
except PaymentRequiredError as e:
|
|
317
|
+
print(e.type, e.detail.resolve if e.detail else e.message)
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Retries and idempotency
|
|
321
|
+
|
|
322
|
+
Connection errors, timeouts, 408, 429 and 5xx responses are retried up to `max_retries` times. Retries back off
|
|
323
|
+
exponentially, or wait as long as the `Retry-After` header says (up to 60 s). Every `POST`, `PUT`, `PATCH` and `DELETE`
|
|
324
|
+
sends an `Idempotency-Key`, and the key stays the same across retries, so a retried write is never applied twice. To
|
|
325
|
+
control the key yourself, for example to deduplicate across processes, pass it to `ingest`:
|
|
326
|
+
|
|
327
|
+
```python
|
|
328
|
+
nx.documents.ingest(url=url, idempotency_key=f"ingest-{document_id}")
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Models
|
|
332
|
+
|
|
333
|
+
Responses are [Pydantic](https://docs.pydantic.dev) models, and every model is importable from `nvisionx.types`. Fields
|
|
334
|
+
the API adds after a release are kept: read them with `model.model_extra` or `model.model_dump()`.
|
|
335
|
+
|
|
336
|
+
## Development
|
|
337
|
+
|
|
338
|
+
```sh
|
|
339
|
+
python -m venv .venv && source .venv/bin/activate
|
|
340
|
+
pip install -e . --group dev
|
|
341
|
+
make check
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The models in `src/nvisionx/_models.py` and the exports in `src/nvisionx/types.py` are generated from
|
|
345
|
+
`openapi/openapi.yaml`, so don't edit them by hand. Run `make generate` after changing the spec.
|
|
346
|
+
|
|
347
|
+
## License
|
|
348
|
+
|
|
349
|
+
[MIT](LICENSE)
|