terminal_agent.main
What it is
- Terminal UI runner for a
naas_abi_core.services.agent.Agent.Agentinstance. - Provides:
- Interactive prompt with a placeholder (TTY mode) and plain input (piped/non-TTY mode).
- Console rendering for AI messages, tool usage, and tool responses.
- Conversation logging to timestamped files under
storage/datastore/interfaces/terminal_agent/.
Public API
-
init_conversation_file() -> Path- Creates a new timestamped conversation log file and writes a header.
- Stores the path in the module-global
conversation_file.
-
save_to_conversation(line: str) -> None- Appends a single line (plus newline) to the active conversation file (if initialized).
-
get_input_with_placeholder(prompt: str = "> ", placeholder: str = "Send a message (/? for help)") -> str- Reads user input.
- If
stdinis not a TTY, falls back to standardinput(). - If
stdinis a TTY, uses raw terminal mode to display a grey placeholder that disappears on first keystroke.
-
on_tool_response(message: AnyMessage) -> None- Formats and prints tool responses via
print_tool_response(...). - If the response text contains words ending in
.png,.jpg,.jpeg,.gif, attempts to render them viaprint_image(path).
- Formats and prints tool responses via
-
on_ai_message(message: Any, agent_name: str) -> None- Renders AI output as Markdown via
rich. - Extracts and displays
<think>...</think>blocks as “Thoughts:” in grey, and removes them from the main displayed content. - Logs output (including thoughts and a fixed-width separator) to the conversation file.
- Renders AI output as Markdown via
-
run_agent(agent: Agent) -> None- Main interactive loop:
- Initializes conversation logging.
- Registers agent hooks:
agent.on_tool_usage(...)→print_tool_usageagent.on_tool_response(...)→on_tool_responseagent.on_ai_message(...)→on_ai_message
- Prints/logs a greeting:
"{agent.name}: Hello, World!". - Repeatedly:
- Prints/logs active agent + model info from
agent.state.current_active_agentandchat_model.model_name/chat_model.model. - Reads user input.
- Handles commands:
- Exit:
exit,quit,/exit,/quit,/bye - Reset:
reset,/reset(callsagent.reset()) - Help:
/?(prints available commands and agent handles)
- Exit:
- Otherwise calls
agent.invoke(user_input).
- Prints/logs active agent + model info from
- On exceptions from
agent.invoke, prints/logs error and full traceback and continues.
- Main interactive loop:
Configuration/Dependencies
-
Terminal/OS:
- Uses
termiosandttyraw mode for interactive placeholder input (POSIX-like terminals). - Checks
sys.stdin.isatty()to decide between interactive vs piped input mode.
- Uses
-
External libraries/services:
naas_abi_core.loggerfor logging conversation file location.naas_abi_core.apps.terminal_agent.terminal_style:console(Rich console)print_image,print_tool_response,print_tool_usage
langchain_core.messages(AnyMessage,ToolMessage) for tool response typing.pydashis imported insiderun_agentto find the active agent inagent.agents + [agent].rich.markdown.Markdownis imported insideon_ai_message.
-
Conversation logs:
- Directory:
storage/datastore/interfaces/terminal_agent/ - Filename:
{YYYYMMDDTHHMMSS}.txt - Uses a fixed separator width in logs:
TERMINAL_WIDTH = 77(even though display separator usesconsole.width).
- Directory:
Usage
Minimal example (requires a compatible Agent instance from your application):
from naas_abi_core.apps.terminal_agent.main import run_agent
from your_project import build_agent # must return an naas_abi_core Agent
agent = build_agent()
run_agent(agent)Non-interactive (piped) input is supported; the prompt falls back to input() when stdin is not a TTY.
Caveats
- Interactive placeholder input uses
termios/tty; it may not work on non-POSIX environments. - Conversation logging must be initialized via
init_conversation_file()(called byrun_agent) orsave_to_conversation()will no-op. - Image rendering is triggered by any whitespace-delimited “word” ending in a supported image extension; paths are not otherwise validated here.
- Log separators are fixed-width (
77), while on-screen separators use dynamicconsole.width, so logs may not match the exact terminal width.