mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-07 05:12:34 +00:00
feat: add semantic index scaffolding, cli/api surfaces, and tui tree nav polish
This commit is contained in:
1 parent
7c9b6253b2
commit
165e2db204
16 files changed
+1087
-67
No files matched your search
@@ -51,7 +51,7 @@ legacy_tui_option = typer.Option(
|
||||
)
|
||||
|
||||
# Sub command groups
|
||||
from . import entry, vault, nostr, config, fingerprint, util, api, agent
|
||||
from . import entry, vault, nostr, config, fingerprint, util, api, agent, semantic
|
||||
from .capabilities import register_capabilities_command
|
||||
|
||||
app.add_typer(entry.app, name="entry")
|
||||
@@ -62,6 +62,7 @@ app.add_typer(fingerprint.app, name="fingerprint")
|
||||
app.add_typer(util.app, name="util")
|
||||
app.add_typer(api.app, name="api")
|
||||
app.add_typer(agent.app, name="agent")
|
||||
app.add_typer(semantic.app, name="semantic")
|
||||
register_capabilities_command(app)
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ def build_capabilities() -> dict[str, Any]:
|
||||
"util",
|
||||
"api",
|
||||
"agent",
|
||||
"semantic",
|
||||
],
|
||||
"global_options": [
|
||||
"--fingerprint",
|
||||
@@ -53,6 +54,10 @@ def build_capabilities() -> dict[str, Any]:
|
||||
"/api/v1/high-risk/status",
|
||||
"/api/v1/high-risk/unlock",
|
||||
"/api/v1/high-risk/lock",
|
||||
"/api/v1/semantic/status",
|
||||
"/api/v1/semantic/build",
|
||||
"/api/v1/semantic/rebuild",
|
||||
"/api/v1/semantic/search",
|
||||
],
|
||||
"auth_headers": ["Authorization: Bearer <token>"],
|
||||
"sensitive_auth_headers": [
|
||||
@@ -206,6 +211,25 @@ def build_capabilities() -> dict[str, Any]:
|
||||
"search_includes_link_metadata": True,
|
||||
"sync_union_merge_fields": ["tags", "custom_fields", "links"],
|
||||
},
|
||||
"semantic_index": {
|
||||
"commands": [
|
||||
"semantic status",
|
||||
"semantic enable",
|
||||
"semantic disable",
|
||||
"semantic build",
|
||||
"semantic rebuild",
|
||||
"semantic search",
|
||||
],
|
||||
"api_endpoints": [
|
||||
"GET /api/v1/semantic/status",
|
||||
"POST /api/v1/semantic/build",
|
||||
"POST /api/v1/semantic/rebuild",
|
||||
"POST /api/v1/semantic/search",
|
||||
"POST /api/v1/semantic/config",
|
||||
],
|
||||
"default_enabled": False,
|
||||
"sync_behavior": "local_derived_only",
|
||||
},
|
||||
"redaction": {
|
||||
"default_safe_output": True,
|
||||
"reveal_override": "agent get --reveal",
|
||||
@@ -244,6 +268,7 @@ def build_capabilities() -> dict[str, Any]:
|
||||
"Use `seedpass --legacy-tui` to force the legacy interface during cutover.",
|
||||
"In TUI v2, use `?` for help, `j` to jump to entry id, `1/2/3` to focus panes, `Ctrl+P` for palette, `p/n` for paging, and `x` to retry.",
|
||||
"For graph workflows, start with `seedpass entry link-add --help` and `seedpass entry links --help`.",
|
||||
"For semantic KB retrieval, run `seedpass semantic --help` and check `seedpass semantic status`.",
|
||||
"For document workflows, run `seedpass entry import-document --help` and `seedpass agent document-import --help`.",
|
||||
"After unlock/login, run `seedpass capabilities` before autonomous runs.",
|
||||
"For agent bootstrap context, run `seedpass agent bootstrap-context`.",
|
||||
@@ -284,6 +309,7 @@ def emit_capabilities(output_format: str) -> str:
|
||||
lines.append("- Security posture checks and remediation bundles")
|
||||
lines.append("- Audited access log with chained HMAC signatures")
|
||||
lines.append("- Knowledge graph links: entry link-add/links/link-remove")
|
||||
lines.append("- Semantic KB retrieval: semantic status/build/rebuild/search")
|
||||
lines.append("- Document file workflows for users and agents")
|
||||
lines.append("")
|
||||
lines.append("Discovery hints:")
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
import click
|
||||
|
||||
from .common import _get_pm
|
||||
from seedpass.core.api import SemanticIndexService
|
||||
from seedpass.core.entry_types import ALL_ENTRY_TYPES
|
||||
|
||||
app = typer.Typer(help="Manage local semantic vector index and search")
|
||||
|
||||
|
||||
def _get_semantic_service(ctx: typer.Context) -> SemanticIndexService:
|
||||
return SemanticIndexService(_get_pm(ctx))
|
||||
|
||||
|
||||
@app.command("status")
|
||||
def semantic_status(ctx: typer.Context) -> None:
|
||||
"""Show semantic index status for the active profile."""
|
||||
service = _get_semantic_service(ctx)
|
||||
payload = service.status()
|
||||
typer.echo(json.dumps(payload, indent=2, sort_keys=True))
|
||||
|
||||
|
||||
@app.command("enable")
|
||||
def semantic_enable(ctx: typer.Context) -> None:
|
||||
"""Enable semantic index for this profile."""
|
||||
service = _get_semantic_service(ctx)
|
||||
payload = service.set_enabled(True)
|
||||
typer.echo(
|
||||
f"semantic index enabled (built={payload.get('built', False)}, records={payload.get('records', 0)})"
|
||||
)
|
||||
|
||||
|
||||
@app.command("disable")
|
||||
def semantic_disable(ctx: typer.Context) -> None:
|
||||
"""Disable semantic index for this profile."""
|
||||
service = _get_semantic_service(ctx)
|
||||
payload = service.set_enabled(False)
|
||||
typer.echo(
|
||||
f"semantic index disabled (built={payload.get('built', False)}, records={payload.get('records', 0)})"
|
||||
)
|
||||
|
||||
|
||||
@app.command("build")
|
||||
def semantic_build(ctx: typer.Context) -> None:
|
||||
"""Build semantic index incrementally from current entries."""
|
||||
service = _get_semantic_service(ctx)
|
||||
payload = service.build()
|
||||
typer.echo(
|
||||
f"semantic index built (records={payload.get('records', 0)}, enabled={payload.get('enabled', False)})"
|
||||
)
|
||||
|
||||
|
||||
@app.command("rebuild")
|
||||
def semantic_rebuild(ctx: typer.Context) -> None:
|
||||
"""Rebuild semantic index from scratch."""
|
||||
service = _get_semantic_service(ctx)
|
||||
payload = service.rebuild()
|
||||
typer.echo(
|
||||
f"semantic index rebuilt (records={payload.get('records', 0)}, enabled={payload.get('enabled', False)})"
|
||||
)
|
||||
|
||||
|
||||
@app.command("search")
|
||||
def semantic_search(
|
||||
ctx: typer.Context,
|
||||
query: str,
|
||||
k: int = typer.Option(10, "--k", help="Max results"),
|
||||
kind: Optional[str] = typer.Option(
|
||||
None,
|
||||
"--kind",
|
||||
help="Restrict to entry kind",
|
||||
click_type=click.Choice(ALL_ENTRY_TYPES),
|
||||
),
|
||||
as_json: bool = typer.Option(False, "--json", help="Emit JSON output"),
|
||||
) -> None:
|
||||
"""Run semantic search over locally indexed KB content."""
|
||||
service = _get_semantic_service(ctx)
|
||||
results = service.search(query, k=max(1, int(k)), kind=kind)
|
||||
if as_json:
|
||||
typer.echo(json.dumps(results, indent=2, sort_keys=True))
|
||||
return
|
||||
if not results:
|
||||
typer.echo("No semantic matches")
|
||||
return
|
||||
for item in results:
|
||||
entry_id = int(item.get("entry_id", 0))
|
||||
label = str(item.get("label", ""))
|
||||
entry_kind = str(item.get("kind", ""))
|
||||
score = float(item.get("score", 0.0))
|
||||
excerpt = str(item.get("excerpt", ""))
|
||||
typer.echo(f"{entry_id}: [{entry_kind}] {label} (score={score:.3f})")
|
||||
if excerpt:
|
||||
typer.echo(f" {excerpt}")
|
||||
Reference in new issue
Block a user