Compare commits

...

1 Commits

Author SHA1 Message Date
Rockachopa
7cd6ecd7bd feat: add Creativity Engine for idle-cycle innovation (#330)
Some checks failed
Smoke Test / smoke (pull_request) Failing after 17s
Architecture Lint / Linter Tests (pull_request) Successful in 21s
Validate Config / YAML Lint (pull_request) Failing after 11s
Validate Config / JSON Validate (pull_request) Successful in 13s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 45s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Shell Script Lint (pull_request) Failing after 46s
Validate Config / Cron Syntax Check (pull_request) Successful in 10s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 9s
Validate Config / Playbook Schema Validation (pull_request) Successful in 20s
Architecture Lint / Lint Repository (pull_request) Failing after 18s
PR Checklist / pr-checklist (pull_request) Successful in 2m23s
Implements PAPERCLIPS-2: 'Use idle operations to generate new problems
and solutions.' Creativity generates when agents have no active work.

Deliverables:
- fleet/creativity_engine.py: detects idle agents, calls Ollama to generate
  code improvements, issue drafts, and skill updates; logs to
  ~/.local/timmy/creativity/
- config.yaml: creativity.enabled flag + model/schedule configuration
- cron/jobs.json: 'Creativity Engine' job, every 15 minutes

Detection: agents marked 'idle' or with no heartbeat for 30+ minutes.
Generation: local Ollama (hermes4:14b) with structured output format.
Logging: markdown logs with timestamp, agent name, and full output.

Closes #330
2026-04-26 11:51:39 -04:00
3 changed files with 201 additions and 1 deletions

View File

@@ -191,6 +191,19 @@ system_prompt_suffix: "You are Timmy. Your soul is defined in SOUL.md \u2014 rea
skills:
creation_nudge_interval: 15
# ── Creativity Engine ───────────────────────────────────────────────────
# Generate innovation during idle cycles. When enabled, the cron job will:
# - detect idle agents (no active work for 30+ minutes)
# - ask them to generate creative output (code improvements, issue drafts, skill updates)
# - log results to ~/.local/timmy/creativity/
creativity:
enabled: true
check_interval_minutes: 15
idle_threshold_minutes: 30
model: hermes4:14b
provider: ollama
base_url: http://localhost:11434/v1
# ── Fallback Model ────────────────────────────────────────────────────
# Automatic provider failover when primary is unavailable.
# Uncomment and configure to enable. Triggers on rate limits (429),

View File

@@ -226,7 +226,38 @@
"tmux-supervisor"
],
"skill": "tmux-supervisor"
},
{
"id": "creativity-eng-8617",
"name": "Creativity Engine",
"prompt": "Run the Creativity Engine (fleet/creativity_engine.py) to generate innovation during idle cycles.\n\nSteps:\n1. Execute: python3 ~/.timmy/timmy-config/fleet/creativity_engine.py\n2. Capture stdout/stderr\n3. If creative output was generated, note what was produced\n4. If no idle agents found or creativity disabled, that's fine \u2014 report 'no action'\n5. Always report: did the script run successfully? Was output generated?\n",
"schedule": {
"kind": "interval",
"minutes": 15,
"display": "every 15m"
},
"schedule_display": "every 15m",
"repeat": {
"times": null,
"completed": 0
},
"enabled": true,
"created_at": "2026-04-26T15:50:17.000000Z",
"next_run_at": null,
"last_run_at": null,
"last_status": null,
"last_error": null,
"deliver": "local",
"origin": null,
"state": "scheduled",
"paused_at": null,
"paused_reason": null,
"skills": [],
"skill": null,
"model": "hermes4:14b",
"provider": "ollama",
"base_url": "http://localhost:11434/v1"
}
],
"updated_at": "2026-04-13T02:00:00+00:00"
"updated_at": "2026-04-26T15:50:17.000000Z"
}

