From chatbot-toolkit
Persist bot conversation history in Postgres using the chatbot-toolkit reference app's PostgresSessionStore — asyncpg, a lazy pool, and a temp-Postgres test. Use when sessions must survive restarts, moving a bot off in-memory storage, or adding a durable SessionStore.
How this skill is triggered — by the user, by Claude, or both
Slash command
/chatbot-toolkit:bot-session-stateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The reference app ships two `SessionStore` implementations behind one Protocol
The reference app ships two SessionStore implementations behind one Protocol
(get_history / append). InMemorySessionStore is the test seam;
PostgresSessionStore is what you run in production so history survives
restarts. Both live in reference-app/app/session.py.
One row per message, ordered by a monotonic key per conversation:
CREATE TABLE IF NOT EXISTS messages (
seq BIGSERIAL PRIMARY KEY,
conversation_id TEXT NOT NULL,
role TEXT NOT NULL,
text TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS messages_conversation_seq
ON messages (conversation_id, seq);
get_history reads WHERE conversation_id = $1 ORDER BY seq, so append order is
preserved and conversations are isolated by the keyed lookup.
create_default_app() is synchronous, so the store can't open a connection in
__init__. Instead PostgresSessionStore(dsn) just stashes the DSN; the
asyncpg pool is created once on first use and the CREATE TABLE IF NOT EXISTS
runs on that first connect:
async def _get_pool(self):
if self._pool is None:
self._pool = await asyncpg.create_pool(self._dsn)
async with self._pool.acquire() as conn:
await conn.execute(_SCHEMA)
return self._pool
get_history / append call _get_pool() then run one query each.
app/main.py builds the store from settings.database_url (env DATABASE_URL,
see .env.example) and injects it into create_app(...). The webhook flow
(parse → guard → load → brain → append → send) is unchanged — it already calls
store.get_history before the Brain and store.append for both the inbound and
outbound message after. Swapping in-memory for Postgres is a one-line change at
the composition root.
Tests use pytest-postgresql, which boots a throwaway real Postgres from the
locally-installed binaries (it finds them via pg_config on PATH — Postgres.app
works, no Docker). The postgresql client fixture yields a connection; build the
asyncpg DSN from its params and drive the async store with asyncio.run:
def _dsn(postgresql):
i = postgresql.info
return f"postgresql://{i.user}@{i.host}:{i.port}/{i.dbname}"
The temp DB uses trust auth (no password), so the DSN omits one. Tests assert append-then-get preserves order and that a second conversation_id stays empty.
bot-safetydeploy-botnpx claudepluginhub ravnhq/sasso-hq --plugin chatbot-toolkitOffers UI/UX design guidance for web and mobile with 50+ styles, 161 color palettes, 57 font pairings, and 99 UX guidelines across 10 stacks. Use for designing pages, components, color systems, or reviewing UI code.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.