Jac-native project-management chatbot: walkers as HTTP handlers, by llm() for intent and copy, SQLite via db.py only, confirmation gate before DB writes.
- Jac / Jaseci (tested with jac 0.13.1)
- API keys via
BeanBot/.env. Default LLM is OpenAI GPT-4o via LiteLLM (openai/gpt-4oinjac.toml). SetOPENAI_API_KEY. To use another model, changedefault_modelinjac.tomland the matching env var (see LiteLLM providers).
cd BeanBot
jac install # sync Python + npm deps from jac.tomlAPI keys: edit BeanBot/.env and set OPENAI_API_KEY=... (no quotes). Environment variables are loaded at startup by bootstrap_env.py (imported first from main.jac) and again when db.py loads. Copy .env.example to .env on a fresh clone. .env is gitignored.
| Variable | Purpose |
|---|---|
OPENAI_API_KEY |
LiteLLM / default model in jac.toml |
JWT_SECRET |
Required when BEANBOT_ENV=production or PRODUCTION=1 (must not be the dev default). Same secret the Jac runtime uses to sign JWTs. |
JWT_ALGORITHM |
Optional, default HS256 |
BEANBOT_DB_PATH |
Optional path to SQLite file (default main.db in this folder) |
On servers, prefer platform-injected env (not only a co-located .env file) so operators can rotate secrets without redeploying the bundle.
After you change .env or jac.toml, stop the server (Ctrl+C) and run jac start main.jac again so BYLLM picks up the new model and keys.
Do not use jac run to open the app. jac run main.jac only runs with entry once: it initializes SQLite and exits. That is a quick smoke test, not the web UI.
Web app (chat + tasks + admin) — one URL for UI and API (no separate Vite dev server):
cd BeanBot
jac start main.jacOpen the Local URL printed in the terminal. The first start may run a short Vite production bundle step under the hood; the browser talks only to that port.
Optional: ./scripts/beanbot-dev.sh (same behavior, default port 8000, override with BEANBOT_DEV_PORT).
For client hot-module replacement (separate Vite process + API port), you can still use jac start main.jac --dev.
API only (Swagger at /docs):
jac start main.jac --no_client -p 8000Database file main.db is created on first run from schema.sql via init_db() in db.py.
Walker endpoints expect a JWT from /user/register or /user/login. The client uses the same username for Jac login and BeanBot SQLite profile resolution (via user_accounts). Public walkers are not used for task/chat/admin operations.
When the app opens, it now shows an auth screen:
- Choose Sign in or Create account
- Enter
usernameandpassword - Pick role:
manageroremployee
On Create account, BeanBot creates both:
- Jac login credentials (
/user/register) - BeanBot profile mapping (
user_accounts->users) with the selected role
On Sign in, BeanBot logs in with /user/login and resolves your BeanBot profile by username.
If the login exists but no BeanBot profile mapping exists yet, BeanBot creates it using the selected role.
| id | Name | role | Login username (seed) |
|---|---|---|---|
emp-1 |
Alice Chen | employee | alice |
mgr-1 |
Dr. Shah | manager | drshah |
Jac login credentials for these accounts are not created by the seed data alone; use the web UI to register new users, or register via /user/register and then EnsureUserAccount with the same username.
| File | Role |
|---|---|
main.jac |
Includes, DB init, client UI (cl { }) |
domain.jac |
Enums + shared shapes |
compliance.jac |
Deterministic validation + policy hints |
tasks.jac |
ListTasks, ListMentionableUsers, GetUser, GetUserByUsername, EnsureUserAccount, … |
beanbot_middleware.jac |
_prime_beanbot_auth — JWT → db.prime_request_auth_from_jac_headers |
chat.jac |
EnsureChatSession, ChatMessage, by llm() intent+extraction |
admin.jac |
RefreshSummary, GetSnapshot, AgentFetch, GetFetchRequests, AckFetchRequest |
db.py |
SQLite repository (only Python module) |
schema.sql |
DDL + seed rows |
- Client calls
EnsureChatSessionwith yourusernameand optionalsession_idto resume or start a thread; messages load fromchat_messages. - Each
ChatMessageappends to the session transcript, loads recent history for the LLM, then runs rule + LLM extraction → compliance (including assignment policy).@usernametokens in the message are resolved server-side (against the team directory) and a short “tagged users” line is prepended to the transcript for grounding—no separate payload is required from the client. - Missing fields →
followup; draft stored inpending_actionskeyed byuser_id+session_id. - When complete →
action_confirm; no DB write untilconfirm(or Confirm in the UI). - New thread in the UI asks for confirmation, then clears the client session id and opens a fresh thread (history remains on the server).
Both walkers require a JWT whose login name matches the username field in the JSON body. They load the BeanBot SQLite profile by that username and return { user: ... }. The client uses GetUser after sign-in to refresh role / id on the Work surface, and GetUserByUsername in the sign-in path when resolving the profile after jacLogin; behavior is the same—pick one in new code if you want to avoid duplication.
jac run main.jac # should print: BeanBot ready.Register, create BeanBot profile, then call a walker (all require Authorization: Bearer):
TOKEN=$(curl -s -X POST http://127.0.0.1:8000/user/register \
-H 'Content-Type: application/json' \
-d '{"username":"curlu","password":"curlp"}' | jq -r .data.token)
curl -s -X POST http://127.0.0.1:8000/walker/EnsureUserAccount \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"username":"curlu","role":"employee","display_name":"curlu"}'
curl -s -X POST http://127.0.0.1:8000/walker/GetUser \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"username":"curlu"}'- Set a strong
JWT_SECRETandBEANBOT_ENV=production(or follow your host’s equivalent). - Terminate TLS at your edge; don’t ship tokens over plain HTTP.
- Configure CORS so only your frontend origin can call the API.
- Set request body size limits at the reverse proxy to match your largest walker payloads.
- Accounts: Register Jac logins with usernames
aliceanddrshahso they attach to seeded SQLite users and tasks. - Rehearse: Run the full employee → manager flow twice on the machine you will present from.
- Backup: Keep a 15–20s screen recording of manager Refresh summary in case the live LLM or Wi‑Fi fails on stage.
Use your project’s validate_jac / IDE tooling on *.jac after edits.
Tests: BeanBot/.jac/venv/bin/python -m pytest tests/ -q (or your project venv).