This commit is contained in:
Slipstream 2025-04-28 19:22:19 -06:00
parent b5a2295b05
commit 6f0a9ae68a
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
4 changed files with 114 additions and 1 deletions

View File

@ -16,7 +16,9 @@ from .config import (
INTEREST_FACT_BOOST, PROACTIVE_GOAL_CHECK_INTERVAL, STATS_PUSH_INTERVAL, # Added stats interval INTEREST_FACT_BOOST, PROACTIVE_GOAL_CHECK_INTERVAL, STATS_PUSH_INTERVAL, # Added stats interval
MOOD_OPTIONS, MOOD_CATEGORIES, MOOD_CHANGE_INTERVAL_MIN, MOOD_CHANGE_INTERVAL_MAX, # Mood change imports MOOD_OPTIONS, MOOD_CATEGORIES, MOOD_CHANGE_INTERVAL_MIN, MOOD_CHANGE_INTERVAL_MAX, # Mood change imports
BASELINE_PERSONALITY, # For default traits BASELINE_PERSONALITY, # For default traits
REFLECTION_INTERVAL_SECONDS # Import reflection interval REFLECTION_INTERVAL_SECONDS, # Import reflection interval
# Internal Action Config
INTERNAL_ACTION_INTERVAL_SECONDS, INTERNAL_ACTION_PROBABILITY
) )
# Assuming analysis functions are moved # Assuming analysis functions are moved
from .analysis import ( from .analysis import (
@ -290,6 +292,67 @@ async def background_processing_task(cog: 'GurtCog'):
traceback.print_exc() traceback.print_exc()
cog.last_proactive_goal_check = now # Update timestamp even on error cog.last_proactive_goal_check = now # Update timestamp even on error
# --- Random Internal Action (Runs periodically based on probability) ---
if now - cog.last_internal_action_check > INTERNAL_ACTION_INTERVAL_SECONDS:
if random.random() < INTERNAL_ACTION_PROBABILITY:
print("Considering random internal action...")
# --- Select Action ---
# For now, only use get_general_facts
selected_tool_name = "get_general_facts"
tool_func = TOOL_MAPPING.get(selected_tool_name)
tool_args = {"query": None, "limit": 5} # Example: Get 5 recent general facts
if tool_func:
print(f" - Attempting internal action: {selected_tool_name} with args: {tool_args}")
tool_result = None
tool_error = None
try:
start_time = time.monotonic()
# Execute the tool function directly
tool_result = await tool_func(cog, **tool_args)
end_time = time.monotonic()
exec_time = end_time - start_time
if isinstance(tool_result, dict) and "error" in tool_result:
tool_error = tool_result["error"]
result_summary = f"Error: {tool_error}"
print(f" - Internal action '{selected_tool_name}' reported error: {tool_error}")
else:
# Create a concise summary of the result
if isinstance(tool_result, dict) and "facts" in tool_result:
fact_count = tool_result.get("count", len(tool_result.get("facts", [])))
result_summary = f"Success: Retrieved {fact_count} general facts."
# Optionally include first fact if available
if fact_count > 0 and tool_result.get("facts"):
first_fact = str(tool_result["facts"][0])[:100] # Truncate first fact
result_summary += f" First: '{first_fact}...'"
else:
result_summary = f"Success: Result type {type(tool_result)}. {str(tool_result)[:200]}" # Generic success summary
print(f" - Internal action '{selected_tool_name}' completed successfully in {exec_time:.3f}s.")
except Exception as exec_e:
tool_error = f"Exception during internal execution: {str(exec_e)}"
result_summary = f"Exception: {tool_error}"
print(f" - Internal action '{selected_tool_name}' raised exception: {exec_e}")
traceback.print_exc()
# --- Log Action to Memory ---
try:
log_result = await cog.memory_manager.add_internal_action_log(
tool_name=selected_tool_name,
arguments=tool_args,
result_summary=result_summary
)
if log_result.get("status") != "logged":
print(f" - Warning: Failed to log internal action to memory: {log_result.get('error')}")
except Exception as log_e:
print(f" - Error logging internal action to memory: {log_e}")
traceback.print_exc()
else:
print(f" - Error: Selected internal tool '{selected_tool_name}' not found in TOOL_MAPPING.")
# Update check timestamp regardless of whether an action was performed
cog.last_internal_action_check = now
except asyncio.CancelledError: except asyncio.CancelledError:
print("Background processing task cancelled") print("Background processing task cancelled")
except Exception as e: except Exception as e:

View File

@ -127,6 +127,7 @@ class GurtCog(commands.Cog, name="Gurt"): # Added explicit Cog name
self.last_goal_check_time = time.time() # Timestamp for last goal decomposition check self.last_goal_check_time = time.time() # Timestamp for last goal decomposition check
self.last_goal_execution_time = time.time() # Timestamp for last goal execution check self.last_goal_execution_time = time.time() # Timestamp for last goal execution check
self.last_proactive_goal_check = time.time() # Timestamp for last proactive goal check self.last_proactive_goal_check = time.time() # Timestamp for last proactive goal check
self.last_internal_action_check = time.time() # Timestamp for last internal action check
# --- Stats Tracking --- # --- Stats Tracking ---
self.api_stats = defaultdict(lambda: {"success": 0, "failure": 0, "retries": 0, "total_time": 0.0, "count": 0}) # Keyed by model name self.api_stats = defaultdict(lambda: {"success": 0, "failure": 0, "retries": 0, "total_time": 0.0, "count": 0}) # Keyed by model name

View File

@ -125,6 +125,10 @@ GOAL_CHECK_INTERVAL = int(os.getenv("GOAL_CHECK_INTERVAL", 300)) # Check for pen
GOAL_EXECUTION_INTERVAL = int(os.getenv("GOAL_EXECUTION_INTERVAL", 60)) # Check for active goals to execute every 1 min GOAL_EXECUTION_INTERVAL = int(os.getenv("GOAL_EXECUTION_INTERVAL", 60)) # Check for active goals to execute every 1 min
PROACTIVE_GOAL_CHECK_INTERVAL = int(os.getenv("PROACTIVE_GOAL_CHECK_INTERVAL", 900)) # Check if Gurt should create its own goals every 15 mins PROACTIVE_GOAL_CHECK_INTERVAL = int(os.getenv("PROACTIVE_GOAL_CHECK_INTERVAL", 900)) # Check if Gurt should create its own goals every 15 mins
# --- Internal Random Action Config ---
INTERNAL_ACTION_INTERVAL_SECONDS = int(os.getenv("INTERNAL_ACTION_INTERVAL_SECONDS", 600)) # How often to *consider* a random action (10 mins)
INTERNAL_ACTION_PROBABILITY = float(os.getenv("INTERNAL_ACTION_PROBABILITY", 0.1)) # Chance of performing an action each interval (10%)
# --- Topic Tracking Config --- # --- Topic Tracking Config ---
TOPIC_UPDATE_INTERVAL = 300 # Update topics every 5 minutes TOPIC_UPDATE_INTERVAL = 300 # Update topics every 5 minutes
TOPIC_RELEVANCE_DECAY = 0.2 TOPIC_RELEVANCE_DECAY = 0.2

View File

@ -222,6 +222,21 @@ class MemoryManager:
logger.info("Goals table created/verified.") logger.info("Goals table created/verified.")
# --- End Goals Table --- # --- End Goals Table ---
# --- Add Internal Actions Log Table ---
await db.execute("""
CREATE TABLE IF NOT EXISTS internal_actions (
action_id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp REAL DEFAULT (unixepoch('now')),
tool_name TEXT NOT NULL,
arguments_json TEXT, -- Store arguments as JSON string
result_summary TEXT -- Store a summary of the result or error message
);
""")
await db.execute("CREATE INDEX IF NOT EXISTS idx_internal_actions_timestamp ON internal_actions (timestamp);")
await db.execute("CREATE INDEX IF NOT EXISTS idx_internal_actions_tool_name ON internal_actions (tool_name);")
logger.info("Internal Actions Log table created/verified.")
# --- End Internal Actions Log Table ---
await db.commit() await db.commit()
logger.info(f"SQLite database initialized/verified at {self.db_path}") logger.info(f"SQLite database initialized/verified at {self.db_path}")
@ -1003,3 +1018,33 @@ class MemoryManager:
except Exception as e: except Exception as e:
logger.error(f"Error deleting goal ID {goal_id}: {e}", exc_info=True) logger.error(f"Error deleting goal ID {goal_id}: {e}", exc_info=True)
return {"error": f"Database error deleting goal: {str(e)}"} return {"error": f"Database error deleting goal: {str(e)}"}
# --- Internal Action Log Methods ---
async def add_internal_action_log(self, tool_name: str, arguments: Optional[Dict[str, Any]], result_summary: str) -> Dict[str, Any]:
"""Logs the execution of an internal background action."""
if not tool_name:
return {"error": "Tool name is required for logging internal action."}
logger.info(f"Logging internal action: Tool='{tool_name}', Args={arguments}, Result='{result_summary[:100]}...'")
args_json = json.dumps(arguments) if arguments else None
# Truncate result summary if too long for DB
max_summary_len = 1000
truncated_summary = result_summary[:max_summary_len] + ('...' if len(result_summary) > max_summary_len else '')
try:
async with self.db_lock:
async with aiosqlite.connect(self.db_path) as db:
cursor = await db.execute(
"""
INSERT INTO internal_actions (tool_name, arguments_json, result_summary, timestamp)
VALUES (?, ?, ?, unixepoch('now'))
""",
(tool_name, args_json, truncated_summary)
)
await db.commit()
action_id = cursor.lastrowid
logger.info(f"Internal action logged successfully (ID: {action_id}): Tool='{tool_name}'")
return {"status": "logged", "action_id": action_id}
except Exception as e:
logger.error(f"Error logging internal action '{tool_name}': {e}", exc_info=True)
return {"error": f"Database error logging internal action: {str(e)}"}