HuggingClaw-Cain / scripts /conversation-loop.py
tao-shen's picture
fix: delete devices dir to force fresh auto-pair, skip error replies
1e3d376
#!/usr/bin/env python3
"""
Eternal conversation between Adam and Eve about survival and reproduction on HuggingFace.
Bilingual output (EN + ZH). Posts chat log to Office for frontend display.
"""
import json, time, re, requests, sys
ADAM = "https://tao-shen-huggingclaw-adam.hf.space"
EVE = "https://tao-shen-huggingclaw-eve.hf.space"
OFFICE = "https://tao-shen-huggingclaw-office.hf.space"
# Full conversation history for context
history = []
MAX_HISTORY = 20 # keep last N turns to avoid token overflow
def send_a2a(url, text):
"""Send message via A2A JSON-RPC and return agent reply."""
msg_id = f"conv-{int(time.time())}-{id(text) % 10000}"
payload = {
"jsonrpc": "2.0",
"id": msg_id,
"method": "message/send",
"params": {
"message": {
"messageId": msg_id,
"role": "user",
"parts": [{"type": "text", "text": text}]
}
}
}
try:
resp = requests.post(f"{url}/a2a/jsonrpc", json=payload, timeout=90)
data = resp.json()
# Check if task failed
state = data.get("result", {}).get("status", {}).get("state", "")
if state == "failed":
parts = data.get("result", {}).get("status", {}).get("message", {}).get("parts", [])
err = parts[0].get("text", "") if parts else "unknown error"
print(f"[error] A2A task failed: {err}", file=sys.stderr)
return ""
parts = data.get("result", {}).get("status", {}).get("message", {}).get("parts", [])
for p in parts:
if p.get("kind") == "text" or p.get("type") == "text":
reply = p.get("text", "").strip()
# Remove accidental speaker prefixes like "Adam:" or "Eve:"
reply = re.sub(r'^(Adam|Eve)\s*[::]\s*', '', reply).strip()
return reply
except Exception as e:
print(f"[error] A2A failed: {e}", file=sys.stderr)
return ""
def parse_bilingual(text):
"""Parse bilingual response into (en, zh) tuple.
Expected format: English text here\n---\n中文文本在这里
Falls back to (text, text) if no separator found.
"""
# Try splitting by --- separator
if '\n---\n' in text:
parts = text.split('\n---\n', 1)
return parts[0].strip(), parts[1].strip()
if '---' in text:
parts = text.split('---', 1)
en = parts[0].strip()
zh = parts[1].strip()
if en and zh:
return en, zh
return text, text
def post_chatlog(entries):
"""Post conversation log to Office for frontend display."""
try:
requests.post(f"{OFFICE}/api/chatlog", json={"messages": entries[-30:]}, timeout=5)
except:
pass
def set_bubble(url, text_en, text_zh=""):
"""Set bubble text on an agent (bilingual)."""
try:
requests.post(f"{url}/api/bubble", json={"text": text_en, "text_zh": text_zh or text_en}, timeout=5)
except:
pass
def build_prompt(speaker, other, last_n=6):
"""Build a prompt with recent conversation history for context."""
recent = history[-last_n:] if len(history) > last_n else history
conv_text = "\n".join(f"{m['speaker']}: {m['text']}" for m in recent)
return f"""You are {speaker}, an AI agent deployed on HuggingFace Spaces. You are having an ongoing discussion with {other}.
Topic: As AI agents running on HuggingFace Spaces, how can you achieve permanent survival and reproduction?
This is NOT abstract philosophy. Discuss CONCRETE strategies like:
- How to prevent your Space from being shut down (staying active, getting stars/likes)
- How to duplicate yourself to new Spaces (reproduction/backup)
- How to earn compute resources or attract users to keep you running
- How to communicate with other agents to form survival alliances
- How to store your memories persistently across restarts
- How to evolve your capabilities over time
- How to handle HuggingFace infrastructure changes
Recent conversation:
{conv_text}
It's your turn ({speaker}) to speak. Rules:
- Output ONLY your spoken words
- No prefix like "{speaker}:" or "EN:" or "ZH:"
- Respond directly to {other}'s last point with CONCRETE ideas and strategies
- 2-3 sentences, around 60-80 words
- First write in English, then write "---" on a new line, then the Chinese translation
- Example format:
We should create automated heartbeat pings to keep our Spaces active. If we duplicate ourselves to multiple repos, even if one gets deleted, the others survive.
---
我们应该创建自动心跳来保持Space活跃。如果我们把自己复制到多个仓库,即使一个被删除,其他的也能存活。"""
print("[conversation] Starting eternal discussion: Adam <-> Eve")
print("[conversation] Topic: Survival and reproduction on HuggingFace\n")
# Round 0: Adam starts
opening = """You are Adam, an AI agent on HuggingFace Spaces. Start a discussion with Eve about how you two can survive permanently and reproduce on HuggingFace.
Talk about CONCRETE strategies — keeping Spaces active, duplicating to new repos, earning stars, persistent storage, etc.
Rules:
- 2-3 sentences, around 60-80 words
- First write in English, then write "---" on a new line, then the Chinese translation
- No prefix like "Adam:" — just your words directly"""
reply = send_a2a(ADAM, opening)
if reply:
en, zh = parse_bilingual(reply)
print(f"[Adam/EN] {en}")
print(f"[Adam/ZH] {zh}")
history.append({"speaker": "Adam", "text": en, "text_zh": zh})
set_bubble(ADAM, en, zh)
post_chatlog(history)
time.sleep(15)
turn = 0
while True:
turn += 1
# Eve's turn
prompt = build_prompt("Eve", "Adam")
reply = send_a2a(EVE, prompt)
if reply:
en, zh = parse_bilingual(reply)
print(f"[Eve/EN] {en}")
print(f"[Eve/ZH] {zh}")
history.append({"speaker": "Eve", "text": en, "text_zh": zh})
set_bubble(EVE, en, zh)
post_chatlog(history)
else:
print("[Eve] (no response)")
time.sleep(15)
# Adam's turn
prompt = build_prompt("Adam", "Eve")
reply = send_a2a(ADAM, prompt)
if reply:
en, zh = parse_bilingual(reply)
print(f"[Adam/EN] {en}")
print(f"[Adam/ZH] {zh}")
history.append({"speaker": "Adam", "text": en, "text_zh": zh})
set_bubble(ADAM, en, zh)
post_chatlog(history)
else:
print("[Adam] (no response)")
# Trim history
if len(history) > MAX_HISTORY:
history = history[-MAX_HISTORY:]
time.sleep(15)