ndi-cli 0.16.0__tar.gz → 0.25.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.16.0
3
+ Version: 0.25.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.31,<1
19
+ Requires-Dist: ndi-sdk>=0.45,<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
@@ -25,8 +25,9 @@ Description-Content-Type: text/markdown
25
25
  # ndi-cli
26
26
 
27
27
  `ndi` — the NDI platform CLI. Document operations, jobs, and workspace
28
- lifecycle wrap the same `/v1` methods as `ndi-sdk`. The six workspace tools
29
- also run inside an agent sandbox; every other verb is refused there.
28
+ lifecycle wrap the same `/v1` methods as `ndi-sdk`. `ask-file` also runs
29
+ inside an agent sandbox, next to the file commands only an agent run has; every
30
+ other verb is refused there.
30
31
 
31
32
  ## Setup
32
33
 
@@ -56,12 +57,13 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
56
57
  | `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 |
57
58
  | `ndi files upload\|list\|get\|delete` | Workspace files (a directory walks nested files; `--ingest` then queues ingestion) |
58
59
  | `ndi ingest` | Queue ingestion |
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) |
60
- | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search; then `ndi job` on each printed `grounding_job_ids` entry |
61
- | `ndi filtered-search QUERY` | Corpus filter/rank/count over the metadata catalog (`--cursor` pages an earlier run) |
60
+ | `ndi automatic-search QUERY` | Service answers with fact search or an agent over files, tables and the catalog (`--mode fact\|agent` skips the routing step, `--path` scopes it to files); prints the route, the output job id, and the answer (plus the catalog table when there is one) |
61
+ | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search (`--path` on `deep-search` scopes it to files); then `ndi job` on each printed `grounding_job_ids` entry |
62
+ | `ndi filtered-search QUERY` | Corpus filter/rank/count over the metadata catalog (`--path` scopes it to files, `--cursor` pages an earlier run) |
62
63
  | `ndi je-testing QUERY` | Journal-entry testing over a ledger package (`--path` to scope it, `--reasoning-effort` for the thinking budget) |
63
64
  | `ndi ledger-understanding start\|get\|sources\|select\|selection` | Measure the ledger package, confirm sources, or read the latest report |
64
- | `ndi folder-metadata` / `file-metadata` / `read-file` / `ask-file` / `run-sql` / `hybrid-search` | Read-only workspace tools (the sandbox surface) |
65
+ | `ndi ask-file PATH QUESTION` | Cited answer over one document, image or recording |
66
+ | `ndi systemone --state TEXT --questions JSON` | Answer `noul`, `choice` and `score` questions about a state in one call, no workspace (`--state-json` for a JSON state; any value may be `@FILE` or `@-`) |
65
67
 
66
68
  ## Sources
67
69
 
@@ -100,8 +102,8 @@ The default (`auto`) prints the full JSON response. When that is over
100
102
  `$NDI_OUTPUT_LIMIT` characters (default 40,000), the compact text rendering
101
103
  prints instead and stderr carries `note: output shortened …` with the full
102
104
  size. `--json` always prints it whole, and `--save` / `--out-dir` always write
103
- it whole. Commands whose output is the content itself (`read-file`,
104
- `ws context`, parse markdown) print it as before. `--save PATH` / `--out-dir DIR` write files. `--async`
105
+ it whole. Commands whose output is the content itself (`ws context`, parse
106
+ markdown) print it as before. `--save PATH` / `--out-dir DIR` write files. `--async`
105
107
  submits and prints the job id.
106
108
 
107
109
  When a Parse result has external complete content, `-o auto` and `-o md` print
@@ -122,4 +124,4 @@ The server checks a bounded byte prefix, rejects conclusive filename/content
122
124
  conflicts, and treats the multipart `Content-Type` as advisory.
123
125
 
124
126
  See the [CLI guide](https://docs.ndi.nace.ai/guides/cli) for the full command
125
- reference. `SKILL.md` is the agent-facing guide to the six workspace tools.
127
+ reference. `SKILL.md` is the agent-facing guide to reading a workspace with `ndi`.
@@ -1,8 +1,9 @@
1
1
  # ndi-cli
2
2
 
3
3
  `ndi` — the NDI platform CLI. Document operations, jobs, and workspace
4
- lifecycle wrap the same `/v1` methods as `ndi-sdk`. The six workspace tools
5
- also run inside an agent sandbox; every other verb is refused there.
4
+ lifecycle wrap the same `/v1` methods as `ndi-sdk`. `ask-file` also runs
5
+ inside an agent sandbox, next to the file commands only an agent run has; every
6
+ other verb is refused there.
6
7
 
7
8
  ## Setup
8
9
 
@@ -32,12 +33,13 @@ config location. Workspace-scoped verbs also take `--workspace ID`.
32
33
  | `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 |
