ndi-cli 0.6.0__tar.gz → 0.7.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ndi-cli
3
- Version: 0.6.0
3
+ Version: 0.7.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.21,<1
19
+ Requires-Dist: ndi-sdk>=0.22,<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
@@ -56,6 +56,7 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
56
56
  | `ndi files upload\|list\|get\|delete` | Workspace files (`--ingest` uploads then queues ingestion) |
57
57
  | `ndi ingest` | Queue ingestion |
58
58
  | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search |
59
+ | `ndi je-testing QUERY` | Journal-entry testing over a ledger package (`--path` to scope it, `--effort` for the level) |
59
60
  | `ndi folder-metadata` / `file-metadata` / `read-file` / `ask-file` / `run-sql` / `hybrid-search` | Read-only workspace tools (the sandbox surface) |
60
61
 
61
62
  ## Sources
@@ -85,4 +86,5 @@ API failures exit 1. Usage / config errors exit 2. Schema-validation
85
86
  failures also show up to five field constraints, each limited to 500
86
87
  characters; request input and unrelated error-detail fields are omitted.
87
88
 
88
- See `SKILL.md` for the agent-facing guide to the six workspace tools.
89
+ See the [CLI guide](https://docs.ndi.nace.ai/guides/cli) for the full command
90
+ reference. `SKILL.md` is the agent-facing guide to the six workspace tools.
@@ -32,6 +32,7 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
32
32
  | `ndi files upload\|list\|get\|delete` | Workspace files (`--ingest` uploads then queues ingestion) |
33
33
  | `ndi ingest` | Queue ingestion |
34
34
  | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search |
35
+ | `ndi je-testing QUERY` | Journal-entry testing over a ledger package (`--path` to scope it, `--effort` for the level) |
35
36
  | `ndi folder-metadata` / `file-metadata` / `read-file` / `ask-file` / `run-sql` / `hybrid-search` | Read-only workspace tools (the sandbox surface) |
36
37
 
37
38
  ## Sources
@@ -61,4 +62,5 @@ API failures exit 1. Usage / config errors exit 2. Schema-validation
61
62
  failures also show up to five field constraints, each limited to 500
62
63
  characters; request input and unrelated error-detail fields are omitted.
63
64
 
64
- See `SKILL.md` for the agent-facing guide to the six workspace tools.
65
+ See the [CLI guide](https://docs.ndi.nace.ai/guides/cli) for the full command
66
+ reference. `SKILL.md` is the agent-facing guide to the six workspace tools.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ndi-cli"
3
- version = "0.6.0"
3
+ version = "0.7.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.21,<1",
27
+ "ndi-sdk>=0.22,<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.6.0"
3
+ version = "0.7.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.21,<1",
30
+ "ndi-sdk>=0.22,<1",
31
31
  "truststore>=0.10.4,<1",
32
32
  ]
33
33
 
@@ -6,4 +6,4 @@ methods. The six workspace tools (``folder-metadata``, ``file-metadata``,
6
6
  in-sandbox surface.
7
7
  """
8
8
 
9
- __version__ = "0.6.0"
9
+ __version__ = "0.7.0"
@@ -6,7 +6,7 @@ import sys
6
6
  from collections.abc import Callable
7
7
  from pathlib import Path
8
8
 
9
- from ndi_sdk.models.jobs import IngestionResult, Job, ParseResult
9
+ from ndi_sdk.models.jobs import IngestionResult, JetResult, Job, ParseResult
10
10
 
11
11
  OUTPUT_CHOICES = ("auto", "md", "json", "payload", "id")
12
12
 
@@ -108,6 +108,13 @@ def _content_failure(job: Job) -> int:
108
108
  file=sys.stderr,
109
109
  )
110
110
  return 1
111
+ # A degraded JET run is the same shape: succeeded, but the procedure broke
112
+ # before it could finalize an answer, so `figures` is empty and only the
113
+ # working survives. `exhausted` and `partially_performed` stay zero — both
114
+ # are documented as successful partial answers, not failed work.
115
+ if isinstance(result, JetResult) and result.degraded:
116
+ print(f"error: journal-entry testing degraded: {result.degraded}", file=sys.stderr)
117
+ return 1
111
118
  return 0
112
119
 
113
120
 
@@ -24,6 +24,7 @@ from ndi_sdk.models.jobs import (
24
24
  GroundResult,
25
25
  IngestionResult,
26
26
  IntelligentSearchResult,
27
+ JetResult,
27
28
  Job,
28
29
  ParseResult,
29
30
  SplitResult,
@@ -662,6 +663,49 @@ def search_job(job: Job) -> str:
662
663
  return job_line(job)
663
664
 
664
665
 
666
+ def je_testing_job(job: Job) -> str:
667
+ """The answer, then the numbers behind it — a JET result read at a glance.
668
+
669
+ Unlike :func:`search_job` this leads with ``figures``: the answer is prose a
670
+ person reads, and the figures are the same content a reviewer can check.
671
+ Each one names the receipt its numbers are readable out of, so
672
+ ``ndi job <id> -o json`` is the next step rather than the only one.
673
+ """
674
+ result = job.result
675
+ if not isinstance(result, JetResult):
676
+ return job_line(job)
677
+ lines: list[str] = []
678
+ if result.degraded:
679
+ lines.append(f"degraded: {result.degraded}")
680
+ if result.answer:
681
+ lines.append(result.answer.strip())
682
+ if result.population_statement:
683
+ lines.append(f"population: {result.population_statement}")
684
+ if result.figures:
685
+ lines.append(f"figures ({len(result.figures)}):")
686
+ for figure in result.figures:
687
+ if figure.cannot_be_performed:
688
+ lines.append(f" {figure.criterion}: not performed")
689
+ else:
690
+ value = f" | {figure.value}" if figure.value else ""
691
+ lines.append(f" {figure.criterion}: {figure.count}/{figure.population}{value}")
692
+ # The exception listing is the receipt worth opening: an unfiltered
693
+ # count points at every row, a listing points at the rows that failed.
694
+ receipt = figure.exception_listing_result_id or next(iter(figure.result_ids), None)
695
+ if receipt:
696
+ lines.append(f" receipt {receipt}")
697
+ for discrepancy in result.discrepancies:
698
+ lines.append(f"discrepancy: {discrepancy}")
699
+ lines.append(f"evidences ({len(result.evidences)}) | receipts ({len(result.sql_receipts)})")
700
+ if result.execution_status and result.execution_status != "performed":
701
+ lines.append(f"execution: {result.execution_status}")
702
+ if result.quality and result.quality.status != "clean":
703
+ lines.append(f"quality: {result.quality.status}")
704
+ if result.exhausted:
705
+ lines.append("exhausted: step budget ran out before the procedure finished")
706
+ return "\n".join(lines)
707
+
708
+
665
709
  def _search_result(answer: str | None, clarification: str | None, evidences: list, *, receipts: int) -> str:
666
710
  lines: list[str] = []
667
711
  if clarification:
@@ -95,6 +95,17 @@ def add_parsers(sub: argparse._SubParsersAction, common: argparse.ArgumentParser
95
95
  add_job_output_args(fact)
96
96
  fact.set_defaults(run=_run_fact_search, family="workspace", needs_workspace=True, needs_client=True)
97
97
 
98
+ jet = sub.add_parser("je-testing", parents=[common], help="Run journal-entry testing over a ledger package.")
99
+ jet.add_argument("query")
100
+ jet.add_argument("--effort", choices=("low", "medium", "high"), default=None)
101
+ jet.add_argument("--prefix", default=None, dest="path_prefix")
102
+ jet.add_argument("--path", action="append", dest="paths", default=None, help="Scope to this file; repeatable.")
103
+ jet.add_argument("--context", default=None)
104
+ # No --session-id, for the reason deep-search omits it: a shell has nowhere
105
+ # to keep a thread. Omitting --effort leaves the deployment's own floor.
106
+ add_job_output_args(jet)
107
+ jet.set_defaults(run=_run_je_testing, family="workspace", needs_workspace=True, needs_client=True)
108
+
98
109
 
99
110
  def _workspace_id(args: argparse.Namespace, fallback: str) -> str:
100
111
  value = getattr(args, "workspace_id", None) or args.workspace or fallback
@@ -224,3 +235,15 @@ def _run_fact_search(client: NdiClient, workspace_id: str, args: argparse.Namesp
224
235
  top_k=args.top_k,
225
236
  )
226
237
  return _finish_job(client, job, args, _render.search_job, "fact-search")
238
+
239
+
240
+ def _run_je_testing(client: NdiClient, workspace_id: str, args: argparse.Namespace) -> int:
241
+ job = client.search.je_testing(
242
+ workspace_id,
243
+ query=args.query,
244
+ context=args.context,
245
+ effort=args.effort,
246
+ path_prefix=args.path_prefix,
247
+ paths=args.paths,
248
+ )
249
+ return _finish_job(client, job, args, _render.je_testing_job, "je-testing")
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