Optional features
The example config has one Discord character. Everything below is added to jingmei.config.json as needed; each snippet says which environment variable it needs (put it in .env, key: value format). Field meanings are in the Configuration reference.
Telegram: needs TELEGRAM_LUNA_TOKEN in .env (the BotFather token). Add a top-level telegram section and a telegram account on the persona; setup steps are in Telegram setup.
"telegram": { "chatIds": ["-1001234567890"] }"telegram": { "tokenEnv": "TELEGRAM_LUNA_TOKEN" }The second snippet goes in the persona object under personas[], next to discord.
Voice (Fish Audio): needs FISH_AUDIO_API_KEY. referenceId is a 32-hex voice ID; a persona uses voice by default and can opt out with voiceEnabled: false.
"voice": {
"apiKeyEnv": "FISH_AUDIO_API_KEY",
"referenceId": "00000000000000000000000000000000",
"model": "s2.1-pro-free"
}Drawing: needs no environment variable, but an Antigravity OAuth login; steps in Drawing. The model defaults to gemini-3.1-flash-image, so write this only to change it:
"imageGeneration": { "model": "gemini-3.1-flash-image" }Vision model: describes images with another model when a persona's main model cannot see images. Needs credentials for that provider (bun run jingmei login <provider> or its API key variable).
"visionModel": "openrouter/google/gemini-2.5-flash"Candlestick charts and long text as an image: no environment variable; both are off by default. See Candlestick charts and Long text as an image.
"kline": { "enabled": true },
"textImage": { "enabled": true, "thresholdChars": 300 }Feature switches (features): everything is on by default. Each of these can be turned off alone, which removes the tools and prompt lines and stops loading dependencies nothing uses.
"features": { "history": false, "memory": false, "soul": false, "search": false, "audit": false }| Switch | When false |
|---|---|
history | No message index, no related_messages / search_history, no "(相关 N 条)" on context lines; the ~96 MB embedding model is not downloaded and fastembed and sqlite-vec are not loaded. Combining it with events is a configuration error |
memory | No remember_member_fact / recall_member_memory, and no passive collection of member profiles, preferences, relationships or birthdays stated in chat. Kept: /memory and /forget still show and delete existing data, and /birthday still records a birthday by hand (which celebrations birthday greetings rely on) |
soul | No update_soul, and stored private soul notes are no longer injected into the system prompt; stored data stays in the database |
search | No search_web and no prefetched search results; DEEPSEEK_API_KEY remains the default model's credential |
audit | No natural-tone audit of final text (the leak-marker check still runs); reply decisions are controlled separately by jev.replyDecision |
run_js is always on. Configuration edits trigger a graceful reload; new switches apply after current conversations finish.
Jev (quick reactions, memory ranking; participation decisions use it too): remote Jev needs TYPESAFE_API_KEY; omit apiKeyEnv to use only the in-process wrapper, which then requires DEEPSEEK_API_KEY (or a localJev section), otherwise startup fails with a configuration error. See Jev.
"jev": {
"apiKeyEnv": "TYPESAFE_API_KEY",
"quickReactions": true,
"memoryScoring": true
}Topics (events): needs a resolvable decision client, that is the Jev section above or DEEPSEEK_API_KEY; the summaryModel provider needs credentials. The first start downloads an embedding model.
"events": { "summaryModel": "deepseek/deepseek-flash" }Holiday and birthday greetings: no environment variable. space and channelId must be a server/channel you configured above; personaId is the character that posts.
"celebrations": [
{
"space": "discord:000000000000000000",
"channelId": "000000000000000001",
"personaId": "luna",
"timeZone": "Australia/Sydney",
"calendar": "both"
}
]