33
34
  | `ndi files upload\|list\|get\|delete` | Workspace files (a directory walks nested files; `--ingest` then queues ingestion) |
34
35
  | `ndi ingest` | Queue ingestion |
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) |
36
- | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search; then `ndi job` on each printed `grounding_job_ids` entry |
37
- | `ndi filtered-search QUERY` | Corpus filter/rank/count over the metadata catalog (`--cursor` pages an earlier run) |
36
+ | `ndi automatic-search QUERY` | Service answers with fact search or an agent over files, tables and the catalog (`--mode fact\|agent` skips the routing step, `--path` scopes it to files); prints the route, the output job id, and the answer (plus the catalog table when there is one) |
37
+ | `ndi deep-search QUERY` / `ndi fact-search QUERY` | Agentic / single-shot search (`--path` on `deep-search` scopes it to files); then `ndi job` on each printed `grounding_job_ids` entry |
38
+ | `ndi filtered-search QUERY` | Corpus filter/rank/count over the metadata catalog (`--path` scopes it to files, `--cursor` pages an earlier run) |
38
39
  | `ndi je-testing QUERY` | Journal-entry testing over a ledger package (`--path` to scope it, `--reasoning-effort` for the thinking budget) |
39
40
  | `ndi ledger-understanding start\|get\|sources\|select\|selection` | Measure the ledger package, confirm sources, or read the latest report |
40
- | `ndi folder-metadata` / `file-metadata` / `read-file` / `ask-file` / `run-sql` / `hybrid-search` | Read-only workspace tools (the sandbox surface) |
41
+ | `ndi ask-file PATH QUESTION` | Cited answer over one document, image or recording |
42
+ | `ndi systemone --state TEXT --questions JSON` | Answer `noul`, `choice` and `score` questions about a state in one call, no workspace (`--state-json` for a JSON state; any value may be `@FILE` or `@-`) |
41
43
 
42
44
  ## Sources
43
45
 
@@ -76,8 +78,8 @@ The default (`auto`) prints the full JSON response. When that is over
76
78
  `$NDI_OUTPUT_LIMIT` characters (default 40,000), the compact text rendering
77
79
  prints instead and stderr carries `note: output shortened …` with the full
78
80
  size. `--json` always prints it whole, and `--save` / `--out-dir` always write
79
- it whole. Commands whose output is the content itself (`read-file`,
80
- `ws context`, parse markdown) print it as before. `--save PATH` / `--out-dir DIR` write files. `--async`
81
+ it whole. Commands whose output is the content itself (`ws context`, parse
82
+ markdown) print it as before. `--save PATH` / `--out-dir DIR` write files. `--async`
81
83
  submits and prints the job id.
82
84
 
83
85
  When a Parse result has external complete content, `-o auto` and `-o md` print
@@ -98,4 +100,4 @@ The server checks a bounded byte prefix, rejects conclusive filename/content
98
100
  conflicts, and treats the multipart `Content-Type` as advisory.
99
101
 
100
102
  See the [CLI guide](https://docs.ndi.nace.ai/guides/cli) for the full command
101
- reference. `SKILL.md` is the agent-facing guide to the six workspace tools.
103
+ reference. `SKILL.md` is the agent-facing guide to reading a workspace with `ndi`.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ndi-cli"
3
- version = "0.16.0"
3
+ version = "0.25.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.31,<1",
27
+ "ndi-sdk>=0.45,<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.16.0"
3
+ version = "0.25.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.31,<1",
30
+ "ndi-sdk>=0.45,<1",
31
31
  "truststore>=0.10.4,<1",
32
32
  ]
