This is a utility method that I’ve refined and used for hundreds of API calls - a broad replacement for input() for about every AI prompting scenario: where you want to type naturally within a Python console-based application at a terminal with scrollback, without writing up or expecting a full TUI application with a windowed terminal takeover.
What this is not: some bash command line to send a prompt. It is a utility for a full chatbot application, that may have its own rich display of output and a robust set of internal commands.
What it does that is not available elsewhere:
-
multi-line input, letting you just type and paste text and file contents and write some more including blank lines, with no accidental premature sends;
-
send via CTRL+ENTER, for no confusion and accidental sends between platforms that train you on SHIFT+ENTER when a linefeed is needed (ChatGPT) and others that use that as a send command;
-
maintains a draft, so that you can issue a single-word slash commands seen as a “turn” to your application mid-prompt on a new line…when you think of it (such as /model, /load, /new, etc), and return to composing an AI prompt after your app services those commands;
-
provides a non-truncated scrollback of input that is copyable out of a terminal without linefeeds at line width;
-
lets you arrow-up and edit previous lines and create inserts in a typical terminal;
-
Provides its own configurable line of prompt, which can include help about how to send or /help guidance;
-
Has fallbacks to work about anywhere and still be multi-line; TTY, shell, dumb terminal, Python REPL, even a Jupyter notebook in VSCode works, and you can send with slash alone when there is no CTRL or Command key to successfully capture;
-
a simpler stateless caller that can almost drop in for
input(); -
coded with lazy imports and internals only - a standalone class to even just paste at the top level of some existing code;
-
have
prompt_toolkitandkeyboardavailable in the environment for the best experience -
a little demo app is included, to see how it works or for teaching an AI how to integrate.
A redundant AI composition about the use cases
MultilineInput(): stateful multiline input for command-aware console apps
Python console programs are starting to feel less like calculators and more like conversations. A user may be writing a long prompt, pasting a stack trace, composing a commit message, or describing a deployment problem — and halfway through, they may need to ask the application for help, switch context, browse a file, change a model, clear state, inspect history, or run another command without throwing the draft away.
MultilineInputexists for that moment.It is a small, reusable input primitive for applications that need both long-form text and live application control. The user can write naturally across multiple lines, then send with a portable marker:
/alone on a line. But when the latest line is a single-word slash command such as/help,/browse,/settings,/model, or/history, that command can interrupt the input. The unfinished draft stays in the session, the command is returned to the application, and after the application handles it, the user comes back to the same message they were already composing.That creates interaction patterns that are awkward or fragile with ordinary
input()and too specific to rebuild every time on top of a full prompt framework:
Draft a long AI prompt, type
/browse, inspect a file, then resume the same prompt.Paste an error log, type
/help, learn the available commands, then continue instead of starting over.Compose a multi-paragraph instruction, type
/model, switch the application backend, and return to the draft.Build a chat, REPL, coding assistant, local agent shell, or workflow console where application commands are available mid-thought rather than only before or after input.
The point is not to replace rich CLI frameworks with advanced windowed drawing. It is to replace a complete absence of input methods that service what you want to send to an AI in the Python eco-system, where a survey reveals a lot of prompt_toolkit wrappers without improvement.
It is also written to survive the messy reality of Python execution environments. In a real terminal with
prompt_toolkitavailable, it can use editable multiline buffers, restored drafts, and supported key bindings. In IDLE, non-TTY shells, redirected input, or less capable environments, it falls back to line-oriented collection while still preserving and replaying drafts around commands. Optional integrations are used only when they work; the portable path remains plain text:/sends, single-word slash commands interrupt, and//escapes a literal slash line.That makes it foundationally useful because it turns “stateful text entry with live commands” into a dependable building block. Applications no longer have to choose between a one-line
input(), a heavyweight UI, or a custom half-working editor. They can accept serious multiline human input, keep the user in flow, and still let the application respond to commands at the exact moment the user needs them.What it cannot do is just as important.
MultilineInputis not a shell, not a command dispatcher, not a full TUI framework, and not a replacement forprompt_toolkit. It returns command tokens; your application decides what those commands mean. It cannot make every environment expose Ctrl+Enter or Command+Enter, so modified-Enter sending is advertised only when the active frontend can support it. In plain input mode, already-entered lines cannot be edited in place; they can be retained, shown, and replayed. In other words, it does not pretend every TTY, REPL, OS, or shell is equally capable — it degrades deliberately so the core experience still works.
Scroll through the code for docstrings and multi_input_demo() that also stand alone as documentation - and saved as a file, can simply be run to try it out.
Pasted right here - no GitHub repo to promote or plug.
My library is saved as chatbot_input_utils.py - but the plurality in the name is not needed for this final solution.
Docstring for the class itself is here, stripped out of the code to get under the character limit of the next post with the Python.
"""
Stateful multiline console input with slash-command interruption.
Optional integrations are imported lazily when available:
prompt_toolkit >= 3.0.0 - editable multiline buffers in real terminals
keyboard - best-effort modified-Enter detection
Configuration attributes:
- prompt_message: prompt text printed before any guidance.
- help_enabled: include or omit additional prompt guidance such as
"/help for commands".
- sending_help: include or omit the "how to send" prompt guidance.
- command_interrupts_enabled: when true, single-word slash commands on the
latest line interrupt and preserve a draft. When false, application
slash commands are recognized only as the first input line. The built-in
/back editor command remains active.
- transcript_echo_enabled: in prompt_toolkit mode, erase the transient
edit buffer after submission and print the returned input once as normal
terminal output. This gives scrollback a complete transcript of long
multiline submissions without requiring the calling application to know
about prompt_toolkit.
Prompt text is composed from class attributes. Applications may subclass
and replace DEFAULT_PROMPT, PROMPT_SEND_HINT, PROMPT_MODIFIED_ENTER_HINT,
and PROMPT_ADDITIONAL_HELP to match their command vocabulary. Empty prompt
fragments are omitted cleanly. Prompt templates may use {send_marker},
{back_command}, and {modified_enter}.
Default behavior:
- Multi-line message: press Enter for new lines.
- Send message: type a line containing only "/" and press Enter.
- Send message: when the prompt advertises it, use CTRL-ENTER, or
COMMAND-ENTER on macOS. The prompt advertises this only when the active
input frontend can register that modified-Enter key path for the
current process.
- Command interruption: type a single-word slash command such as
"/browse" or "/help" on the latest line and press Enter. The command
is returned by itself, while earlier draft lines are preserved for the
next prompt. Slash-starting lines with spaces are normal message text.
Set command_interrupts_enabled to false to keep this behavior only for
the first input line.
- Built-in line backup: type "/back" on a new line to restore the draft
in the input editor instead of returning a command to the caller. This
is handled entirely inside the session; applications may document it but
do not need to dispatch it.
- Escape: start any line with "//" to make it a literal "/" line rather
than a command marker. For example, "//help" becomes "/help" in the
eventual message.
UX notes:
- Real terminals use prompt_toolkit when available, giving editable
multi-line buffers and restored drafts.
- IDLE and non-TTY shells fall back to line-oriented input. In that mode
already-entered draft lines cannot be edited in place, but they are
shown and retained while commands run or when "/back" replays the
draft.
- In IDLE and other non-TTY shells, Ctrl+Enter cannot be detected via
stdin alone; if the optional `keyboard` package is available, it is used
as an out-of-band key detector to treat modified Enter as "send".
- In prompt_toolkit mode, ordinary Enter inserts a new line. The portable
send path remains a line containing only "/". Ctrl+Enter is not assumed
to be a prompt_toolkit key; if modified-Enter is advertised, it is only
because the current process can register a supported prompt_toolkit
sequence or the optional `keyboard` bridge for the desired Ctrl+Enter.
- If a terminal-like host passes the cheap environment gate but
prompt_toolkit cannot initialize its frontend, the session silently
disables prompt_toolkit and retries with line-oriented input.
"""