This release is a pre-release and may not be stable for production use.
TalkPipe Writing Assistant
Making the AI write with you, not for you.
An AI-powered writing assistant that transforms how you create structured documents. This application combines intelligent content generation with intuitive document management, enabling writers to craft professional documents with contextually-aware AI assistance that understands your style, audience, and objectives.
Built on the TalkPipe framework, this tool helps you:
- Break writer's block: Generate initial drafts and ideas for any section
- Maintain consistency: AI understands your document's context, style, and tone across all sections
- Iterate quickly: Multiple generation modes (rewrite, improve, proofread, ideas) let you refine content efficiently
- Stay organized: Structure documents into sections with main points and supporting text
- Use the LLM of your choice: Works with LLM endpoints including OpenAI, Anthropic, and Ollama — cloud APIs, compatible gateways, or fully local, offline models
Features
- Multi-User Support: JWT-based authentication with per-user document isolation
- Structured Document Creation: Organize your writing into sections with main points and user text
- AI-Powered Generation: Generate contextually-aware paragraph content using advanced language models
- Multiple Generation Modes:
- Rewrite: Complete rewrite with new ideas and improved clarity
- Improve: Polish existing text while maintaining structure
- Proofread: Fix grammar and spelling errors only
- Ideas: Get specific suggestions for enhancement
- Real-time Editing: Dynamic web interface for seamless writing and editing
- Light and dark themes: the round toggle in the web header switches between them
- Terminal Interface:
writing-assistant-tuioffers the same features in any terminal — an SSH session, a tmux window, a machine with no browser - Document Management: Save, load, and manage multiple documents, with snapshots you can revert to
- User Preferences: Per-user AI settings, writing style, and environment variables
- Customizable Metadata: Configure writing style, tone, audience, and generation parameters
- Quick-access Templates: Save a set of writing settings under a name (an "Email" template, say) and start a new document from it in one step — from the Templates ▾ menu in the web UI or
F4in the terminal interface - Flexible AI Backend: Works with LLM endpoints including OpenAI (GPT-4, GPT-4o), Anthropic (Claude 3.5 Sonnet, Claude 3 Opus), and Ollama (llama3, mistral, etc.) — chosen per account; see LLM providers
- Database Storage: SQLite database with configurable location for easy backup and deployment
- Async Processing: Efficient queuing system for AI generation requests
Pre-built container (Podman or Docker)
CI publishes a public image to GitHub Container Registry (ghcr.io/sandialabs/talkpipe-writing-assistant; Linux amd64/arm64; no registry login needed). Run it with the database persisted under /app/data:
podman run --rm -p 8001:8001 \
-v wa_data:/app/data \
ghcr.io/sandialabs/talkpipe-writing-assistant:latest
Then open http://localhost:8001 (use http, not https). docker run works with the same flags. run pulls the image automatically — no separate pull step is needed.
Tags: latest — stable release; experimental — pre-releases. Images are published only on releases, each also tagged with its version and commit SHA.
For Windows notes, connectivity troubleshooting, building from a local clone, and production deployment, see the Container Deployment Guide.
Installation
Prerequisites
- Python 3.11.4 or higher
- Access to an LLM endpoint — any one of OpenAI, Anthropic, Ollama (local), or a compatible endpoint; Ollama is not required (see LLM providers)
Note: On most modern systems (Debian/Ubuntu, Fedora, macOS with Homebrew), installing into the system Python is blocked or
pipis not installed at all. Create a virtual environment first — thepipcommands below assume one is active:python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate
Install from pip (Recommended)
pip install talkpipe-writing-assistant
After installation, you can start the application immediately:
writing-assistant
Your browser opens at http://localhost:8001 (pass --no-browser to skip
that). See the Quick Start section below for next steps.
Install from source
git clone https://github.com/sandialabs/talkpipe-writing-assistant.git
cd talkpipe-writing-assistant
pip install -e .
Install from a clone, not from a downloaded ZIP or tarball: the version
comes from the repository's git metadata, so without a .git directory the
install stops in setuptools_scm with "unable to detect version".
Development Installation
git clone https://github.com/sandialabs/talkpipe-writing-assistant.git
cd talkpipe-writing-assistant
pip install -e '.[dev]'
Development environment (uv, with a reproducible lock)
The default branch, stable, is release-only: it points at the latest
release, so what you see on the repository's front page describes that
release. Development happens on main, which is where merge requests go and
where unreleased changes and their documentation accumulate — check it out
first (git checkout main after cloning).
The repo includes uv.lock so contributors share one resolved set of versions. Install uv, then:
git clone https://github.com/sandialabs/talkpipe-writing-assistant.git
cd talkpipe-writing-assistant
uv sync --extra dev
Run tests and tools via the project environment, for example uv run pytest, or activate the virtualenv (.venv on Unix: source .venv/bin/activate).
Code quality. CI fails on any finding from ruff check ., ruff format --check ., or mypy (rule set and type-checking config live in pyproject.toml), so run them before pushing — ruff check --fix . && ruff format . fixes most findings. To run the same checks on every commit, opt in once per clone with uv run pre-commit install; pre-commit run --all-files reproduces the CI gate locally.
CI does not use the lockfile. It installs with pip (pip install -e '.[dev]') and resolves dependencies fresh, on purpose: that is what someone running pip install talkpipe-writing-assistant gets, so the build breaks when they would break. A dependency problem that only the lockfile hides is one we want CI to see — this project has been bitten by exactly that, when an unpinned FastAPI release broke it.
Two consequences worth remembering:
uv.lockis a development convenience. It pins nothing for users and is not a security control — the version floors inpyproject.tomlare what actually protect an install. Fix a vulnerable dependency by raising its floor, not by refreshing the lock.- The lock must still stay honest. CI runs
uv lock --check, which installs nothing and fails only whenuv.lockandpyproject.tomlhave drifted apart. After changing dependencies inpyproject.toml, runuv lockand commituv.lock. To bump versions, useuv lock --upgradeoruv lock --upgrade-package <name>.
Using a container (Podman or Docker)
Build and run from the repository (as opposed to the pre-built GHCR image above):
# Optional: create a local configuration file first
cp .env.example .env
# Production deployment
podman-compose up writing-assistant
# Development with live reload
podman-compose --profile dev up writing-assistant-dev
docker-compose (or docker compose) works with the same arguments.
See the Container Deployment Guide for the full deployment guide (configuration, backups, user management, and production hardening).
Quick Start
TL;DR: After pip install talkpipe-writing-assistant, just run writing-assistant — it opens http://localhost:8001 in your browser!
After installing with pip, follow these steps to get started:
1. Start the Server
writing-assistant
The server will start on http://localhost:8001, open it in your browser,
and display:
🔐 Writing Assistant Server - Multi-User Edition
📝 Access your writing assistant at: http://localhost:8001/
🔑 Register a new account at: http://localhost:8001/register
🔐 Login at: http://localhost:8001/login
📚 API documentation: http://localhost:8001/docs
💻 Terminal interface (no browser needed): run `writing-assistant-tui` in another terminal
💾 Database: /home/user/.writing_assistant/writing_assistant.db
🌐 Opening in your web browser...
If another program already holds port 8001, the server uses the next free
port and says so — the terminal-interface line then carries that address
(writing-assistant-tui --server http://localhost:8002), as it does for any
--port or --host you pass. Running writing-assistant while it is
already running just opens the browser at the running instance.
2. Create Your Account
- Open your browser and navigate to
http://localhost:8001/register - Enter your email address and a password (at least 8 characters, typed twice)
- Click "Create Account", then log in on the login page
3. Configure AI Backend
Pick a model provider. Any one of the three below works — Ollama is not
required, and neither is a cloud account. Whichever you choose is set the
same way, in Settings → AI Settings (in the terminal interface: F3 →
AI Settings). LLM providers is the full reference,
including OpenAI-compatible servers and server-wide defaults.
Option A: OpenAI (Cloud)
- Get an API key from OpenAI Platform
- In AI Settings, set Source to
openaiand Model to your model of choice (e.g.gpt-4o), and paste the key into Connection → API Key. Alternatively, set it server-wide:export OPENAI_API_KEY="sk-your-api-key-here"in the shell you startwriting-assistantfrom.
Option B: Anthropic (Cloud)
- Get an API key from Anthropic Console
- In AI Settings, set Source to
anthropicand Model to your model of choice (e.g.claude-sonnet-4-5), and paste the key into Connection → API Key. Alternatively, set it server-wide:export ANTHROPIC_API_KEY="sk-ant-your-api-key-here"in the shell you startwriting-assistantfrom.
Option C: Ollama (Local, Free)
- Install Ollama from ollama.com
- Pull a model — for example
ollama pull llama3.1:8b - Start Ollama:
ollama serve - In AI Settings, set Source to
ollamaand Model to the name you pulled (llama3.1:8bhere). The name must match one Ollama has pulled:ollama listshows them on the Ollama machine, orcurl http://your-ollama-host:11434/api/tagsfrom anywhere; Test Connection reports a name Ollama does not have.
If Ollama runs on a different machine (or a non-default port), enter its
address in Connection → Server URL, or set TALKPIPE_OLLAMA_SERVER_URL
before starting the server:
export TALKPIPE_OLLAMA_SERVER_URL="http://your-ollama-host:11434"
writing-assistant
A server-wide variable (an API key or the Ollama address) is read when the server starts: if it is already running, stop it and start it again with the variable set. The Connection fields need no restart.
Whichever option you picked, press Test Connection — it makes a real round trip and reports exactly what is wrong (missing key, unreachable server, model not pulled) — then Save AI Settings. The settings are saved with your account on the server, so the terminal interface uses them too.
4. Start Writing!
- After logging in, the editor opens directly — add a title and start typing. Leave a blank line between sections (paragraphs).
- Place your cursor in a section, then click one of the generation buttons
below the editor — Ideas, Rewrite, Improve, or Proofread —
to create AI-assisted content for that section (
Ctrl+Gruns Ideas from the keyboard). - When you like a suggestion, click "← Use This Text" (or press
Ctrl+U) to replace the section with it. Both keys can be changed under Settings → AI Settings → Hotkeys. - Save your work via the File ▾ menu (File → Save); the first save asks for a name in your library on the server (shared with the terminal interface — use File → Export for a file). The File menu also offers Save As, Open, snapshots, and import/export. Open, New and Import save the current document first when it has a name; a document that was never saved gets a Save… / Discard / Cancel prompt instead.
- Settings → Writing Settings holds the style, audience, tone, context, directive, and word limit that shape every suggestion. Suggestions use whatever is in the form, so you can try a setting straight away; Save to Document is what stores it with this document, and Save as Default makes the form the starting point for new ones.
That's it! You're ready to use the AI writing assistant.
Quick-access templates
For writing you do again and again — emails, status updates, cover letters — save the writing settings once as a template and reuse them:
- Open Settings → Writing Settings, fill in the fields (a short directive such as "Three short paragraphs; end with a clear ask" is the useful part), enter a name under Quick-access Templates, and press Save as Template. Saving under an existing name replaces that template; Apply fills the form from one, and Delete removes it.
- Choose the template from the Templates ▾ menu in the header. This starts a new, empty document with the template's settings. The document you were working on is saved first when it has a name; a document that was never saved gets a Save… / Discard / Cancel prompt instead. Authoring a template does not change the open document — only Save to Document does.
Templates are stored with your account, so the terminal interface sees
the same list (F4 there). A blank template field falls back to your
saved default, as a blank document field does; AI source and model are
not part of a template — they come from your defaults.
LLM providers
The writing assistant is not tied to Ollama, or to any one provider. It
generates text through TalkPipe, and AI Settings (Settings → AI Settings
in the web interface, F3 → AI Settings in the terminal interface) offers
these sources. Their client libraries are installed with the application, so
nothing extra is needed on the server beyond access to the model service.
| AI Source | What it talks to | Credentials | Endpoint (default → override) |
|---|---|---|---|
openai |
OpenAI's API, or any OpenAI-compatible server (LM Studio, vLLM, llama.cpp's server, a corporate gateway) | API Key field, or OPENAI_API_KEY |
OpenAI's API → Server URL field, or OPENAI_BASE_URL |
anthropic |
Anthropic's API, or an Anthropic-compatible gateway | API Key field, or ANTHROPIC_API_KEY |
Anthropic's API → Server URL field, or ANTHROPIC_BASE_URL |
ollama |
An Ollama server on this machine or another — free, and can run fully offline | none | http://localhost:11434 → Server URL field, or TALKPIPE_OLLAMA_SERVER_URL |
| Server default | Whichever source the administrator configured — see Server default | as for that source | as for that source |
Model is the model's name on that service — e.g. gpt-4o,
claude-sonnet-4-5, or llama3.1:8b (for Ollama, one already pulled
there). There is no built-in source or model: until one is chosen, in AI
Settings or as a server default, generation says none is configured.
TalkPipe also registers a scripted eliza source, which is why an error
message about an unknown source lists four names. It talks to no model
service and answers from a fixed script without reading the document, so it
is only good for checking that the plumbing works; AI Settings deliberately
does not offer it.
Choosing a source. Each account chooses its own source and model in AI Settings and keeps them with Save AI Settings — stored with the account on the server, so the web and terminal interfaces share them. Switching source is just a change in AI Settings; documents are not tied to a provider.
Supplying keys and addresses. Either place works for every source:
- Per account — the Connection fields in AI Settings. API Key is
the key for
openaioranthropic(ignored forollama); Server URL is the endpoint for whichever source is selected. They are saved with the account, apply only to that account's requests, and take precedence over the server's environment. They are hidden when the server runs with--disable-custom-env-vars(orALLOW_CUSTOM_ENV_VARS=false), and a request that sends them anyway is ignored. A key entered here is stored unencrypted with the account, in the server's database — so whoever can read the database file (or a copy of it, including a backup) can read the key. On a shared server, keep keys in the server's environment instead and start it with--disable-custom-env-vars. - Server-wide — the environment variables in the table, set where the
server starts:
exportbeforewriting-assistant,Environment=in a systemd unit,-eor.envfor a container (see Connecting the Container to an LLM). They are read when the server starts.
OpenAI-compatible servers: choose openai as the source, put the
server's base URL in Server URL (usually ending in /v1, e.g.
http://localhost:1234/v1), and set API Key to whatever it expects —
any non-empty value if it does not check one. The server must implement
OpenAI's Responses API (/v1/responses), which TalkPipe's openai source
uses rather than Chat Completions; Test Connection shows whether it does.
Press Test Connection after any change: it makes a real, token-capped request through the selected source and model and reports what is wrong — missing or rejected key, unreachable server, unknown model.
Server default (administrators)
To give every account a working model without each user visiting AI Settings, set TalkPipe's default source and model — and that source's key or address — in the server's environment before starting it. Accounts that leave AI Source on "Server default" and Model blank then use it; a source or model a user picks in AI Settings takes precedence.
# A cloud API ...
export TALKPIPE_DEFAULT_MODEL_SOURCE=openai # openai | anthropic | ollama
export TALKPIPE_DEFAULT_MODEL_NAME=gpt-4o
export OPENAI_API_KEY="sk-your-api-key-here"
# ... or an Ollama server:
# export TALKPIPE_DEFAULT_MODEL_SOURCE=ollama
# export TALKPIPE_DEFAULT_MODEL_NAME=llama3.1:8b
# export TALKPIPE_OLLAMA_SERVER_URL="http://your-ollama-host:11434" # if not local
writing-assistant --host 0.0.0.0 --disable-custom-env-vars
The same keys can go in ~/.talkpipe.toml (default_model_source = "ollama",
default_model_name = "llama3.1:8b") of the account running the server;
environment variables override the file. API keys belong in the
environment, not the file — the provider SDKs read only the environment.
Test Connection with the Server-default source reports which model the
server resolved.
TalkPipe's model and source configuration guide covers these settings in more depth.
Terminal interface (TUI)
Everything the web interface does is also available from a terminal, for places where a browser is not an option (an SSH session, a tmux window, a headless box). The TUI is a client of the same server, so it shares your account, documents, snapshots, and settings with the web UI.
# 1. Start the server (in another terminal, tmux pane, or as a service)
writing-assistant
# 2. Open the terminal interface
writing-assistant-tui
No server? Let the TUI start one. writing-assistant-tui --standalone
runs the server inside the TUI's own process, on localhost only, and stops
it when you quit — one command, nothing to keep running. It is the same
server writing-assistant starts, with the same database, JWT secret and
AI configuration (WRITING_ASSISTANT_DB_PATH, WRITING_ASSISTANT_SECRET,
and the provider variables such as OPENAI_API_KEY or
TALKPIPE_OLLAMA_SERVER_URL, … from the environment), so your documents
and login are the same whichever way you run it, and a browser on the same
machine can open http://localhost:8001 while the TUI is up. It listens on
port 8001 (--port <n> or WRITING_ASSISTANT_PORT to change it) and refuses
to start if that port is taken — if the thing on it is a writing-assistant
server, just drop the flag. The remembered login is tied to the server's
address, so a run on a non-default port asks for the password once (the same
account and the same documents either way). Its log goes to tui_server.log next to the
session file, since the terminal belongs to the TUI. The standalone server
only lives as long as the TUI does; for one that other machines or other
terminals share, start it separately as follows.
Keeping the server running. The server must outlive the terminal you
started it in. On a headless or SSH-only machine the simplest way is a tmux
session (tmux new -d -s writing-assistant writing-assistant; reattach with
tmux attach -t writing-assistant). To have it start at login and restart
on failure, install it as a systemd user service — put the following in
~/.config/systemd/user/writing-assistant.service (set ExecStart to the
path which writing-assistant prints with the virtual environment active —
the example assumes the venv from the install steps lives in
~/talkpipe-writing-assistant — and add any Environment= lines you need,
e.g. the provider variables OPENAI_API_KEY,
ANTHROPIC_API_KEY or TALKPIPE_OLLAMA_SERVER_URL), then
systemctl --user enable --now writing-assistant:
[Unit]
Description=TalkPipe Writing Assistant server
[Service]
ExecStart=%h/talkpipe-writing-assistant/.venv/bin/writing-assistant
Restart=on-failure
[Install]
WantedBy=default.target
Run loginctl enable-linger $USER once if the service should also run while
you are not logged in. The container images in
CONTAINER_DEPLOYMENT.md are the other option.
Log in (or choose Create an account) on the first screen — the server
URL defaults to http://localhost:8001; pass --server http://host:port or
set WRITING_ASSISTANT_TUI_SERVER if the server runs on another machine or
port (for example after writing-assistant --port 8080). The editor then
works like the web one: a title field, the document (leave a blank line
between sections), and a suggestion panel that follows the section under
the cursor. The cursor starts in the document body, so type your text
straight away; press Shift+Tab to reach the title field above it.
Before asking for suggestions, tell the TUI which model to use: press F3,
press F3 again to switch to the AI Settings tab (or Left/Right
with the tab bar focused), choose the AI source (Enter opens the
dropdown) and enter a model name (see LLM providers; an
API key for OpenAI or Anthropic, or the address of an Ollama server on
another machine, goes in the API key / Server URL fields on the
same tab, or in the server's environment), press Test Connection, then
Save AI Settings. The choice is stored with your
account, so the web interface uses it too. If the administrator configured
a server default, leave the source on "Server default" and the model blank
— Test Connection shows which model the server resolves.
| Key | Action |
|---|---|
F5 / F6 / F7 / F8 |
Ideas / Rewrite / Improve / Proofread the current section (Ctrl+G also runs Ideas). A request made while another is still generating is queued and runs next |
Ctrl+U |
Use the suggestion as the section's text (for Ideas, which are advice rather than prose, it asks first) |
Ctrl+S |
Save (asks for a library name the first time — the document is stored on the server, shared with the web UI, not written to a file here; use File → Export for a file) |
Ctrl+N / Ctrl+O |
New document (a title and optional outline; Ctrl+S then stores it in your library) / Open a document from your library (type to filter the list by name or title; Up/Down and Enter pick one) |
F2 |
File menu: New, Save, Save As, Open, Delete, Create snapshot, Revert to snapshot, Import, Export, Copy, Account (change email or password), Log out |
F3 |
Settings: writing style, tone, audience, context, directive, word limit (and, below them, Save as Template / Apply / Delete for quick-access templates); AI source/model, Server URL, API key, environment variables, Test Connection |
F4 |
Templates: start a new document from a saved template (see Quick-access templates). The open document is saved first when it has a library name; otherwise the Save / Discard / Cancel prompt appears |
F1 |
Help |
Ctrl+P |
Command palette: type part of a command's name (Save As, Create snapshot, Export, Account, Log out, …) and press Enter |
Tab / Shift+Tab |
Move between the title, the editor, the suggestion panel (arrow keys scroll it) and the buttons |
Esc |
Close a dialog or menu without changes |
Ctrl+Q |
Quit (asks first if there are unsaved changes — Save, Discard changes or Cancel; the same prompt guards Open, New, Import, Revert and Log out. Ctrl+C copies the editor selection and does not quit) |
Shift+Arrows, Ctrl+X, Ctrl+Z / Ctrl+Y |
Select text, cut it, undo / redo. To move a section: select it, Ctrl+X, put the cursor on the blank line where it belongs, Ctrl+V; to delete one, select it and press Delete |
Ctrl+C / Ctrl+V |
Copy the selection / paste — always work within the app (editor, title, and every dialog field). They also use the system clipboard when wl-paste, xclip, xsel or pbpaste is installed; to paste text from another program without one of those (e.g. over SSH) use the terminal's own paste — Ctrl+Shift+V, Shift+Insert, or Shift+middle-click |
The editor works down to 60x16 (smaller than that, it says so). Below 22 rows the mode buttons are hidden so the suggestion panel stays on screen (F5–F8 and Ctrl+U still work), and below 90 columns the buttons use short labels. If a suggestion comes back as several paragraphs, Use This Text inserts them as several sections and puts the cursor on the first.
The login token is remembered in ~/.writing_assistant/tui_session.json
(mode 600; set WRITING_ASSISTANT_TUI_HOME to move it), so the next launch
skips the login screen and reopens the last document at the section you
were working on. writing-assistant-tui --logout forgets the saved
session and starts at the login screen. Import/Export use the same JSON
document format as the web UI, so files move between the two freely:
Export writes that JSON to the path you give (the default is the document's
library name in the directory you started the TUI from), and the text
itself is the file's content field — for plain text, use Copy document
to clipboard in the File menu.
Usage
Starting the Server
# Default: http://localhost:8001, opened in your browser. If another program
# holds 8001 the next free port is used; if the assistant itself is already
# running there, the browser is opened at it and nothing else starts.
writing-assistant
# Do not open a browser (containers, servers, remote sessions)
writing-assistant --no-browser
# Custom port (an explicit port is never substituted: if another program holds
# it, this fails; if the assistant itself is already there, the running
# instance is reported instead)
writing-assistant --port 8080
# Custom host and port (0.0.0.0 accepts connections from other machines;
# the banner then shows this machine's name in the URLs)
writing-assistant --host 0.0.0.0 --port 8080
# Enable auto-reload for development
writing-assistant --reload
# Custom database location
writing-assistant --db-path /path/to/database.db
# Disable custom environment variables from UI (security)
writing-assistant --disable-custom-env-vars
# Initialize database without starting server
writing-assistant --init-db
# You can also use environment variables
WRITING_ASSISTANT_PORT=8080 writing-assistant
WRITING_ASSISTANT_RELOAD=true writing-assistant
WRITING_ASSISTANT_DB_PATH=/path/to/database.db writing-assistant
When the server starts, it will display:
- The URL to access the application
- Registration and login URLs
- API documentation URL
- The command that starts the terminal interface against this server
- Database location
Serving other machines. The server speaks plain HTTP: with
--host 0.0.0.0, passwords, login tokens and any API key entered in AI
Settings cross the network unencrypted. Put a TLS-terminating reverse proxy in
front of it (see
Use HTTPS in Production)
and set WRITING_ASSISTANT_SECRET — the server warns you when it is
reachable from other machines and that variable is still unset.
Authentication: The application uses JWT-based multi-user authentication with FastAPI Users. Each user has their own account with secure password storage. New users can register through the web interface at /register, and existing users log in at /login. To change your email address or password once logged in, open Settings → Account in the web interface, or File → Account (F2) in the terminal interface; both ask for the current password first, and a changed email is what you log in with next time.
Environment Variables
Configure the application with these environment variables. Only the provider you actually use needs its variables set — see LLM providers.
| Variable | Description | Default |
|---|---|---|
WRITING_ASSISTANT_HOST |
Server host address | localhost |
WRITING_ASSISTANT_PORT |
Server port number (setting it disables the free-port fallback, like --port) |
8001 |
WRITING_ASSISTANT_RELOAD |
Enable auto-reload (development) | false |
WRITING_ASSISTANT_DB_PATH |
Database file location | ~/.writing_assistant/writing_assistant.db |
WRITING_ASSISTANT_SECRET |
JWT secret key for authentication. Not generated per install — unset, it falls back to a fixed placeholder that every install shares, so anyone can forge a login token. Set it to a random value (python -c "import secrets; print(secrets.token_urlsafe(32))") before the server is reachable from another machine; changing it invalidates existing logins |
a fixed placeholder (CHANGE_THIS_IN_PRODUCTION_PLEASE) |
OPENAI_API_KEY / OPENAI_BASE_URL |
OpenAI key, and an alternate OpenAI-compatible endpoint (users can also set both per account in AI Settings → Connection) | unset |
ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL |
Anthropic key, and an alternate Anthropic-compatible endpoint (same per-account override) | unset |
TALKPIPE_OLLAMA_SERVER_URL |
Ollama server URL, local or remote (same per-account override, as Server URL) | http://localhost:11434 |
TALKPIPE_DEFAULT_MODEL_SOURCE |
Server-wide default AI source, used when a request leaves the source on "Server default" (openai, anthropic, ollama) |
unset (users must choose one) |
TALKPIPE_DEFAULT_MODEL_NAME |
Server-wide default model name, used when a request leaves Model blank | unset |
ALLOW_CUSTOM_ENV_VARS |
Allow users to configure environment variables through the UI (false to disable) |
true |
WRITING_ASSISTANT_TUI_SERVER |
Server URL for writing-assistant-tui (overridden by --server; needed when the server fell back to another port; ignored with --standalone, which uses WRITING_ASSISTANT_PORT) |
last used, else http://localhost:8001 |
WRITING_ASSISTANT_TUI_HOME |
Directory for the TUI's saved session (tui_session.json) |
~/.writing_assistant |
Security Options:
--disable-custom-env-vars(orALLOW_CUSTOM_ENV_VARS=false): Prevents users from configuring environment variables through the browser interface- Use this for shared deployments or when you want centralized credential management
- Environment variables must be set at the server level (via shell environment) — see Server default for choosing the model centrally as well
- The Connection (Server URL, API Key) and Environment Variables fields are hidden in both the web and terminal interfaces; the startup banner notes that the switch is on
Configure document metadata:
- AI Source:
openai,anthropic, orollama(see LLM providers) - Model: e.g.,
gpt-4o,claude-sonnet-4-5, orllama3.1:8b - Writing style: formal, casual, technical, etc.
- Target audience: general public, experts, students, etc.
- Tone: neutral, persuasive, informative, etc.
- Word limit: approximate words per paragraph
Document Storage
Documents are stored in an SQLite database with multi-user isolation:
Default Location: ~/.writing_assistant/writing_assistant.db
Custom Location: Use --db-path or WRITING_ASSISTANT_DB_PATH to specify an alternative location
Features:
- Per-user document isolation (users only see their own documents)
- Snapshots on demand (File → Create snapshot); the 10 most recent are kept per document
- User-specific preferences (AI settings, writing style, etc.)
- Cascade deletion (removing a user deletes all their documents)
Backup: Simply copy the database file to create a backup. The database can be moved to a different location using the --db-path option.
Administration
Two console commands are installed alongside the application for user management:
# Create the first admin (superuser) account
writing-assistant-create-superuser
# Manage users (list, info, delete, reset-password, toggle-active, make-superuser)
writing-assistant-admin list
writing-assistant-admin help
See the Admin Guide for the full user-administration reference and the Container Deployment Guide for running these commands inside a container.
Architecture
Package Structure
src/writing_assistant/
├── __init__.py # Package initialization and version
├── core/ # Core business logic
│ ├── __init__.py
│ ├── callbacks.py # AI text generation functionality
│ ├── definitions.py # Data models (Metadata)
│ └── segments.py # TalkPipe segment registration
├── app/ # Web application
│ ├── __init__.py
│ ├── main.py # FastAPI application and API endpoints
│ ├── server.py # Application entry point
│ ├── static/ # CSS and JavaScript assets
│ └── templates/ # Jinja2 HTML templates
└── tui/ # Terminal interface (Textual), a client of the REST API
├── app.py # Screens, dialogs, key bindings; `writing-assistant-tui`
├── app.tcss # Styling
├── client.py # Async HTTP client for the server's API
├── sections.py # Section parsing / suggestion tracking (mirrors script.js)
└── session.py # Saved server URL, token, last document
Core Components
- Metadata: Configuration for writing style, audience, tone, and AI settings
- Section: Individual document sections with async text generation and queuing
- Document: Complete document with sections, metadata, and snapshot management
- Callbacks: AI text generation using TalkPipe with context-aware prompting
Customizing Generation
The prompt templates and the four generation modes (rewrite, improve,
proofread, ideas) live in src/writing_assistant/core/callbacks.py, built on
TalkPipe's LLMPrompt segment. To change how text is generated — adjust the
prompts or swap in a different TalkPipe pipeline — edit that module, install
the checkout (pip install -e .; after that, edits only need the server
restarted), and start writing-assistant again. To add a whole new mode,
register its name in GENERATION_MODES in callbacks.py and give it a
branch in get_system_prompt() — the server rejects a mode it does not know
with a 400 that lists the known ones rather than quietly answering with
another mode's prompt — then add its button to the web UI: a .mode-btn
element in src/writing_assistant/app/templates/index.html whose
data-mode is the new name (script.js sends that attribute as the mode, so
it needs no change), and a tuple in GENERATION_MODES in
src/writing_assistant/tui/app.py for the TUI. Changing the model
provider needs no code change: see LLM providers.
Troubleshooting
Application Issues
"Port already in use"
- Only an explicit
--port(orWRITING_ASSISTANT_PORT) fails this way; without one the server moves to the next free port and announces it - Change the port:
writing-assistant --port 8080 - Or kill the process using the port
"Cannot save document" or "Database error"
- Check write permissions to the database directory (default:
~/.writing_assistant/) - Ensure the directory exists:
mkdir -p ~/.writing_assistant - Try a different database location:
writing-assistant --db-path /tmp/test.db - Initialize the database manually:
writing-assistant --init-db
"Authentication failed" or "Invalid credentials"
- Double-check your email and password
- Register a new account if you haven't already
- The database may have been reset - check the database location
- Logins from before a change to
WRITING_ASSISTANT_SECRETstop working; log in again
Forgotten password
- Whoever runs the server resets it:
writing-assistant-admin reset-password you@example.com(see the Admin Guide) - There is no self-service reset: the application sends no email, and the
/auth/forgot-passwordendpoint only writes the reset token to the server's own log, so that log deserves the same care as a password
"Cannot connect to database"
- Verify the database file exists and is not corrupted
- Check file permissions on the database file
- Try initializing a new database:
writing-assistant --db-path /tmp/new.db --init-db
The terminal interface cannot reach the server
- The server prints the exact command to use, including the address, when it
is not on the default port:
writing-assistant-tui --server http://localhost:8002 - Or set it once:
export WRITING_ASSISTANT_TUI_SERVER=http://localhost:8002 - No server at all?
writing-assistant-tui --standalonestarts its own
Generation Issues
"No AI source or model is configured"
- Nothing is chosen yet: open Settings → AI Settings (
F3in the terminal interface), pick an AI Source and type a Model name, then press Test Connection and Save AI Settings — see LLM providers - Or have the administrator set a server-wide default, and leave AI Source on "Server default" with Model blank: see Server default
Generation fails, or a suggestion never arrives
- Press Test Connection in AI Settings. It makes a real request and names the cause: a missing or rejected API key, an unreachable server (with the address it tried), or a model the service does not have
- Ollama on another machine, or a non-default port: put its address in
Connection → Server URL, or start the server with
TALKPIPE_OLLAMA_SERVER_URLset. A server-wide variable is read at startup, so the server has to be restarted; the Connection fields take effect immediately - "does not have the model": pull it on the Ollama host (
ollama pull <model>) and check the spelling — the Model name must matchollama listexactly - Cloud keys: a key typed in Connection → API Key applies to your account
only;
OPENAI_API_KEY/ANTHROPIC_API_KEYmust be exported before the server starts, in the shell that starts it
Releasing
The release process — tag conventions, the manual application test that must pass before tagging, and the publish steps — is in RELEASING.md.
License
This project is licensed under the Apache License 2.0. See the LICENSE file for details.
Acknowledgments
Built with TalkPipe, a flexible framework for AI pipeline construction developed at Sandia National Laboratories.
Release files for talkpipe-writing-assistant 1.1.1b1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| talkpipe_writing_assistant-1.1.1b1.tar.gz | 3.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| talkpipe_writing_assistant-1.1.1b1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:4.4 MB
Release files / talkpipe_writing_assistant-1.1.1b1.tar.gz
| Download URL | talkpipe_writing_assistant-1.1.1b1.tar.gz |
|---|---|
| Size | 3.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ed3a235d2399a0ce044a6db9e58d88c6e0d08efc4a85bba640980e8eb7a403c6
|
|
BLAKE2b-256 checksum How to use checksums |
7f677fa0e0d7617c1892e74884c7f2e1b6b5c81ab293e0b91be444c8bfd4b11f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / talkpipe_writing_assistant-1.1.1b1-py3-none-any.whl
| Download URL | talkpipe_writing_assistant-1.1.1b1-py3-none-any.whl |
|---|---|
| Size | 768.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c0f1dc578fa444b12e7918980ba4837094c5d1988f2afe814102163daeb7265
|
|
BLAKE2b-256 checksum How to use checksums |
bbcd8f45245103e1125e9ee36296233ec7a6a37e0237867206ab2960d5d1c3ab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|