A rebuild of 'Jot' designed to be more scaleable, and built with some of the learnings from the first Jot.
Implements many of the same functionalities of Jot. Primary enhancements include:
- Tools becoming more modular
- Should be able to add tools without needing to change any of the code, just drop in a new module
- Less sequential
- Jot was very sequential in it's processing, would wait for one thing to happen before the next would start. Needs to be able to support multiple activities running in parallel, and make use of streaming where possible
- More inputs
- Jot only allowed voice input, but I would like to be able to interface with Jot from anywhere
Jot-two is built around a few key principles:
- Event-driven architecture
- Modular tool system
- Multi-channel interaction
- Self-hostable
- Asynchronous processing
The system follows an event-driven architecture.
- Input adapters receive messages from external systems.
- Each message is converted into the standard message format.
- The message is pushed to the Input Queue.
- The Processing Layer consumes the request.
- The processing layer may invoke one or more tool modules.
- A response message is created and pushed to the Output Queue.
- The Output Router sends the response to the appropriate destination.
Jot-two will be primarily built in Python, using Redis Streams for queing messages. Any web interface (if needed) will be built in React.JS.
All input interface adapters will produce a standard JSON output as follows:
{
"request_id":"uuid-123",
"source":"whatsapp",
"user_id":"user_42",
"reply_channel":"whatsapp", // optional: if not provided, defaults to source
"timestamp":1710000000,
"payload":{
"text":"Turn the lights on"
}
}This is sent to the input queue to be picked up by the agent.
Tools provide the assistant with the ability to interact with external systems.
Each tool is implemented as a module and placed in the src/agent/tools/ directory.
Each module can expose one or many tools (for example, weather.py can include get_current_weather and get_future_weather).
Tools must implement a standard interface:
- name
- description
- parameters
- execute() The agent dynamically discovers and loads all tools at startup, then exposes them to the LLM through function-calling so tools can be selected and executed automatically. Example:
src/agent/tools/
home_assistant.py
rss_reader.py
calendar.py
To ensure the bot retains context of what is said and conversation history, each payload sent to the LLM includes the following:
- System Prompt - The first message defining the rules of the assistant
- A conversation summary - Every 20 (configurable) messages, a summary is updated and this is sent to the LLM as well
- Retrieved memory - From a vector DB, to get any additional relevant context. A classic RAG approach
- Recent messages - the past 5 (configurable) messages sent are included
- User message - the actual user request
Conversation messages and summaries are persisted in PostgreSQL. Recent message retrieval and latest-summary lookup are read from the database instead of in-memory state.
src/
adapters/ # input adapters
agent/ # core assistant logic
core/ # shared message models
tools/ # modular tool implementations
router/ # output router
main.py # async event pipeline entrypoint
Recommended run with docker-compose.yml
docker compose up --buildInput adapters are auto-discovered from src/adapters/ at startup.
To add a new adapter, create a new module in src/adapters/ and define a class that:
- Inherits from
BaseInputAdapter(src/adapters/base.py) - Accepts
input_queueandstop_eventin__init__ - Implements
async def run(self) -> None - Pushes normalized
Messageobjects toinput_queue
No changes are required outside src/adapters/ for adapter registration.
To be set in the .env file
TELEGRAM_BOT_TOKEN: Telegram bot token from BotFatherOPENAI_API_KEY: API key for your OpenAI-compatible endpoint
The below need to be populated, but can be left as default and will be autopopulated.
DATABASE_URL: PostgreSQL DSN used byjot-coreto persist conversation messages and summariesPOSTGRES_DB: PostgreSQL database name for docker-compose setupPOSTGRES_USER: PostgreSQL username for docker-compose setupPOSTGRES_PASSWORD: PostgreSQL password for docker-compose setupOPENAI_BASE_URL(defaulthttps://api.openai.com): provider base URLOPENAI_CHAT_ENDPOINT(default/v1/chat/completions): chat completion pathOPENAI_MODEL(defaultgpt-5-nano): model identifierOPENAI_SYSTEM_PROMPT_FILE: path to the text file containing the system promptOPENAI_TIMEOUT_SECONDS(default60): HTTP timeout for model calls
To be set in the .env file
TELEGRAM_POLL_TIMEOUT(default20): long-poll timeout in seconds forgetUpdatesTELEGRAM_RETRY_DELAY(default2): delay before retrying after Telegram API errorsOPENAI_SUMMARY_EVERY_MESSAGES(default10): refresh summary after this many new conversation messagesOPENAI_RECENT_MESSAGES_LIMIT(default10): number of recent messages included in each requestOPENAI_MAX_TOOL_ROUNDS(default5): max tool-call rounds per user message before stoppingOPENWEATHERMAP_API_KEY: API key used by the weather toolOPENWEATHERMAP_BASE_URL(defaulthttps://api.openweathermap.org/data/2.5/weather): weather endpoint overrideOPENWEATHERMAP_FORECAST_BASE_URL(defaulthttps://api.openweathermap.org/data/2.5/forecast): forecast endpoint overrideOPENWEATHERMAP_TIMEOUT_SECONDS(default10): HTTP timeout used by the weather tool
docker compose up --buildA web-based management interface is available at http://localhost:8080 when running with Docker Compose.
The dashboard provides a real-time overview of the system:
- Service Status — connectivity indicators for Redis, PostgreSQL, and each Docker container
- Redis Streams — message volumes, consumer groups, and recent message sources for
jot:inputandjot:output - PostgreSQL Tables — lists all tables with row counts; click any table to expand and inspect its rows
The Chat page connects directly to Jot-two via a WebSocket. Messages are submitted with web-app as the source and replies are streamed back in real time.
The frontend dev server proxies API and WebSocket calls to the backend, so you can run both separately:
# Terminal 1 — Python backend (requires Redis + PostgreSQL running)
pip install -r requirements.txt
python -m src.main_webapp
# Terminal 2 — React frontend with hot reload
cd web-app
npm install
npm run devThe dev server will be available at http://localhost:5173 and proxies /api and /ws to localhost:8080.