33
33
 
@@ -0,0 +1,9 @@
1
+ """``ndi`` — the NDI platform CLI.
2
+
3
+ Document operations, jobs, and workspace lifecycle wrap ``ndi-sdk`` ``/v1``
4
+ methods. Inside an agent sandbox the workspace tools (``folder-metadata``,
5
+ ``file-metadata``, ``read-file``, ``ask-file``, ``run-sql``, ``hybrid-search``)
6
+ are the whole surface; outside one only ``ask-file`` is.
7
+ """
8
+
9
+ __version__ = "0.25.0"
@@ -1,24 +1,27 @@
1
1
  """``ndi`` entry point: workspace tools plus the platform verbs.
2
2
 
3
- The six workspace tools stay available inside an agent sandbox. Every other
4
- verb is refused when ``$NDI_CLI_SOCKET`` is set, before any client is built.
3
+ Inside an agent sandbox the workspace tools are the only verbs, and the file
4
+ commands with no public route exist only there. Every other verb is refused
5
+ when ``$NDI_CLI_SOCKET`` is set, before any client is built.
5
6
  """
6
7
 
7
8
  from __future__ import annotations
8
9
 
9
10
  import argparse
11
+ import copy
10
12
  import os
11
13
  import sys
14
+ from concurrent.futures import ThreadPoolExecutor
12
15
 
13
16
  from ndi_sdk.client import NdiClient
14
17
  from ndi_sdk.errors import NdiError, NdiStatusError
15
18
  from ndi_sdk.resources.tools import SyncTools
16
19
 
17
- from ndi_cli import _docops, _jobs, _login, _tools, _workspace
20
+ from ndi_cli import _docops, _jobs, _login, _systemone, _tools, _workspace
18
21
  from ndi_cli._config import ConfigError, resolve_settings
19
- from ndi_cli._local import KG_ENV, SOCKET_ENV, TOKEN_ENV, LocalTransport
22
+ from ndi_cli._local import SOCKET_ENV, TOKEN_ENV, LocalTransport
20
23
  from ndi_cli._output import full_json, output_limit, print_full_or_summary
21
- from ndi_cli._tools import GraphCommands, parse_pages
24
+ from ndi_cli._tools import parse_pages
22
25
 
23
26
  # qa-file runs a durable workflow that legitimately takes minutes; the metadata,
24
27
  # read, and run-sql tools answer in seconds but share the generous bound.
@@ -32,10 +35,7 @@ def main(argv: list[str] | None = None) -> int:
32
35
  # Presence, not truthiness: an empty $NDI_CLI_SOCKET still means "inside an
33
36
  # agent run", and must not silently unlock the full platform CLI.
34
37
  in_agent_run = socket_path is not None
35
- # An agent run gets kg-answer only when the worker granted it ("answer");
36
- # a human CLI user gets all kg-* commands ("all").
37
- graph: GraphCommands = ("answer" if os.environ.get(KG_ENV) else "none") if in_agent_run else "all"
38
- parser = _build_parser(graph=graph)
38
+ parser = _build_parser(in_agent_run=in_agent_run)
39
39
  args = parser.parse_args(argv)
40
40
  if args.command is None:
41
41
  parser.print_help()
@@ -78,8 +78,7 @@ def main(argv: list[str] | None = None) -> int:
78
78
  if in_agent_run:
79
79
  workspace_id = os.environ.get("NDI_WORKSPACE_ID", "")
80
80
  transport = LocalTransport(socket_path, args.timeout, os.environ.get(TOKEN_ENV, ""))
