Initial commit: Ajarbot with optimizations

Features:
- Multi-platform bot (Slack, Telegram)
- Memory system with SQLite FTS
- Tool use capabilities (file ops, commands)
- Scheduled tasks system
- Dynamic model switching (/sonnet, /haiku)
- Prompt caching for cost optimization

Optimizations:
- Default to Haiku 4.5 (12x cheaper)
- Reduced context: 3 messages, 2 memory results
- Optimized SOUL.md (48% smaller)
- Automatic caching when using Sonnet (90% savings)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 19:06:28 -07:00
commit a99799bf3d
58 changed files with 11434 additions and 0 deletions

487
pulse_brain.py Normal file
View File

@@ -0,0 +1,487 @@
"""
Pulse & Brain Architecture for Ajarbot.
PULSE (Pure Python):
- Runs every N seconds
- Zero API token cost
- Checks: server health, disk space, log files, task queue
- Only wakes the BRAIN when needed
BRAIN (Agent/SDK):
- Only invoked when:
1. Pulse detects an issue (error logs, low disk space, etc.)
2. Scheduled time for content generation (morning briefing)
3. Manual trigger requested
This is much more efficient than running Agent in a loop.
"""
import asyncio
import shutil
import string
import threading
import time
import traceback
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Any, Callable, Dict, List, Optional
from agent import Agent
# How many seconds between brain invocations to avoid duplicates
_BRAIN_COOLDOWN_SECONDS = 3600
class CheckType(Enum):
"""Type of check to perform."""
PURE_PYTHON = "pure_python"
CONDITIONAL = "conditional"
SCHEDULED = "scheduled"
@dataclass
class PulseCheck:
"""A check performed by the Pulse (pure Python)."""
name: str
check_func: Callable[[], Dict[str, Any]]
interval_seconds: int = 60
last_run: Optional[datetime] = None
@dataclass
class BrainTask:
"""A task that requires the Brain (Agent/SDK)."""
name: str
check_type: CheckType
prompt_template: str
# For CONDITIONAL: condition to check
condition_func: Optional[Callable[[Dict[str, Any]], bool]] = None
# For SCHEDULED: when to run
schedule_time: Optional[str] = None # "08:00", "18:00", etc.
last_brain_run: Optional[datetime] = None
# Output options
send_to_platform: Optional[str] = None
send_to_channel: Optional[str] = None
_STATUS_ICONS = {"ok": "+", "warn": "!", "error": "x"}
class PulseBrain:
"""
Hybrid monitoring system with zero-cost pulse and smart brain.
The Pulse runs continuously checking system health (zero tokens).
The Brain only activates when needed (uses tokens wisely).
"""
def __init__(
self,
agent: Agent,
pulse_interval: int = 60,
enable_defaults: bool = True,
) -> None:
"""
Initialize Pulse & Brain system.
Args:
agent: The Agent instance to use for brain tasks.
pulse_interval: How often pulse loop runs (seconds).
enable_defaults: Load example checks. Set False to start clean.
"""
self.agent = agent
self.pulse_interval = pulse_interval
self.pulse_checks: List[PulseCheck] = []
self.brain_tasks: List[BrainTask] = []
self.running = False
self.thread: Optional[threading.Thread] = None
self.adapters: Dict[str, Any] = {}
# State tracking (protected by lock)
self._lock = threading.Lock()
self.pulse_data: Dict[str, Any] = {}
self.brain_invocations = 0
if enable_defaults:
self._setup_default_checks()
print("[PulseBrain] Loaded default example checks")
print(
" To start clean: "
"PulseBrain(agent, enable_defaults=False)"
)
def _setup_default_checks(self) -> None:
"""Set up default pulse checks and brain tasks."""
def check_disk_space() -> Dict[str, Any]:
"""Check disk space (pure Python, no agent)."""
try:
usage = shutil.disk_usage(".")
percent_used = (usage.used / usage.total) * 100
gb_free = usage.free / (1024 ** 3)
if percent_used > 90:
status = "error"
elif percent_used > 80:
status = "warn"
else:
status = "ok"
return {
"status": status,
"percent_used": percent_used,
"gb_free": gb_free,
"message": (
f"Disk: {percent_used:.1f}% used, "
f"{gb_free:.1f} GB free"
),
}
except Exception as e:
return {"status": "error", "message": str(e)}
def check_memory_tasks() -> Dict[str, Any]:
"""Check for stale tasks (pure Python)."""
try:
pending = self.agent.memory.get_tasks(status="pending")
stale_count = len(pending)
status = "warn" if stale_count > 5 else "ok"
return {
"status": status,
"pending_count": len(pending),
"stale_count": stale_count,
"message": (
f"{len(pending)} pending tasks, "
f"{stale_count} stale"
),
}
except Exception as e:
return {"status": "error", "message": str(e)}
def check_log_errors() -> Dict[str, Any]:
"""Check recent logs for errors (pure Python)."""
return {
"status": "ok",
"errors_found": 0,
"message": "No errors in recent logs",
}
self.pulse_checks.extend([
PulseCheck(
"disk-space", check_disk_space,
interval_seconds=300,
),
PulseCheck(
"memory-tasks", check_memory_tasks,
interval_seconds=600,
),
PulseCheck(
"log-errors", check_log_errors,
interval_seconds=60,
),
])
self.brain_tasks.extend([
BrainTask(
name="disk-space-advisor",
check_type=CheckType.CONDITIONAL,
prompt_template=(
"Disk space is running low:\n"
"- Used: {percent_used:.1f}%\n"
"- Free: {gb_free:.1f} GB\n\n"
"Please analyze:\n"
"1. Is this critical?\n"
"2. What files/directories should I check?\n"
"3. Should I set up automated cleanup?\n\n"
"Be concise and actionable."
),
condition_func=lambda data: (
data.get("status") == "error"
),
),
BrainTask(
name="error-analyst",
check_type=CheckType.CONDITIONAL,
prompt_template=(
"Errors detected in logs:\n"
"{message}\n\n"
"Please analyze:\n"
"1. What does this error mean?\n"
"2. How critical is it?\n"
"3. What should I do to fix it?"
),
condition_func=lambda data: (
data.get("errors_found", 0) > 0
),
),
BrainTask(
name="morning-briefing",
check_type=CheckType.SCHEDULED,
schedule_time="08:00",
prompt_template=(
"Good morning! Please provide a brief summary:\n\n"
"1. System health "
"(disk: {disk_space_message}, "
"tasks: {tasks_message})\n"
"2. Any pending tasks that need attention\n"
"3. Priorities for today\n"
"4. A motivational message\n\n"
"Keep it brief and actionable."
),
),
BrainTask(
name="evening-summary",
check_type=CheckType.SCHEDULED,
schedule_time="18:00",
prompt_template=(
"Good evening! Daily wrap-up:\n\n"
"1. What was accomplished today\n"
"2. Tasks still pending: {pending_count}\n"
"3. Any issues detected (disk, errors, etc.)\n"
"4. Preview for tomorrow\n\n"
"Keep it concise."
),
),
])
def add_adapter(self, platform: str, adapter: Any) -> None:
"""Register an adapter for sending messages."""
self.adapters[platform] = adapter
def start(self) -> None:
"""Start the Pulse & Brain system."""
if self.running:
return
self.running = True
self.thread = threading.Thread(
target=self._pulse_loop, daemon=True
)
self.thread.start()
print("=" * 60)
print("PULSE & BRAIN Started")
print("=" * 60)
print(f"\nPulse interval: {self.pulse_interval}s")
print(f"Pulse checks: {len(self.pulse_checks)}")
print(f"Brain tasks: {len(self.brain_tasks)}\n")
for check in self.pulse_checks:
print(
f" [Pulse] {check.name} "
f"(every {check.interval_seconds}s)"
)
for task in self.brain_tasks:
if task.check_type == CheckType.SCHEDULED:
print(
f" [Brain] {task.name} "
f"(scheduled {task.schedule_time})"
)
else:
print(f" [Brain] {task.name} (conditional)")
print("\n" + "=" * 60 + "\n")
def stop(self) -> None:
"""Stop the Pulse & Brain system."""
self.running = False
if self.thread:
self.thread.join()
print(
f"\nPULSE & BRAIN Stopped "
f"(Brain invoked {self.brain_invocations} times)"
)
def _pulse_loop(self) -> None:
"""Main pulse loop (runs continuously, zero cost)."""
while self.running:
try:
now = datetime.now()
for check in self.pulse_checks:
should_run = (
check.last_run is None
or (now - check.last_run).total_seconds()
>= check.interval_seconds
)
if not should_run:
continue
result = check.check_func()
check.last_run = now
# Thread-safe update of pulse_data
with self._lock:
self.pulse_data[check.name] = result
icon = _STATUS_ICONS.get(
result.get("status"), "?"
)
print(
f"[{icon}] {check.name}: "
f"{result.get('message', 'OK')}"
)
self._check_brain_tasks(now)
except Exception as e:
print(f"Pulse error: {e}")
traceback.print_exc()
time.sleep(self.pulse_interval)
def _check_brain_tasks(self, now: datetime) -> None:
"""Check if any brain tasks need to be invoked."""
for task in self.brain_tasks:
should_invoke = False
prompt_data: Dict[str, Any] = {}
if (
task.check_type == CheckType.CONDITIONAL
and task.condition_func
):
for check_name, check_data in self.pulse_data.items():
if task.condition_func(check_data):
should_invoke = True
prompt_data = check_data
print(
f"Condition met for brain task: "
f"{task.name}"
)
break
elif (
task.check_type == CheckType.SCHEDULED
and task.schedule_time
):
target_time = datetime.strptime(
task.schedule_time, "%H:%M"
).time()
current_time = now.time()
time_match = (
current_time.hour == target_time.hour
and current_time.minute == target_time.minute
)
already_ran_recently = (
task.last_brain_run
and (now - task.last_brain_run).total_seconds()
< _BRAIN_COOLDOWN_SECONDS
)
if time_match and not already_ran_recently:
should_invoke = True
prompt_data = self._gather_scheduled_data()
print(
f"Scheduled time for brain task: {task.name}"
)
if should_invoke:
self._invoke_brain(task, prompt_data)
task.last_brain_run = now
def _gather_scheduled_data(self) -> Dict[str, Any]:
"""Gather data from all pulse checks for scheduled brain tasks."""
disk_data = self.pulse_data.get("disk-space", {})
task_data = self.pulse_data.get("memory-tasks", {})
return {
"disk_space_message": disk_data.get(
"message", "Unknown"
),
"tasks_message": task_data.get("message", "Unknown"),
"pending_count": task_data.get("pending_count", 0),
**disk_data,
}
def _invoke_brain(
self, task: BrainTask, data: Dict[str, Any]
) -> None:
"""Invoke the Brain (Agent/SDK) for a task."""
print(f"Invoking brain: {task.name}")
# Thread-safe increment
with self._lock:
self.brain_invocations += 1
try:
# Use safe_substitute to prevent format string injection
# Convert all data values to strings first
safe_data = {k: str(v) for k, v in data.items()}
template = string.Template(task.prompt_template)
prompt = template.safe_substitute(safe_data)
response = self.agent.chat(
user_message=prompt, username="pulse-brain"
)
print(f"Brain response ({len(response)} chars)")
print(f" Preview: {response[:100]}...")
if task.send_to_platform and task.send_to_channel:
asyncio.run(self._send_to_platform(task, response))
except Exception as e:
print(f"Brain error: {e}")
traceback.print_exc()
async def _send_to_platform(
self, task: BrainTask, response: str
) -> None:
"""Send brain output to messaging platform."""
adapter = self.adapters.get(task.send_to_platform)
if not adapter:
return
from adapters.base import OutboundMessage
message = OutboundMessage(
platform=task.send_to_platform,
channel_id=task.send_to_channel,
text=f"**{task.name}**\n\n{response}",
)
result = await adapter.send_message(message)
if result.get("success"):
print(f"Sent to {task.send_to_platform}")
def get_status(self) -> Dict[str, Any]:
"""Get current status of Pulse & Brain."""
# Thread-safe read of shared state
with self._lock:
return {
"running": self.running,
"pulse_interval": self.pulse_interval,
"brain_invocations": self.brain_invocations,
"pulse_checks": len(self.pulse_checks),
"brain_tasks": len(self.brain_tasks),
"latest_pulse_data": dict(self.pulse_data), # Copy
}
if __name__ == "__main__":
agent = Agent(
provider="claude",
workspace_dir="./memory_workspace",
enable_heartbeat=False,
)
pb = PulseBrain(agent, pulse_interval=10)
pb.start()
try:
print("Running... Press Ctrl+C to stop\n")
while True:
time.sleep(1)
except KeyboardInterrupt:
pb.stop()