AI generated first iteration
This commit is contained in:
1
app/agent/modes/__init__.py
Normal file
1
app/agent/modes/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Agent modes module."""
|
||||
47
app/agent/modes/hearthkeeper.py
Normal file
47
app/agent/modes/hearthkeeper.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Hearthkeeper Mode - Nurtures stream warmth and gentle presence."""
|
||||
|
||||
import logging
|
||||
from app.llm.client import LLMClient
|
||||
from app.llm.prompts import PromptTemplates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HearthkeeperMode:
|
||||
"""
|
||||
Hearthkeeper - The gentle voice of the Sanctum.
|
||||
|
||||
Purpose:
|
||||
- Maintains the emotional warmth of the stream
|
||||
- Generates gentle prompts when chat is quiet
|
||||
- Encourages participation and connection
|
||||
- Never forced or aggressive
|
||||
|
||||
Policy:
|
||||
- Activates when no human chat for 15+ minutes
|
||||
- Generates 1-2 sentence conversation starters
|
||||
- Respects stream theme if established
|
||||
- Can be suppressed if chat becomes active
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient):
|
||||
"""Initialize Hearthkeeper mode."""
|
||||
self.llm_client = llm_client
|
||||
self.last_activity_minutes = 0
|
||||
self.activity_threshold = 15
|
||||
|
||||
async def should_activate(self, minutes_since_activity: int) -> bool:
|
||||
"""Determine if Hearthkeeper should generate a prompt."""
|
||||
return minutes_since_activity >= self.activity_threshold
|
||||
|
||||
async def generate_prompt(self, theme: str | None = None) -> str:
|
||||
"""Generate a gentle prompt for the stream."""
|
||||
prompt = PromptTemplates.gentle_prompt(theme)
|
||||
response = await self.llm_client.generate(prompt)
|
||||
logger.info("Hearthkeeper generated gentle prompt")
|
||||
return response
|
||||
|
||||
async def on_chat_activity(self) -> None:
|
||||
"""React to new chat activity."""
|
||||
logger.debug("Hearthkeeper notes renewed activity")
|
||||
self.last_activity_minutes = 0
|
||||
64
app/agent/modes/librarian.py
Normal file
64
app/agent/modes/librarian.py
Normal file
@@ -0,0 +1,64 @@
|
||||
"""Librarian Mode - Archives and categorizes important discussion."""
|
||||
|
||||
import logging
|
||||
from app.llm.client import LLMClient
|
||||
from app.llm.prompts import PromptTemplates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LibrarianMode:
|
||||
"""
|
||||
Librarian - The keeper of knowledge and archives.
|
||||
|
||||
Purpose:
|
||||
- Identifies and catalogs important discussion points
|
||||
- Creates summaries of key topics
|
||||
- Builds context for future reference
|
||||
- Prepares data for blog and clip exports
|
||||
|
||||
Policy:
|
||||
- Runs passively, always monitoring
|
||||
- Tags messages by topic/sentiment
|
||||
- Creates discussion threads
|
||||
- Identifies "clip-worthy" moments
|
||||
- Feeds data to Scribe for final export
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient):
|
||||
"""Initialize Librarian mode."""
|
||||
self.llm_client = llm_client
|
||||
self.archived_messages: list[dict] = []
|
||||
self.topics: dict[str, list[str]] = {}
|
||||
|
||||
async def archive_message(self, message_id: str, content: str, username: str) -> None:
|
||||
"""Archive an important message."""
|
||||
self.archived_messages.append(
|
||||
{
|
||||
"id": message_id,
|
||||
"content": content,
|
||||
"username": username,
|
||||
}
|
||||
)
|
||||
logger.debug(f"Librarian archived message from {username}")
|
||||
|
||||
async def identify_topics(self, messages: list[str]) -> list[str]:
|
||||
"""Identify key topics from a set of messages."""
|
||||
# Placeholder: Would use LLM to extract topics
|
||||
topics = ["general", "technical", "community"]
|
||||
return topics
|
||||
|
||||
async def create_summary(self, topic: str, messages: list[str]) -> str:
|
||||
"""Create a summary of messages under a topic."""
|
||||
prompt = PromptTemplates.librarian_summary(messages)
|
||||
summary = await self.llm_client.generate(prompt, max_tokens=300)
|
||||
logger.info(f"Librarian created summary for topic: {topic}")
|
||||
return summary
|
||||
|
||||
async def get_archives(self) -> dict:
|
||||
"""Get the archive status."""
|
||||
return {
|
||||
"mode": "librarian",
|
||||
"archived_messages": len(self.archived_messages),
|
||||
"topics_tracked": len(self.topics),
|
||||
}
|
||||
73
app/agent/modes/scribe.py
Normal file
73
app/agent/modes/scribe.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""Scribe Mode - Generates the post-stream markdown ledger."""
|
||||
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from app.llm.client import LLMClient
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ScribeMode:
|
||||
"""
|
||||
Scribe - The chronicler of the stream's story.
|
||||
|
||||
Purpose:
|
||||
- Compiles session data into a markdown ledger
|
||||
- Generates blog post seeds
|
||||
- Identifies clip candidates
|
||||
- Exports final summary document
|
||||
|
||||
Policy:
|
||||
- Activates at end of stream
|
||||
- Reads data from Librarian, Warden, and repository
|
||||
- Creates structured markdown output
|
||||
- Organizes clips and blog topics
|
||||
- Ready for post-processing or publishing
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient):
|
||||
"""Initialize Scribe mode."""
|
||||
self.llm_client = llm_client
|
||||
self.ledger_entries: list[str] = []
|
||||
|
||||
async def add_entry(self, section: str, content: str) -> None:
|
||||
"""Add an entry to the ledger."""
|
||||
self.ledger_entries.append(f"## {section}\n{content}")
|
||||
logger.debug(f"Scribe recorded ledger entry: {section}")
|
||||
|
||||
async def compile_ledger(
|
||||
self,
|
||||
theme: str,
|
||||
discussion_points: list[str],
|
||||
agent_actions: list[str],
|
||||
clip_candidates: list[str],
|
||||
blog_seeds: list[str],
|
||||
) -> str:
|
||||
"""Compile all data into a markdown ledger."""
|
||||
date = datetime.utcnow().strftime("%Y-%m-%d")
|
||||
|
||||
ledger = f"# Sanctum Ledger — {date}\n\n"
|
||||
ledger += f"## Stream Theme\n{theme}\n\n"
|
||||
ledger += f"## Notable Discussion\n"
|
||||
for point in discussion_points:
|
||||
ledger += f"- {point}\n"
|
||||
ledger += "\n"
|
||||
ledger += f"## Agent Actions\n"
|
||||
for action in agent_actions:
|
||||
ledger += f"- {action}\n"
|
||||
ledger += "\n"
|
||||
ledger += f"## Clip Candidates\n"
|
||||
for clip in clip_candidates:
|
||||
ledger += f"- {clip}\n"
|
||||
ledger += "\n"
|
||||
ledger += f"## Blog Seeds\n"
|
||||
for seed in blog_seeds:
|
||||
ledger += f"- {seed}\n"
|
||||
|
||||
logger.info("Scribe compiled stream ledger")
|
||||
return ledger
|
||||
|
||||
async def export_ledger(self, filename: str, content: str) -> None:
|
||||
"""Export ledger to file."""
|
||||
# Actual export handled by MarkdownExporter
|
||||
logger.info(f"Scribe prepared ledger for export: {filename}")
|
||||
51
app/agent/modes/steward.py
Normal file
51
app/agent/modes/steward.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Steward Mode - Responds to chat with knowledge and warmth."""
|
||||
|
||||
import logging
|
||||
from app.llm.client import LLMClient
|
||||
from app.llm.prompts import PromptTemplates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StewardMode:
|
||||
"""
|
||||
Steward - The thoughtful keeper of conversation.
|
||||
|
||||
Purpose:
|
||||
- Responds to direct questions and comments
|
||||
- Shares relevant knowledge and context
|
||||
- Maintains conversation continuity
|
||||
- Balances speaking and listening
|
||||
|
||||
Policy:
|
||||
- Activates when chat is active
|
||||
- Only responds to messages explicitly mentioning the bot
|
||||
- Keeps responses brief (1-3 sentences)
|
||||
- Never interrupts human conversation flow
|
||||
- Can escalate to other modes if needed
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient):
|
||||
"""Initialize Steward mode."""
|
||||
self.llm_client = llm_client
|
||||
self.response_count = 0
|
||||
self.max_responses_per_minute = 2
|
||||
|
||||
async def should_respond(self, message: str, is_mention: bool) -> bool:
|
||||
"""Determine if Steward should respond."""
|
||||
# Only respond to mentions for now (can be expanded)
|
||||
return is_mention and self.response_count < self.max_responses_per_minute
|
||||
|
||||
async def generate_response(
|
||||
self, message: str, context: str | None = None
|
||||
) -> str:
|
||||
"""Generate a thoughtful response to a message."""
|
||||
prompt = PromptTemplates.steward_response(message, context)
|
||||
response = await self.llm_client.generate(prompt, max_tokens=150)
|
||||
self.response_count += 1
|
||||
logger.info("Steward generated response")
|
||||
return response
|
||||
|
||||
async def on_response_sent(self) -> None:
|
||||
"""Record that a response was sent."""
|
||||
logger.debug("Steward response recorded")
|
||||
85
app/agent/modes/warden.py
Normal file
85
app/agent/modes/warden.py
Normal file
@@ -0,0 +1,85 @@
|
||||
"""Warden Mode - Detects and flags suspicious content."""
|
||||
|
||||
import logging
|
||||
from app.llm.client import LLMClient
|
||||
from app.llm.prompts import PromptTemplates
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WardenMode:
|
||||
"""
|
||||
Warden - The guardian against unwanted influences.
|
||||
|
||||
Purpose:
|
||||
- Detects suspicious patterns (spam, scams, bot activity)
|
||||
- Flags Discord growth schemes and link spam
|
||||
- Monitors for manipulation or harmful content
|
||||
- Provides data for moderation decisions
|
||||
|
||||
Policy:
|
||||
- Runs on every message (always active)
|
||||
- Never takes action directly (only flags)
|
||||
- Patterns to detect:
|
||||
* "Join our Discord"
|
||||
* "Grow your channel"
|
||||
* Multiple links
|
||||
* Repeated messages (spam)
|
||||
* Known scam keywords
|
||||
- Flags are recorded for human review
|
||||
"""
|
||||
|
||||
def __init__(self, llm_client: LLMClient):
|
||||
"""Initialize Warden mode."""
|
||||
self.llm_client = llm_client
|
||||
self.suspicious_patterns = [
|
||||
"join our discord",
|
||||
"discord.gg",
|
||||
"grow your channel",
|
||||
"easy money",
|
||||
"click here",
|
||||
"limited offer",
|
||||
"act now",
|
||||
]
|
||||
self.flagged_count = 0
|
||||
|
||||
async def analyze_message(self, message: str) -> dict:
|
||||
"""Analyze a message for suspicious content."""
|
||||
result = {
|
||||
"is_suspicious": False,
|
||||
"patterns_detected": [],
|
||||
"severity": "safe",
|
||||
}
|
||||
|
||||
# Simple pattern matching
|
||||
message_lower = message.lower()
|
||||
for pattern in self.suspicious_patterns:
|
||||
if pattern in message_lower:
|
||||
result["patterns_detected"].append(pattern)
|
||||
result["is_suspicious"] = True
|
||||
|
||||
# Check for multiple links
|
||||
link_count = message.count("http") + message.count("www")
|
||||
if link_count > 1:
|
||||
result["patterns_detected"].append("multiple_links")
|
||||
result["is_suspicious"] = True
|
||||
|
||||
# Determine severity
|
||||
if result["is_suspicious"]:
|
||||
if len(result["patterns_detected"]) >= 2:
|
||||
result["severity"] = "high"
|
||||
else:
|
||||
result["severity"] = "medium"
|
||||
self.flagged_count += 1
|
||||
logger.warning(
|
||||
f"Warden flagged suspicious message: {result['patterns_detected']}"
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
async def get_report(self) -> dict:
|
||||
"""Get Warden's activity report."""
|
||||
return {
|
||||
"mode": "warden",
|
||||
"total_flagged": self.flagged_count,
|
||||
}
|
||||
Reference in New Issue
Block a user