refactor(cli): remove obsolete workflow commands

This commit is contained in:
2026-08-16 02:29:00 +02:00
parent 91650d9c67
commit 7651b63cea
19 changed files with 211 additions and 646 deletions
+2 -8
View File
@@ -1,8 +1,8 @@
"""tht CLI -- Typer app `tht`.
Minimal skeleton for now: registers `phase_app` (the gate needs `tht phase meta --json`).
Other command groups (db, schema, lsh, vector, memory, session, decision, sql, cte,
evidence, search) are wired in their porting tasks, once their backend deps land
Other command groups (db, schema, vector, memory, session, decision, sql, cte,
search) are wired in their porting tasks, once their backend deps land
(A9 ports db/mschema/_guards; B1 ports vectorstore; B3 ports search/lshindex/sampling).
"""
from __future__ import annotations
@@ -42,9 +42,6 @@ from tht.cli.datamart_cmd import datamart_app
from tht.cli.db_cmd import db_app
from tht.cli.decision_cmd import decision_app
from tht.cli.doctor_cmd import doctor
from tht.cli.evidence_cmd import evidence_app
from tht.cli.formula_cmd import formula_app
from tht.cli.lsh_cmd import lsh_app
from tht.cli.memory_cmd import memory_app
from tht.cli.ollama_cmd import ollama_app
from tht.cli.phase_cmd import phase_app
@@ -63,13 +60,10 @@ app.add_typer(session_app, name="session")
app.add_typer(vector_app, name="vector")
app.add_typer(memory_app, name="memory")
app.add_typer(search_app, name="search")
app.add_typer(evidence_app, name="evidence")
app.add_typer(formula_app, name="formula")
app.add_typer(db_app, name="db")
app.add_typer(decision_app, name="decision")
app.add_typer(sql_app, name="sql")
app.add_typer(cte_app, name="cte")
app.add_typer(datamart_app, name="datamart")
app.add_typer(lsh_app, name="lsh")
app.add_typer(ollama_app, name="ollama")
app.command("doctor")(doctor)
-31
View File
@@ -291,34 +291,3 @@ def info_cmd(
return
typer.echo(f"{name}: {payload['index']}/{payload['total']} — "
f"approvato={payload['approved']}")
@cte_app.command("list")
def list_cmd(
session: str = typer.Option(..., "--session"),
config: Path = CONFIG_OPT,
) -> None:
"""Ultimo esito registrato per ogni CTE della sessione."""
from tht.ctetest import CteError, load_cte_tests_text
cfg = _load_config_or_exit(config)
load_session_or_exit(cfg, session)
try:
records = load_cte_tests_text(load_snapshot_or_exit(cfg, session).artifacts.get("cte_tests", ""))
except CteError as e:
typer.secho(f"ERRORE: impossibile leggere cte_tests.json: {e}",
fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
if not records:
typer.echo("Nessun test CTE registrato.")
return
latest = {}
for r in records:
latest[r.name] = r
for name, r in sorted(latest.items()):
line = f"{name}: {r.status} ({r.ts:%Y-%m-%d %H:%M}, {r.execution_ms} ms)"
if r.error:
line += f" — {r.error}"
if r.warnings:
line += f" [{len(r.warnings)} warning]"
typer.echo(line)
-60
View File
@@ -162,63 +162,3 @@ def add_cmd(
}])[0]
typer.secho(f"OK: decisione [{record.seq}] {record.type}: {record.subject}",
fg=typer.colors.GREEN)
@decision_app.command("retract")
def retract_cmd(
session: str = typer.Option(..., "--session", help="Id della sessione."),
config: Path = CONFIG_OPT,
) -> None:
"""Ritira l'ultima decisione sostanziale della fase corrente (D15 granularita' step).
Granularita' (a) del rollback §4.8: 'rispondi di nuovo a questa domanda'. Scrive un
marker decision_retracted (append-only, l'audit resta) che effective_decisions onora;
il widget corrente puo' essere riproposto. Non cambia la fase."""
from tht.phase import effective_decisions
cfg = _load_config_or_exit(config)
load_session_or_exit(cfg, session)
snapshot = load_snapshot_or_exit(cfg, session)
# Ultima decisione NON-meta della vista effective = quella associata al widget corrente.
meta = {
"phase_approved", "phase_auto_approved", "phase_reopened",
"phase_skipped", "decision_retracted",
}
substantive = [d for d in effective_decisions(snapshot) if d.type not in meta]
if not substantive:
typer.secho(
"Nessuna decisione sostanziale da ritirare nella fase corrente.",
fg=typer.colors.YELLOW, err=True,
)
raise typer.Exit(code=6)
target = substantive[-1]
record = session_repository(cfg).append_decisions(session, [{
"type": "decision_retracted", "subject": target.subject,
"rationale": f"ritira [{target.seq}] {target.type}", "retracts": target.seq,
}])[0]
typer.secho(
f"OK: ritirata decisione [{target.seq}] {target.type}: {target.subject} "
f"(marker #{record.seq}).",
fg=typer.colors.GREEN,
)
@decision_app.command("list")
def list_cmd(
session: str = typer.Option(..., "--session"),
config: Path = CONFIG_OPT,
) -> None:
"""Elenca le decisioni della sessione."""
cfg = _load_config_or_exit(config)
decisions = load_snapshot_or_exit(cfg, session).decisions
if not decisions:
typer.echo("Nessuna decisione registrata.")
return
for d in decisions:
line = f"[{d.seq}] {d.ts:%Y-%m-%d %H:%M} {d.type}: {d.subject}"
if d.detail:
line += f" — {d.detail}"
if d.rationale:
line += f" ({d.rationale})"
typer.echo(line)
-76
View File
@@ -1,81 +1,5 @@
from pathlib import Path
import typer
from tht.cli._guards import require_vector_write_allowed
from tht.cli.config_cmd import CONFIG_OPT
from tht.cli.schema_cmd import _load_config_or_exit
evidence_app = typer.Typer(help="Generazione e gestione delle evidence")
def evidence_root(cfg) -> Path:
return cfg.paths.artifacts / "evidence"
def _require_evidence_cfg(cfg):
if cfg.evidence is None:
typer.secho(
"ERRORE: sezione `evidence` mancante nel workspace yaml (serve `source_root`).",
fg=typer.colors.RED, err=True,
)
raise typer.Exit(code=1)
return cfg.evidence
@evidence_app.command("extract")
def extract_cmd(config: Path = CONFIG_OPT) -> None:
"""Rispecchia la cartella curata dell'ETL in artifacts/evidence/.
artifacts/evidence/ e' un artefatto derivato e rigenerabile: viene riallineato a
ogni estrazione (le evidence rimosse a monte spariscono anche qui). La gerarchia
per dominio della cartella sorgente viene preservata.
"""
import shutil
from tht.evidence.extract import load_curated
cfg = _load_config_or_exit(config)
sources = _require_evidence_cfg(cfg)
out_dir = evidence_root(cfg)
if out_dir.exists():
shutil.rmtree(out_dir)
written = 0
for rel, doc in load_curated(sources):
doc.save(out_dir / rel)
written += 1
if written == 0:
typer.secho(
f"ATTENZIONE: nessuna evidence trovata in "
f"{sources.source_root / sources.evidence_dir}",
fg=typer.colors.YELLOW, err=True,
)
raise typer.Exit(code=1)
typer.secho(f"OK: {written} evidence rispecchiate in {out_dir}", fg=typer.colors.GREEN)
@evidence_app.command("index")
def index_cmd(config: Path = CONFIG_OPT) -> None:
"""Embedda e sincronizza nel semantic store tutte le evidence presenti in artifacts/."""
from tht.adapters.factory import build_vector_store
from tht.cli.vector_cmd import (
_print_stats,
make_embedder,
require_vector_cfg,
sync_canonical_records,
)
from tht.evidence.model import load_evidence_dir
from tht.vectorstore.records import evidence_records
cfg = _load_config_or_exit(config)
require_vector_write_allowed(cfg, "evidence index")
require_vector_cfg(cfg)
docs = load_evidence_dir(evidence_root(cfg))
records = evidence_records(docs, cfg.vector.max_chunk_chars)
stats = sync_canonical_records(
"evidence",
records,
store=build_vector_store(cfg, require_write=True),
embedder=make_embedder(cfg.embeddings),
)
_print_stats(stats)
-81
View File
@@ -1,81 +0,0 @@
"""tht formula -- concept->SQL formula evidence store (spec D14b, §4.7.2).
Authoring + listing of reusable concept formulas. Retrieval for the workflow is via
`tht search find --kind formula "<concept>"`. The reviewer approves/rejects a candidate
formula in F4 (decisions concept_formula_approved / concept_formula_rejected), and the
approved one is reflected in schema_linking.json (SchemaLinking.concept_formulas).
"""
from pathlib import Path
import typer
from tht.cli.config_cmd import CONFIG_OPT
from tht.cli.evidence_cmd import evidence_root
from tht.cli.schema_cmd import _load_config_or_exit
formula_app = typer.Typer(help="Formule di concetto (concept -> SQL) riusabili (D14b)")
@formula_app.command("save")
def save_cmd(
concept: str = typer.Option(..., "--concept", help="Il concetto, es. 'fascia pediatrica'."),
sql_file: Path = typer.Option(
None, "--sql-file", help="File con l'espressione SQL della formula."
),
sql: str = typer.Option(None, "--sql", help="Espressione SQL inline (alternativa a --sql-file)."),
column: list[str] = typer.Option(None, "--column", help="Colonna usata (ripetibile)."),
status: str = typer.Option("draft", "--status", help="auto | draft | reviewed."),
source: list[str] = typer.Option(None, "--source", help="Fonte/evidence (ripetibile)."),
config: Path = CONFIG_OPT,
) -> None:
"""Salva una formula di concetto sotto artifacts/evidence/formulas/."""
from tht.evidence.formula_store import ConceptFormula, save_formula
cfg = _load_config_or_exit(config)
if sql_file is None and not sql:
typer.secho("ERRORE: indica --sql-file <path> oppure --sql \"<espr>\".",
fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
body = sql_file.read_text() if sql_file else sql
try:
formula = ConceptFormula(
concept=concept, columns=column or [], sql=body.strip(),
status=status, sources=source or [],
)
except ValueError as e:
typer.secho(f"ERRORE: formula non valida: {e}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
path = save_formula(evidence_root(cfg), formula)
typer.secho(f"OK: formula '{concept}' salvata in {path}.", fg=typer.colors.GREEN)
@formula_app.command("list")
def list_cmd(
concept: str = typer.Option(None, "--concept", help="Filtra per concetto (substring)."),
json_out: bool = typer.Option(False, "--json"),
config: Path = CONFIG_OPT,
) -> None:
"""Elenca le formule salvate (tutte, o filtrate per concetto)."""
import json as _json
from rich.console import Console
from rich.table import Table
from tht.evidence.formula_store import _load_all, search_formulas
cfg = _load_config_or_exit(config)
root = evidence_root(cfg)
formulas = search_formulas(root, concept) if concept else _load_all(root)
if json_out:
typer.echo(_json.dumps([f.model_dump(mode="json") for f in formulas],
ensure_ascii=False, indent=2))
return
if not formulas:
typer.secho("Nessuna formula salvata.", fg=typer.colors.YELLOW)
return
t = Table(title="Formule di concetto")
for col in ("Concetto", "Status", "Colonne"):
t.add_column(col)
for f in formulas:
t.add_row(f.concept, f.status, ", ".join(f.columns))
Console().print(t)
+1 -88
View File
@@ -1,17 +1,6 @@
from pathlib import Path
import typer
from tht.cli.config_cmd import CONFIG_OPT
from tht.cli.schema_cmd import _load_config_or_exit, physical_path
lsh_app = typer.Typer(help="Indice LSH su valori dei campi (derivato, rigenerabile)")
def _lsh_dir(cfg) -> Path:
from tht.jobs.dwh_pipeline import resolve_dwh_snapshot
return resolve_dwh_snapshot(cfg).lsh_dir
from tht.cli.schema_cmd import physical_path
def _extract_lsh_values(dwh, physical, annotations, limit):
@@ -63,79 +52,3 @@ def build_lsh_artifacts(
name=cfg.database.db_schema,
)
return minhashes, skipped, truncated, values
@lsh_app.command("build")
def build_cmd(config: Path = CONFIG_OPT) -> None:
"""Costruisce l'indice LSH dai valori del database e lo salva su pickle."""
cfg = _load_config_or_exit(config)
dwh_root = cfg.paths.artifacts.parent / ".tht-dwh"
initialized = dwh_root.exists() or dwh_root.is_symlink()
if initialized:
phys_file = physical_path(cfg)
if not phys_file.exists():
typer.secho(
f"ERRORE: {phys_file} non trovato. Esegui prima `tht schema introspect`.",
fg=typer.colors.RED, err=True,
)
raise typer.Exit(code=1)
typer.echo("Estrazione valori (i più frequenti) dalle colonne testuali eligible...")
from tht.cli.preprocess_cmd import run_dwh_from_config
from tht.lshindex import load_index
report = run_dwh_from_config(
config, steps=("lsh",) if initialized else ("introspect", "lsh")
)
if report.status != "succeeded":
typer.secho("ERRORE: DWH preprocessing failed", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
_, minhashes, _ = load_index(_lsh_dir(cfg), name=cfg.database.db_schema)
skipped, truncated = [], []
n_values = len(minhashes)
n_columns = len({(entry[1], entry[2]) for entry in minhashes.values()})
typer.echo(f" {n_values} valori da {n_columns} colonne")
for s in skipped:
typer.secho(f" saltata {s.table}.{s.column}: {s.reason}", fg=typer.colors.YELLOW)
for t in truncated:
typer.secho(
f" troncata {t.table}.{t.column}: indicizzati i {t.indexed} valori più frequenti "
f"(limite max_values_per_column raggiunto; altri valori distinti NON indicizzati)",
fg=typer.colors.YELLOW,
)
typer.secho(
f"OK: indice LSH ({len(minhashes)} entry) -> {_lsh_dir(cfg)}", fg=typer.colors.GREEN
)
@lsh_app.command("query")
def query_cmd(
keyword: str = typer.Argument(..., help="Termine da cercare, es. 'ablazione'."),
config: Path = CONFIG_OPT,
top: int = typer.Option(10, "--top", help="Numero massimo di risultati."),
) -> None:
"""Probe visuale: mostra i candidati LSH per un termine, con score Jaccard."""
from rich.console import Console
from rich.table import Table
from tht.lshindex import LshIndexError, load_index, query_index
cfg = _load_config_or_exit(config)
try:
lsh, minhashes, meta = load_index(_lsh_dir(cfg), name=cfg.database.db_schema)
except LshIndexError as e:
typer.secho(f"ERRORE: {e}", fg=typer.colors.RED)
raise typer.Exit(code=1)
hits = query_index(lsh, minhashes, keyword, meta, top_n=top)
if not hits:
typer.secho(f"Nessun candidato LSH per '{keyword}'.", fg=typer.colors.YELLOW)
return
table = Table(title=f"Candidati LSH per '{keyword}' ({len(hits)})")
table.add_column("Tabella")
table.add_column("Colonna")
table.add_column("Valore")
table.add_column("Score", justify="right")
for h in hits:
table.add_row(h.table, h.column, h.value, f"{h.score:.3f}")
Console().print(table)
+2 -101
View File
@@ -26,7 +26,7 @@ DECISION_OPT = typer.Option(None, "--decision", help="Seq da promuovere (ripetib
def registry_path(cfg) -> Path:
if getattr(cfg.paths, "memory", None) is not None:
return cfg.paths.memory / "registry.jsonl"
# Legacy location; migrate with `tht memory migrate` (P3).
# Legacy location remains readable while old workspaces are retired.
return cfg.paths.artifacts / "memory" / "registry.jsonl"
@@ -45,12 +45,6 @@ def _resync_memory(cfg):
)
def clear_memory_index(cfg):
from tht.adapters.factory import build_vector_store
return build_vector_store(cfg, require_write=True).delete_kinds("memory", ["memory"])
@memory_app.command("promote")
def promote_cmd(
session: str = typer.Option(..., "--session"),
@@ -130,7 +124,7 @@ def promote_cmd(
f"{len(promoted)} memorie promosse nel registro, ma l'indice vettoriale "
"NON e' stato sincronizzato (runtime vettoriale mancante o irraggiungibile): "
"NON saranno trovate da `tht memory search` finche' non reindicizzi sul "
"server (`tht vector init`, poi `tht memory index`)."
"server (`tht memory index` quando il runtime vettoriale è disponibile)."
)
if json_out:
@@ -194,35 +188,6 @@ def save_one_cmd(
typer.secho(f"OK: {msg}", fg=typer.colors.GREEN if count else typer.colors.YELLOW)
@memory_app.command("clear")
def clear_cmd(
yes: bool = typer.Option(False, "--yes", "-y", help="Salta la richiesta di conferma."),
config: Path = CONFIG_OPT,
) -> None:
"""Cancella TUTTA la review memory: registro canonico + indice semantico (kind=memory)."""
from tht.memory import load_registry
cfg = _load_config_or_exit(config)
require_server_profile(cfg, "memory clear")
registry = registry_path(cfg)
count = len(load_registry(registry))
if count == 0:
typer.secho("Nessuna memoria da cancellare.", fg=typer.colors.YELLOW)
return
if not yes and not typer.confirm(
f"Cancellare definitivamente {count} memorie (registro + indice)?"
):
typer.secho("Annullato.", fg=typer.colors.YELLOW)
raise typer.Exit(code=1)
clear_memory_index(cfg)
# Registro canonico.
registry.unlink()
typer.secho(f"OK: {count} memorie cancellate.", fg=typer.colors.GREEN)
@memory_app.command("index")
def index_cmd(config: Path = CONFIG_OPT) -> None:
"""Sincronizza il registro memory nell'indice semantico (full-resync)."""
@@ -553,67 +518,3 @@ def solved_search_cmd(
table.add_row(r["session_id"], r["question"][:60],
", ".join(r["tables"]), f"{r['score']:.3f}")
Console().print(table)
@memory_app.command("migrate")
def memory_migrate_cmd(
config: Path = CONFIG_OPT,
json_output: bool = typer.Option(False, "--json"),
) -> None:
"""Migrate the legacy artifacts/memory registry to the explicit workspace memory root (P3).
Copies and verifies exactly one legacy canonical JSONL under the workspace lock, then rebuilds
the Qdrant projection. Conflicting legacy registries fail closed; no in-place reinterpretation.
"""
from tht.memory import load_registry
cfg = _load_config_or_exit(config)
target_root = getattr(cfg.paths, "memory", None)
if target_root is None:
payload = {"status": "failed", "error": "explicit memory root is not configured"}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho("ERRORE: memory root esplicito non configurato", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
legacy = cfg.paths.artifacts / "memory" / "registry.jsonl"
target = registry_path(cfg)
if target.exists():
payload = {"status": "unchanged", "path": str(target)}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho(f"OK: memory registry già in {target}", fg=typer.colors.GREEN)
return
if not legacy.exists():
payload = {"status": "failed", "error": "legacy memory registry is missing"}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho("ERRORE: registry legacy mancante", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
try:
records = load_registry(legacy)
except Exception: # noqa: BLE001
payload = {"status": "failed", "error": "legacy memory registry is invalid"}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho("ERRORE: registry legacy non valido", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
target_root.mkdir(parents=True, exist_ok=True)
from tht.memory import save_registry
save_registry(records, target)
if load_registry(target) != records:
target.unlink(missing_ok=True)
payload = {"status": "failed", "error": "memory registry migration verification failed"}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho("ERRORE: verifica migrazione fallita", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
payload = {"status": "migrated", "path": str(target), "records": len(records)}
if json_output:
typer.echo(json.dumps(payload, sort_keys=True))
else:
typer.secho(f"OK: migrate {len(records)} record verso {target}", fg=typer.colors.GREEN)
+1 -1
View File
@@ -115,7 +115,7 @@ def search_cmd(
if not json_out: # in JSON mode lo stdout resta puro: niente warning umano
typer.secho(
"ATTENZIONE: indice LSH assente, ricerca solo vettoriale "
"(esegui `tht lsh build`).", fg=typer.colors.YELLOW,
"(esegui `tht preprocess dwh --steps lsh`).", fg=typer.colors.YELLOW,
)
if kind == "schema":
-39
View File
@@ -148,32 +148,6 @@ def validate_cmd(
typer.secho("OK: SQL valido (statico).", fg=typer.colors.GREEN)
@sql_app.command("explain")
def explain_cmd(
file: Path = typer.Argument(...),
session: str = typer.Option(None, "--session"),
config: Path = CONFIG_OPT,
) -> None:
"""EXPLAIN (FORMAT JSON) con sintesi e warning dal piano. Mai ANALYZE."""
from tht.execute import ExecutionError
from tht.execute.warnings import plan_warnings
cfg = _load_config_or_exit(config)
require_action(cfg, "explain")
sql = _read_sql(file)
validate_or_exit(cfg, sql, session)
try:
plan = do_explain(cfg, sql)
except ExecutionError as e:
typer.secho(f"ERRORE: {e}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
typer.echo(f"costo totale stimato: {plan.total_cost}")
typer.echo(f"righe stimate: {plan.plan_rows}")
typer.echo(f"nodi del piano: {', '.join(plan.node_types)}")
for w in plan_warnings(plan, cfg.execution):
typer.secho(f" warning: {w}", fg=typer.colors.YELLOW)
@sql_app.command("preview")
def preview_cmd(
file: Path = typer.Argument(None, help="File SQL da eseguire. Opzionale se --session è dato."),
@@ -278,19 +252,6 @@ def set_final_cmd(
typer.secho("OK: SQL finale salvato.", fg=typer.colors.GREEN)
@sql_app.command("save")
def save_cmd(
dest: Path = typer.Argument(..., help="Percorso di destinazione del file SQL."),
session: str = typer.Option(..., "--session"),
config: Path = CONFIG_OPT,
) -> None:
"""Salva una copia di sql_final.sql nel percorso indicato (su richiesta esplicita)."""
cfg = _load_config_or_exit(config)
dest.parent.mkdir(parents=True, exist_ok=True)
dest.write_text(_session_sql(cfg, session))
typer.secho(f"OK: SQL salvato in {dest}", fg=typer.colors.GREEN)
@sql_app.command("export")
def export_cmd(
dest: Path = typer.Argument(..., help="Percorso del CSV di destinazione."),
+1 -35
View File
@@ -4,7 +4,7 @@ from pathlib import Path
import typer
from tht.cli._guards import require_server_profile, require_vector_write_allowed
from tht.cli._guards import require_vector_write_allowed
from tht.cli.config_cmd import CONFIG_OPT
from tht.cli.schema_cmd import _load_config_or_exit, annotations_path, physical_path
from tht.ports.vector import VectorWriteRecord
@@ -97,40 +97,6 @@ def _print_stats(stats) -> None:
)
@vector_app.command("init")
def init_cmd(
config: Path = CONFIG_OPT,
skip_ollama_check: bool = typer.Option(
False, "--skip-ollama-check", help="Non verificare la raggiungibilita' di Ollama."
),
) -> None:
"""Verifica il runtime Qdrant e la raggiungibilita' dell'embedder configurato."""
from tht.adapters.factory import build_vector_store
from tht.vectorstore.embeddings import EmbeddingsError
cfg = _load_config_or_exit(config)
require_server_profile(cfg, "vector init")
require_vector_cfg(cfg)
health = build_vector_store(cfg, require_write=True).health()
if not health.ok:
typer.secho(
f"ERRORE runtime vettoriale: {health.detail or 'Qdrant non raggiungibile o incompatibile'}",
fg=typer.colors.RED,
err=True,
)
raise typer.Exit(code=1)
if not skip_ollama_check:
try:
make_embedder(cfg.embeddings).embed_query("ping")
except EmbeddingsError as e:
typer.secho(f"ERRORE: {e}", fg=typer.colors.RED, err=True)
raise typer.Exit(code=1)
typer.secho(
f"OK: runtime Qdrant pronto per la collezione {cfg.vectors.collection}",
fg=typer.colors.GREEN,
)
@vector_app.command("index-schema")
def index_schema_cmd(
config: Path = CONFIG_OPT,
+2 -2
View File
@@ -58,7 +58,7 @@ def load_index(directory: Path, name: str) -> tuple[Any, Any, dict]:
if not (lsh_path.exists() and mh_path.exists() and meta_path.exists()):
raise LshIndexError(
f"Indice LSH non trovato in {directory} (atteso {name}_lsh.pkl). "
f"Esegui prima `tht lsh build`."
f"Esegui prima `tht preprocess dwh --steps lsh`."
)
lsh = pickle.loads(lsh_path.read_bytes())
minhashes = pickle.loads(mh_path.read_bytes())
@@ -79,7 +79,7 @@ def query_index(lsh, minhashes, keyword: str, meta: dict, top_n: int = 10) -> li
if entry is None:
raise LshIndexError(
"Indice LSH disallineato (lsh.pkl e minhashes.pkl non coerenti): "
"rigenera con `tht lsh build`."
"rigenera con `tht preprocess dwh --steps lsh`."
)
scored.append((key, qmh.jaccard(entry[0])))
scored.sort(key=lambda kv: kv[1], reverse=True)