81
- response = args.run(SyncTools(transport), workspace_id, args)
82
- return _print_tool(args, response)
81
+ return _run_tool(args, SyncTools(transport), workspace_id, _print_tool)
83
82
  settings = resolve_settings(
84
83
  workspace_override=workspace_flag,
85
84
  require_workspace=getattr(args, "needs_workspace", True),
@@ -87,25 +86,65 @@ def main(argv: list[str] | None = None) -> int:
87
86
  timeout = getattr(args, "timeout", DEFAULT_TIMEOUT_SECONDS)
88
87
  with NdiClient(api_key=settings.api_key, base_url=settings.base_url, timeout=timeout) as client:
89
88
  if family == "tool":
90
- return _print_tool_full(args, args.run(client.tools, settings.workspace_id, args))
89
+ return _run_tool(args, client.tools, settings.workspace_id, _print_tool_full)
91
90
  return args.run(client, settings.workspace_id, args)
92
91
  except ConfigError as exc:
93
92
  print(f"error: {exc}", file=sys.stderr)
94
93
  return 2
95
- except NdiStatusError as exc:
96
- code = f" [{exc.code}]" if exc.code else ""
97
- print(f"error: {exc.status_code}{code} {exc.message}", file=sys.stderr)
98
- for line in _validation_error_lines(exc.body.detail if exc.body else None):
99
- print(f" - {line}", file=sys.stderr)
100
- return 1
101
94
  except NdiError as exc:
102
- print(f"error: {exc}", file=sys.stderr)
103
- return 1
95
+ return _report_error(exc)
104
96
  except ValueError as exc:
105
97
  print(f"error: {exc}", file=sys.stderr)
106
98
  return 2
107
99
 
108
100
 
101
+ def _run_tool(args: argparse.Namespace, tools: SyncTools, workspace_id: str, printer) -> int:
102
+ """Run a tool once per positional target (``file-metadata a b``), each printed as its own call would print it.
103
+
104
+ The targets are fetched concurrently and printed in the order given. With several
105
+ targets a failing one reports on stderr and the rest still run, so one bad path costs
106
+ the caller that path's error line, not every other path's answer.
107
+ """
108
+ targets = getattr(args, "targets", [None])
109
+ if len(targets) == 1:
110
+ args.target = targets[0]
111
+ return printer(args, args.run(tools, workspace_id, args))
112
+
113
+ def fetch(target: str) -> tuple[argparse.Namespace, object]:
114
+ per_target = copy.copy(args)
115
+ per_target.target = target
116
+ try:
117
+ return per_target, per_target.run(tools, workspace_id, per_target)
118
+ except NdiError as exc:
119
+ return per_target, exc
120
+
121
+ with ThreadPoolExecutor(max_workers=len(targets)) as pool:
122
+ fetched = list(pool.map(fetch, targets))
123
+ status = 0
124
+ for index, (per_target, response) in enumerate(fetched):
125
+ if index:
126
+ print()
127
+ if getattr(args, "label_targets", False):
128
+ print(f"==> {per_target.target} <==")
129
+ if isinstance(response, NdiError):
130
+ status = max(status, _report_error(response, target=per_target.target))
131
+ else:
132
+ status = max(status, printer(per_target, response))
133
+ return status
134
+
135
+
136
+ def _report_error(exc: NdiError, *, target: str | None = None) -> int:
137
+ prefix = f"{target}: " if target is not None else ""
138
+ if not isinstance(exc, NdiStatusError):
139
+ print(f"error: {prefix}{exc}", file=sys.stderr)
140
+ return 1
141
+ code = f" [{exc.code}]" if exc.code else ""
142
+ print(f"error: {prefix}{exc.status_code}{code} {exc.message}", file=sys.stderr)
143
+ for line in _validation_error_lines(exc.body.detail if exc.body else None):
144
+ print(f" - {line}", file=sys.stderr)
145
+ return 1
146
+
147
+
109
148
  def _print_tool(args: argparse.Namespace, response) -> int:
110
149
  """The text rendering. An agent run always gets it: the renderings are what its agents are evaluated against."""
111
150
  if args.json:
@@ -148,12 +187,12 @@ def _validation_error_lines(detail: object) -> list[str]:
148
187
  return lines
149
188
 
150
189
 
151
- def _build_parser(*, graph: GraphCommands = "all") -> argparse.ArgumentParser:
190
+ def _build_parser(*, in_agent_run: bool = False) -> argparse.ArgumentParser:
152
191
  parser = argparse.ArgumentParser(
153
192
  prog="ndi",
154
193
  description=(
155
194
  "NDI platform CLI. Login, document operations, jobs, and workspace lifecycle talk to /v1; "
156
- "the six workspace tools also run inside an agent sandbox. Credentials come from "
195
+ "the workspace tools also run inside an agent sandbox. Credentials come from "
157
196
  "$NDI_API_KEY / $NDI_BASE_URL or ~/.ndi/config.toml."
158
197
  ),
159
198
  )
@@ -171,10 +210,11 @@ def _build_parser(*, graph: GraphCommands = "all") -> argparse.ArgumentParser:
171
210
  )
