Spaces:
Running
Running
File size: 14,278 Bytes
fa8f622 9594221 fa8f622 6afc342 fa8f622 72547e7 fa8f622 9594221 fa8f622 ab0a14d fa8f622 ab0a14d addec5c c57e87b ab0a14d 1d3bf6d ab0a14d c57e87b ab0a14d c57e87b ab0a14d addec5c ab0a14d 76b9a7f ab0a14d 76b9a7f ab0a14d addec5c ab0a14d 81375eb ab0a14d 6afc342 addec5c ab0a14d 6afc342 ab0a14d addec5c c57e87b addec5c 6afc342 c57e87b 31404a1 6afc342 31404a1 6afc342 addec5c c57e87b addec5c c57e87b addec5c c57e87b addec5c 31404a1 c57e87b 6afc342 c57e87b 1d3bf6d addec5c ab0a14d addec5c ab0a14d addec5c c57e87b ab0a14d addec5c ab0a14d 81375eb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | ---
title: CharacterChatBot
emoji: π¬
colorFrom: yellow
colorTo: purple
sdk: gradio
sdk_version: 6.12.0
app_file: app.py
pinned: true
hf_oauth: true
hf_oauth_scopes:
- inference-api
short_description: A Character-Based Chatbot
---
# CharacterChatBot
A Gradio-based roleplay chatbot for immersive AI character conversations, built on HuggingFace Spaces.
## Features
- **Character system** β Personalities, speech styles, and behaviors defined in JSON
- **Three-layer memory** β Session-isolated history, rolling summary, and long-term facts per character
- **Memory controls** β Remember, edit, and forget facts from the sidebar
- **Relationship state** β Four independent axes shown as short phrases; romance and attraction are optional
- **Edit last message** β Revise your last turn and send it again
- **Reply suggestions** β 4 contextual suggestions on demand, using the selected language and persona
- **User persona** β Define who you're playing in the roleplay
- **Action & thought formatting** β `(actions)` and `*thoughts*` rendered distinctly in chat
- **Multi-character support** β Switch characters via sidebar dropdown
## Project structure
```
CharacterChatBot/
βββ app.py # Gradio UI β layout, events, respond()
βββ requirements.txt
βββ characters/ # One JSON file per character
β βββ jessica_jones.json
βββ engine/ # Core logic (no UI dependencies)
β βββ chat.py # LLM streaming generator
β βββ prompt_builder.py # System prompt assembly
β βββ static_files.py # Bounded static JSON/text cache
β βββ memory.py # Three-layer memory system
β βββ memory_controls.py # Manual facts and source exclusions
β βββ memory_jobs.py # Bounded background extraction
β βββ parser.py # Action/thought response parser
β βββ suggestions.py # Reply suggestion generator
β βββ persona.py # User persona management
β βββ relationship.py # Independent relationship axes
βββ prompts/ # Prompt templates (plain text)
β βββ system_prompt.txt # Global roleplay + format rules
β βββ reply_suggestion.txt # Suggestion generation prompt
β βββ memory_prompt.txt # Memory extraction prompt
βββ memory/sessions/ # Auto-created; isolated by browser session
β βββ {session_id}/
β βββ jessica_jones.json # Atomic snapshot of all memory layers
βββ assets/
βββ characters/
βββ jessica_jones/
βββ avatar.png
```
## Character categories
The sidebar first selects a category, then lists characters assigned to it. Categories are configured in each character JSON file using a `categories` array. A character can belong to more than one category (for example, Luke Cage is listed under both Marvel and Marvel Defenders).
To add a category to an existing character, edit `characters/{character_id}.json` and add or update the field at the top level:
```json
"categories": ["Marvel", "Marvel Defenders"]
```
Category names are shown as written and grouped automatically. Use the same spelling and capitalization for characters that belong to the same category. To add a new character, create its JSON file as described below and include a `categories` array. The category appears in the UI automatically; no UI code change is needed.
## Adding a new character
1. Create `characters/{character_id}.json`:
```json
{
"id": "my_character",
"name": "My Character",
"categories": ["My Category"],
"description": "One-line description shown in the selector.",
"identity": {
"role": "Their role or profession",
"background": "Brief backstory"
},
"personality": ["trait1", "trait2", "trait3"],
"speech_style": {
"tone": ["calm", "precise"],
"formality": "formal",
"sentence_style": "short_to_medium",
"uses_humor": false,
"uses_rhetorical_questions": false,
"avoids_excessive_enthusiasm": true
},
"behavior": {
"shows_emotions": true,
"deflects_uncomfortable_topics_with_humor": false
},
"world": {
"universe": "Original",
"setting": "Where they live",
"description": "World context for the LLM."
},
"roleplay": {
"action_format": "<action>{content}</action>",
"thought_format": "<thought>{content}</thought>",
"break_character": false
},
"system_prompt": "Character-specific fine-tuning. This adds to global rules, not replaces them.",
"greeting": "The character's opening line when the chat starts.",
"example_dialogues": [
{
"situation": "Optional context",
"user": "Example user message",
"character": "Example character response"
}
],
"visuals": {
"avatar": "assets/characters/my_character/avatar.png"
}
}
```
2. Add an avatar image under `assets/characters/{character_id}/`, for example `{character_id}.jpg` or `{character_id}_00.png`. PNG, JPG, JPEG, and WebP are supported. The first available image is used (PNG first, then JPG, JPEG, WebP; filenames sorted within each format). If no image is available, the portrait is hidden. Character images are stored directly in Git so they work in checkouts without Git LFS.
3. Restart the Space. The character will appear in the sidebar dropdown automatically.
## Local setup
Requires Python 3.10+; Gradio is pinned to the Space SDK version (6.12.0).
```bash
git clone https://huggingface.co/spaces/your-username/CharacterChatBot
cd CharacterChatBot
pip install -r requirements.txt
export HF_TOKEN=hf_your_token_here
python app.py
```
The app opens at `http://localhost:7860`.
Keep the Gradio pin in `requirements.txt` and this READMEβs `sdk_version` aligned.
Version 6.12.0 is browser-tested for replacing chatbot history on character changes
and imports; 6.5.1 retained stale message views in these tests.
## HuggingFace Space deployment
1. Push your changes to the Space repository.
2. Go to **Settings β Variables and secrets β New secret**.
3. Name: `HF_TOKEN`, Value: your HuggingFace API token.
4. The Space restarts automatically and picks up the secret.
## Memory system
Each browser session has its own per-character snapshot under
`memory/sessions/{session_id}/{character_id}.json`. The session key is derived
from Gradio's request context, never accepted from an uploaded save file.
Switching characters restores that session's complete visible conversation.
Clearing the chat clears its transcript, summary, facts, exclusions, and relationship state. Retry and
undo update the same history the model uses; changing already-summarized content
invalidates its derived memory.
The complete transcript is retained for display and export. Once the unsummarized
history exceeds 20 messages (not 20 user/assistant pairs), a batch of about 10 older
messages is summarized **after the answer is displayed**. When extraction falls
behind, a batch can grow to 40 messages (plus one to keep a reply with its user turn). This periodic model
call also extracts long-term facts and relationship developments. Subsequent prompts
contain the cumulative summary, up to 40 automatic facts plus 60 manually pinned
facts, relationship context, and the remaining recent messages.
Extraction runs in a background pool with at most two jobs across the app and
one per conversation. The chat request completes without waiting for extraction.
There is no growing job queue: if the pool is busy, messages remain intact and
scheduling is tried after a later response. The same retry policy applies to
extraction errors.
Before committing, a worker checks a separate memory revision, its original
transcript prefix, and the summary it started from. Appending completed turns
does not invalidate extraction; only derived fields are merged into the newer
conversation. Edits, resets, imports, and memory/relationship setting changes
still invalidate pending work.
Memory-only updates do not advance the transcript revision. A response being
streamed can therefore append to the current snapshot without losing a summary
that finished in the meantime. Recent model context has a fallback limit of 40
messages and 24,000 characters so a slow or failed summary cannot make it grow
without bound. Older unsummarized turns can temporarily be omitted, and an
oversized final message is clipped with an explicit marker. These limits apply
to recent history, not the system prompt or current user input. The full transcript
remains available for display, later summarization, and export. Invalid relationship
observations are discarded without discarding an otherwise valid summary.
Storage is isolated by browser session, not an authenticated user account.
Refreshing the page or opening a new browser session starts fresh; use **Save /
Load** to resume later. Legacy shared `memory/{character_id}` files are never
loaded automatically because their owner is unknown. Existing version 1.0 and 2.0 save
files can still be imported explicitly into the current session.
Save files use version 3.0 and include the full visible transcript, persona,
summary, facts and their sources, summary position, memory exclusions, and
relationship options and events. Imports validate the entire file before
writing: known character ID, supported version, text-only user/assistant
messages, memory importance and provenance, relationship events, field lengths,
and a 4 MB file limit. Exports have
unique filenames. Each snapshot is replaced atomically; in-process revision
checks protect concurrent writes in the standard single-process Gradio app.
Deployments with multiple app processes would require a shared transactional
store instead of this file backend.
Translation is off by default in both the UI and backend. The main model streams
in the selected language. If translation is enabled explicitly, the displayed
translation is also the persisted conversation, so reload and subsequent context
match what the user saw.
Reply suggestions remain opt-in through the **Suggest replies** button. Their
single model call receives the selected language and the applied user persona.
On failure, four neutral fallback suggestions are returned in the selected UI
language without another model call. Changing language, persona, character, or
conversation clears the old suggestion buttons.
## Memory controls and editing
Open **Memories** to add a fact with **Remember**, or select a saved fact to
**Update selected** or **Forget selected**. Manual facts are pinned and automatic
extraction cannot overwrite them. Editing or forgetting invalidates pending
extraction, clears the summary for rebuilding, and excludes the fact's recorded
source messages and verbatim echoes from future model context. The visible
transcript remains unchanged. Other automatic facts and relationship events
sharing those sources are removed too.
Forgetting is source-based, not guaranteed semantic erasure: paraphrases elsewhere
or separately pinned facts can still contain the information. Exact normalized
re-extraction is blocked; explicitly remembering the same text removes that block.
**Edit last message** removes the last user turn and its reply, puts that user's
text back in the input, and rolls back derived memory from the removed portion.
Edit the text and send it to generate a new answer. The existing retry action
regenerates an answer without changing your message. Previous replies are not
saved as alternate branches.
## Relationship state
Trust, familiarity, romantic closeness, and sexual attraction develop independently.
Romance and attraction are individually opt-in for each character in the session;
disabling an axis clears its recorded developments. Low trust or familiarity does
not prevent romance or attraction, and no fixed relationship route is enforced.
The state describes the character's attitude toward the user persona and does not
assume reciprocal feelings or override the character's personality and motivations.
The sidebar shows short phrases rather than scores (Turkish and English labels;
other languages currently use English). Changes are extracted with the periodic
summary call, so they appear after batches of older messages are processed, not
after every turn. The panel checks local state every ten seconds without making
model calls. Source-linked events allow edits and retries to roll back affected
developments and prevent counting the same extraction twice. Save / Load preserves
this state; older saves begin with optional axes disabled.
## Static file cache
Character JSON and the system, reply-suggestion, and memory-extraction prompt
files are cached in process RAM (up to 64 entries, least recently used eviction).
Each access checks file metadata; edits and atomic replacements refresh the
entry. Missing or invalid files raise errors instead of silently reusing old
content. Character data is deep-copied for each caller to avoid shared mutations.
The cache is protected by a lock for chat and background worker access.
Conversation history, personas, summaries, and user memories are not cached here.
This saves local file reads and JSON parsing only; model calls and token usage
remain unchanged. Restarting the process clears the cache.
## Tests
```bash
python -m unittest discover -s tests -v
```
Tests use temporary storage and fake inference clients; they make no model calls.
They cover session isolation, import validation, full-history roundtrips,
summary extraction and failures, stale writes, translation, and Gradio event
processing for character switching and streaming, manual memory controls,
independent relationship axes, source rollback, and last-message editing.
## Configuration
In `app.py`, two constants control the active character and model:
```python
DEFAULT_CHARACTER = "hermione_granger" # must match a file in characters/
DEFAULT_MODEL = MODELS[4][1] # selected initial model
```
Supported models include anything available on the HuggingFace Serverless Inference API β for example `openai/gpt-4o-mini`, `deepseek-ai/DeepSeek-V3`, or `mistralai/Mistral-7B-Instruct-v0.3`.
|