156
fleet/creativity_engine.py Executable file
View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
"""
Creativity Engine — generate innovation during idle cycles.
PAPERCLIPS-2: "Use idle operations to generate new problems and solutions."
When agents are idle (no active work), this daemon asks them to generate:
- Code improvement proposals
- New issue drafts
- Skill updates
Run via cron every 15 minutes. Config controlled via creativity: section in config.yaml.
Usage:
python3 fleet/creativity_engine.py --dry-run # Show what would run, no generation
python3 fleet/creativity_engine.py # Run full creativity cycle
"""
import os, sys, json, time
from datetime import datetime, timezone
from pathlib import Path
# ─── Paths ────────────────────────────────────────────────────────────────────
CONFIG_PATH = Path.home() / ".hermes" / "config.yaml"
AGENTS_DB = Path.home() / ".local" / "timmy" / "fleet-agents" / "agents.json"
LOG_DIR = Path.home() / ".local" / "timmy" / "creativity"
LOG_DIR.mkdir(parents=True, exist_ok=True)
# ─── Config loader ────────────────────────────────────────────────────────────
def load_config():
try:
import yaml
except ImportError:
print("[ERROR] PyYAML not installed. Install: pip install pyyaml", file=sys.stderr)
sys.exit(1)
if CONFIG_PATH.exists():
with open(CONFIG_PATH) as f:
return yaml.safe_load(f) or {}
return {}
# ─── Agent state ──────────────────────────────────────────────────────────────
def load_agents():
if AGENTS_DB.exists():
with open(AGENTS_DB) as f:
return json.load(f)
return {}
def is_agent_idle(agent, idle_threshold_minutes=30):
"""An agent is idle if state==idle OR no heartbeat for threshold_minutes."""
state = agent.get("state", "")
if state == "idle":
return True
hb = agent.get("last_heartbeat")
if hb:
try:
hb_t = datetime.fromisoformat(hb)
age = (datetime.now(timezone.utc) - hb_t).total_seconds() / 60
if age > idle_threshold_minutes:
return True
except (ValueError, TypeError):
pass
return False
# ─── Ollama client ────────────────────────────────────────────────────────────
def call_ollama(prompt: str, model: str, base_url: str) -> str:
"""Call local Ollama via OpenAI-compatible /chat/completions endpoint."""
import urllib.request
import json
url = base_url.rstrip('/') + '/chat/completions'
body = json.dumps({
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.8,
"stream": False,
}).encode()
try:
req = urllib.request.Request(url, data=body, headers={"Content-Type": "application/json"}, method="POST")
resp = urllib.request.urlopen(req, timeout=120)
data = json.loads(resp.read())
return data["choices"][0]["message"]["content"]
except Exception as e:
return f"[CREATIVITY ERROR: {e}]"
# ─── Main ─────────────────────────────────────────────────────────────────────
def main():
cfg = load_config()
creativity_cfg = cfg.get("creativity", {})
# 1. Check if enabled
if not creativity_cfg.get("enabled", False):
print("Creativity engine disabled in config.")
sys.exit(0)
# 2. Load agents
agents = load_agents()
if not agents:
print("No agents found in agents DB.")
sys.exit(0)
# 3. Find idle agents
idle_threshold = creativity_cfg.get("idle_threshold_minutes", 30)
idle_agents = [
(name, a) for name, a in agents.items()
if a.get("state") == "deployed" and is_agent_idle(a, idle_threshold)
]
if not idle_agents:
print(f"No idle agents found (threshold={idle_threshold}m).")
sys.exit(0)
print(f"Idle agents: {[n for n,_ in idle_agents]}")
# 4. Generation settings
model = creativity_cfg.get("model", "hermes4:14b")
provider = creativity_cfg.get("provider", "ollama")
base_url = creativity_cfg.get("base_url", "http://localhost:11434/v1")
# 5. For each idle agent, generate creative output
session_ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
log_file = LOG_DIR / f"creativity-{session_ts}.md"
log_lines = [
f"# Creativity Engine — {session_ts} UTC\n",
f"**Idle agents:** {len(idle_agents)}\n",
f"**Model:** {model}\n",
"---\n\n",
]
for agent_name, agent_data in idle_agents:
vps = agent_data.get("vps", "unknown")
log_lines.append(f"## Agent: {agent_name} (VPS: {vps})\n\n")
prompt = (
"You are an AI agent with idle capacity. Generate innovative ideas.\n"
"Produce THREE distinct creative outputs:\n"
"1. **Code Improvement** — 1-2 concrete suggestions for existing codebases\n"
"2. **New Issue Draft** — a well-formed issue idea for a Timmy_Foundation repo\n"
"3. **Skill Update** — a new skill or skill improvement for Hermes agents\n\n"
"Be specific, actionable, and concise. Format as:\n"
"### Code Improvement\n<description>\n\n"
"### New Issue\n**Title:** <title>\n**Body:** <description>\n\n"
"### Skill Update\n**Skill Name:** <name>\n**Summary:** <what and why>\n"
)
output = call_ollama(prompt, model, base_url)
log_lines.append(output)
log_lines.append("\n\n---\n\n")
# 6. Write log
log_file.write_text("".join(log_lines))
print(f"Creativity log written: {log_file}")
# 7. Also print a brief summary
for agent_name, _ in idle_agents:
print(f" ✓ Generated creative output for {agent_name}")
if __name__ == "__main__":
main()