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.
@@ -0,0 +1,13 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .coverage
11
+ htmlcov/
12
+ .DS_Store
13
+ .env
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ First release. Sync and async clients for the Nx Platform API data plane: ingest, documents (list, parsed text,
6
+ summary, delete), jobs, search, agents, chat (with streaming), data sources, MCP settings, usage and budgets.
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.
@@ -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
+ [![CI](https://github.com/Nvision-x/nx-saas-platform-api-python/actions/workflows/ci.yml/badge.svg)](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)