Skip to main content

Overview

Handles slash commands (/command) sent through the WebSocket connection. Provides administrative and configuration functions for managing worlds, characters, sessions, models, and rules.

Main Handler

handle_command(cmd_str: str, websocket: WebSocket)

async def handle_command(cmd_str: str, websocket: WebSocket)
Parses and executes slash commands received from the client.
cmd_str
string
required
Command string starting with / (e.g., /world select fantasy)
websocket
WebSocket
required
FastAPI WebSocket connection for sending responses

Command Parsing

parts = cmd_str.split(" ", 2)
cmd = parts[0]  # e.g., "/model"
Commands are split into at most 3 parts:
  • parts[0]: Command name (e.g., /model)
  • parts[1]: Subcommand or first argument (e.g., select)
  • parts[2]: Remaining arguments (e.g., my long lore text)

Commands

Model Management

/model <model_id>

Switches the active AI model (hot-swaps without restarting).
if cmd == "/model" and len(parts) >= 2:
    new_model = parts[1]
    if any(m["id"] == new_model for m in state.AVAILABLE_MODELS):
        state.CURRENT_MODEL = new_model
        state.MODEL_CONFIRMED = True
        state.save_state()
        await websocket.send_text(
            build_ws_payload(
                "system_update",
                f"Hot-swapped model to {state.CURRENT_MODEL}",
                {"model": state.CURRENT_MODEL, "model_confirmed": True},
            )
        )
Example
/model deepseek-chat
model
string
Newly activated model ID
model_confirmed
boolean
Always true when model switch succeeds
Error Response
else:
    await websocket.send_text(
        build_ws_payload(
            "system_update", f"Error: {new_model} is not recognized."
        )
    )

World Commands

/world attach_lore <text>

Dynamically adds lore text to the current world’s RAG vector store.
elif cmd == "/world" and len(parts) >= 3:
    sub = parts[1]
    if sub == "attach_lore":
        lore_text = parts[2]
        rag_manager.add_lore(state.ACTIVE_WORLD_ID, str(uuid.uuid4()), lore_text)
        await websocket.send_text(
            build_ws_payload("system_update", "Lore attached successfully.")
        )
Example
/world attach_lore The ancient ruins hold a dark secret that only the elders remember.
lore_text
string
required
Lore text to add to the world’s knowledge base (automatically embedded)

/world select <world_id>

Sets the active world for the current session.
elif sub == "select":
    state.ACTIVE_WORLD_ID = parts[2].strip()
    state.save_state()
    await websocket.send_text(
        build_ws_payload(
            "system_update",
            f"Active world set to {state.ACTIVE_WORLD_ID}",
            {"world_id": state.ACTIVE_WORLD_ID},
        )
    )
    await broadcast_sync_state(websocket)
Example
/world select fantasy
world_id
string
ID of the newly selected world

Character Commands

/character select <character_id>

Sets the active character (persona) for conversations.
elif cmd == "/character" and len(parts) >= 3 and parts[1] == "select":
    state.ACTIVE_CHARACTER_ID = parts[2].strip()
    state.save_state()
    await websocket.send_text(
        build_ws_payload(
            "system_update",
            f"Active character set to {state.ACTIVE_CHARACTER_ID}",
            {"character_id": state.ACTIVE_CHARACTER_ID},
        )
    )
    await broadcast_sync_state(websocket)
Example
/character select elara
character_id
string
ID of the newly selected character

Rules Management

/rules add <rule_id>

Activates a rule file from assets/rules/.
elif cmd == "/rules":
    if len(parts) >= 3 and parts[1] == "add":
        rule_id = parts[2].strip()
        if os.path.exists(f"assets/rules/{rule_id}.yaml"):
            if rule_id not in state.ACTIVE_RULES:
                state.ACTIVE_RULES.append(rule_id)
            state.save_state()
            await websocket.send_text(
                build_ws_payload(
                    "system_update",
                    f"✓ Rule '{rule_id}' activated",
                    {"active_rules": state.ACTIVE_RULES},
                )
            )
            await broadcast_sync_state(websocket)
Example
/rules add nsfw
active_rules
List[string]
List of all currently active rule IDs

/rules clear

Removes all active rules.
elif len(parts) >= 2 and parts[1] == "clear":
    state.ACTIVE_RULES.clear()
    state.save_state()
    await websocket.send_text(
        build_ws_payload(
            "system_update",
            "✓ All active rules cleared",
            {"active_rules": state.ACTIVE_RULES},
        )
    )
    await broadcast_sync_state(websocket)
Example
/rules clear

NSFW Mode

/nsfw

Toggles NSFW (Not Safe For Work) mode by adding or removing the nsfw rule.
elif cmd == "/nsfw":
    if "nsfw" not in state.ACTIVE_RULES:
        if os.path.exists("assets/rules/nsfw.yaml"):
            state.ACTIVE_RULES.append("nsfw")
            state.save_state()
            await websocket.send_text(
                build_ws_payload(
                    "system_update",
                    "✓ NSFW mode ENABLED - Unrestricted adult content",
                    {"active_rules": state.ACTIVE_RULES},
                )
            )
            await broadcast_sync_state(websocket)
Example
/nsfw
active_rules
List[string]
Updated rules list (includes or excludes "nsfw")
Disabling NSFW
else:
    state.ACTIVE_RULES.remove("nsfw")
    state.save_state()
    await websocket.send_text(
        build_ws_payload(
            "system_update",
            "✓ NSFW mode DISABLED - Back to standard content",
            {"active_rules": state.ACTIVE_RULES},
        )
    )
    await broadcast_sync_state(websocket)