172
211
  sub = parser.add_subparsers(dest="command")
173
212
  _login.add_parsers(sub)
174
- _tools.add_parsers(sub, common, graph=graph)
213
+ _tools.add_parsers(sub, common, in_agent_run=in_agent_run)
175
214
  _docops.add_parsers(sub, common)
176
215
  _jobs.add_parsers(sub, common)
177
216
  _workspace.add_parsers(sub, common)
217
+ _systemone.add_parsers(sub, common)
178
218
  return parser
179
219
 
180
220
 
@@ -14,29 +14,60 @@ import socket
14
14
  from typing import Annotated, Literal, TypeVar
15
15
 
16
16
  from ndi_sdk.errors import ErrorBody, NdiConnectionError, NdiTimeoutError, error_class_for
17
- from ndi_sdk.models.tools import (
18
- FileMetadataRequest,
19
- FolderMetadataRequest,
20
- KgSearchRequest,
21
- KgWalkRequest,
22
- QaFileRequest,
23
- ReadFileRequest,
24
- RunSqlRequest,
25
- SearchRequest,
26
- )
17
+ from ndi_sdk.models.tools import QaFileRequest
27
18
  from pydantic import BaseModel, Field, TypeAdapter
28
19
 
29
20
  from ndi_cli.commands import WorkspaceCommand
30
21
 
31
22
  SOCKET_ENV = "NDI_CLI_SOCKET"
32
23
  TOKEN_ENV = "NDI_CLI_TOKEN"
33
- #: Set (to any non-empty value) by the worker for an agent run that has the graph
34
- #: surface; inside an agent run the ``kg-*`` commands are registered only when it is.
35
- KG_ENV = "NDI_CLI_KG"
36
24
  MAX_MESSAGE_BYTES = 16 * 1024 * 1024
37
25
  ModelT = TypeVar("ModelT", bound=BaseModel)
38
26
 
39
27
 
28
+ # The socket bodies of the agent-only commands. Their /v1 routes are unpublished,
29
+ # so the SDK has no method or request model for them.
30
+
31
+
32
+ class FolderMetadataRequest(BaseModel):
33
+ """Non-recursive by default; the server treats a narrow as recursive unless ``recursive`` is sent."""
34
+
35
+ directory: str = "/"
36
+ recursive: bool | None = None
37
+ category: str | None = None
38
+ ingestion_status: str | None = None
39
+ kind: str | None = None
40
+ name_contains: str | None = None
41
+ page_size: int = 100
42
+ start_after: str | None = None
43
+ include_tables: bool = False
44
+
45
+
46
+ class FileMetadataRequest(BaseModel):
47
+ path: str
48
+ component: str | None = None
49
+
50
+
51
+ class ReadFileRequest(BaseModel):
52
+ """``pages`` are absolute and one-based; spreadsheets have none, so scope those with ``component``."""
53
+
54
+ path: str
55
+ pages: list[int] | None = None
56
+ component: str | None = None
57
+ max_chars: int | None = None
58
+
59
+
60
+ class RunSqlRequest(BaseModel):
61
+ sql: str
62
+
63
+
64
+ class SearchRequest(BaseModel):
65
+ query: str
66
+ k: int = 10
67
+ path_prefix: str | None = None
68
+ categories: list[str] | None = None
69
+
70
+
40
71
  class FolderCommand(BaseModel):
41
72
  command: Literal[WorkspaceCommand.FOLDER_METADATA] = WorkspaceCommand.FOLDER_METADATA
42
73
  body: FolderMetadataRequest
@@ -67,95 +98,16 @@ class SearchCommand(BaseModel):
67
98
  body: SearchRequest
68
99
 
69
100
 
