Spaces:
Running
Running
|
Download README.md from cereeenn120/CharacterChatBot: direct link, hf CLI and curl.
- Browser
- Download file 14.3 kB
-
https://huggingface.co/spaces/cereeenn120/CharacterChatBot/resolve/main/README.md
- Command line
-
hf download hf://spaces/cereeenn120/CharacterChatBot/README.md
-
curl -L -o README.md https://huggingface.co/spaces/cereeenn120/CharacterChatBot/resolve/main/README.md
14.3 kB
| 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`. | |