AI generated first iteration

This commit is contained in:
2026-05-11 15:01:55 -05:00
parent af3e282fda
commit 412d7caec3
28 changed files with 2094 additions and 157 deletions

View File

@@ -0,0 +1 @@
"""Agent modes module."""

View 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

View 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
View 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}")

View 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
View 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,
}