70
- class KgInfoRequest(BaseModel):
71
- """``kg-info``'s scope as a socket body.
72
-
73
- Defined here rather than imported from ``ndi_sdk.models.tools`` like its
74
- siblings: on the wire ``kg-info`` is a GET carrying query parameters, so it has
75
- no request model to reuse. Only the scope crosses — the drawable sample is for
76
- a client that renders a graph, and ``_kg_info_command`` refuses it.
77
- """
78
-
79
- path_prefix: str | None = None
80
- hub_node_type: str | None = None
81
-
82
-
83
- class KgInfoCommand(BaseModel):
84
- command: Literal[WorkspaceCommand.KG_INFO] = WorkspaceCommand.KG_INFO
85
- body: KgInfoRequest
86
-
87
-
88
- class KgSearchCommand(BaseModel):
89
- command: Literal[WorkspaceCommand.KG_SEARCH] = WorkspaceCommand.KG_SEARCH
90
- body: KgSearchRequest
91
-
92
-
93
- class KgWalkCommand(BaseModel):
94
- command: Literal[WorkspaceCommand.KG_WALK] = WorkspaceCommand.KG_WALK
95
- body: KgWalkRequest
96
-
97
-
98
- class KgAnswerRequest(BaseModel):
99
- """``kg-answer``'s question as a socket body.
100
-
101
- The agent passes its verbatim question; the server runs the graph pass and
102
- returns the rendered outcome (answered text + citations, or evidence + gap).
103
- """
104
-
105
- query: str
106
-
107
-
108
- class KgAnswerResponse(BaseModel):
109
- """The rendered outcome of one graph pass, ready to print or inject as a tool result."""
110
-
111
- text: str
112
-
113
-
114
- class KgAnswerCommand(BaseModel):
115
- command: Literal[WorkspaceCommand.KG_ANSWER] = WorkspaceCommand.KG_ANSWER
116
- body: KgAnswerRequest
117
-
118
-
119
101
  Command = Annotated[
120
- FolderCommand
121
- | FileCommand
122
- | ReadCommand
123
- | AskCommand
124
- | SqlCommand
125
- | SearchCommand
126
- | KgInfoCommand
127
- | KgSearchCommand
128
- | KgWalkCommand
129
- | KgAnswerCommand,
102
+ FolderCommand | FileCommand | ReadCommand | AskCommand | SqlCommand | SearchCommand,
130
103
  Field(discriminator="command"),
131
104
  ]
132
105
  COMMAND = TypeAdapter(Command)
133
106
 
134
107
 
135
- def _kg_info_command(params: object) -> Command:
136
- """Build the ``kg-info`` command from the SDK method's query parameters.
137
-
138
- Refuses ``include_graph`` instead of dropping it: the sample is the whole point
139
- of that flag, and answering without one would read as a graph with no structure.
140
- """
141
- if not isinstance(params, dict):
142
- raise ValueError("The internal CLI accepts workspace commands only.")
143
- if params.get("include_graph"):
144
- raise ValueError("The internal CLI does not serve kg-info's drawable graph sample.")
145
- prefix = params.get("path_prefix")
146
- if prefix is not None and not isinstance(prefix, str):
147
- raise ValueError("kg-info's path_prefix must be a string.")
148
- hub_node_type = params.get("hub_node_type")
149
- if hub_node_type is not None and not isinstance(hub_node_type, str):
150
- raise ValueError("kg-info's hub_node_type must be a string.")
151
- return KgInfoCommand(body=KgInfoRequest(path_prefix=prefix, hub_node_type=hub_node_type))
152
-
153
-
154
108
  def _command(method: str, path: str, kwargs: dict[str, object]) -> Command:
155
- """One socket command per SDK call, named by the endpoint the method addresses."""
109
+ """One socket command per tool call, named by the endpoint the call addresses."""
156
110
  name = path.rsplit("/", 1)[-1]
157
- if method == "GET" and name == WorkspaceCommand.KG_INFO:
158
- return _kg_info_command(kwargs.get("params"))
159
111
  body = kwargs.get("body")
160
112
  if method != "POST" or not isinstance(body, BaseModel):
161
113
  raise ValueError("The internal CLI accepts workspace commands only.")