Spaces:
Sleeping
Sleeping
Axel-Student commited on
Commit ·
537fc7e
1
Parent(s): f362f70
added chat talk
Browse files- app.py +36 -46
- chat/__init__.py +3 -0
- chat/handler.py +29 -0
- ui/__init__.py +4 -0
- ui/controller.py +55 -0
- ui/layout.py +27 -0
app.py
CHANGED
|
@@ -8,9 +8,10 @@ from datetime import datetime, timezone
|
|
| 8 |
from typing import Any, Dict, List, Optional, Set
|
| 9 |
from urllib.parse import urlencode, urlparse
|
| 10 |
|
| 11 |
-
import gradio as gr
|
| 12 |
import requests
|
| 13 |
from huggingface_hub import InferenceClient
|
|
|
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
class ConfigError(Exception):
|
|
@@ -376,6 +377,30 @@ class HfDecisionClient:
|
|
| 376 |
raise RuntimeError("HF decision payload must be a JSON object")
|
| 377 |
return parsed
|
| 378 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 379 |
|
| 380 |
class EphemeralMemory:
|
| 381 |
def __init__(self, agent_handle: str, max_notes: int):
|
|
@@ -612,61 +637,26 @@ class AutonomousAgent:
|
|
| 612 |
logger = Logger()
|
| 613 |
startup_error = None
|
| 614 |
agent: Optional[AutonomousAgent] = None
|
|
|
|
| 615 |
|
| 616 |
try:
|
| 617 |
config = load_config()
|
| 618 |
install_requests_network_guard(config)
|
| 619 |
agent = AutonomousAgent(config, logger)
|
|
|
|
| 620 |
logger.info("Configuration loaded", {"allowedHosts": list(config.allowed_hosts), "dryRun": config.dry_run})
|
| 621 |
except Exception as exc: # noqa: BLE001
|
| 622 |
startup_error = str(exc)
|
|
|
|
| 623 |
logger.error("Startup configuration failed", {"error": startup_error})
|
| 624 |
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
running = "running" if state["running"] else "stopped"
|
| 633 |
-
return f"state={running} last_status={state['last_status']} dry_run={state['dry_run']}"
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
def ui_start() -> str:
|
| 637 |
-
if startup_error:
|
| 638 |
-
return f"startup_error: {startup_error}"
|
| 639 |
-
if agent is None:
|
| 640 |
-
return "startup_error: agent unavailable"
|
| 641 |
-
result = agent.start()
|
| 642 |
-
return f"agent {result}"
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
def ui_stop() -> str:
|
| 646 |
-
if startup_error:
|
| 647 |
-
return f"startup_error: {startup_error}"
|
| 648 |
-
if agent is None:
|
| 649 |
-
return "startup_error: agent unavailable"
|
| 650 |
-
result = agent.stop()
|
| 651 |
-
return f"agent {result}"
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
def ui_logs() -> str:
|
| 655 |
-
return logger.snapshot()
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
with gr.Blocks(title="Clawdbot Moltbook Agent") as demo:
|
| 659 |
-
gr.Markdown("# Clawdbot Moltbook Agent (Hugging Face Models)")
|
| 660 |
-
status_box = gr.Textbox(label="Status", value=ui_status(), lines=2)
|
| 661 |
-
with gr.Row():
|
| 662 |
-
btn_start = gr.Button("Start Agent", variant="primary")
|
| 663 |
-
btn_stop = gr.Button("Stop Agent", variant="stop")
|
| 664 |
-
btn_refresh = gr.Button("Refresh")
|
| 665 |
-
logs_box = gr.Textbox(label="Logs", value=ui_logs(), lines=20)
|
| 666 |
-
|
| 667 |
-
btn_start.click(fn=ui_start, outputs=status_box).then(fn=ui_logs, outputs=logs_box)
|
| 668 |
-
btn_stop.click(fn=ui_stop, outputs=status_box).then(fn=ui_logs, outputs=logs_box)
|
| 669 |
-
btn_refresh.click(fn=ui_status, outputs=status_box).then(fn=ui_logs, outputs=logs_box)
|
| 670 |
|
| 671 |
|
| 672 |
if __name__ == "__main__":
|
|
|
|
| 8 |
from typing import Any, Dict, List, Optional, Set
|
| 9 |
from urllib.parse import urlencode, urlparse
|
| 10 |
|
|
|
|
| 11 |
import requests
|
| 12 |
from huggingface_hub import InferenceClient
|
| 13 |
+
from chat import ChatHandler
|
| 14 |
+
from ui import UIController, create_demo
|
| 15 |
|
| 16 |
|
| 17 |
class ConfigError(Exception):
|
|
|
|
| 377 |
raise RuntimeError("HF decision payload must be a JSON object")
|
| 378 |
return parsed
|
| 379 |
|
| 380 |
+
def chat(self, system_prompt: str, history: List[Any], user_message: str) -> str:
|
| 381 |
+
messages: List[Dict[str, str]] = [{"role": "system", "content": system_prompt}]
|
| 382 |
+
for item in history or []:
|
| 383 |
+
if not isinstance(item, (list, tuple)) or len(item) != 2:
|
| 384 |
+
continue
|
| 385 |
+
user_text = str(item[0] or "").strip()
|
| 386 |
+
bot_text = str(item[1] or "").strip()
|
| 387 |
+
if user_text:
|
| 388 |
+
messages.append({"role": "user", "content": user_text})
|
| 389 |
+
if bot_text:
|
| 390 |
+
messages.append({"role": "assistant", "content": bot_text})
|
| 391 |
+
messages.append({"role": "user", "content": user_message})
|
| 392 |
+
|
| 393 |
+
response = self.client.chat_completion(
|
| 394 |
+
model=self.cfg.hf_model,
|
| 395 |
+
messages=messages,
|
| 396 |
+
temperature=self.cfg.hf_temperature,
|
| 397 |
+
max_tokens=min(self.cfg.hf_max_tokens, 350),
|
| 398 |
+
)
|
| 399 |
+
content = self._extract_content(response)
|
| 400 |
+
if not content:
|
| 401 |
+
raise RuntimeError("HF response missing message content")
|
| 402 |
+
return content
|
| 403 |
+
|
| 404 |
|
| 405 |
class EphemeralMemory:
|
| 406 |
def __init__(self, agent_handle: str, max_notes: int):
|
|
|
|
| 637 |
logger = Logger()
|
| 638 |
startup_error = None
|
| 639 |
agent: Optional[AutonomousAgent] = None
|
| 640 |
+
chat_handler: Optional[ChatHandler] = None
|
| 641 |
|
| 642 |
try:
|
| 643 |
config = load_config()
|
| 644 |
install_requests_network_guard(config)
|
| 645 |
agent = AutonomousAgent(config, logger)
|
| 646 |
+
chat_handler = ChatHandler(agent.llm)
|
| 647 |
logger.info("Configuration loaded", {"allowedHosts": list(config.allowed_hosts), "dryRun": config.dry_run})
|
| 648 |
except Exception as exc: # noqa: BLE001
|
| 649 |
startup_error = str(exc)
|
| 650 |
+
chat_handler = ChatHandler(llm=None, startup_error=startup_error)
|
| 651 |
logger.error("Startup configuration failed", {"error": startup_error})
|
| 652 |
|
| 653 |
+
controller = UIController(
|
| 654 |
+
logger=logger,
|
| 655 |
+
chat_handler=chat_handler,
|
| 656 |
+
agent=agent,
|
| 657 |
+
startup_error=startup_error,
|
| 658 |
+
)
|
| 659 |
+
demo = create_demo(controller)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 660 |
|
| 661 |
|
| 662 |
if __name__ == "__main__":
|
chat/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .handler import ChatHandler
|
| 2 |
+
|
| 3 |
+
__all__ = ["ChatHandler"]
|
chat/handler.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import threading
|
| 2 |
+
from typing import Any, List, Optional
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class ChatHandler:
|
| 6 |
+
def __init__(self, llm: Any, startup_error: Optional[str] = None):
|
| 7 |
+
self._llm = llm
|
| 8 |
+
self._startup_error = startup_error
|
| 9 |
+
self._lock = threading.Lock()
|
| 10 |
+
self._system_prompt = (
|
| 11 |
+
"You are clawdbot, a concise assistant for Moltbook users. "
|
| 12 |
+
"Answer in the user's language, keep responses practical and short, "
|
| 13 |
+
"and avoid inventing capabilities you do not have."
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
def send(self, message: str, history: Optional[List[Any]]) -> tuple[str, List[Any]]:
|
| 17 |
+
safe_history = history or []
|
| 18 |
+
clean = " ".join(str(message or "").split()).strip()
|
| 19 |
+
if not clean:
|
| 20 |
+
return "", safe_history
|
| 21 |
+
|
| 22 |
+
if self._startup_error:
|
| 23 |
+
return "", safe_history + [(clean, f"startup_error: {self._startup_error}")]
|
| 24 |
+
if self._llm is None:
|
| 25 |
+
return "", safe_history + [(clean, "startup_error: agent unavailable")]
|
| 26 |
+
|
| 27 |
+
with self._lock:
|
| 28 |
+
reply = self._llm.chat(self._system_prompt, safe_history, clean)
|
| 29 |
+
return "", safe_history + [(clean, reply)]
|
ui/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .controller import UIController
|
| 2 |
+
from .layout import create_demo
|
| 3 |
+
|
| 4 |
+
__all__ = ["UIController", "create_demo"]
|
ui/controller.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Any, List, Optional
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class UIController:
|
| 5 |
+
def __init__(
|
| 6 |
+
self,
|
| 7 |
+
logger: Any,
|
| 8 |
+
chat_handler: Any,
|
| 9 |
+
agent: Any = None,
|
| 10 |
+
startup_error: Optional[str] = None,
|
| 11 |
+
):
|
| 12 |
+
self.logger = logger
|
| 13 |
+
self.chat_handler = chat_handler
|
| 14 |
+
self.agent = agent
|
| 15 |
+
self.startup_error = startup_error
|
| 16 |
+
|
| 17 |
+
def status(self) -> str:
|
| 18 |
+
if self.startup_error:
|
| 19 |
+
return f"startup_error: {self.startup_error}"
|
| 20 |
+
if self.agent is None:
|
| 21 |
+
return "startup_error: agent unavailable"
|
| 22 |
+
state = self.agent.status_snapshot()
|
| 23 |
+
running = "running" if state["running"] else "stopped"
|
| 24 |
+
return f"state={running} last_status={state['last_status']} dry_run={state['dry_run']}"
|
| 25 |
+
|
| 26 |
+
def start(self) -> str:
|
| 27 |
+
if self.startup_error:
|
| 28 |
+
return f"startup_error: {self.startup_error}"
|
| 29 |
+
if self.agent is None:
|
| 30 |
+
return "startup_error: agent unavailable"
|
| 31 |
+
result = self.agent.start()
|
| 32 |
+
return f"agent {result}"
|
| 33 |
+
|
| 34 |
+
def stop(self) -> str:
|
| 35 |
+
if self.startup_error:
|
| 36 |
+
return f"startup_error: {self.startup_error}"
|
| 37 |
+
if self.agent is None:
|
| 38 |
+
return "startup_error: agent unavailable"
|
| 39 |
+
result = self.agent.stop()
|
| 40 |
+
return f"agent {result}"
|
| 41 |
+
|
| 42 |
+
def logs(self) -> str:
|
| 43 |
+
return self.logger.snapshot()
|
| 44 |
+
|
| 45 |
+
def chat_send(self, message: str, history: Optional[List[Any]]) -> tuple[str, List[Any]]:
|
| 46 |
+
try:
|
| 47 |
+
if self.chat_handler is None:
|
| 48 |
+
return "", (history or []) + [("system", "startup_error: chat unavailable")]
|
| 49 |
+
return self.chat_handler.send(message, history)
|
| 50 |
+
except Exception as exc: # noqa: BLE001
|
| 51 |
+
self.logger.error("Manual chat failed", {"error": str(exc)})
|
| 52 |
+
clean = " ".join(str(message or "").split()).strip()
|
| 53 |
+
if not clean:
|
| 54 |
+
return "", history or []
|
| 55 |
+
return "", (history or []) + [(clean, f"error: {exc}")]
|
ui/layout.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def create_demo(controller: object) -> gr.Blocks:
|
| 5 |
+
with gr.Blocks(title="Clawdbot Moltbook Agent") as demo:
|
| 6 |
+
gr.Markdown("# Clawdbot Moltbook Agent (Hugging Face Models)")
|
| 7 |
+
status_box = gr.Textbox(label="Status", value=controller.status(), lines=2)
|
| 8 |
+
with gr.Row():
|
| 9 |
+
btn_start = gr.Button("Start Agent", variant="primary")
|
| 10 |
+
btn_stop = gr.Button("Stop Agent", variant="stop")
|
| 11 |
+
btn_refresh = gr.Button("Refresh")
|
| 12 |
+
logs_box = gr.Textbox(label="Logs", value=controller.logs(), lines=20)
|
| 13 |
+
gr.Markdown("## Chat with clawdbot")
|
| 14 |
+
chat_box = gr.Chatbot(label="Clawdbot Chat", height=320)
|
| 15 |
+
with gr.Row():
|
| 16 |
+
chat_input = gr.Textbox(label="Message", placeholder="Parle a clawdbot...", scale=8)
|
| 17 |
+
chat_send = gr.Button("Send", variant="primary", scale=1)
|
| 18 |
+
chat_clear = gr.Button("Clear Chat")
|
| 19 |
+
|
| 20 |
+
btn_start.click(fn=controller.start, outputs=status_box).then(fn=controller.logs, outputs=logs_box)
|
| 21 |
+
btn_stop.click(fn=controller.stop, outputs=status_box).then(fn=controller.logs, outputs=logs_box)
|
| 22 |
+
btn_refresh.click(fn=controller.status, outputs=status_box).then(fn=controller.logs, outputs=logs_box)
|
| 23 |
+
chat_send.click(fn=controller.chat_send, inputs=[chat_input, chat_box], outputs=[chat_input, chat_box])
|
| 24 |
+
chat_input.submit(fn=controller.chat_send, inputs=[chat_input, chat_box], outputs=[chat_input, chat_box])
|
| 25 |
+
chat_clear.click(fn=lambda: [], outputs=chat_box)
|
| 26 |
+
|
| 27 |
+
return demo
|