274 lines
8.7 KiB
Markdown
274 lines
8.7 KiB
Markdown
|
|
# Conversation History System V2
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The new conversation history system provides centralized, intelligent management of conversation context across all bot interactions.
|
||
|
|
|
||
|
|
## Key Improvements
|
||
|
|
|
||
|
|
### 1. **Per-Channel History** (Was: Per-User Globally)
|
||
|
|
- **Servers**: History tracked per `guild_id` - all users in a server share conversation context
|
||
|
|
- **DMs**: History tracked per `user_id` - each DM has its own conversation thread
|
||
|
|
- **Benefit**: Miku can follow multi-user conversations in servers and remember context across users
|
||
|
|
|
||
|
|
### 2. **Rich Message Metadata**
|
||
|
|
Each message stores:
|
||
|
|
- `author_name`: Display name of the speaker
|
||
|
|
- `content`: Message text
|
||
|
|
- `timestamp`: When the message was sent
|
||
|
|
- `is_bot`: Whether it's from Miku or a user
|
||
|
|
|
||
|
|
### 3. **Intelligent Formatting**
|
||
|
|
The system formats messages differently based on context:
|
||
|
|
- **Multi-user servers**: `"Alice: Hello!"` format to distinguish speakers
|
||
|
|
- **DMs**: Simple content without author prefix
|
||
|
|
- **LLM output**: OpenAI-compatible `{"role": "user"|"assistant", "content": "..."}` format
|
||
|
|
|
||
|
|
### 4. **Automatic Filtering**
|
||
|
|
- Empty messages automatically skipped
|
||
|
|
- Messages truncated to 500 characters to prevent context overflow
|
||
|
|
- Vision analysis context preserved inline
|
||
|
|
|
||
|
|
### 5. **Backward Compatibility**
|
||
|
|
- Still writes to `globals.conversation_history` for legacy code
|
||
|
|
- Uses same `user_id` parameter in `query_llama()`
|
||
|
|
- Autonomous functions work without modification
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
### Core Class: `ConversationHistory`
|
||
|
|
|
||
|
|
Located in: `bot/utils/conversation_history.py`
|
||
|
|
|
||
|
|
```python
|
||
|
|
from utils.conversation_history import conversation_history
|
||
|
|
|
||
|
|
# Add a message
|
||
|
|
conversation_history.add_message(
|
||
|
|
channel_id="123456789", # guild_id or user_id
|
||
|
|
author_name="Alice", # Display name
|
||
|
|
content="Hello Miku!", # Message text
|
||
|
|
is_bot=False # True if from Miku
|
||
|
|
)
|
||
|
|
|
||
|
|
# Get recent messages
|
||
|
|
messages = conversation_history.get_recent_messages("123456789", max_messages=8)
|
||
|
|
# Returns: [(author, content, is_bot), ...]
|
||
|
|
|
||
|
|
# Format for LLM
|
||
|
|
llm_messages = conversation_history.format_for_llm("123456789", max_messages=8)
|
||
|
|
# Returns: [{"role": "user", "content": "Alice: Hello!"}, ...]
|
||
|
|
|
||
|
|
# Get statistics
|
||
|
|
stats = conversation_history.get_channel_stats("123456789")
|
||
|
|
# Returns: {"total_messages": 10, "bot_messages": 5, "user_messages": 5}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Usage in `query_llama()`
|
||
|
|
|
||
|
|
### Updated Signature
|
||
|
|
|
||
|
|
```python
|
||
|
|
async def query_llama(
|
||
|
|
user_prompt,
|
||
|
|
user_id, # For DMs: actual user ID; For servers: can be anything
|
||
|
|
guild_id=None, # Server ID (None for DMs)
|
||
|
|
response_type="dm_response",
|
||
|
|
model=None,
|
||
|
|
author_name=None # NEW: Display name for multi-user context
|
||
|
|
):
|
||
|
|
```
|
||
|
|
|
||
|
|
### Channel ID Logic
|
||
|
|
|
||
|
|
```python
|
||
|
|
channel_id = str(guild_id) if guild_id else str(user_id)
|
||
|
|
```
|
||
|
|
|
||
|
|
- **Server messages**: `channel_id = guild_id` → All server users share history
|
||
|
|
- **DM messages**: `channel_id = user_id` → Each DM has separate history
|
||
|
|
|
||
|
|
### Example Calls
|
||
|
|
|
||
|
|
**Server message:**
|
||
|
|
```python
|
||
|
|
response = await query_llama(
|
||
|
|
prompt="What's the weather?",
|
||
|
|
user_id=str(message.author.id),
|
||
|
|
guild_id=message.guild.id, # Server context
|
||
|
|
response_type="server_response",
|
||
|
|
author_name=message.author.display_name # "Alice"
|
||
|
|
)
|
||
|
|
# History saved to channel_id=guild_id
|
||
|
|
```
|
||
|
|
|
||
|
|
**DM message:**
|
||
|
|
```python
|
||
|
|
response = await query_llama(
|
||
|
|
prompt="Tell me a joke",
|
||
|
|
user_id=str(message.author.id),
|
||
|
|
guild_id=None, # No server
|
||
|
|
response_type="dm_response",
|
||
|
|
author_name=message.author.display_name
|
||
|
|
)
|
||
|
|
# History saved to channel_id=user_id
|
||
|
|
```
|
||
|
|
|
||
|
|
**Autonomous message:**
|
||
|
|
```python
|
||
|
|
message = await query_llama(
|
||
|
|
prompt="Say something fun!",
|
||
|
|
user_id=f"miku-autonomous-{guild_id}", # Consistent ID
|
||
|
|
guild_id=guild_id, # Server context
|
||
|
|
response_type="autonomous_general"
|
||
|
|
)
|
||
|
|
# History saved to channel_id=guild_id
|
||
|
|
```
|
||
|
|
|
||
|
|
## Image/Video Analysis
|
||
|
|
|
||
|
|
### Updated `rephrase_as_miku()`
|
||
|
|
|
||
|
|
```python
|
||
|
|
async def rephrase_as_miku(
|
||
|
|
vision_output,
|
||
|
|
user_prompt,
|
||
|
|
guild_id=None,
|
||
|
|
user_id=None, # NEW: Actual user ID
|
||
|
|
author_name=None # NEW: Display name
|
||
|
|
):
|
||
|
|
```
|
||
|
|
|
||
|
|
### How It Works
|
||
|
|
|
||
|
|
1. **Vision analysis injected into history**:
|
||
|
|
```python
|
||
|
|
conversation_history.add_message(
|
||
|
|
channel_id=channel_id,
|
||
|
|
author_name="Vision System",
|
||
|
|
content=f"[Image/Video Analysis: {vision_output}]",
|
||
|
|
is_bot=False
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Follow-up questions remember the image**:
|
||
|
|
- User sends image → Vision analysis added to history
|
||
|
|
- User asks "What color is the car?" → Miku sees the vision analysis in history
|
||
|
|
- User asks "Who made this meme?" → Still has vision context
|
||
|
|
|
||
|
|
### Example Flow
|
||
|
|
|
||
|
|
```
|
||
|
|
[USER] Bob: *sends meme.gif*
|
||
|
|
[Vision System]: [Image/Video Analysis: A cat wearing sunglasses with text "deal with it"]
|
||
|
|
[BOT] Miku: Haha, that's a classic meme! The cat looks so cool! 😎
|
||
|
|
[USER] Bob: Who made this meme?
|
||
|
|
[BOT] Miku: The "Deal With It" meme originated from...
|
||
|
|
```
|
||
|
|
|
||
|
|
## Migration from Old System
|
||
|
|
|
||
|
|
### What Changed
|
||
|
|
|
||
|
|
| **Old System** | **New System** |
|
||
|
|
|----------------|----------------|
|
||
|
|
| `globals.conversation_history[user_id]` | `conversation_history.add_message(channel_id, ...)` |
|
||
|
|
| Per-user globally | Per-server or per-DM |
|
||
|
|
| `[(user_msg, bot_msg), ...]` tuples | Rich metadata with author, timestamp, role |
|
||
|
|
| Manual filtering in `llm.py` | Automatic filtering in `ConversationHistory` |
|
||
|
|
| Image analysis used `user_id="image_analysis"` | Uses actual user's channel_id |
|
||
|
|
| Reply feature added `("", message)` tuples | No manual reply handling needed |
|
||
|
|
|
||
|
|
### Backward Compatibility
|
||
|
|
|
||
|
|
The new system still writes to `globals.conversation_history` for any code that might depend on it:
|
||
|
|
|
||
|
|
```python
|
||
|
|
# In llm.py after getting LLM response
|
||
|
|
globals.conversation_history[user_id].append((user_prompt, reply))
|
||
|
|
```
|
||
|
|
|
||
|
|
This ensures existing code doesn't break during migration.
|
||
|
|
|
||
|
|
## Testing
|
||
|
|
|
||
|
|
Run the test suite:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd /home/koko210Serve/docker/ollama-discord/bot
|
||
|
|
python test_conversation_history.py
|
||
|
|
```
|
||
|
|
|
||
|
|
Tests cover:
|
||
|
|
- ✅ Adding messages to server channels
|
||
|
|
- ✅ Adding messages to DM channels
|
||
|
|
- ✅ Formatting for LLM (OpenAI messages)
|
||
|
|
- ✅ Empty message filtering
|
||
|
|
- ✅ Message truncation (500 char limit)
|
||
|
|
- ✅ Channel statistics
|
||
|
|
|
||
|
|
## Benefits
|
||
|
|
|
||
|
|
### 1. **Context Preservation**
|
||
|
|
- Multi-user conversations tracked properly
|
||
|
|
- Image/video descriptions persist across follow-up questions
|
||
|
|
- No more lost context when using Discord reply feature
|
||
|
|
|
||
|
|
### 2. **Token Efficiency**
|
||
|
|
- Automatic truncation prevents context overflow
|
||
|
|
- Empty messages filtered out
|
||
|
|
- Configurable message limits (default: 8 messages)
|
||
|
|
|
||
|
|
### 3. **Better Multi-User Support**
|
||
|
|
- Server conversations include author names: `"Alice: Hello!"`
|
||
|
|
- Miku understands who said what
|
||
|
|
- Enables natural group chat dynamics
|
||
|
|
|
||
|
|
### 4. **Debugging & Analytics**
|
||
|
|
- Rich metadata for each message
|
||
|
|
- Channel statistics (total, bot, user message counts)
|
||
|
|
- Timestamp tracking for future features
|
||
|
|
|
||
|
|
### 5. **Maintainability**
|
||
|
|
- Single source of truth for conversation history
|
||
|
|
- Clean API: `add_message()`, `get_recent_messages()`, `format_for_llm()`
|
||
|
|
- Centralized filtering and formatting logic
|
||
|
|
|
||
|
|
## Future Enhancements
|
||
|
|
|
||
|
|
Possible improvements:
|
||
|
|
- [ ] Persistent storage (save history to disk/database)
|
||
|
|
- [ ] Conversation summarization for very long threads
|
||
|
|
- [ ] Per-user preferences (some users want more/less context)
|
||
|
|
- [ ] Automatic context pruning based on relevance
|
||
|
|
- [ ] Export conversation history for analysis
|
||
|
|
- [ ] Integration with dm_interaction_analyzer
|
||
|
|
|
||
|
|
## Code Locations
|
||
|
|
|
||
|
|
| **File** | **Changes** |
|
||
|
|
|----------|-------------|
|
||
|
|
| `bot/utils/conversation_history.py` | **NEW** - Core history management class |
|
||
|
|
| `bot/utils/llm.py` | Updated to use new system, added `author_name` parameter |
|
||
|
|
| `bot/bot.py` | Pass `author_name` to `query_llama()`, removed reply pollution |
|
||
|
|
| `bot/utils/image_handling.py` | `rephrase_as_miku()` accepts `user_id` and `author_name` |
|
||
|
|
| `bot/utils/autonomous_v1_legacy.py` | No changes needed (already guild-based) |
|
||
|
|
| `bot/test_conversation_history.py` | **NEW** - Test suite |
|
||
|
|
|
||
|
|
## Summary
|
||
|
|
|
||
|
|
The new conversation history system provides:
|
||
|
|
- ✅ **Per-channel tracking** (server-wide or DM-specific)
|
||
|
|
- ✅ **Rich metadata** (author, timestamp, role)
|
||
|
|
- ✅ **Intelligent formatting** (with author names in servers)
|
||
|
|
- ✅ **Automatic filtering** (empty messages, truncation)
|
||
|
|
- ✅ **Image/video context** (vision analysis persists)
|
||
|
|
- ✅ **Backward compatibility** (legacy code still works)
|
||
|
|
- ✅ **Clean API** (simple, testable functions)
|
||
|
|
|
||
|
|
This solves the original problems:
|
||
|
|
1. ❌ ~~Video descriptions lost~~ → ✅ Now preserved in channel history
|
||
|
|
2. ❌ ~~Reply feature polluted history~~ → ✅ No manual reply handling
|
||
|
|
3. ❌ ~~Image analysis separate user_id~~ → ✅ Uses actual channel_id
|
||
|
|
4. ❌ ~~Autonomous actions broke history~~ → ✅ Guild-based IDs work naturally
|