brainpalace-cli 26.7.2__tar.gz → 26.7.4__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.
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/PKG-INFO +2 -2
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/cli.py +8 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/client/api_client.py +252 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/__init__.py +6 -0
- brainpalace_cli-26.7.4/brainpalace_cli/commands/entities.py +231 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/folders.py +41 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/hook.py +17 -0
- brainpalace_cli-26.7.4/brainpalace_cli/commands/ingest.py +372 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/init.py +244 -48
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/query.py +192 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/records.py +42 -0
- brainpalace_cli-26.7.4/brainpalace_cli/commands/references.py +163 -0
- brainpalace_cli-26.7.4/brainpalace_cli/commands/rules.py +164 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/status.py +55 -1
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/verify_docs.py +23 -2
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_fields.py +14 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/data/ai_guidance.md +51 -13
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/cli_commands.py +2 -2
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/generator.py +12 -6
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/serializer.py +16 -0
- brainpalace_cli-26.7.4/brainpalace_cli/merge.py +34 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/pyproject.toml +2 -2
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/README.md +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/__main__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/ai_guidance.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/client/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/client/errors.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/_dashboard_url.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/ai_guide.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/backfill.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/bm25_project.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/cache.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/config.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/context.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/dashboard.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/doctor.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/estimate_util.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/extraction.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/extraction_drain.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/graph.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/index.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/init_plan.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/inject.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/install_agent.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/jobs.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/list_cmd.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/lsp.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/mcp.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/memories.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/plugin_detect.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/read_only.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/reap.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/recall.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/remember.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/reset.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/session_hooks.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/sessions.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/start.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/stop.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/sync_docs.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/types.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/uninstall.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/update.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/commands/whoami.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_migrate.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_parity.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_resolve.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_review.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/config_schema.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/data/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/data/hooks/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/data/hooks/sessionstart-hook.sh +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/diagnostics.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/discovery.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/allowlist.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/base.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/config.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/endpoints.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/mcp.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/mode_parity_checker.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/modes.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/plugin_docs.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/provider_tables.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/checkers/skills.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/contract_parity.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/facts.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/introspect.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/markers.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/mode_meta.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/mode_parity.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/orchestrator.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/referential.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/doc_sync/triggers.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/known_projects.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/lsp_install.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/mcp_server/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/mcp_server/lifecycle.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/mcp_server/schemas.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/mcp_server/server.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/mcp_server/tools.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/migration.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/optional_deps.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/prompt_render.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/providers.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/__init__.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/claude_converter.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/codex_converter.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/converter_base.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/gemini_converter.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/opencode_converter.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/parser.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/skill_runtime_converter.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/tool_maps.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/runtime/types.py +0 -0
- {brainpalace_cli-26.7.2 → brainpalace_cli-26.7.4}/brainpalace_cli/xdg_paths.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: brainpalace-cli
|
|
3
|
-
Version: 26.7.
|
|
4
|
-
Summary: BrainPalace CLI - Command-line interface for
|
|
3
|
+
Version: 26.7.4
|
|
4
|
+
Summary: BrainPalace CLI - Command-line interface for the typed-memory & retrieval engine for AI agents (code & docs today, more domains coming)
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: brainpalace,rag,cli,ai-memory,llm-memory,semantic-search,ai-agent,claude-code,agent-memory
|
|
7
7
|
Author: bxw91
|
|
@@ -22,6 +22,7 @@ from .commands import (
|
|
|
22
22
|
folders_group,
|
|
23
23
|
hook_group,
|
|
24
24
|
index_command,
|
|
25
|
+
ingest_command,
|
|
25
26
|
init_command,
|
|
26
27
|
inject_command,
|
|
27
28
|
install_agent_command,
|
|
@@ -35,8 +36,10 @@ from .commands import (
|
|
|
35
36
|
query_command,
|
|
36
37
|
recall_command,
|
|
37
38
|
records_group,
|
|
39
|
+
references_group,
|
|
38
40
|
remember_command,
|
|
39
41
|
reset_command,
|
|
42
|
+
rules_group,
|
|
40
43
|
session_path_command,
|
|
41
44
|
start_command,
|
|
42
45
|
status_command,
|
|
@@ -49,6 +52,7 @@ from .commands import (
|
|
|
49
52
|
verify_docs_command,
|
|
50
53
|
whoami_command,
|
|
51
54
|
)
|
|
55
|
+
from .commands.entities import entities_group
|
|
52
56
|
from .commands.graph import graph_group
|
|
53
57
|
|
|
54
58
|
|
|
@@ -137,6 +141,7 @@ cli.add_command(context_command, name="context")
|
|
|
137
141
|
cli.add_command(submit_session_command, name="submit-session")
|
|
138
142
|
cli.add_command(session_path_command, name="session-path")
|
|
139
143
|
cli.add_command(index_command, name="index")
|
|
144
|
+
cli.add_command(ingest_command, name="ingest")
|
|
140
145
|
cli.add_command(inject_command, name="inject")
|
|
141
146
|
cli.add_command(jobs_command, name="jobs")
|
|
142
147
|
cli.add_command(reset_command, name="reset")
|
|
@@ -152,8 +157,11 @@ cli.add_command(install_agent_command, name="install-agent")
|
|
|
152
157
|
cli.add_command(install_session_hooks_command, name="install-session-hooks")
|
|
153
158
|
cli.add_command(backfill_command, name="backfill-sessions")
|
|
154
159
|
cli.add_command(records_group, name="records")
|
|
160
|
+
cli.add_command(references_group, name="references")
|
|
161
|
+
cli.add_command(rules_group, name="rules")
|
|
155
162
|
cli.add_command(extraction_group, name="extraction")
|
|
156
163
|
cli.add_command(graph_group, name="graph")
|
|
164
|
+
cli.add_command(entities_group, name="entities")
|
|
157
165
|
|
|
158
166
|
# Register doc-sync maintenance commands (hidden; deterministic, never call an LLM)
|
|
159
167
|
cli.add_command(sync_docs_command, name="sync-docs")
|
|
@@ -58,6 +58,9 @@ class IndexingStatus:
|
|
|
58
58
|
migration: dict[str, Any] | None = None
|
|
59
59
|
graph_index: dict[str, Any] | None = None
|
|
60
60
|
features: dict[str, Any] | None = None
|
|
61
|
+
#: Programmatic text-ingest chunk count block ({"chunks": int}); separate
|
|
62
|
+
#: from folder-manifest-derived total_documents (spec Item 3).
|
|
63
|
+
text_ingest: dict[str, Any] | None = None
|
|
61
64
|
#: Index-drift warnings (embedding provider/model or storage backend changed
|
|
62
65
|
#: away from what the existing index was built with). Empty when consistent.
|
|
63
66
|
index_warnings: list[str] = field(default_factory=list)
|
|
@@ -287,6 +290,7 @@ class DocServeClient:
|
|
|
287
290
|
embedding_cache=data.get("embedding_cache"),
|
|
288
291
|
graph_index=data.get("graph_index"),
|
|
289
292
|
features=data.get("features"),
|
|
293
|
+
text_ingest=data.get("text_ingest"),
|
|
290
294
|
index_warnings=data.get("index_warnings") or [],
|
|
291
295
|
)
|
|
292
296
|
|
|
@@ -300,8 +304,11 @@ class DocServeClient:
|
|
|
300
304
|
source_types: list[str] | None = None,
|
|
301
305
|
languages: list[str] | None = None,
|
|
302
306
|
file_paths: list[str] | None = None,
|
|
307
|
+
domains: list[str] | None = None,
|
|
308
|
+
metadata_filter: dict[str, str] | None = None,
|
|
303
309
|
time_decay: bool = True,
|
|
304
310
|
language: str | None = None,
|
|
311
|
+
include_sensitive: bool = False,
|
|
305
312
|
) -> QueryResponse:
|
|
306
313
|
"""
|
|
307
314
|
Query indexed documents.
|
|
@@ -315,8 +322,15 @@ class DocServeClient:
|
|
|
315
322
|
source_types: Filter by source types (doc, code, test).
|
|
316
323
|
languages: Filter by programming languages.
|
|
317
324
|
file_paths: Filter by file path patterns.
|
|
325
|
+
domains: Filter to chunks ingested under one of these domains
|
|
326
|
+
(reserved `domain` metadata key; OR across values).
|
|
327
|
+
metadata_filter: Filter to chunks whose metadata exact-matches
|
|
328
|
+
every key/value pair (AND across keys).
|
|
318
329
|
language: BM25 query language override (ISO 639-1). Defaults to
|
|
319
330
|
the project bm25.language setting when None.
|
|
331
|
+
include_sensitive: Reveal rows marked sensitive (interactive CLI
|
|
332
|
+
only). Omitted from the request when False, so MCP/dashboard
|
|
333
|
+
callers — which never pass it — stay at default-deny.
|
|
320
334
|
|
|
321
335
|
Returns:
|
|
322
336
|
QueryResponse with matching results.
|
|
@@ -330,6 +344,8 @@ class DocServeClient:
|
|
|
330
344
|
}
|
|
331
345
|
if not time_decay:
|
|
332
346
|
request_data["time_decay"] = False
|
|
347
|
+
if include_sensitive:
|
|
348
|
+
request_data["include_sensitive"] = True
|
|
333
349
|
# Truthy (not ``is not None``) so an empty list means "no filter" — the
|
|
334
350
|
# MCP QueryInput now defaults these to ``[]`` (avoids a nullable-union
|
|
335
351
|
# JSON schema that some LLM clients mishandle). Empty == omit == default.
|
|
@@ -339,6 +355,10 @@ class DocServeClient:
|
|
|
339
355
|
request_data["languages"] = languages
|
|
340
356
|
if file_paths:
|
|
341
357
|
request_data["file_paths"] = file_paths
|
|
358
|
+
if domains:
|
|
359
|
+
request_data["domains"] = domains
|
|
360
|
+
if metadata_filter:
|
|
361
|
+
request_data["metadata_filter"] = metadata_filter
|
|
342
362
|
if language is not None:
|
|
343
363
|
request_data["language"] = language
|
|
344
364
|
|
|
@@ -449,6 +469,9 @@ class DocServeClient:
|
|
|
449
469
|
watch_mode: str | None = None,
|
|
450
470
|
watch_debounce_seconds: int | None = None,
|
|
451
471
|
rebuild_graph: bool = False,
|
|
472
|
+
domain: str | None = None,
|
|
473
|
+
authority: str | None = None,
|
|
474
|
+
force_authority: bool = False,
|
|
452
475
|
) -> IndexResponse:
|
|
453
476
|
"""
|
|
454
477
|
Enqueue an indexing job for documents and optionally code from a folder.
|
|
@@ -474,6 +497,11 @@ class DocServeClient:
|
|
|
474
497
|
watch_debounce_seconds: Per-folder debounce in seconds.
|
|
475
498
|
rebuild_graph: Rebuild the graph index from existing chunks only
|
|
476
499
|
(no embedding); returns a completed response, not a queued job.
|
|
500
|
+
domain: Optional user-facing domain label for the folder (6.5).
|
|
501
|
+
authority: Binary provenance trust level: 'authoritative' or
|
|
502
|
+
'reference' (6.5). None lets the server resolve the default.
|
|
503
|
+
force_authority: Allow an external folder to be registered as
|
|
504
|
+
'authoritative' (6.5).
|
|
477
505
|
|
|
478
506
|
Returns:
|
|
479
507
|
IndexResponse with job ID and queue status.
|
|
@@ -490,7 +518,12 @@ class DocServeClient:
|
|
|
490
518
|
"exclude_patterns": exclude_patterns,
|
|
491
519
|
"force": force,
|
|
492
520
|
"force_budget": force_budget,
|
|
521
|
+
"force_authority": force_authority,
|
|
493
522
|
}
|
|
523
|
+
if domain is not None:
|
|
524
|
+
body["domain"] = domain
|
|
525
|
+
if authority is not None:
|
|
526
|
+
body["authority"] = authority
|
|
494
527
|
if include_types is not None:
|
|
495
528
|
body["include_types"] = include_types
|
|
496
529
|
if injector_script is not None:
|
|
@@ -521,6 +554,225 @@ class DocServeClient:
|
|
|
521
554
|
message=data.get("message"),
|
|
522
555
|
)
|
|
523
556
|
|
|
557
|
+
def ingest_text(
|
|
558
|
+
self,
|
|
559
|
+
*,
|
|
560
|
+
items: list[dict[str, Any]],
|
|
561
|
+
sensitivity: str = "normal",
|
|
562
|
+
language: str | None = None,
|
|
563
|
+
) -> dict[str, Any]:
|
|
564
|
+
"""
|
|
565
|
+
Ingest free text with caller-supplied provenance (spec Item 3).
|
|
566
|
+
|
|
567
|
+
Args:
|
|
568
|
+
items: List of {text, metadata, domain, source, source_id} dicts.
|
|
569
|
+
sensitivity: Sensitivity mark applied to all items in this call.
|
|
570
|
+
language: Optional BM25 language override.
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
Response dict with chunks_new, chunks_kept, chunks_deleted,
|
|
574
|
+
chunk_ids, source_ids.
|
|
575
|
+
"""
|
|
576
|
+
body: dict[str, Any] = {"items": items, "sensitivity": sensitivity}
|
|
577
|
+
if language:
|
|
578
|
+
body["language"] = language
|
|
579
|
+
return self._request("POST", "/ingest/text", json=body)
|
|
580
|
+
|
|
581
|
+
def ingest_records(
|
|
582
|
+
self,
|
|
583
|
+
*,
|
|
584
|
+
items: list[dict[str, Any]],
|
|
585
|
+
sensitivity: str = "normal",
|
|
586
|
+
) -> dict[str, Any]:
|
|
587
|
+
"""Ingest caller-asserted typed records (POST /ingest/records).
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
items: List of {subject, metric, value, unit?, ts?, domain, source,
|
|
591
|
+
source_id, confidence?, properties?} dicts.
|
|
592
|
+
sensitivity: Sensitivity mark applied to all items in this call.
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
Response dict with the number of records persisted.
|
|
596
|
+
"""
|
|
597
|
+
return self._request(
|
|
598
|
+
"POST",
|
|
599
|
+
"/ingest/records",
|
|
600
|
+
json={"items": items, "sensitivity": sensitivity},
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
def ingest_references(
|
|
604
|
+
self,
|
|
605
|
+
*,
|
|
606
|
+
items: list[dict[str, Any]],
|
|
607
|
+
sensitivity: str = "normal",
|
|
608
|
+
) -> dict[str, Any]:
|
|
609
|
+
"""Ingest lazy-tier references (POST /ingest/references).
|
|
610
|
+
|
|
611
|
+
Args:
|
|
612
|
+
items: List of {pointer, summary, domain, source, source_id} dicts.
|
|
613
|
+
sensitivity: Sensitivity mark applied to all items in this call.
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
Response dict with the number of references persisted.
|
|
617
|
+
"""
|
|
618
|
+
return self._request(
|
|
619
|
+
"POST",
|
|
620
|
+
"/ingest/references",
|
|
621
|
+
json={"items": items, "sensitivity": sensitivity},
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
def references_list(self, domain: str | None = None) -> dict[str, Any]:
|
|
625
|
+
"""List references, optionally filtered by domain (GET /references)."""
|
|
626
|
+
params = {"domain": domain} if domain else None
|
|
627
|
+
return self._request("GET", "/references", params=params)
|
|
628
|
+
|
|
629
|
+
def references_search(
|
|
630
|
+
self,
|
|
631
|
+
*,
|
|
632
|
+
query: str,
|
|
633
|
+
top_k: int = 5,
|
|
634
|
+
domain: str | None = None,
|
|
635
|
+
include_sensitive: bool = False,
|
|
636
|
+
) -> dict[str, Any]:
|
|
637
|
+
"""Semantic search over reference summaries (POST /references/search)."""
|
|
638
|
+
body: dict[str, Any] = {
|
|
639
|
+
"query": query,
|
|
640
|
+
"top_k": top_k,
|
|
641
|
+
"include_sensitive": include_sensitive,
|
|
642
|
+
}
|
|
643
|
+
if domain:
|
|
644
|
+
body["domain"] = domain
|
|
645
|
+
return self._request("POST", "/references/search", json=body)
|
|
646
|
+
|
|
647
|
+
def references_embed_missing(self) -> dict[str, Any]:
|
|
648
|
+
"""Backfill embeddings for references lacking one
|
|
649
|
+
(POST /references/embed-missing)."""
|
|
650
|
+
return self._request("POST", "/references/embed-missing")
|
|
651
|
+
|
|
652
|
+
def ingest_sources(
|
|
653
|
+
self,
|
|
654
|
+
*,
|
|
655
|
+
domain: str | None = None,
|
|
656
|
+
source: str | None = None,
|
|
657
|
+
include_sensitive: bool = False,
|
|
658
|
+
) -> dict[str, Any]:
|
|
659
|
+
"""List distinct ingested source_ids (GET /ingest/sources).
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
domain: Optional domain filter.
|
|
663
|
+
source: Optional raw-source filter.
|
|
664
|
+
include_sensitive: Reveal sources whose chunks are marked sensitive.
|
|
665
|
+
|
|
666
|
+
Returns:
|
|
667
|
+
Response dict with `sources` (each: source_id, domain, source,
|
|
668
|
+
chunk_count, ingested_at) and `total`.
|
|
669
|
+
"""
|
|
670
|
+
params: dict[str, Any] = {}
|
|
671
|
+
if domain:
|
|
672
|
+
params["domain"] = domain
|
|
673
|
+
if source:
|
|
674
|
+
params["source"] = source
|
|
675
|
+
if include_sensitive:
|
|
676
|
+
params["include_sensitive"] = True
|
|
677
|
+
return self._request("GET", "/ingest/sources", params=params or None)
|
|
678
|
+
|
|
679
|
+
def ingest_show(
|
|
680
|
+
self,
|
|
681
|
+
source_id: str,
|
|
682
|
+
*,
|
|
683
|
+
offset: int = 0,
|
|
684
|
+
limit: int = 50,
|
|
685
|
+
include_sensitive: bool = False,
|
|
686
|
+
) -> dict[str, Any]:
|
|
687
|
+
"""Show one source_id's ingested chunks, paginated
|
|
688
|
+
(GET /ingest/text/{source_id}).
|
|
689
|
+
|
|
690
|
+
Args:
|
|
691
|
+
source_id: Caller-supplied source id used at ingest time.
|
|
692
|
+
offset: Pagination offset.
|
|
693
|
+
limit: Page size.
|
|
694
|
+
include_sensitive: Reveal chunks marked sensitive.
|
|
695
|
+
|
|
696
|
+
Returns:
|
|
697
|
+
Response dict with source_id, total, offset, limit, and chunks
|
|
698
|
+
(each: chunk_id, text, metadata).
|
|
699
|
+
"""
|
|
700
|
+
params: dict[str, Any] = {"offset": offset, "limit": limit}
|
|
701
|
+
if include_sensitive:
|
|
702
|
+
params["include_sensitive"] = True
|
|
703
|
+
return self._request("GET", f"/ingest/text/{source_id}", params=params)
|
|
704
|
+
|
|
705
|
+
def ingest_delete(self, source_id: str) -> dict[str, Any]:
|
|
706
|
+
"""
|
|
707
|
+
Delete all ingested chunks for a source_id (un-ingest).
|
|
708
|
+
|
|
709
|
+
Args:
|
|
710
|
+
source_id: Caller-supplied source id used at ingest time.
|
|
711
|
+
|
|
712
|
+
Returns:
|
|
713
|
+
Response dict with chunks_deleted.
|
|
714
|
+
"""
|
|
715
|
+
return self._request("DELETE", f"/ingest/text/{source_id}")
|
|
716
|
+
|
|
717
|
+
def ingest_forget(self, source_id: str) -> dict[str, Any]:
|
|
718
|
+
"""
|
|
719
|
+
Full forget: delete a source_id across all three ingest tiers
|
|
720
|
+
(DELETE /ingest/source/{source_id}).
|
|
721
|
+
|
|
722
|
+
Args:
|
|
723
|
+
source_id: Caller-supplied source id used at ingest time.
|
|
724
|
+
|
|
725
|
+
Returns:
|
|
726
|
+
Response dict with chunks_deleted, records_deleted,
|
|
727
|
+
references_deleted.
|
|
728
|
+
"""
|
|
729
|
+
return self._request("DELETE", f"/ingest/source/{source_id}")
|
|
730
|
+
|
|
731
|
+
# --- identity (G5): person / alias / link ---------------------------
|
|
732
|
+
|
|
733
|
+
def entities_person(self, person: dict[str, Any]) -> dict[str, Any]:
|
|
734
|
+
"""Upsert a person (POST /entities/person)."""
|
|
735
|
+
return self._request("POST", "/entities/person", json=person)
|
|
736
|
+
|
|
737
|
+
def entities_alias(self, alias: dict[str, Any]) -> dict[str, Any]:
|
|
738
|
+
"""Bind a surface to a person (POST /entities/alias)."""
|
|
739
|
+
return self._request("POST", "/entities/alias", json=alias)
|
|
740
|
+
|
|
741
|
+
def entities_link(self, link: dict[str, Any]) -> dict[str, Any]:
|
|
742
|
+
"""Attach a ref to a person or record it unresolved
|
|
743
|
+
(POST /entities/link)."""
|
|
744
|
+
return self._request("POST", "/entities/link", json=link)
|
|
745
|
+
|
|
746
|
+
def entities_resolve(
|
|
747
|
+
self,
|
|
748
|
+
*,
|
|
749
|
+
surface: str,
|
|
750
|
+
scope: str | None = None,
|
|
751
|
+
at: str | None = None,
|
|
752
|
+
ref: str | None = None,
|
|
753
|
+
session_id: str | None = None,
|
|
754
|
+
) -> dict[str, Any]:
|
|
755
|
+
"""Ranked candidates + evidence (GET /entities/resolve). Never picks."""
|
|
756
|
+
params: dict[str, Any] = {"surface": surface}
|
|
757
|
+
if scope is not None:
|
|
758
|
+
params["scope"] = scope
|
|
759
|
+
if at is not None:
|
|
760
|
+
params["at"] = at
|
|
761
|
+
if ref is not None:
|
|
762
|
+
params["ref"] = ref
|
|
763
|
+
if session_id is not None:
|
|
764
|
+
params["session_id"] = session_id
|
|
765
|
+
return self._request("GET", "/entities/resolve", params=params)
|
|
766
|
+
|
|
767
|
+
def entities_unresolved(self) -> dict[str, Any]:
|
|
768
|
+
"""The unresolved-link bucket (GET /entities/unresolved)."""
|
|
769
|
+
return self._request("GET", "/entities/unresolved")
|
|
770
|
+
|
|
771
|
+
def entities_backfill(self) -> dict[str, Any]:
|
|
772
|
+
"""Re-score unresolved links against current aliases
|
|
773
|
+
(POST /entities/backfill)."""
|
|
774
|
+
return self._request("POST", "/entities/backfill")
|
|
775
|
+
|
|
524
776
|
def estimate_index(
|
|
525
777
|
self,
|
|
526
778
|
folder_path: str,
|
|
@@ -11,6 +11,7 @@ from .extraction import extraction_group
|
|
|
11
11
|
from .folders import folders_group
|
|
12
12
|
from .hook import hook_group
|
|
13
13
|
from .index import index_command
|
|
14
|
+
from .ingest import ingest_command
|
|
14
15
|
from .init import init_command
|
|
15
16
|
from .inject import inject_command
|
|
16
17
|
from .install_agent import install_agent_command
|
|
@@ -23,8 +24,10 @@ from .plugin_detect import plugin_group
|
|
|
23
24
|
from .query import query_command
|
|
24
25
|
from .recall import recall_command
|
|
25
26
|
from .records import records_group
|
|
27
|
+
from .references import references_group
|
|
26
28
|
from .remember import remember_command
|
|
27
29
|
from .reset import reset_command
|
|
30
|
+
from .rules import rules_group
|
|
28
31
|
from .session_hooks import install_session_hooks_command
|
|
29
32
|
from .sessions import session_path_command, submit_session_command
|
|
30
33
|
from .start import start_command
|
|
@@ -47,6 +50,7 @@ __all__ = [
|
|
|
47
50
|
"doctor_command",
|
|
48
51
|
"folders_group",
|
|
49
52
|
"index_command",
|
|
53
|
+
"ingest_command",
|
|
50
54
|
"inject_command",
|
|
51
55
|
"init_command",
|
|
52
56
|
"install_agent_command",
|
|
@@ -61,9 +65,11 @@ __all__ = [
|
|
|
61
65
|
"hook_group",
|
|
62
66
|
"query_command",
|
|
63
67
|
"records_group",
|
|
68
|
+
"references_group",
|
|
64
69
|
"recall_command",
|
|
65
70
|
"remember_command",
|
|
66
71
|
"reset_command",
|
|
72
|
+
"rules_group",
|
|
67
73
|
"session_path_command",
|
|
68
74
|
"start_command",
|
|
69
75
|
"submit_session_command",
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Identity commands (G5): person / alias / link over the engine's identity
|
|
2
|
+
store. The engine stores and RANKS candidates; it never picks a winner — the
|
|
3
|
+
consumer app decides (D7).
|
|
4
|
+
|
|
5
|
+
``--json`` contract (mirrors ``ingest``): on success the result dict is printed;
|
|
6
|
+
on failure a ``{"error": ...}`` object is printed with a non-zero exit and NO
|
|
7
|
+
``results`` key."""
|
|
8
|
+
|
|
9
|
+
import json as _json
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
|
|
16
|
+
from ..client import (
|
|
17
|
+
ConnectionError,
|
|
18
|
+
DocServeClient,
|
|
19
|
+
ServerError,
|
|
20
|
+
exit_on_connection_error,
|
|
21
|
+
)
|
|
22
|
+
from ..config import get_server_url
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _emit(result: dict[str, Any], json_output: bool) -> None:
|
|
28
|
+
if json_output:
|
|
29
|
+
click.echo(_json.dumps(result, indent=2))
|
|
30
|
+
else:
|
|
31
|
+
console.print(result)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _run(
|
|
35
|
+
resolved_url: str,
|
|
36
|
+
json_output: bool,
|
|
37
|
+
fn: Callable[[DocServeClient], dict[str, Any]],
|
|
38
|
+
) -> None:
|
|
39
|
+
"""Execute ``fn(client)`` with the shared connection/error handling and
|
|
40
|
+
the ``--json`` failure contract."""
|
|
41
|
+
try:
|
|
42
|
+
with DocServeClient(base_url=resolved_url) as client:
|
|
43
|
+
result = fn(client)
|
|
44
|
+
_emit(result, json_output)
|
|
45
|
+
except ConnectionError as e:
|
|
46
|
+
exit_on_connection_error(e, base_url=resolved_url, json_output=json_output)
|
|
47
|
+
except ServerError as e:
|
|
48
|
+
if json_output:
|
|
49
|
+
click.echo(_json.dumps({"error": str(e), "detail": e.detail}))
|
|
50
|
+
else:
|
|
51
|
+
console.print(f"[red]Server Error ({e.status_code}):[/] {e.detail}")
|
|
52
|
+
raise SystemExit(1) from e
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_URL = click.option(
|
|
56
|
+
"--url",
|
|
57
|
+
envvar="BRAINPALACE_URL",
|
|
58
|
+
default=None,
|
|
59
|
+
help="BrainPalace server URL (default: from config or http://127.0.0.1:8000)",
|
|
60
|
+
)
|
|
61
|
+
_JSON = click.option("--json", "json_output", is_flag=True, help="Output as JSON")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@click.group("entities")
|
|
65
|
+
def entities_group() -> None:
|
|
66
|
+
"""Manage identity: person / alias / link (who someone is)."""
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@entities_group.command("person")
|
|
70
|
+
@click.option("--id", "person_id", default=None, help="Existing person id (upsert).")
|
|
71
|
+
@click.option("--name", default=None, help="Display name (omit for an unknown person).")
|
|
72
|
+
@click.option("--kind", default="person", show_default=True, help="person|place|org.")
|
|
73
|
+
@click.option("--domain", required=True, help="Domain the person belongs to.")
|
|
74
|
+
@click.option("--sensitivity", default="normal", show_default=True)
|
|
75
|
+
@_URL
|
|
76
|
+
@_JSON
|
|
77
|
+
def person_cmd(
|
|
78
|
+
person_id: str | None,
|
|
79
|
+
name: str | None,
|
|
80
|
+
kind: str,
|
|
81
|
+
domain: str,
|
|
82
|
+
sensitivity: str,
|
|
83
|
+
url: str | None,
|
|
84
|
+
json_output: bool,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Upsert a person (naming an unknown one is an update in place)."""
|
|
87
|
+
resolved_url = url or get_server_url()
|
|
88
|
+
body = {"kind": kind, "domain": domain, "sensitivity": sensitivity}
|
|
89
|
+
if person_id:
|
|
90
|
+
body["id"] = person_id
|
|
91
|
+
if name is not None:
|
|
92
|
+
body["name"] = name
|
|
93
|
+
_run(resolved_url, json_output, lambda c: c.entities_person(body))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@entities_group.command("alias")
|
|
97
|
+
@click.option("--surface", required=True, help='Surface string, e.g. "Mama".')
|
|
98
|
+
@click.option(
|
|
99
|
+
"--person-id", "person_id", required=True, help="Person the surface names."
|
|
100
|
+
)
|
|
101
|
+
@click.option("--scope", default=None, help="Speaker person id; omit for global.")
|
|
102
|
+
@click.option("--valid-from", "valid_from", default=None)
|
|
103
|
+
@click.option("--valid-to", "valid_to", default=None)
|
|
104
|
+
@_URL
|
|
105
|
+
@_JSON
|
|
106
|
+
def alias_cmd(
|
|
107
|
+
surface: str,
|
|
108
|
+
person_id: str,
|
|
109
|
+
scope: str | None,
|
|
110
|
+
valid_from: str | None,
|
|
111
|
+
valid_to: str | None,
|
|
112
|
+
url: str | None,
|
|
113
|
+
json_output: bool,
|
|
114
|
+
) -> None:
|
|
115
|
+
"""Bind a surface to a person (scoped + time-bounded)."""
|
|
116
|
+
resolved_url = url or get_server_url()
|
|
117
|
+
body = {"surface": surface, "person_id": person_id}
|
|
118
|
+
if scope is not None:
|
|
119
|
+
body["scope"] = scope
|
|
120
|
+
if valid_from is not None:
|
|
121
|
+
body["valid_from"] = valid_from
|
|
122
|
+
if valid_to is not None:
|
|
123
|
+
body["valid_to"] = valid_to
|
|
124
|
+
_run(resolved_url, json_output, lambda c: c.entities_alias(body))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@entities_group.command("link")
|
|
128
|
+
@click.option(
|
|
129
|
+
"--ref", required=True, help="chunk address, session id, or external key."
|
|
130
|
+
)
|
|
131
|
+
@click.option(
|
|
132
|
+
"--ref-kind", "ref_kind", required=True, help="chunk|span|session|external."
|
|
133
|
+
)
|
|
134
|
+
@click.option("--role", required=True, help="speaker|mentioned|participant.")
|
|
135
|
+
@click.option(
|
|
136
|
+
"--method", required=True, help="user_asserted|call_log|llm_inferred|alias_match."
|
|
137
|
+
)
|
|
138
|
+
@click.option("--at", required=True, help="Mention timestamp (ISO-8601).")
|
|
139
|
+
@click.option(
|
|
140
|
+
"--person-id", "person_id", default=None, help="Resolved person; omit = unresolved."
|
|
141
|
+
)
|
|
142
|
+
@click.option("--span-start", "span_start", type=int, default=None)
|
|
143
|
+
@click.option("--span-end", "span_end", type=int, default=None)
|
|
144
|
+
@click.option("--surface", default=None, help="Surface being resolved (for backfill).")
|
|
145
|
+
@click.option("--scope", default=None, help="Scope of that surface (for backfill).")
|
|
146
|
+
@click.option("--confidence", type=float, default=None)
|
|
147
|
+
@_URL
|
|
148
|
+
@_JSON
|
|
149
|
+
def link_cmd(
|
|
150
|
+
ref: str,
|
|
151
|
+
ref_kind: str,
|
|
152
|
+
role: str,
|
|
153
|
+
method: str,
|
|
154
|
+
at: str,
|
|
155
|
+
person_id: str | None,
|
|
156
|
+
span_start: int | None,
|
|
157
|
+
span_end: int | None,
|
|
158
|
+
surface: str | None,
|
|
159
|
+
scope: str | None,
|
|
160
|
+
confidence: float | None,
|
|
161
|
+
url: str | None,
|
|
162
|
+
json_output: bool,
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Attach a ref to a person, or record it unresolved."""
|
|
165
|
+
resolved_url = url or get_server_url()
|
|
166
|
+
body: dict[str, Any] = {
|
|
167
|
+
"ref": ref,
|
|
168
|
+
"ref_kind": ref_kind,
|
|
169
|
+
"role": role,
|
|
170
|
+
"method": method,
|
|
171
|
+
"at": at,
|
|
172
|
+
}
|
|
173
|
+
if person_id is not None:
|
|
174
|
+
body["person_id"] = person_id
|
|
175
|
+
if span_start is not None:
|
|
176
|
+
body["span_start"] = span_start
|
|
177
|
+
if span_end is not None:
|
|
178
|
+
body["span_end"] = span_end
|
|
179
|
+
if surface is not None:
|
|
180
|
+
body["surface"] = surface
|
|
181
|
+
if scope is not None:
|
|
182
|
+
body["scope"] = scope
|
|
183
|
+
if confidence is not None:
|
|
184
|
+
body["confidence"] = confidence
|
|
185
|
+
_run(resolved_url, json_output, lambda c: c.entities_link(body))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
@entities_group.command("resolve")
|
|
189
|
+
@click.option("--surface", required=True, help="Surface string to resolve.")
|
|
190
|
+
@click.option("--scope", default=None, help="Speaker person id; omit for global.")
|
|
191
|
+
@click.option("--at", default=None, help="Resolution timestamp (defaults to now).")
|
|
192
|
+
@click.option("--ref", default=None, help="Chunk/session ref for co-occurrence.")
|
|
193
|
+
@click.option("--session-id", "session_id", default=None, help="Session for recency.")
|
|
194
|
+
@_URL
|
|
195
|
+
@_JSON
|
|
196
|
+
def resolve_cmd(
|
|
197
|
+
surface: str,
|
|
198
|
+
scope: str | None,
|
|
199
|
+
at: str | None,
|
|
200
|
+
ref: str | None,
|
|
201
|
+
session_id: str | None,
|
|
202
|
+
url: str | None,
|
|
203
|
+
json_output: bool,
|
|
204
|
+
) -> None:
|
|
205
|
+
"""Ranked candidates + evidence. Never picks a winner."""
|
|
206
|
+
resolved_url = url or get_server_url()
|
|
207
|
+
_run(
|
|
208
|
+
resolved_url,
|
|
209
|
+
json_output,
|
|
210
|
+
lambda c: c.entities_resolve(
|
|
211
|
+
surface=surface, scope=scope, at=at, ref=ref, session_id=session_id
|
|
212
|
+
),
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@entities_group.command("unresolved")
|
|
217
|
+
@_URL
|
|
218
|
+
@_JSON
|
|
219
|
+
def unresolved_cmd(url: str | None, json_output: bool) -> None:
|
|
220
|
+
"""List unresolved links (the bucket the app must decide or ask about)."""
|
|
221
|
+
resolved_url = url or get_server_url()
|
|
222
|
+
_run(resolved_url, json_output, lambda c: c.entities_unresolved())
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@entities_group.command("backfill")
|
|
226
|
+
@_URL
|
|
227
|
+
@_JSON
|
|
228
|
+
def backfill_cmd(url: str | None, json_output: bool) -> None:
|
|
229
|
+
"""Re-score unresolved links against the current aliases."""
|
|
230
|
+
resolved_url = url or get_server_url()
|
|
231
|
+
_run(resolved_url, json_output, lambda c: c.entities_backfill())
|