Kei Typing Assistant
An AI-powered desktop typing assistant that provides real-time text completions as ghost text via a floating overlay window, accessible from any application. Learns your writing style over time for increasingly personalized suggestions.
Features
- Global hotkey opens a compact overlay window from any app; press it again to copy and close
- Streaming AI suggestions appear as faint ghost text while you type
- Accept a suggestion with
Tab, dismiss withEsc - Three AI providers: local LLM (llama.cpp, runs fully offline), MiniMax, and OpenRouter
- Hybrid mode: races the local model against a cloud provider and upgrades to the cloud result if it arrives first, within a configurable time window
- Model warm-up loads the local model in the background at startup so the first suggestion isn't slowed by a cold start
- Accepted suggestions are stored with semantic embeddings and retrieved as personalized few-shot context, with per-example search/shown counts tracked over time
- Manage stored examples (view, search, add, delete) from a dedicated GUI window
- Reusable, ordered context fragments — curated notes you can prepend to the AI system prompt — managed from their own GUI window
- Mid-word suppression skips AI requests while the cursor sits inside a word
- API keys stored securely in the system keychain
- SQLite storage with Alembic-managed schema migrations
Requirements
- Python 3.12+
- macOS 12+ (primary), Windows 10/11, Linux supported
- API key for MiniMax or OpenRouter (not required for the default local LLM provider)
- macOS: Accessibility permission required for global hotkey
Quick Start
1. Install
git clone https://github.com/l0kifs/kei-typing-assistant.git
cd kei-typing-assistant
uv sync
2. Configure (optional)
Create a .env file in the project root to override defaults:
KEI__AI_PROVIDER=local_llm # local_llm | minimax | openrouter
KEI__HOTKEY=ctrl+alt+space # global hotkey
KEI__FLAVOUR="Concise, formal" # personalization hint for AI
All settings with defaults are documented in src/kei_typing_assistant/config/settings.py.
3. Add your API key (cloud providers only)
If using MiniMax or OpenRouter, open Settings from the tray icon and enter your API key. Keys are stored in the system keychain — never in plaintext. The default local LLM provider needs no API key; the model is downloaded from Hugging Face on first run.
4. Grant Accessibility permission (macOS)
System Settings → Privacy & Security → Accessibility — add the app or your terminal to allow global hotkey capture.
5. Run
uv run kei
The app runs in the system tray. Press the hotkey (Ctrl+Alt+Space by default) to open the overlay.
Usage
| Action | Key |
|---|---|
| Open overlay | Ctrl+Alt+Space (configurable) |
| Accept suggestion | Tab |
| Copy text & close | Ctrl+Alt+Space again |
| Dismiss without copying | Esc |
AI Providers
| Provider | Setting value | Notes |
|---|---|---|
| Local LLM | local_llm (default) |
Runs fully offline via llama.cpp; GGUF model auto-downloaded from Hugging Face; GPU-accelerated via Metal/CUDA where available |
| MiniMax | minimax |
Cloud API, accessed via an Anthropic-compatible endpoint |
| OpenRouter | openrouter |
Cloud API, accessed via an OpenAI-compatible endpoint; free-tier models available |
| Hybrid | enable hybrid_mode |
Races the local model against a chosen cloud provider (MiniMax or OpenRouter) and swaps in the cloud result if it beats the local one within hybrid_upgrade_window_s |
Settings Window
| Tab | Contents |
|---|---|
| General | Writing-style ("flavour") hint injected into the AI system prompt |
| AI Model | Provider selection (Local LLM / MiniMax / OpenRouter / Hybrid) and per-provider configuration (model, API key, context window, GPU layers, sampling params) |
| Behaviour | Suggestion length, temperature, debounce delay, mid-word suppression, include-context-fragments toggle |
| Appearance | Theme, ghost-text opacity, font family/size, overlay position |
| Hotkeys | Global hotkey binding |
| Storage | Data directory location (models, database) |
Two standalone windows, opened from the tray/settings menu, manage personalization data directly:
- Context Fragments — add, edit, reorder, search, and delete reusable prompt snippets prepended to the AI system prompt when enabled
- Suggestion Examples — browse, search, add, and delete the accepted (input → suggestion) pairs used for semantic few-shot retrieval, including how often each has been searched/shown
Configuration Reference
| Setting | Default | Description |
|---|---|---|
KEI__HOTKEY |
ctrl+alt+space |
Global trigger hotkey |
KEI__AI_PROVIDER |
local_llm |
local_llm, minimax, or openrouter |
KEI__AI_MAX_TOKENS |
60 |
Max suggestion length |
KEI__AI_TEMPERATURE |
0.8 |
Sampling temperature |
KEI__AI_DEBOUNCE_MS |
300 |
Delay before AI request (ms) |
KEI__MID_WORD_SUPPRESSION |
false |
Skip requests while the cursor is mid-word |
KEI__INCLUDE_CONTEXT_FRAGMENTS |
false |
Prepend saved context fragments to the AI system prompt |
KEI__FLAVOUR |
`` | Writing style hint for AI |
KEI__THEME |
system |
system, dark, or light |
KEI__HYBRID_MODE |
false |
Race local LLM against a cloud provider |
KEI__HYBRID_UPGRADE_WINDOW_S |
1.5 |
Seconds to wait for the cloud provider before keeping the local result |
KEI__LOCAL_LLM_MODEL_ID |
Qwen/Qwen2.5-3B-Instruct-GGUF |
Hugging Face repo for the local model |
KEI__LOCAL_LLM_N_GPU_LAYERS |
-1 |
GPU layers to offload (-1 = all, 0 = CPU only) |
KEI__SEMANTIC_EXAMPLES_TOP_K |
5 |
Max similar accepted examples injected as few-shot context |
KEI__DATA_DIR |
~/.kei_typing_assistant |
Base directory for models, database, and settings |
Full list of settings, defaults, and validation ranges: src/kei_typing_assistant/config/settings.py.
Development
# Run tests
pytest
# Run tests with coverage
pytest --cov=src/kei_typing_assistant
# Lint & format
ruff check .
ruff format .
CLI Reference
kei-cli provides a command-line interface for testing suggestions, managing the semantic example store, local models, database migrations, and configuration.
uv run kei-cli [--verbose] COMMAND [ARGS]
Suggestion commands
| Command | Description |
|---|---|
suggest TEXT |
Stream an AI suggestion for the given text |
accept TEXT SUGGESTION |
Save an accepted (input, suggestion) pair to the semantic example store |
test [--text TEXT] |
Full E2E flow: suggest → save accepted example |
Typical workflow:
# 1. Get a suggestion
uv run kei-cli suggest "The weather today is"
# → warm and sunny with clear skies
# 2. Accept it — embeds and saves for future retrieval
uv run kei-cli accept "The weather today is" "warm and sunny with clear skies"
# ✓ Saved: 'The weather today is' → 'warm and sunny with clear skies'
# 3. Next suggest call retrieves similar stored examples as context
uv run kei-cli suggest "The weather this morning is"
suggest options: --flavour TEXT, --max-tokens INT
test options: --text TEXT, --flavour TEXT, --max-tokens INT
Local model
uv run kei-cli model list # List downloaded models
uv run kei-cli model status MODEL_ID # Check if a model is downloaded
uv run kei-cli model delete MODEL_ID # Delete a downloaded model from disk
Database
uv run kei-cli db upgrade # Migrate the database to the latest schema revision
uv run kei-cli db current # Show the currently applied schema revision
Configuration
uv run kei-cli config show # Show current configuration
Architecture
The project follows Domain-Driven Design. See docs/ddd-architecture-rules.md for conventions and docs/BRD.md for the business requirements behind it.
src/kei_typing_assistant/
├── main.py # Composition root: wiring, bootstrap, launch
├── config/ # Settings & logging
├── domains/ # Pure business logic (suggestion, context_fragments, local_model)
├── infrastructure/ # AI provider clients, SQLite database + Alembic migrations, keychain
└── entry_points/ # Qt GUI, hotkey listener, tray, CLI, background workers
Database schema changes are managed with Alembic; migrations run automatically at startup (kei-cli db upgrade for manual control).
Publishing
See docs/PUBLISHING.md. Releases are published to PyPI automatically via GitHub Actions on tag push. See CHANGELOG.md for release history.
Metadata
Release files for kei-typing-assistant 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| kei_typing_assistant-0.1.0.tar.gz | 53.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| kei_typing_assistant-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 136.8 kB
Release files / kei_typing_assistant-0.1.0.tar.gz
| Download URL | kei_typing_assistant-0.1.0.tar.gz |
|---|---|
| Size | 53.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8fa36a8da924ebef44846b0ae828b6a49155c3fe780798dee93a864b11e5408d
|
|
BLAKE2b-256 checksum How to use checksums |
c64c2fd361cda0e57d8dbd7ae6589820750667c6ecb9a13801b8a4cd79b042aa
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency logRelease files / kei_typing_assistant-0.1.0-py3-none-any.whl
| Download URL | kei_typing_assistant-0.1.0-py3-none-any.whl |
|---|---|
| Size | 82.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b57b6562d427c9f83ab386d918bd1b5db831488eb83827fcec944c283bd3b304
|
|
BLAKE2b-256 checksum How to use checksums |
bd2bf6e4165d9a3d42ffd634913f1280840ed3229f0b4db03eded5657cc80b0f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.
Transparency log