Session Commands

/session new

Creates a new conversation session.
elif cmd == "/session":
    if len(parts) >= 2 and parts[1] == "new":
        if not state.ACTIVE_WORLD_ID or not state.ACTIVE_CHARACTER_ID:
            await websocket.send_text(
                build_ws_payload("system_update", "✗ Select world/char first.")
            )
        else:
            state.ACTIVE_SESSION_ID = uuid.uuid4().hex[:12]
            session_manager.create_session(
                state.ACTIVE_SESSION_ID,
                state.ACTIVE_CHARACTER_ID,
                state.ACTIVE_WORLD_ID,
                state.CURRENT_MODEL,
            )
            state.save_state()
Example
/session new
session_id
string
Generated 12-character session ID
Start Message Handling If the world has a start_message, it’s automatically added to the session:
world = world_manager.get_world(state.ACTIVE_WORLD_ID)
start_history = []
if world:
    if world.start_message:
        msg_manager.add_message(
            Message(
                role="assistant",
                content=world.start_message,
                character_id=state.ACTIVE_CHARACTER_ID,
                session_id=state.ACTIVE_SESSION_ID,
            )
        )
        start_history.append(
            {"role": "assistant", "content": world.start_message}
        )
The start message is sent back to the client via a chat_history event:
await websocket.send_text(
    json.dumps(
        {
            "event": "chat_history",
            "payload": {
                "content": "",
                "metadata": {"history": start_history},
            },
        }
    )
)

/session continue <session_id>

Resumes an existing session by loading its history.
elif len(parts) >= 3 and parts[1] == "continue":
    sess_id = parts[2].strip()
    sess = session_manager.get_session(sess_id)
    if sess:
        state.ACTIVE_SESSION_ID = sess.id
        state.ACTIVE_WORLD_ID = sess.world_id
        state.ACTIVE_CHARACTER_ID = sess.character_id
        if sess.model:
            state.CURRENT_MODEL = sess.model
        session_manager.touch(sess_id)
        state.save_state()
        
        await send_chat_history(
            websocket, state.ACTIVE_CHARACTER_ID, state.ACTIVE_SESSION_ID
        )
        await broadcast_sync_state(websocket)
Example
/session continue a1b2c3d4e5f6
session_id
string
required
12-character session ID to resume

/session delete <session_id>

Permanently deletes a session and all associated data.
elif len(parts) >= 3 and parts[1] == "delete":
    sess_id = parts[2].strip()
    session_manager.delete_session(sess_id)
    msg_manager.delete_session_messages(sess_id)
    rag_manager.delete_session_memory(sess_id)
    if state.ACTIVE_SESSION_ID == sess_id:
        state.ACTIVE_SESSION_ID = ""
    await websocket.send_text(
        build_ws_payload("system_update", f"✓ Deleted session {sess_id}")
    )
    await broadcast_sync_state(websocket)
Example
/session delete a1b2c3d4e5f6
Deletes:
  • Session metadata from database
  • All messages in the session
  • All RAG vector embeddings for the session

State Synchronization

Many commands call broadcast_sync_state(websocket) to send updated state to the client:
await broadcast_sync_state(websocket)
This sends a sync_state event with:
  • Available worlds
  • Available characters
  • Recent sessions
  • Active selections
  • Available models
  • Active rules

Error Handling

Invalid Model
if any(m["id"] == new_model for m in state.AVAILABLE_MODELS):
    # success
else:
    await websocket.send_text(
        build_ws_payload(
            "system_update", f"Error: {new_model} is not recognized."
        )
    )
Missing Prerequisites
if not state.ACTIVE_WORLD_ID or not state.ACTIVE_CHARACTER_ID:
    await websocket.send_text(
        build_ws_payload("system_update", "✗ Select world/char first.")
    )
Missing Rule File
if os.path.exists("assets/rules/nsfw.yaml"):
    # success
else:
    await websocket.send_text(
        build_ws_payload("system_update", "✗ NSFW rule file not found.")
    )

Complete Command Reference

CommandDescriptionExample
/model <model_id>Switch AI model/model deepseek-chat
/world select <world_id>Select world/world select fantasy
/world attach_lore <text>Add dynamic lore/world attach_lore Ancient ruins...
/character select <char_id>Select character/character select elara
/rules add <rule_id>Activate rule/rules add nsfw
/rules clearClear all rules/rules clear
/nsfwToggle NSFW mode/nsfw
/session newCreate session/session new
/session continue <id>Resume session/session continue a1b2c3d4e5f6
/session delete <id>Delete session/session delete a1b2c3d4e5f6

Usage Example

import asyncio
import websockets
import json

async def setup_engine():
    uri = "ws://localhost:8000/ws/rpc"
    async with websockets.connect(uri) as ws:
        # Wait for ready message
        await ws.recv()
        
        # Setup commands
        commands = [
            "/model deepseek-chat",
            "/world select cyberpunk",
            "/character select nova",
            "/rules add nsfw",
            "/session new"
        ]
        
        for cmd in commands:
            await ws.send(json.dumps({"prompt": cmd}))
            response = await ws.recv()
            print(json.loads(response))

asyncio.run(setup_engine())

Build docs developers (and LLMs) love