Most chatbot interfaces follow the same decades-old pattern: you type a message, press send, and wait. A small open-source project from Scalattice inverts that entirely. With dont-hit-send, the model begins answering the moment you start typing, and there is no send button at all.

How Constant Completion Actually Works

The core mechanism is simpler than it sounds but tricky to get right. Every time you pause while typing, the client aborts the in-flight chat completion and fires a new one containing your entire draft so far. There is no persistent WebSocket or duplex connection streaming keystrokes. Each pause triggers a fresh HTTP request to the completions endpoint with the full accumulated text.

The interesting engineering challenge is the user experience layer on top of that basic loop. The client debounces at roughly 350 milliseconds, then fires a request. But it is smarter than a naive debounce: if the last token looks like a partial word, like you are mid-way through typing "and" or "NASA", the client holds off and waits for you to finish the word before sending. This prevents the model from receiving garbled fragments and producing confused responses.

When a new completion arrives, the client does not try to merge it with the previous response or edit an existing chat bubble. Instead, it stacks a new reply on top, killing the previous stream hard. The result is a timeline of model responses that correspond to snapshots of your evolving input, each one a complete attempt rather than a partially rewritten stream.

Proxy Architecture Keeps Keys Out of the Browser

The server component is deliberately minimal. It exists primarily as a proxy so that API keys never reach the browser. The client JavaScript lives in dont_hit_send/static/app.js and communicates only with the local server, which forwards requests to whichever backend you configure. No additional Python packages are required beyond what the setup script installs.

Out of the box, the tool defaults to Scalattice's hosted API at api.scalattice.cloud/v1, but any endpoint that implements the OpenAI-compatible /v1/chat/completions interface works. You can swap in your own provider by setting environment variables:

export OPENAI_BASE_URL=https://api.example.com/v1
export OPENAI_API_KEY=...
export DHS_MODEL=your-model

The settings UI also lets you configure these without touching the shell.

Running It Locally or in Docker

The setup script handles everything. Clone the repository, export your API key, and run the shell script. It creates a virtual environment, installs dependencies, and starts the server on port 8766. Pass-through flags work for changing the host or port:

git clone https://github.com/scalattice/dont-hit-send.git
cd dont-hit-send
export SCALATTICE_API_KEY=slt_...
./run.sh --host 0.0.0.0 --port 8766

Docker is equally straightforward. Build the image, run it with your API key as an environment variable, and expose port 8766. If you save a key through the Settings UI, the config persists at ~/.dont-hit-send/config.json, overridable with the DHS_DATA_DIR environment variable.

Metrics and What They Reveal

The tool exposes meters for streams started, streams aborted, time to first token, and rough token count. These are not just debug readouts. They expose the real cost of the constant-completion model. Every keystroke pause triggers an abort and a new request, so the ratio of started to aborted streams tells you how much work the system is discarding. Time to first token reveals the latency floor users actually experience. The rough token count gives a sense of API consumption, which matters when you are paying per token.

For developers evaluating whether this interaction pattern is viable for production use, these metrics are the starting point for understanding the tradeoffs. The approach trades API efficiency for immediacy, and whether that trade is worth it depends on the use case.

Why This Interaction Model Matters

The dominant chat UI paradigm was designed for an era when AI responses took seconds and streaming was novel. As models get faster, the pause between typing and seeing output becomes the main friction point. Removing the send button and starting completion immediately eliminates that friction, but it introduces new problems: wasted API calls, partial responses that need cleanup, and a fundamentally different mental model for the user.

Dont-hit-send is a working proof that the second set of problems is solvable. The debounce-and-hold logic, the stack-not-mutate response strategy, and the proxy architecture are all practical patterns that other projects can adopt. The project is MIT licensed, so there is nothing preventing incorporation into larger systems.

For developers building AI-powered tools, the repository is a useful reference implementation of an interaction pattern that will likely become more common as model latency drops further. For users tired of the send-wait-read loop, it is a working alternative available today.