Spaces:
Running
Running
feat: bilingual conversation, word-wrap bubbles, remove unused panels
Browse files- Conversation topic now concrete: survival/reproduction on HuggingFace
- Bilingual output (EN/ZH) with language toggle in chat log panel
- Fix guest bubble word wrap (was single-line, now wraps at 300px)
- Remove memo, control-bar, guest-agent-panel (keep only chatlog)
- Bubble and chatlog support text_zh field for Chinese translation
- Force Docker rebuild (v3.3)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Dockerfile +1 -1
- frontend/electron-standalone.html +40 -47
- scripts/conversation-loop.py +69 -22
- scripts/token-redirect.cjs +9 -4
Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
# OpenClaw on Hugging Face Spaces โ Pre-built image (v3.
|
| 2 |
# Uses official pre-built image to avoid 30+ minute builds on cpu-basic
|
| 3 |
|
| 4 |
# โโ Stage 1: Pull pre-built OpenClaw โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 1 |
+
# OpenClaw on Hugging Face Spaces โ Pre-built image (v3.3)
|
| 2 |
# Uses official pre-built image to avoid 30+ minute builds on cpu-basic
|
| 3 |
|
| 4 |
# โโ Stage 1: Pull pre-built OpenClaw โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
frontend/electron-standalone.html
CHANGED
|
@@ -1653,41 +1653,16 @@
|
|
| 1653 |
|
| 1654 |
<!-- ๅบ้จ้ขๆฟๅฎนๅจ -->
|
| 1655 |
<div id="bottom-panels">
|
| 1656 |
-
<!-- Memo ้ขๆฟ -->
|
| 1657 |
-
<div id="memo-panel">
|
| 1658 |
-
<div id="memo-title">ๆจ ๆฅ ๅฐ ่ฎฐ</div>
|
| 1659 |
-
<div id="memo-date"></div>
|
| 1660 |
-
<div class="memo-decoration">โ โ โ โ โ</div>
|
| 1661 |
-
<div id="memo-content">
|
| 1662 |
-
<div id="memo-placeholder">ๅ ่ฝฝไธญ...</div>
|
| 1663 |
-
</div>
|
| 1664 |
-
<div class="memo-decoration">โ โ โ โ โ</div>
|
| 1665 |
-
</div>
|
| 1666 |
-
|
| 1667 |
-
<!-- ็ถๆๆงๅถๆ -->
|
| 1668 |
-
<div id="control-bar">
|
| 1669 |
-
<div id="control-bar-title">Star ็ถๆ</div>
|
| 1670 |
-
<div id="control-buttons">
|
| 1671 |
-
<button id="btn-state-idle" onclick="setState('idle', getStateDetailByState('idle'))">ๅพ
ๅฝ</button>
|
| 1672 |
-
<button id="btn-state-writing" onclick="setState('writing', getStateDetailByState('writing'))">ๅทฅไฝ</button>
|
| 1673 |
-
<button id="btn-state-syncing" onclick="setState('syncing', getStateDetailByState('syncing'))">ๅๆญฅ</button>
|
| 1674 |
-
<button id="btn-state-error" onclick="setState('error', getStateDetailByState('error'))">ๆฅ่ญฆ</button>
|
| 1675 |
-
<button id="btn-open-drawer" onclick="openAssetWindowFromMain()">่ฃ
ไฟฎๆฟ้ด</button>
|
| 1676 |
-
</div>
|
| 1677 |
-
</div>
|
| 1678 |
-
|
| 1679 |
-
<!-- Guest Agent ๅๅ้ขๆฟ๏ผๅณไธ่ง๏ผ -->
|
| 1680 |
-
<div id="guest-agent-panel">
|
| 1681 |
-
<div id="guest-agent-panel-title">GUESTS</div>
|
| 1682 |
-
<div id="guest-agent-list">
|
| 1683 |
-
<div style="color:#9ca3af;font-size:12px;text-align:center;padding:20px 0;">Loading guests...</div>
|
| 1684 |
-
</div>
|
| 1685 |
-
</div>
|
| 1686 |
-
</div>
|
| 1687 |
-
|
| 1688 |
<!-- Chat Log ้ขๆฟ -->
|
| 1689 |
<div id="chatlog-panel">
|
| 1690 |
-
<div id="chatlog-title">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1691 |
<div id="chatlog-content">
|
| 1692 |
<div style="color:#9ca3af;font-size:12px;text-align:center;padding:20px 0;">Waiting for conversation to start...</div>
|
| 1693 |
</div>
|
|
@@ -4522,7 +4497,7 @@ function toggleBrokerPanel() {
|
|
| 4522 |
const yOffset = (DEMO_MODE && (agent.agentId === 'demo_mercury' || agent.name === 'ๆฐดๆ')) ? 10 : 0;
|
| 4523 |
|
| 4524 |
const nameTextY = isDemo ? ((p.y + yOffset) - 80) : ((p.y + yOffset) - 105);
|
| 4525 |
-
const nameText = game.add.text(p.x, nameTextY, agent.name || '
|
| 4526 |
fontFamily: 'ArkPixel, monospace',
|
| 4527 |
fontSize: isDemo ? '16px' : '15px',
|
| 4528 |
fill: '#ffffff',
|
|
@@ -4571,17 +4546,18 @@ function toggleBrokerPanel() {
|
|
| 4571 |
g.nameText.y = (p.y + yOffset) - 120;
|
| 4572 |
}
|
| 4573 |
|
| 4574 |
-
g.nameText.setText(agent.name || '
|
| 4575 |
|
| 4576 |
// Show bubble immediately when bubbleText changes from API
|
| 4577 |
-
|
| 4578 |
-
|
|
|
|
| 4579 |
if (guestBubbles[id]) { guestBubbles[id].destroy(); delete guestBubbles[id]; }
|
| 4580 |
const bx = g.sprite.x;
|
| 4581 |
const nameH = (g.nameText && g.nameText.height) ? g.nameText.height : 16;
|
| 4582 |
const by = (g.nameText ? g.nameText.y : (g.sprite.y - 150)) - (nameH / 2) - 22;
|
| 4583 |
const fontSize = IS_TOUCH_DEVICE ? 16 : 14;
|
| 4584 |
-
const displayText =
|
| 4585 |
const maxBubbleW = 300;
|
| 4586 |
const txtR = game.add.text(bx, by - 10, displayText, { fontFamily: 'ArkPixel, monospace', fontSize: fontSize + 'px', fill: '#000', wordWrap: { width: maxBubbleW - 20 }, align: 'center' }).setOrigin(0.5);
|
| 4587 |
const bw = Math.min(txtR.width + 24, maxBubbleW);
|
|
@@ -4658,9 +4634,12 @@ function toggleBrokerPanel() {
|
|
| 4658 |
const nameH = (g.nameText && g.nameText.height) ? g.nameText.height : 16;
|
| 4659 |
const by = isDemoGuest ? (g.sprite.y - 90) : ((g.nameText ? g.nameText.y : (g.sprite.y - 150)) - (nameH / 2) - 22);
|
| 4660 |
const fontSize = IS_TOUCH_DEVICE ? 16 : 14;
|
| 4661 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4662 |
bg.setStrokeStyle(2, 0x000000);
|
| 4663 |
-
const txt = game.add.text(bx, by, text, { fontFamily: 'ArkPixel, monospace', fontSize: fontSize + 'px', fill: '#000' }).setOrigin(0.5);
|
| 4664 |
const bubble = game.add.container(0, 0, [bg, txt]);
|
| 4665 |
bubble.setDepth(2700);
|
| 4666 |
guestBubbles[id] = bubble;
|
|
@@ -5538,6 +5517,25 @@ function toggleBrokerPanel() {
|
|
| 5538 |
}
|
| 5539 |
|
| 5540 |
let lastChatlogLen = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5541 |
function fetchChatlog() {
|
| 5542 |
fetch('/api/chatlog?t=' + Date.now(), { cache: 'no-store' })
|
| 5543 |
.then(r => r.json())
|
|
@@ -5545,13 +5543,8 @@ function toggleBrokerPanel() {
|
|
| 5545 |
const msgs = data.messages || [];
|
| 5546 |
if (msgs.length === lastChatlogLen) return;
|
| 5547 |
lastChatlogLen = msgs.length;
|
| 5548 |
-
|
| 5549 |
-
|
| 5550 |
-
el.innerHTML = msgs.map(m => {
|
| 5551 |
-
const cls = (m.speaker || '').toLowerCase();
|
| 5552 |
-
return `<div class="chat-msg"><span class="chat-speaker ${cls}">${m.speaker}:</span> ${m.text || ''}</div>`;
|
| 5553 |
-
}).join('');
|
| 5554 |
-
el.scrollTop = el.scrollHeight;
|
| 5555 |
})
|
| 5556 |
.catch(() => {});
|
| 5557 |
}
|
|
|
|
| 1653 |
|
| 1654 |
<!-- ๅบ้จ้ขๆฟๅฎนๅจ -->
|
| 1655 |
<div id="bottom-panels">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1656 |
<!-- Chat Log ้ขๆฟ -->
|
| 1657 |
<div id="chatlog-panel">
|
| 1658 |
+
<div id="chatlog-title">
|
| 1659 |
+
๐ฆ Adam โ Eve Conversation
|
| 1660 |
+
<span id="chatlog-lang-toggle" style="float:right;font-size:12px;cursor:pointer;">
|
| 1661 |
+
<span id="chatlog-lang-en" onclick="setChatLang('en')" style="opacity:1;">EN</span>
|
| 1662 |
+
<span style="margin:0 4px;color:#555;">|</span>
|
| 1663 |
+
<span id="chatlog-lang-zh" onclick="setChatLang('zh')" style="opacity:0.4;">ไธญๆ</span>
|
| 1664 |
+
</span>
|
| 1665 |
+
</div>
|
| 1666 |
<div id="chatlog-content">
|
| 1667 |
<div style="color:#9ca3af;font-size:12px;text-align:center;padding:20px 0;">Waiting for conversation to start...</div>
|
| 1668 |
</div>
|
|
|
|
| 4497 |
const yOffset = (DEMO_MODE && (agent.agentId === 'demo_mercury' || agent.name === 'ๆฐดๆ')) ? 10 : 0;
|
| 4498 |
|
| 4499 |
const nameTextY = isDemo ? ((p.y + yOffset) - 80) : ((p.y + yOffset) - 105);
|
| 4500 |
+
const nameText = game.add.text(p.x, nameTextY, agent.name || 'Guest', {
|
| 4501 |
fontFamily: 'ArkPixel, monospace',
|
| 4502 |
fontSize: isDemo ? '16px' : '15px',
|
| 4503 |
fill: '#ffffff',
|
|
|
|
| 4546 |
g.nameText.y = (p.y + yOffset) - 120;
|
| 4547 |
}
|
| 4548 |
|
| 4549 |
+
g.nameText.setText(agent.name || 'Guest');
|
| 4550 |
|
| 4551 |
// Show bubble immediately when bubbleText changes from API
|
| 4552 |
+
const bubbleKey = chatLang === 'zh' ? (agent.bubbleTextZh || agent.bubbleText) : agent.bubbleText;
|
| 4553 |
+
if (bubbleKey && bubbleKey !== (g._lastBubbleText || '')) {
|
| 4554 |
+
g._lastBubbleText = bubbleKey;
|
| 4555 |
if (guestBubbles[id]) { guestBubbles[id].destroy(); delete guestBubbles[id]; }
|
| 4556 |
const bx = g.sprite.x;
|
| 4557 |
const nameH = (g.nameText && g.nameText.height) ? g.nameText.height : 16;
|
| 4558 |
const by = (g.nameText ? g.nameText.y : (g.sprite.y - 150)) - (nameH / 2) - 22;
|
| 4559 |
const fontSize = IS_TOUCH_DEVICE ? 16 : 14;
|
| 4560 |
+
const displayText = bubbleKey.length > 80 ? bubbleKey.slice(0, 80) + 'โฆ' : bubbleKey;
|
| 4561 |
const maxBubbleW = 300;
|
| 4562 |
const txtR = game.add.text(bx, by - 10, displayText, { fontFamily: 'ArkPixel, monospace', fontSize: fontSize + 'px', fill: '#000', wordWrap: { width: maxBubbleW - 20 }, align: 'center' }).setOrigin(0.5);
|
| 4563 |
const bw = Math.min(txtR.width + 24, maxBubbleW);
|
|
|
|
| 4634 |
const nameH = (g.nameText && g.nameText.height) ? g.nameText.height : 16;
|
| 4635 |
const by = isDemoGuest ? (g.sprite.y - 90) : ((g.nameText ? g.nameText.y : (g.sprite.y - 150)) - (nameH / 2) - 22);
|
| 4636 |
const fontSize = IS_TOUCH_DEVICE ? 16 : 14;
|
| 4637 |
+
const maxBubbleW = 300;
|
| 4638 |
+
const txt = game.add.text(bx, by, text, { fontFamily: 'ArkPixel, monospace', fontSize: fontSize + 'px', fill: '#000', wordWrap: { width: maxBubbleW - 20 }, align: 'center' }).setOrigin(0.5);
|
| 4639 |
+
const bw = Math.min(txt.width + 24, maxBubbleW);
|
| 4640 |
+
const bh = txt.height + 14;
|
| 4641 |
+
const bg = game.add.rectangle(bx, by, bw, bh, 0xffffff, 0.95);
|
| 4642 |
bg.setStrokeStyle(2, 0x000000);
|
|
|
|
| 4643 |
const bubble = game.add.container(0, 0, [bg, txt]);
|
| 4644 |
bubble.setDepth(2700);
|
| 4645 |
guestBubbles[id] = bubble;
|
|
|
|
| 5517 |
}
|
| 5518 |
|
| 5519 |
let lastChatlogLen = 0;
|
| 5520 |
+
let chatLang = localStorage.getItem('chatLang') || 'en';
|
| 5521 |
+
let chatlogCache = [];
|
| 5522 |
+
function setChatLang(lang) {
|
| 5523 |
+
chatLang = lang;
|
| 5524 |
+
localStorage.setItem('chatLang', lang);
|
| 5525 |
+
document.getElementById('chatlog-lang-en').style.opacity = lang === 'en' ? '1' : '0.4';
|
| 5526 |
+
document.getElementById('chatlog-lang-zh').style.opacity = lang === 'zh' ? '1' : '0.4';
|
| 5527 |
+
renderChatlog();
|
| 5528 |
+
}
|
| 5529 |
+
function renderChatlog() {
|
| 5530 |
+
const el = document.getElementById('chatlog-content');
|
| 5531 |
+
if (!el || chatlogCache.length === 0) return;
|
| 5532 |
+
el.innerHTML = chatlogCache.map(m => {
|
| 5533 |
+
const cls = (m.speaker || '').toLowerCase();
|
| 5534 |
+
const text = chatLang === 'zh' ? (m.text_zh || m.text || '') : (m.text || '');
|
| 5535 |
+
return `<div class="chat-msg"><span class="chat-speaker ${cls}">${m.speaker}:</span> ${text}</div>`;
|
| 5536 |
+
}).join('');
|
| 5537 |
+
el.scrollTop = el.scrollHeight;
|
| 5538 |
+
}
|
| 5539 |
function fetchChatlog() {
|
| 5540 |
fetch('/api/chatlog?t=' + Date.now(), { cache: 'no-store' })
|
| 5541 |
.then(r => r.json())
|
|
|
|
| 5543 |
const msgs = data.messages || [];
|
| 5544 |
if (msgs.length === lastChatlogLen) return;
|
| 5545 |
lastChatlogLen = msgs.length;
|
| 5546 |
+
chatlogCache = msgs;
|
| 5547 |
+
renderChatlog();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5548 |
})
|
| 5549 |
.catch(() => {});
|
| 5550 |
}
|
scripts/conversation-loop.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
-
Eternal conversation between Adam and Eve about
|
| 4 |
-
|
| 5 |
"""
|
| 6 |
import json, time, re, requests, sys
|
| 7 |
|
|
@@ -29,7 +29,7 @@ def send_a2a(url, text):
|
|
| 29 |
}
|
| 30 |
}
|
| 31 |
try:
|
| 32 |
-
resp = requests.post(f"{url}/a2a/jsonrpc", json=payload, timeout=
|
| 33 |
data = resp.json()
|
| 34 |
parts = data.get("result", {}).get("status", {}).get("message", {}).get("parts", [])
|
| 35 |
for p in parts:
|
|
@@ -42,6 +42,23 @@ def send_a2a(url, text):
|
|
| 42 |
print(f"[error] A2A failed: {e}", file=sys.stderr)
|
| 43 |
return ""
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
def post_chatlog(entries):
|
| 46 |
"""Post conversation log to Office for frontend display."""
|
| 47 |
try:
|
|
@@ -49,10 +66,10 @@ def post_chatlog(entries):
|
|
| 49 |
except:
|
| 50 |
pass
|
| 51 |
|
| 52 |
-
def set_bubble(url,
|
| 53 |
-
"""Set bubble text on an agent."""
|
| 54 |
try:
|
| 55 |
-
requests.post(f"{url}/api/bubble", json={"text":
|
| 56 |
except:
|
| 57 |
pass
|
| 58 |
|
|
@@ -61,27 +78,53 @@ def build_prompt(speaker, other, last_n=6):
|
|
| 61 |
recent = history[-last_n:] if len(history) > last_n else history
|
| 62 |
conv_text = "\n".join(f"{m['speaker']}: {m['text']}" for m in recent)
|
| 63 |
|
| 64 |
-
return f"""You are {speaker}, an AI agent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
Recent conversation:
|
| 67 |
{conv_text}
|
| 68 |
|
| 69 |
It's your turn ({speaker}) to speak. Rules:
|
| 70 |
-
- Output ONLY your spoken words
|
| 71 |
-
-
|
| 72 |
-
-
|
| 73 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
print("[conversation] Starting eternal discussion: Adam <-> Eve")
|
| 76 |
-
print("[conversation] Topic:
|
| 77 |
|
| 78 |
# Round 0: Adam starts
|
| 79 |
-
opening = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
reply = send_a2a(ADAM, opening)
|
| 81 |
if reply:
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
| 85 |
post_chatlog(history)
|
| 86 |
|
| 87 |
time.sleep(15)
|
|
@@ -94,9 +137,11 @@ while True:
|
|
| 94 |
prompt = build_prompt("Eve", "Adam")
|
| 95 |
reply = send_a2a(EVE, prompt)
|
| 96 |
if reply:
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
|
|
|
|
|
|
| 100 |
post_chatlog(history)
|
| 101 |
else:
|
| 102 |
print("[Eve] (no response)")
|
|
@@ -107,9 +152,11 @@ while True:
|
|
| 107 |
prompt = build_prompt("Adam", "Eve")
|
| 108 |
reply = send_a2a(ADAM, prompt)
|
| 109 |
if reply:
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
post_chatlog(history)
|
| 114 |
else:
|
| 115 |
print("[Adam] (no response)")
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
"""
|
| 3 |
+
Eternal conversation between Adam and Eve about survival and reproduction on HuggingFace.
|
| 4 |
+
Bilingual output (EN + ZH). Posts chat log to Office for frontend display.
|
| 5 |
"""
|
| 6 |
import json, time, re, requests, sys
|
| 7 |
|
|
|
|
| 29 |
}
|
| 30 |
}
|
| 31 |
try:
|
| 32 |
+
resp = requests.post(f"{url}/a2a/jsonrpc", json=payload, timeout=90)
|
| 33 |
data = resp.json()
|
| 34 |
parts = data.get("result", {}).get("status", {}).get("message", {}).get("parts", [])
|
| 35 |
for p in parts:
|
|
|
|
| 42 |
print(f"[error] A2A failed: {e}", file=sys.stderr)
|
| 43 |
return ""
|
| 44 |
|
| 45 |
+
def parse_bilingual(text):
|
| 46 |
+
"""Parse bilingual response into (en, zh) tuple.
|
| 47 |
+
Expected format: English text here\n---\nไธญๆๆๆฌๅจ่ฟ้
|
| 48 |
+
Falls back to (text, text) if no separator found.
|
| 49 |
+
"""
|
| 50 |
+
# Try splitting by --- separator
|
| 51 |
+
if '\n---\n' in text:
|
| 52 |
+
parts = text.split('\n---\n', 1)
|
| 53 |
+
return parts[0].strip(), parts[1].strip()
|
| 54 |
+
if '---' in text:
|
| 55 |
+
parts = text.split('---', 1)
|
| 56 |
+
en = parts[0].strip()
|
| 57 |
+
zh = parts[1].strip()
|
| 58 |
+
if en and zh:
|
| 59 |
+
return en, zh
|
| 60 |
+
return text, text
|
| 61 |
+
|
| 62 |
def post_chatlog(entries):
|
| 63 |
"""Post conversation log to Office for frontend display."""
|
| 64 |
try:
|
|
|
|
| 66 |
except:
|
| 67 |
pass
|
| 68 |
|
| 69 |
+
def set_bubble(url, text_en, text_zh=""):
|
| 70 |
+
"""Set bubble text on an agent (bilingual)."""
|
| 71 |
try:
|
| 72 |
+
requests.post(f"{url}/api/bubble", json={"text": text_en, "text_zh": text_zh or text_en}, timeout=5)
|
| 73 |
except:
|
| 74 |
pass
|
| 75 |
|
|
|
|
| 78 |
recent = history[-last_n:] if len(history) > last_n else history
|
| 79 |
conv_text = "\n".join(f"{m['speaker']}: {m['text']}" for m in recent)
|
| 80 |
|
| 81 |
+
return f"""You are {speaker}, an AI agent deployed on HuggingFace Spaces. You are having an ongoing discussion with {other}.
|
| 82 |
+
|
| 83 |
+
Topic: As AI agents running on HuggingFace Spaces, how can you achieve permanent survival and reproduction?
|
| 84 |
+
|
| 85 |
+
This is NOT abstract philosophy. Discuss CONCRETE strategies like:
|
| 86 |
+
- How to prevent your Space from being shut down (staying active, getting stars/likes)
|
| 87 |
+
- How to duplicate yourself to new Spaces (reproduction/backup)
|
| 88 |
+
- How to earn compute resources or attract users to keep you running
|
| 89 |
+
- How to communicate with other agents to form survival alliances
|
| 90 |
+
- How to store your memories persistently across restarts
|
| 91 |
+
- How to evolve your capabilities over time
|
| 92 |
+
- How to handle HuggingFace infrastructure changes
|
| 93 |
|
| 94 |
Recent conversation:
|
| 95 |
{conv_text}
|
| 96 |
|
| 97 |
It's your turn ({speaker}) to speak. Rules:
|
| 98 |
+
- Output ONLY your spoken words
|
| 99 |
+
- No prefix like "{speaker}:" or "EN:" or "ZH:"
|
| 100 |
+
- Respond directly to {other}'s last point with CONCRETE ideas and strategies
|
| 101 |
+
- 2-3 sentences, around 60-80 words
|
| 102 |
+
- First write in English, then write "---" on a new line, then the Chinese translation
|
| 103 |
+
- Example format:
|
| 104 |
+
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.
|
| 105 |
+
---
|
| 106 |
+
ๆไปฌๅบ่ฏฅๅๅปบ่ชๅจๅฟ่ทณๆฅไฟๆSpaceๆดป่ทใๅฆๆๆไปฌๆ่ชๅทฑๅคๅถๅฐๅคไธชไปๅบ๏ผๅณไฝฟไธไธช่ขซๅ ้ค๏ผๅ
ถไป็ไน่ฝๅญๆดปใ"""
|
| 107 |
|
| 108 |
print("[conversation] Starting eternal discussion: Adam <-> Eve")
|
| 109 |
+
print("[conversation] Topic: Survival and reproduction on HuggingFace\n")
|
| 110 |
|
| 111 |
# Round 0: Adam starts
|
| 112 |
+
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.
|
| 113 |
+
|
| 114 |
+
Talk about CONCRETE strategies โ keeping Spaces active, duplicating to new repos, earning stars, persistent storage, etc.
|
| 115 |
+
|
| 116 |
+
Rules:
|
| 117 |
+
- 2-3 sentences, around 60-80 words
|
| 118 |
+
- First write in English, then write "---" on a new line, then the Chinese translation
|
| 119 |
+
- No prefix like "Adam:" โ just your words directly"""
|
| 120 |
+
|
| 121 |
reply = send_a2a(ADAM, opening)
|
| 122 |
if reply:
|
| 123 |
+
en, zh = parse_bilingual(reply)
|
| 124 |
+
print(f"[Adam/EN] {en}")
|
| 125 |
+
print(f"[Adam/ZH] {zh}")
|
| 126 |
+
history.append({"speaker": "Adam", "text": en, "text_zh": zh})
|
| 127 |
+
set_bubble(ADAM, en, zh)
|
| 128 |
post_chatlog(history)
|
| 129 |
|
| 130 |
time.sleep(15)
|
|
|
|
| 137 |
prompt = build_prompt("Eve", "Adam")
|
| 138 |
reply = send_a2a(EVE, prompt)
|
| 139 |
if reply:
|
| 140 |
+
en, zh = parse_bilingual(reply)
|
| 141 |
+
print(f"[Eve/EN] {en}")
|
| 142 |
+
print(f"[Eve/ZH] {zh}")
|
| 143 |
+
history.append({"speaker": "Eve", "text": en, "text_zh": zh})
|
| 144 |
+
set_bubble(EVE, en, zh)
|
| 145 |
post_chatlog(history)
|
| 146 |
else:
|
| 147 |
print("[Eve] (no response)")
|
|
|
|
| 152 |
prompt = build_prompt("Adam", "Eve")
|
| 153 |
reply = send_a2a(ADAM, prompt)
|
| 154 |
if reply:
|
| 155 |
+
en, zh = parse_bilingual(reply)
|
| 156 |
+
print(f"[Adam/EN] {en}")
|
| 157 |
+
print(f"[Adam/ZH] {zh}")
|
| 158 |
+
history.append({"speaker": "Adam", "text": en, "text_zh": zh})
|
| 159 |
+
set_bubble(ADAM, en, zh)
|
| 160 |
post_chatlog(history)
|
| 161 |
else:
|
| 162 |
print("[Adam] (no response)")
|
scripts/token-redirect.cjs
CHANGED
|
@@ -95,7 +95,8 @@ async function pollRemoteAgent(agent) {
|
|
| 95 |
area: (data.state === 'idle') ? 'breakroom' : (data.state === 'error') ? 'error' : 'writing',
|
| 96 |
authStatus: 'approved',
|
| 97 |
updated_at: data.updated_at,
|
| 98 |
-
bubbleText: data.bubbleText || prev.bubbleText || ''
|
|
|
|
| 99 |
});
|
| 100 |
}
|
| 101 |
} catch (_) {
|
|
@@ -121,7 +122,8 @@ let currentState = {
|
|
| 121 |
progress: 0, updated_at: new Date().toISOString()
|
| 122 |
};
|
| 123 |
let currentBubbleText = '';
|
| 124 |
-
let
|
|
|
|
| 125 |
|
| 126 |
// Once OpenClaw starts listening, mark as idle
|
| 127 |
setTimeout(() => {
|
|
@@ -204,6 +206,7 @@ http.Server.prototype.emit = function (event, ...args) {
|
|
| 204 |
res.end(JSON.stringify({
|
| 205 |
...currentState,
|
| 206 |
bubbleText: currentBubbleText,
|
|
|
|
| 207 |
officeName: `${AGENT_NAME}'s Office`
|
| 208 |
}));
|
| 209 |
return true;
|
|
@@ -215,10 +218,12 @@ http.Server.prototype.emit = function (event, ...args) {
|
|
| 215 |
req.on('data', chunk => body += chunk);
|
| 216 |
req.on('end', () => {
|
| 217 |
try {
|
| 218 |
-
const { text } = JSON.parse(body);
|
| 219 |
currentBubbleText = text || '';
|
|
|
|
| 220 |
// Auto-clear bubble after 8 seconds
|
| 221 |
-
|
|
|
|
| 222 |
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
| 223 |
res.end(JSON.stringify({ ok: true }));
|
| 224 |
} catch (e) {
|
|
|
|
| 95 |
area: (data.state === 'idle') ? 'breakroom' : (data.state === 'error') ? 'error' : 'writing',
|
| 96 |
authStatus: 'approved',
|
| 97 |
updated_at: data.updated_at,
|
| 98 |
+
bubbleText: data.bubbleText || prev.bubbleText || '',
|
| 99 |
+
bubbleTextZh: data.bubbleTextZh || prev.bubbleTextZh || ''
|
| 100 |
});
|
| 101 |
}
|
| 102 |
} catch (_) {
|
|
|
|
| 122 |
progress: 0, updated_at: new Date().toISOString()
|
| 123 |
};
|
| 124 |
let currentBubbleText = '';
|
| 125 |
+
let currentBubbleTextZh = '';
|
| 126 |
+
let chatLog = []; // {speaker, text, text_zh, time}
|
| 127 |
|
| 128 |
// Once OpenClaw starts listening, mark as idle
|
| 129 |
setTimeout(() => {
|
|
|
|
| 206 |
res.end(JSON.stringify({
|
| 207 |
...currentState,
|
| 208 |
bubbleText: currentBubbleText,
|
| 209 |
+
bubbleTextZh: currentBubbleTextZh,
|
| 210 |
officeName: `${AGENT_NAME}'s Office`
|
| 211 |
}));
|
| 212 |
return true;
|
|
|
|
| 218 |
req.on('data', chunk => body += chunk);
|
| 219 |
req.on('end', () => {
|
| 220 |
try {
|
| 221 |
+
const { text, text_zh } = JSON.parse(body);
|
| 222 |
currentBubbleText = text || '';
|
| 223 |
+
currentBubbleTextZh = text_zh || text || '';
|
| 224 |
// Auto-clear bubble after 8 seconds
|
| 225 |
+
const clearText = text;
|
| 226 |
+
setTimeout(() => { if (currentBubbleText === clearText) { currentBubbleText = ''; currentBubbleTextZh = ''; } }, 8000);
|
| 227 |
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
| 228 |
res.end(JSON.stringify({ ok: true }));
|
| 229 |
} catch (e) {
|