ndi-cli 0.12.0__tar.gz → 0.13.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.
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/PKG-INFO +3 -2
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/README.md +1 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/pyproject.toml +2 -2
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/pyproject.toml.orig +2 -2
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/__init__.py +1 -1
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_render.py +7 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_workspace.py +43 -8
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/LICENSE +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_cli.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_config.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_docops.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_entrypoint.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_jobs.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_local.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_login.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_output.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_sources.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/_tools.py +0 -0
- {ndi_cli-0.12.0 → ndi_cli-0.13.0}/src/ndi_cli/commands.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ndi-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: NDI platform CLI: document operations, jobs, workspaces, and the agent workspace tools
|
|
5
5
|
Keywords: ndi,cli,document-intelligence,coding-agents
|
|
6
6
|
Author: Nace AI
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.14
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
18
|
Classifier: Typing :: Typed
|
|
19
|
-
Requires-Dist: ndi-sdk>=0.
|
|
19
|
+
Requires-Dist: ndi-sdk>=0.28,<1
|
|
20
20
|
Requires-Dist: truststore>=0.10.4,<1
|
|
21
21
|
Requires-Python: >=3.11
|
|
22
22
|
Project-URL: Homepage, https://ndi-api.nace.ai
|
|
@@ -53,6 +53,7 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
|
|
|
53
53
|
| `ndi ground SOURCE --target id=TEXT` | Locate quoted text (standalone quotes only; after search, poll `grounding_job_ids`) |
|
|
54
54
|
| `ndi job ID` / `ndi jobs` / `ndi cancel ID` | Inspect or cancel jobs |
|
|
55
55
|
| `ndi workspace create\|list\|get\|stats\|delete\|use` | Workspace lifecycle |
|
|
56
|
+
| `ndi workspace global-context [--set TEXT\|--file PATH\|--clear]` | Show, replace, or remove (`--clear`) the background every query without `--context` runs with; `--no-context` on a query runs with none |
|
|
56
57
|
| `ndi files upload\|list\|get\|delete` | Workspace files (a directory walks nested files; `--ingest` then queues ingestion) |
|
|
57
58
|
| `ndi ingest` | Queue ingestion |
|
|
58
59
|
| `ndi automatic-search QUERY` | Service answers with fact search or an agent over files, tables and the catalog; prints the route, the output job id, and the answer (plus the catalog table when there is one) |
|
|
@@ -29,6 +29,7 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
|
|
|
29
29
|
| `ndi ground SOURCE --target id=TEXT` | Locate quoted text (standalone quotes only; after search, poll `grounding_job_ids`) |
|
|
30
30
|
| `ndi job ID` / `ndi jobs` / `ndi cancel ID` | Inspect or cancel jobs |
|
|
31
31
|
| `ndi workspace create\|list\|get\|stats\|delete\|use` | Workspace lifecycle |
|
|
32
|
+
| `ndi workspace global-context [--set TEXT\|--file PATH\|--clear]` | Show, replace, or remove (`--clear`) the background every query without `--context` runs with; `--no-context` on a query runs with none |
|
|
32
33
|
| `ndi files upload\|list\|get\|delete` | Workspace files (a directory walks nested files; `--ingest` then queues ingestion) |
|
|
33
34
|
| `ndi ingest` | Queue ingestion |
|
|
34
35
|
| `ndi automatic-search QUERY` | Service answers with fact search or an agent over files, tables and the catalog; prints the route, the output job id, and the answer (plus the catalog table when there is one) |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ndi-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.13.0"
|
|
4
4
|
description = "NDI platform CLI: document operations, jobs, workspaces, and the agent workspace tools"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
"Typing :: Typed",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"ndi-sdk>=0.
|
|
27
|
+
"ndi-sdk>=0.28,<1",
|
|
28
28
|
"truststore>=0.10.4,<1",
|
|
29
29
|
]
|
|
30
30
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ndi-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.13.0"
|
|
4
4
|
description = "NDI platform CLI: document operations, jobs, workspaces, and the agent workspace tools"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Nace AI", email = "engineering@nace.ai" }
|
|
@@ -27,7 +27,7 @@ classifiers = [
|
|
|
27
27
|
"Typing :: Typed",
|
|
28
28
|
]
|
|
29
29
|
dependencies = [
|
|
30
|
-
"ndi-sdk>=0.
|
|
30
|
+
"ndi-sdk>=0.28,<1",
|
|
31
31
|
"truststore>=0.10.4,<1",
|
|
32
32
|
]
|
|
33
33
|
|
|
@@ -14,6 +14,7 @@ import uuid
|
|
|
14
14
|
|
|
15
15
|
import html
|
|
16
16
|
import re
|
|
17
|
+
import textwrap
|
|
17
18
|
from typing import Literal
|
|
18
19
|
|
|
19
20
|
from ndi_sdk.models.common import Coverage, Evidence, File
|
|
@@ -809,6 +810,12 @@ def workspace(item: Workspace) -> str:
|
|
|
809
810
|
return f"{item.workspace_id} {item.name} {item.domain_slug}@{item.domain_version} {item.status}{purpose}"
|
|
810
811
|
|
|
811
812
|
|
|
813
|
+
def workspace_detail(item: Workspace) -> str:
|
|
814
|
+
if not item.global_context:
|
|
815
|
+
return workspace(item)
|
|
816
|
+
return f"{workspace(item)}\nglobal context:\n{textwrap.indent(item.global_context, ' ')}"
|
|
817
|
+
|
|
818
|
+
|
|
812
819
|
def workspace_page(page) -> str:
|
|
813
820
|
lines = [f"workspaces ({page.total_count}):"]
|
|
814
821
|
lines.extend(f" {workspace(item)}" for item in page.items)
|
|
@@ -24,6 +24,8 @@ from ndi_cli._sources import collect_workspace_uploads, folder_upload_prefix, wo
|
|
|
24
24
|
DEFAULT_UPLOAD_PARALLEL = 8
|
|
25
25
|
INGEST_FILE_ID_BATCH = 1000
|
|
26
26
|
_SKIP_WARNING_LIST_CAP = 20
|
|
27
|
+
_CONTEXT_HELP = "Background for this run, replacing the workspace's global context. Omit to use the global context."
|
|
28
|
+
_NO_CONTEXT_HELP = "Run with no context at all, not even the workspace's global context."
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser) -> None:
|
|
@@ -43,6 +45,19 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
43
45
|
get.add_argument("workspace_id", nargs="?", default=None)
|
|
44
46
|
get.set_defaults(run=_run_ws_get, family="workspace", needs_workspace=False, needs_client=True)
|
|
45
47
|
|
|
48
|
+
global_context = ws.add_parser(
|
|
49
|
+
"global-context",
|
|
50
|
+
parents=[common],
|
|
51
|
+
help="Show, replace (--set/--file) or remove (--clear) the workspace's global context: the background every "
|
|
52
|
+
"query without --context runs with.",
|
|
53
|
+
)
|
|
54
|
+
global_context.add_argument("workspace_id", nargs="?", default=None)
|
|
55
|
+
replace = global_context.add_mutually_exclusive_group()
|
|
56
|
+
replace.add_argument("--set", default=None, dest="text", help="Replace the global context with this text.")
|
|
57
|
+
replace.add_argument("--file", default=None, type=Path, help="Replace the global context with this file's text.")
|
|
58
|
+
replace.add_argument("--clear", action="store_true", help="Remove the global context from the workspace.")
|
|
59
|
+
global_context.set_defaults(run=_run_ws_global_context, family="workspace", needs_workspace=False, needs_client=True)
|
|
60
|
+
|
|
46
61
|
stats = ws.add_parser("stats", parents=[common], help="Content summary for a workspace.")
|
|
47
62
|
stats.add_argument("workspace_id", nargs="?", default=None)
|
|
48
63
|
stats.set_defaults(run=_run_ws_stats, family="workspace", needs_workspace=False, needs_client=True)
|
|
@@ -104,7 +119,7 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
104
119
|
deep.add_argument("query")
|
|
105
120
|
deep.add_argument("--effort", choices=("low", "medium", "high"), default=None)
|
|
106
121
|
deep.add_argument("--prefix", default=None, dest="path_prefix")
|
|
107
|
-
deep
|
|
122
|
+
_add_context_args(deep)
|
|
108
123
|
add_job_output_args(deep)
|
|
109
124
|
deep.set_defaults(run=_run_deep_search, family="workspace", needs_workspace=True, needs_client=True)
|
|
110
125
|
|
|
@@ -112,7 +127,7 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
112
127
|
fact.add_argument("query")
|
|
113
128
|
fact.add_argument("--prefix", default=None, dest="path_prefix")
|
|
114
129
|
fact.add_argument("--top-k", type=int, default=None)
|
|
115
|
-
fact
|
|
130
|
+
_add_context_args(fact)
|
|
116
131
|
add_job_output_args(fact)
|
|
117
132
|
fact.set_defaults(run=_run_fact_search, family="workspace", needs_workspace=True, needs_client=True)
|
|
118
133
|
|
|
@@ -126,7 +141,7 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
126
141
|
automatic.add_argument("query")
|
|
127
142
|
automatic.add_argument("--prefix", default=None, dest="path_prefix")
|
|
128
143
|
automatic.add_argument("--top-k", type=int, default=None, help="Cap the citation list when the fact pipeline answers.")
|
|
129
|
-
automatic
|
|
144
|
+
_add_context_args(automatic)
|
|
130
145
|
add_job_output_args(automatic)
|
|
131
146
|
automatic.set_defaults(run=_run_automatic_search, family="workspace", needs_workspace=True, needs_client=True)
|
|
132
147
|
|
|
@@ -138,7 +153,7 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
138
153
|
"--cursor", default=None, help="Page an earlier run: paste its result's next_cursor. A page is a new job."
|
|
139
154
|
)
|
|
140
155
|
filtered.add_argument("--prefix", default=None, dest="path_prefix")
|
|
141
|
-
filtered
|
|
156
|
+
_add_context_args(filtered)
|
|
142
157
|
# No --result-unit: the server deprecates the grouping override and returns
|
|
143
158
|
# catalog entries. No --allow-clarification: a clarification is answered by a
|
|
144
159
|
# new initial query, and a shell has nowhere to keep the thread — the reason
|
|
@@ -151,7 +166,7 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
151
166
|
jet.add_argument("--reasoning-effort", choices=("none", "minimal", "low", "medium", "high"), default=None)
|
|
152
167
|
jet.add_argument("--prefix", default=None, dest="path_prefix")
|
|
153
168
|
jet.add_argument("--path", action="append", dest="paths", default=None, help="Scope to this file; repeatable.")
|
|
154
|
-
jet
|
|
169
|
+
_add_context_args(jet)
|
|
155
170
|
# No --session-id, for the reason deep-search omits it: a shell has nowhere
|
|
156
171
|
# to keep a thread. Omitting --reasoning-effort uses the service default.
|
|
157
172
|
add_job_output_args(jet)
|
|
@@ -205,6 +220,13 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
|
|
|
205
220
|
lu_selection.set_defaults(run=_run_ledger_selection, family="workspace", needs_workspace=True, needs_client=True)
|
|
206
221
|
|
|
207
222
|
|
|
223
|
+
def _add_context_args(parser: argparse.ArgumentParser) -> None:
|
|
224
|
+
context = parser.add_mutually_exclusive_group()
|
|
225
|
+
context.add_argument("--context", default=None, help=_CONTEXT_HELP)
|
|
226
|
+
# The server reads an empty context as "none" and an omitted one as "use the global context".
|
|
227
|
+
context.add_argument("--no-context", action="store_const", const="", dest="context", help=_NO_CONTEXT_HELP)
|
|
228
|
+
|
|
229
|
+
|
|
208
230
|
def _workspace_id(args: argparse.Namespace, fallback: str) -> str:
|
|
209
231
|
value = getattr(args, "workspace_id", None) or args.workspace or fallback
|
|
210
232
|
if not value:
|
|
@@ -235,7 +257,20 @@ def _run_ws_list(client: NdiClient, workspace_id: str, args: argparse.Namespace)
|
|
|
235
257
|
|
|
236
258
|
def _run_ws_get(client: NdiClient, workspace_id: str, args: argparse.Namespace) -> int:
|
|
237
259
|
workspace = client.workspaces.get(_workspace_id(args, workspace_id))
|
|
238
|
-
return _dump_or_text(workspace, args, text=_render.
|
|
260
|
+
return _dump_or_text(workspace, args, text=_render.workspace_detail(workspace), stem="workspace")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _run_ws_global_context(client: NdiClient, workspace_id: str, args: argparse.Namespace) -> int:
|
|
264
|
+
target = _workspace_id(args, workspace_id)
|
|
265
|
+
if args.clear:
|
|
266
|
+
workspace = client.workspaces.update(target, global_context="")
|
|
267
|
+
elif args.file is not None:
|
|
268
|
+
workspace = client.workspaces.update(target, global_context=args.file.read_text(encoding="utf-8"))
|
|
269
|
+
elif args.text is not None:
|
|
270
|
+
workspace = client.workspaces.update(target, global_context=args.text)
|
|
271
|
+
else:
|
|
272
|
+
workspace = client.workspaces.get(target)
|
|
273
|
+
return _dump_or_text(workspace, args, text=workspace.global_context or "(no global context)", stem="workspace")
|
|
239
274
|
|
|
240
275
|
|
|
241
276
|
def _run_ws_stats(client: NdiClient, workspace_id: str, args: argparse.Namespace) -> int:
|
|
@@ -521,8 +556,8 @@ def _run_filtered_search(client: NdiClient, workspace_id: str, args: argparse.Na
|
|
|
521
556
|
# A page re-executes the parent's own stored query, so the scoping flags
|
|
522
557
|
# cannot apply to it. Accepting and dropping them would quietly answer a
|
|
523
558
|
# different question than the one typed.
|
|
524
|
-
if args.path_prefix or args.context:
|
|
525
|
-
print("error: --prefix and --context belong to the initial query, not a page", file=sys.stderr)
|
|
559
|
+
if args.path_prefix or args.context is not None:
|
|
560
|
+
print("error: --prefix, --context and --no-context belong to the initial query, not a page", file=sys.stderr)
|
|
526
561
|
return 2
|
|
527
562
|
job = client.search.filtered_page(workspace_id, cursor=args.cursor)
|
|
528
563
|
else:
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|