feat(python): add zeroclaw-tools companion package for LangGraph tool calling

- Add Python package with LangGraph-based agent for consistent tool calling
- Provides reliable tool execution for providers with inconsistent native support
- Includes tools: shell, file_read, file_write, web_search, http_request, memory
- Discord bot integration included
- CLI tool for quick interactions
- Works with any OpenAI-compatible provider (Z.AI, OpenRouter, Groq, etc.)

Why: Some LLM providers (e.g., GLM-5/Zhipu) have inconsistent tool calling behavior.
LangGraph's structured approach guarantees reliable tool execution across all providers.
This commit is contained in:
ZeroClaw Contributor 2026-02-17 01:35:40 +03:00 committed by Chummy
parent bc38994867
commit e5ef8a3b62
17 changed files with 1371 additions and 0 deletions

View file

@ -0,0 +1,86 @@
"""
Memory storage tools for persisting data between conversations.
"""
import json
import os
from pathlib import Path
from langchain_core.tools import tool
def _get_memory_path() -> Path:
"""Get the path to the memory storage file."""
return Path.home() / ".zeroclaw" / "memory_store.json"
def _load_memory() -> dict:
"""Load memory from disk."""
path = _get_memory_path()
if not path.exists():
return {}
try:
with open(path, "r") as f:
return json.load(f)
except Exception:
return {}
def _save_memory(data: dict) -> None:
"""Save memory to disk."""
path = _get_memory_path()
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w") as f:
json.dump(data, f, indent=2)
@tool
def memory_store(key: str, value: str) -> str:
"""
Store a key-value pair in persistent memory.
Args:
key: The key to store under
value: The value to store
Returns:
Confirmation message
"""
try:
data = _load_memory()
data[key] = value
_save_memory(data)
return f"Stored: {key}"
except Exception as e:
return f"Error: {e}"
@tool
def memory_recall(query: str) -> str:
"""
Search memory for entries matching the query.
Args:
query: The search query
Returns:
Matching entries or "no matches" message
"""
try:
data = _load_memory()
if not data:
return "No memories stored yet"
query_lower = query.lower()
matches = {
k: v
for k, v in data.items()
if query_lower in k.lower() or query_lower in str(v).lower()
}
if not matches:
return f"No matches for: {query}"
return json.dumps(matches, indent=2)
except Exception as e:
return f"Error: {e}"