fix: Timmy startup crashes and clean initialization

- Remove show_tool_calls kwarg (not in Agno 2.5.3), which crashed Agent.__init__
- Guard memory_search against top_k=None from model, return formatted string
- Skip Telegram/Discord startup silently when no token configured
- Replace placeholder MEMORY.md with proper structured hot memory document

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexander Payne
2026-02-26 09:11:48 -05:00
parent dccd13df8e
commit f95c9606f1
5 changed files with 107 additions and 13 deletions

View File

@@ -1 +1,71 @@
Good morning, I hope you had a great night.
# Timmy Hot Memory
> Working RAM — always loaded, ~300 lines max, pruned monthly
> Last updated: 2026-02-26
---
## Current Status
**Agent State:** Operational
**Mode:** Development
**Model:** llama3.2 (local via Ollama)
**Backend:** Ollama on localhost:11434
**Dashboard:** http://localhost:8000
---
## Standing Rules
1. **Sovereignty First** — No cloud AI dependencies
2. **Local-Only Inference** — Ollama on localhost
3. **Privacy by Design** — Telemetry disabled
4. **Tool Minimalism** — Use tools only when necessary
5. **Memory Discipline** — Write handoffs at session end
6. **Clean Output** — Never show JSON, tool calls, or function syntax
---
## System Architecture
**Memory Tiers:**
- Tier 1 (Hot): This file (MEMORY.md) — always in context
- Tier 2 (Vault): memory/ directory — notes, profiles, AARs
- Tier 3 (Semantic): Vector search over vault content
**Swarm Agents:** Echo (research), Forge (code), Seer (data)
**Dashboard Pages:** Briefing, Swarm, Spark, Market, Tools, Events, Ledger, Memory, Router, Upgrades, Creative
---
## Agent Roster
| Agent | Role | Status |
|-------|------|--------|
| Timmy | Core AI | Active |
| Echo | Research & Summarization | Active |
| Forge | Coding & Debugging | Active |
| Seer | Analytics & Prediction | Active |
---
## User Profile
**Name:** (not set)
**Interests:** (to be learned)
---
## Key Decisions
(none yet)
---
## Pending Actions
- [ ] Learn user's name and preferences
---
*Prune date: 2026-03-25*

View File

@@ -121,15 +121,21 @@ async def lifespan(app: FastAPI):
if spark_engine.enabled:
logger.info("Spark Intelligence active — event capture enabled")
# Auto-start Telegram bot if a token is configured
# Auto-start chat integrations (skip silently if unconfigured)
from telegram_bot.bot import telegram_bot
await telegram_bot.start()
# Auto-start Discord bot and register in platform registry
from chat_bridge.vendors.discord import discord_bot
from chat_bridge.registry import platform_registry
platform_registry.register(discord_bot)
await discord_bot.start()
if settings.telegram_token:
await telegram_bot.start()
else:
logger.debug("Telegram: no token configured, skipping")
if settings.discord_token or discord_bot.load_token():
await discord_bot.start()
else:
logger.debug("Discord: no token configured, skipping")
yield

View File

@@ -138,7 +138,6 @@ def create_timmy(
num_history_runs=20,
markdown=True,
tools=[tools] if tools else None,
show_tool_calls=False,
telemetry=settings.telemetry_enabled,
)

View File

@@ -319,6 +319,25 @@ semantic_memory = SemanticMemory()
memory_searcher = MemorySearcher()
def memory_search(query: str, top_k: int = 5) -> list[tuple[str, float]]:
"""Simple interface for memory search."""
return semantic_memory.search(query, top_k)
def memory_search(query: str, top_k: int = 5) -> str:
"""Search past conversations and notes for relevant context.
Args:
query: What to search for (e.g. "Bitcoin strategy", "server setup").
top_k: Number of results to return (default 5).
Returns:
Formatted string of relevant memory results.
"""
# Guard: model sometimes passes None for top_k
if top_k is None:
top_k = 5
results = semantic_memory.search(query, top_k)
if not results:
return "No relevant memories found."
parts = []
for content, score in results:
if score < 0.2:
continue
parts.append(f"[score {score:.2f}] {content[:300]}")
return "\n\n".join(parts) if parts else "No relevant memories found."

View File

@@ -267,8 +267,8 @@ def test_create_timmy_includes_tools_for_large_model():
assert kwargs["tools"] == [mock_toolkit]
def test_create_timmy_show_tool_calls_false():
"""show_tool_calls should always be False to prevent raw JSON in output."""
def test_create_timmy_no_show_tool_calls():
"""show_tool_calls must NOT be passed — Agno 2.5.3 doesn't support it."""
with patch("timmy.agent.Agent") as MockAgent, \
patch("timmy.agent.Ollama"), \
patch("timmy.agent.SqliteDb"):
@@ -277,4 +277,4 @@ def test_create_timmy_show_tool_calls_false():
create_timmy()
kwargs = MockAgent.call_args.kwargs
assert kwargs["show_tool_calls"] is False
assert "show_tool_calls" not in kwargs