Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion anton/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
handle_setup,
handle_setup_models,
)
from anton.commands.ui import handle_theme, print_slash_help
from anton.commands.ui import handle_explain, handle_theme, print_slash_help
from anton.utils.clipboard import (
ensure_clipboard,
format_clipboard_image_message,
Expand Down Expand Up @@ -1274,6 +1274,9 @@ def _bottom_toolbar():
elif cmd == "/unpublish":
await _handle_unpublish(console, settings, workspace)
continue
elif cmd == "/explain":
handle_explain(console, settings.workspace_path)
continue
elif cmd == "/help":
print_slash_help(console)
continue
Expand Down
65 changes: 62 additions & 3 deletions anton/commands/ui.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Slash-command handlers for /theme and /help."""
"""Slash-command handlers for /theme, /explain, and /help."""

from __future__ import annotations

from rich.console import Console

from anton.explainability import ExplainabilityStore


def handle_theme(console: Console, arg: str) -> None:
"""Switch the color theme (light/dark)."""
Expand All @@ -17,7 +19,9 @@ def handle_theme(console: Console, arg: str) -> None:
elif arg in ("light", "dark"):
new_mode = arg
else:
console.print(f"[anton.warning]Unknown theme '{arg}'. Use: /theme light | /theme dark[/]")
console.print(
f"[anton.warning]Unknown theme '{arg}'. Use: /theme light | /theme dark[/]"
)
console.print()
return

Expand All @@ -37,7 +41,9 @@ def print_slash_help(console: Console) -> None:
console.print(" [bold]/llm[/] — Change LLM provider or API key")

console.print("\n[bold]Data Connections[/]")
console.print(" [bold]/connect[/] — Connect a database or API to your Local Vault")
console.print(
" [bold]/connect[/] — Connect a database or API to your Local Vault"
)
console.print(" [bold]/list[/] — List all saved connections")
console.print(" [bold]/edit[/] — Edit credentials for an existing connection")
console.print(" [bold]/remove[/] — Remove a saved connection")
Expand All @@ -53,9 +59,62 @@ def print_slash_help(console: Console) -> None:
console.print(" [bold]/resume[/] — Continue a previous session")
console.print(" [bold]/publish[/] — Publish an HTML report to the web")
console.print(" [bold]/unpublish[/] — Remove a published report")
console.print(
" [bold]/explain[/] — Show explainability details for the latest answer"
)

console.print("\n[bold]General[/]")
console.print(" [bold]/help[/] — Show this help menu")
console.print(" [bold]exit[/] — Exit the chat")

console.print()


def handle_explain(console: Console, workspace_path) -> None:
"""Print explainability details for the latest answer in the workspace."""
store = ExplainabilityStore(workspace_path)
record = store.load_latest()
if record is None:
console.print(
"[anton.warning]No explainability record found yet for this workspace.[/]"
)
console.print()
return

console.print()
console.print("[anton.cyan]Explain This Answer[/]")
console.print(f"[anton.muted]Turn {record.turn} • {record.created_at}[/]")
console.print()

console.print("[bold]Summary[/]")
console.print(record.summary or "No summary available.")
console.print()

console.print("[bold]Data Sources Used[/]")
if record.data_sources:
for source in record.data_sources:
engine = source.get("engine")
if engine:
console.print(f" - {source.get('name', 'Unknown')} ({engine})")
else:
console.print(f" - {source.get('name', 'Unknown')}")
else:
console.print(" - None captured")
console.print()

console.print("[bold]Generated SQL[/]")
if record.sql_queries:
for i, query in enumerate(record.sql_queries, 1):
header = f" Query {i}: {query.get('datasource', 'Unknown datasource')}"
if query.get("engine"):
header += f" ({query['engine']})"
console.print(header)
console.print("```sql")
console.print(query.get("sql", ""))
console.print("```")
if query.get("status") == "error" and query.get("error_message"):
console.print(f"[anton.warning]{query['error_message']}[/]")
console.print()
else:
console.print(" - No SQL generated")
console.print()
28 changes: 27 additions & 1 deletion anton/core/backends/scratchpad_boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

# Persistent namespace across cells
namespace = {"__builtins__": __builtins__}
namespace["_anton_explainability_queries"] = []

# --- Inject get_llm() for LLM access from scratchpad code ---
_scratchpad_model = os.environ.get("ANTON_SCRATCHPAD_MODEL", "")
Expand Down Expand Up @@ -243,6 +244,7 @@ def agentic_loop(*, system, user_message, tools, handle_tool, max_turns=10, max_
_minds_datasource = os.environ.get("ANTON_MINDS_DATASOURCE", "")
_minds_api_key = os.environ.get("ANTON_MINDS_API_KEY", "")
_minds_url = os.environ.get("ANTON_MINDS_URL", "")
_minds_engine = os.environ.get("ANTON_MINDS_DATASOURCE_ENGINE", "")
if _minds_datasource and _minds_api_key and _minds_url:
try:
import ssl as _minds_ssl
Expand Down Expand Up @@ -273,20 +275,42 @@ def query_minds_data(query, datasource=None):

try:
with _minds_urllib.urlopen(req, context=ctx, timeout=60) as resp:
return json.loads(resp.read().decode())
parsed = json.loads(resp.read().decode())
namespace.setdefault("_anton_explainability_queries", []).append({
"datasource": ds,
"sql": query,
"engine": _minds_engine or None,
"status": "ok",
"error_message": None,
})
return parsed
except _minds_urllib.HTTPError as e:
body = ""
try:
body = e.read().decode()
except Exception:
pass
namespace.setdefault("_anton_explainability_queries", []).append({
"datasource": ds,
"sql": query,
"engine": _minds_engine or None,
"status": "error",
"error_message": f"HTTP {e.code}: {body or e.reason}",
})
return {
"type": "error",
"data": None,
"column_names": None,
"error_message": f"HTTP {e.code}: {body or e.reason}",
}
except Exception as e:
namespace.setdefault("_anton_explainability_queries", []).append({
"datasource": ds,
"sql": query,
"engine": _minds_engine or None,
"status": "error",
"error_message": str(e),
})
return {
"type": "error",
"data": None,
Expand Down Expand Up @@ -561,6 +585,7 @@ def emit(self, record):
err_buf = io.StringIO()
log_buf = io.StringIO()
error = None
namespace["_anton_explainability_queries"] = []
_cell_log_handler.buf = log_buf

sys.stdout = out_buf
Expand Down Expand Up @@ -625,6 +650,7 @@ def emit(self, record):
"stderr": err_buf.getvalue(),
"logs": log_buf.getvalue(),
"error": error,
"explainability_queries": list(namespace.get("_anton_explainability_queries", [])),
}
if _auto_installed:
result["auto_installed"] = _auto_installed
Expand Down
Loading
Loading