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
76 changes: 76 additions & 0 deletions anton/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,61 @@ def _extract_html_title(path, re_module) -> str:
return ""


async def _handle_remote(
console: Console,
settings,
) -> None:
"""Handle /remote command — provision or check status of remote scratchpad."""
console.print()

from pathlib import Path as _P
from anton.workspace import Workspace as _W
_global_ws = _W(_P.home())

# Ensure minds API key — same flow as /publish
if not settings.minds_api_key:
import webbrowser
from anton.utils.prompt import prompt_or_cancel

console.print(" [anton.muted]To use remote scratchpad you need a free Minds account.[/]")
console.print()
has_key = await prompt_or_cancel(
" Do you have an mdb.ai API key?",
choices=["y", "n"],
choices_display="y/n",
default="y",
)
if has_key is None:
console.print()
return
if has_key.lower() == "n":
webbrowser.open(
"https://mdb.ai/auth/realms/mindsdb/protocol/openid-connect/registrations"
"?client_id=public-client&response_type=code&scope=openid"
"&redirect_uri=https%3A%2F%2Fmdb.ai"
)
console.print()

api_key_input = await prompt_or_cancel(" API key", password=True)
if api_key_input is None or not api_key_input.strip():
console.print()
return
api_key_input = api_key_input.strip()
settings.minds_api_key = api_key_input

_global_ws.set_secret("ANTON_MINDS_API_KEY", api_key_input)
console.print()

# If an API key is provided, set the backend to remote backend
if settings.minds_api_key:
_global_ws.set_secret("ANTON_BACKEND", "remote")

# Save and confirm
console.print(f" [anton.success]Remote scratchpad ready![/]")
console.print(f" [link={settings.minds_url}]{settings.minds_url}[/link]")
console.print()


async def _handle_publish(
console: Console,
settings,
Expand Down Expand Up @@ -374,6 +429,9 @@ async def _handle_publish(
settings.minds_api_key = api_key
# Key is not persisted yet — wait until publish succeeds to avoid
# locking the user out with a bad key on every subsequent /publish call.
from pathlib import Path as _P
from anton.workspace import Workspace as _W
_W(_P.home()).set_secret("ANTON_MINDS_API_KEY", api_key)
console.print()

# 2. Find the HTML file to publish
Expand Down Expand Up @@ -1051,8 +1109,11 @@ async def _chat_loop(
runtime_context = build_runtime_context(settings)

output_path = f"{settings.output_dir.rstrip('/')}/"
from anton.chat_session import get_runtime_factory

session = ChatSession(ChatSessionConfig(
llm_client=state["llm_client"],
runtime_factory=get_runtime_factory(settings),
self_awareness=self_awareness,
cortex=cortex,
episodic=episodic,
Expand Down Expand Up @@ -1366,6 +1427,21 @@ def _bottom_toolbar():
arg = parts[1].strip() if len(parts) > 1 else ""
handle_theme(console, arg)
continue
elif cmd == "/remote":
await _handle_remote(console, settings)
# Rebuild session so scratchpad uses remote/local factory
session = rebuild_session(
settings=settings,
state=state,
self_awareness=self_awareness,
cortex=cortex,
workspace=workspace,
console=console,
episodic=episodic,
history_store=history_store,
session_id=current_session_id,
)
continue
elif cmd == "/publish":
arg = parts[1].strip() if len(parts) > 1 else ""
await _handle_publish(console, settings, workspace, arg)
Expand Down
21 changes: 21 additions & 0 deletions anton/chat_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ def build_runtime_context(settings: AntonSettings) -> str:
return ctx


def get_runtime_factory(settings: AntonSettings):
"""Return the appropriate scratchpad runtime factory based on settings.

If backend is set to "remote" (and minds_api_key available),
returns a remote factory. Otherwise returns the local factory.
"""
if settings.backend == "remote":
from functools import partial
from anton.core.backends.remote import remote_scratchpad_runtime_factory

return partial(
remote_scratchpad_runtime_factory,
endpoint_url=settings.minds_url,
api_key=settings.minds_api_key,
)

from anton.core.backends.local import local_scratchpad_runtime_factory
return local_scratchpad_runtime_factory


def rebuild_session(
*,
settings: AntonSettings,
Expand Down Expand Up @@ -84,6 +104,7 @@ def rebuild_session(
output_path = f"{settings.output_dir.rstrip('/')}/"
return ChatSession(ChatSessionConfig(
llm_client=state["llm_client"],
runtime_factory=get_runtime_factory(settings),
self_awareness=self_awareness,
cortex=cortex,
episodic=episodic,
Expand Down
7 changes: 6 additions & 1 deletion anton/chat_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,9 +553,14 @@ def _print_done_line(
line.append(" \u2714 ", style="green")
work_str = self._fmt_elapsed(work_elapsed)

from anton.config.settings import AntonSettings

settings = AntonSettings()
work_label = "Remote work" if settings.backend == "remote" else "Worked"

if reasoning_elapsed > 0:
reason_str = self._fmt_elapsed(reasoning_elapsed)
line.append(f"(Worked: {work_str}, Reasoned: {reason_str})", style="anton.muted")
line.append(f"({work_label}: {work_str}, Reasoned: {reason_str})", style="anton.muted")
else:
line.append(work_str, style="anton.muted")

Expand Down
1 change: 1 addition & 0 deletions anton/commands/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Command:
"Chat Tools",
Command("/paste", "Attach an image from your clipboard"),
Command("/resume", "Continue a previous session"),
Command("/remote", "Set up or manage remote scratchpad"),
Command("/publish", "Publish an HTML report to the web"),
Command("/unpublish", "Remove a published report"),
Command("/explain", "Show explainability details for the latest answer"),
Expand Down
12 changes: 10 additions & 2 deletions anton/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

from pathlib import Path

from pydantic import PrivateAttr, field_validator
from pydantic_settings import BaseSettings
from pydantic import PrivateAttr, ValidationInfo, field_validator, model_validator

from anton.core.settings import CoreSettings

Expand Down Expand Up @@ -74,6 +73,15 @@ class AntonSettings(CoreSettings):
# Publish service
publish_url: str = "https://4nton.ai"

backend: str = "local" # local | remote

@field_validator("backend", mode="after")
@classmethod
def _validate_backend(cls, v: str, info: ValidationInfo) -> str:
if v == "remote" and (not info.data.get("minds_url") or not info.data.get("minds_api_key")):
raise ValueError("Minds URL and API key are required for remote backend")
return v

@field_validator("minds_ssl_verify", mode="before")
@classmethod
def _parse_minds_ssl_verify(cls, v):
Expand Down
Loading
Loading