Interactive Spring Shell-based command-line interface for the Embabel Agent platform, providing terminal interaction, chat sessions, and agent management commands.
The TerminalServices class provides terminal interaction capabilities including chat session management, form handling, confirmation dialogs, and output channels for agent communication.
import com.embabel.agent.shell.TerminalServices@Component
class TerminalServices(
private val terminal: Terminal,
private val shellProperties: ShellProperties
) : GoalChoiceApproverThe class is automatically instantiated by Spring when included in your application's component scan. It implements the GoalChoiceApprover interface for human-in-the-loop goal approval.
Run an interactive chat session in the terminal.
fun chat(
chatSession: ChatSession,
welcome: String? = null,
colorPalette: ColorPalette = DefaultColorPalette()
): StringParameters:
chatSession: The chat session to runwelcome: Optional welcome message to display (default: null)colorPalette: Color scheme for terminal output (default: DefaultColorPalette())Returns: "Conversation finished" when the session ends
Java Interoperability: This method is annotated with @JvmOverloads, allowing Java callers to omit the optional welcome and colorPalette parameters. Java code can call this method as:
chat(chatSession) - uses default values for both optional parameterschat(chatSession, welcome) - uses default value for colorPalettechat(chatSession, welcome, colorPalette) - provides all parameters explicitlyBehavior:
Usage:
import com.embabel.agent.shell.TerminalServices
import com.embabel.chat.ChatSession
val terminalServices: TerminalServices = ... // injected
val chatSession: ChatSession = ... // created from chatbot
val result = terminalServices.chat(
chatSession = chatSession,
welcome = "Welcome to the agent chat!",
colorPalette = myColorPalette
)Create a terminal-based output channel for agent communication.
fun outputChannel(agentPlatform: AgentPlatform): OutputChannelParameters:
agentPlatform: The agent platform to use for process lookupReturns: OutputChannel implementation that sends events to the terminal
Behavior:
MessageOutputChannelEvent: Displays messages with sender and content, handles awaitablesContentOutputChannelEvent: Displays content eventsProgressOutputChannelEvent: Displays progress with "▶" prefixLoggingOutputChannelEvent: Displays log messages with "🪵" prefixUsage:
import com.embabel.agent.shell.TerminalServices
import com.embabel.agent.core.AgentPlatform
import com.embabel.agent.api.channel.OutputChannel
val terminalServices: TerminalServices = ... // injected
val agentPlatform: AgentPlatform = ... // injected
val outputChannel = terminalServices.outputChannel(agentPlatform)
// Use outputChannel for agent communicationHandle awaitable requests (confirmations, forms) from agent processes.
fun handleAwaitable(awaitable: Awaitable<*, *>): AwaitableResponse?Parameters:
awaitable: The awaitable to handle (ConfirmationRequest or FormBindingRequest)Returns: AwaitableResponse if operation completes, null if cancelled by user
Supported Awaitable Types:
ConfirmationRequest: Prompts user for yes/no confirmationFormBindingRequest: Prompts user to fill out form fieldsForm Field Handling:
TextField: Text input with validation
control.required is true)control.maxLength is specified)control.validationPattern is specified)control.validationMessage)Button: Submit button (partial implementation)Form Submission:
Usage:
import com.embabel.agent.shell.TerminalServices
import com.embabel.agent.api.common.autonomy.Awaitable
import com.embabel.agent.core.AgentProcess
import com.embabel.agent.api.common.autonomy.AwaitableResponse
val terminalServices: TerminalServices = ... // injected
val awaitable: Awaitable<*, *> = ... // from agent process
val response = terminalServices.handleAwaitable(awaitable)
if (response != null) {
// Process the response
awaitable.onResponse(response, agentProcess)
} else {
// User cancelled
}Display a confirmation dialog and get user response.
fun confirm(message: String): BooleanParameters:
message: The confirmation message to displayReturns: true if user types "y" (case-insensitive), false otherwise
Behavior:
Usage:
import com.embabel.agent.shell.TerminalServices
val terminalServices: TerminalServices = ... // injected
if (terminalServices.confirm("Do you want to continue?")) {
// User confirmed
} else {
// User declined
}Print text to the terminal.
fun print(what: String): UnitParameters:
what: The text to printBehavior:
printAbove to display text without interfering with input promptUsage:
import com.embabel.agent.shell.TerminalServices
val terminalServices: TerminalServices = ... // injected
terminalServices.print("Processing request...")Redirect all logging output to a file and return a function to restore original configuration.
fun redirectLoggingToFile(filename: String, dir: String): () -> UnitParameters:
filename: Name for the log file (without extension)dir: Directory where logs subdirectory will be createdReturns: Function that restores original logging configuration when called
Behavior:
logs/{filename}.log%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%nUsage:
import com.embabel.agent.shell.TerminalServices
val terminalServices: TerminalServices = ... // injected
val restoreLogging = terminalServices.redirectLoggingToFile(
filename = "chat-session",
dir = System.getProperty("user.dir")
)
try {
// Do work with logs redirected to file
runChatSession()
} finally {
// Restore original logging
restoreLogging()
}Implement human-in-the-loop goal choice approval.
override fun approve(goalChoiceApprovalRequest: GoalChoiceApprovalRequest): GoalChoiceApprovalResponseParameters:
goalChoiceApprovalRequest: Request containing goal to approveReturns: GoalChoiceApproved if user confirms, GoalChoiceNotApproved otherwise
Behavior:
Usage: This method is called automatically by the agent platform when human approval is required for goal selection. Typically used by passing the TerminalServices instance as a GoalChoiceApprover:
import com.embabel.agent.shell.TerminalServices
import com.embabel.agent.api.common.autonomy.Autonomy
val terminalServices: TerminalServices = ... // injected
val autonomy: Autonomy = ... // injected
val goalSeeker = autonomy.createGoalSeeker(
intent = "Find news about AI",
agentScope = agentPlatform,
goalChoiceApprover = terminalServices, // Uses TerminalServices as approver
goalSelectionOptions = GoalSelectionOptions()
)The TerminalOutputChannel inner class implements OutputChannel for terminal-based agent communication. It is created internally by the outputChannel() method and handles various event types:
private inner class TerminalOutputChannel(
private val agentPlatform: AgentPlatform,
private val colorPalette: ColorPalette = DefaultColorPalette()
) : OutputChannel {
override fun send(event: OutputChannelEvent)
}Event Types:
MessageOutputChannelEvent: Formats and displays messages with sender and contentContentOutputChannelEvent: Displays content eventsProgressOutputChannelEvent: Displays progress messages with "▶" prefixLoggingOutputChannelEvent: Displays log messages with "🪵" prefixThe output channel automatically wraps text according to shellProperties.lineLength.
tessl i tessl/maven-com-embabel-agent--embabel-agent-shell@0.3.0