pythonclaw 0.3.3__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pythonclaw-0.3.3/pythonclaw.egg-info → pythonclaw-0.6.0}/PKG-INFO +63 -38
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/README.md +60 -36
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pyproject.toml +16 -2
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/__init__.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/channels/discord_bot.py +72 -11
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/channels/telegram_bot.py +145 -15
- pythonclaw-0.6.0/pythonclaw/channels/whatsapp_bot.py +304 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/config.py +25 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/agent.py +439 -92
- pythonclaw-0.6.0/pythonclaw/core/llm/anthropic_client.py +294 -0
- pythonclaw-0.6.0/pythonclaw/core/llm/base.py +55 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/llm/gemini_client.py +52 -1
- pythonclaw-0.6.0/pythonclaw/core/llm/openai_compatible.py +105 -0
- pythonclaw-0.6.0/pythonclaw/core/memory/manager.py +221 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/memory/storage.py +83 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/persistent_agent.py +33 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/session_store.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/skill_loader.py +93 -10
- pythonclaw-0.6.0/pythonclaw/core/skillhub.py +309 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/tools.py +37 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/init.py +2 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/main.py +21 -13
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/onboard.py +62 -15
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/server.py +21 -2
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/session_manager.py +63 -12
- pythonclaw-0.6.0/pythonclaw/templates/skills/communication/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/communication/email/SKILL.md +65 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/communication/slack/SKILL.md +98 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/communication/slack/slack_api.py +153 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/csv_analyzer/SKILL.md +61 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/finance/SKILL.md +51 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/news/SKILL.md +52 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/pdf_reader/SKILL.md +51 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/scraper/SKILL.md +1 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/weather/SKILL.md +68 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/data/youtube/SKILL.md +54 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/dev/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/dev/code_runner/SKILL.md +58 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/dev/github/SKILL.md +78 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/dev/http_request/SKILL.md +50 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/google/CATEGORY.md +5 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/google/workspace/SKILL.md +43 -36
- pythonclaw-0.6.0/pythonclaw/templates/skills/google/workspace/check_setup.sh +52 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/image_gen/SKILL.md +99 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/image_gen/generate.py +103 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/spotify/SKILL.md +124 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/spotify/spotify_ctl.py +231 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/tts/SKILL.md +83 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/media/tts/speak.py +50 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/meta/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/meta/skill_creator/SKILL.md +79 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/notion/SKILL.md +99 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/notion/notion_api.py +185 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/obsidian/SKILL.md +110 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/obsidian/obsidian_vault.py +165 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/trello/SKILL.md +110 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/productivity/trello/trello_api.py +141 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/change_persona/SKILL.md +47 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/change_setting/SKILL.md +69 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/system/change_setting/update_config.py +1 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/change_soul/SKILL.md +46 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/model_usage/SKILL.md +73 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/model_usage/usage_stats.py +73 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/onboarding/SKILL.md +58 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/random/SKILL.md +51 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/session_logs/SKILL.md +80 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/session_logs/search_sessions.py +55 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/system/time/SKILL.md +51 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/system/time/time_util.py +1 -1
- pythonclaw-0.6.0/pythonclaw/templates/skills/text/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/text/translator/SKILL.md +52 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/web/CATEGORY.md +5 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/web/summarize/SKILL.md +84 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/web/summarize/summarize_url.py +107 -0
- pythonclaw-0.6.0/pythonclaw/templates/skills/web/tavily/SKILL.md +62 -0
- pythonclaw-0.6.0/pythonclaw/templates/tools/TOOLS.md +43 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/web/app.py +237 -28
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/web/static/index.html +419 -85
- {pythonclaw-0.3.3 → pythonclaw-0.6.0/pythonclaw.egg-info}/PKG-INFO +63 -38
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw.egg-info/SOURCES.txt +25 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw.egg-info/requires.txt +1 -0
- pythonclaw-0.3.3/pythonclaw/core/llm/anthropic_client.py +0 -107
- pythonclaw-0.3.3/pythonclaw/core/llm/base.py +0 -26
- pythonclaw-0.3.3/pythonclaw/core/llm/openai_compatible.py +0 -39
- pythonclaw-0.3.3/pythonclaw/core/memory/manager.py +0 -120
- pythonclaw-0.3.3/pythonclaw/core/skillhub.py +0 -291
- pythonclaw-0.3.3/pythonclaw/templates/skills/communication/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/communication/email/SKILL.md +0 -54
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/csv_analyzer/SKILL.md +0 -51
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/finance/SKILL.md +0 -41
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/news/SKILL.md +0 -39
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/pdf_reader/SKILL.md +0 -40
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/weather/SKILL.md +0 -42
- pythonclaw-0.3.3/pythonclaw/templates/skills/data/youtube/SKILL.md +0 -43
- pythonclaw-0.3.3/pythonclaw/templates/skills/dev/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/dev/code_runner/SKILL.md +0 -46
- pythonclaw-0.3.3/pythonclaw/templates/skills/dev/github/SKILL.md +0 -52
- pythonclaw-0.3.3/pythonclaw/templates/skills/dev/http_request/SKILL.md +0 -40
- pythonclaw-0.3.3/pythonclaw/templates/skills/google/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/meta/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/meta/skill_creator/SKILL.md +0 -151
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_persona/SKILL.md +0 -41
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_setting/SKILL.md +0 -65
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/change_soul/SKILL.md +0 -41
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/onboarding/SKILL.md +0 -63
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/random/SKILL.md +0 -33
- pythonclaw-0.3.3/pythonclaw/templates/skills/system/time/SKILL.md +0 -33
- pythonclaw-0.3.3/pythonclaw/templates/skills/text/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/text/translator/SKILL.md +0 -47
- pythonclaw-0.3.3/pythonclaw/templates/skills/web/CATEGORY.md +0 -4
- pythonclaw-0.3.3/pythonclaw/templates/skills/web/tavily/SKILL.md +0 -61
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/LICENSE +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/MANIFEST.in +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/__main__.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/__init__.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/compaction.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/knowledge/rag.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/llm/response.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/__init__.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/chunker.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/dense.py +2 -2
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/fusion.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/reranker.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/retriever.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/retrieval/sparse.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/core/utils.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/daemon.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/scheduler/cron.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/scheduler/heartbeat.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/persona/demo_persona.md +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/communication/email/send_email.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/csv_analyzer/analyze.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/finance/fetch_quote.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/news/search_news.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/pdf_reader/read_pdf.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/scraper/scrape.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/weather/weather.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/data/youtube/youtube_info.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/dev/code_runner/run_code.py +1 -1
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/dev/github/gh.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/dev/http_request/request.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/system/onboarding/write_identity.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/system/random/random_util.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/skills/text/translator/translate.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/templates/soul/SOUL.md +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/web/__init__.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/web/static/favicon.png +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw/web/static/logo.png +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw.egg-info/dependency_links.txt +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw.egg-info/entry_points.txt +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/pythonclaw.egg-info/top_level.txt +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/setup.cfg +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/tests/test_compaction.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/tests/test_persistence.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/tests/test_rag_hybrid.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/tests/test_skills.py +0 -0
- {pythonclaw-0.3.3 → pythonclaw-0.6.0}/tests/test_soul.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonclaw
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: OpenClaw reimagined in pure Python — autonomous AI agent with memory, RAG, skills, web dashboard, and multi-channel support.
|
|
5
5
|
Author-email: Eric Wang <wangchen2007915@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,7 +8,7 @@ Project-URL: Homepage, https://github.com/ericwang915/PythonClaw
|
|
|
8
8
|
Project-URL: Documentation, https://github.com/ericwang915/PythonClaw#readme
|
|
9
9
|
Project-URL: Repository, https://github.com/ericwang915/PythonClaw
|
|
10
10
|
Project-URL: Issues, https://github.com/ericwang915/PythonClaw/issues
|
|
11
|
-
Keywords: llm,agent,ai,autonomous,memory,rag,skills,telegram,chatbot,framework
|
|
11
|
+
Keywords: llm,agent,ai,autonomous,memory,rag,skills,telegram,discord,whatsapp,chatbot,framework
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
@@ -27,6 +27,7 @@ Requires-Dist: anthropic
|
|
|
27
27
|
Requires-Dist: google-generativeai
|
|
28
28
|
Requires-Dist: python-telegram-bot[job-queue]>=20.0
|
|
29
29
|
Requires-Dist: discord.py>=2.3
|
|
30
|
+
Requires-Dist: pywa[fastapi]>=2.8
|
|
30
31
|
Requires-Dist: apscheduler>=3.10
|
|
31
32
|
Requires-Dist: pyyaml>=6.0
|
|
32
33
|
Requires-Dist: fastapi>=0.100
|
|
@@ -77,15 +78,18 @@ Dynamic: license-file
|
|
|
77
78
|
| | Feature | Details |
|
|
78
79
|
|---|---------|---------|
|
|
79
80
|
| 🧠 | **Provider-agnostic** | DeepSeek, Grok, Claude, Gemini, Kimi, GLM — or any OpenAI-compatible API |
|
|
80
|
-
| 🛠️ | **Three-tier skills** | Progressive loading: metadata → instructions → resources. Community marketplace via [
|
|
81
|
+
| 🛠️ | **Three-tier skills** | Progressive loading: metadata → instructions → resources. Community marketplace via [ClawHub](https://clawhub.com) (13K+ free skills) |
|
|
81
82
|
| 💾 | **Persistent memory** | Markdown-based long-term memory with daily logs and semantic recall |
|
|
82
83
|
| 🔍 | **Hybrid RAG** | BM25 + dense embeddings + RRF fusion + LLM re-ranking |
|
|
83
84
|
| 🌐 | **Web dashboard** | Browser UI for chat, config, skill catalog, identity editing, and marketplace |
|
|
84
85
|
| 🎙️ | **Voice input** | Deepgram speech-to-text in the web chat |
|
|
85
86
|
| ⏰ | **Cron jobs** | Schedule tasks via YAML or let the agent create its own |
|
|
86
|
-
| 📡 | **Multi-channel** | CLI, Web, Telegram, Discord — same agent, different interfaces |
|
|
87
|
+
| 📡 | **Multi-channel** | CLI, Web, Telegram, Discord, WhatsApp — same agent, different interfaces |
|
|
87
88
|
| 🔄 | **Daemon mode** | PID-managed background process with `start` / `stop` / `status` |
|
|
88
89
|
| 🧬 | **Soul + Persona** | Separate core identity from swappable role presentation |
|
|
90
|
+
| 🔧 | **TOOLS.md** | Local environment notes — your cheat sheet for the agent |
|
|
91
|
+
| 🔒 | **Per-group isolation** | Each chat session gets its own memory (optional) |
|
|
92
|
+
| 🔁 | **Concurrency control** | Per-session locks + global semaphore prevent interleaving |
|
|
89
93
|
|
|
90
94
|
---
|
|
91
95
|
|
|
@@ -112,7 +116,7 @@ pythonclaw stop
|
|
|
112
116
|
```bash
|
|
113
117
|
git clone https://github.com/ericwang915/PythonClaw.git
|
|
114
118
|
cd PythonClaw
|
|
115
|
-
pip install -e
|
|
119
|
+
pip install -e .
|
|
116
120
|
pythonclaw onboard
|
|
117
121
|
```
|
|
118
122
|
|
|
@@ -125,11 +129,11 @@ pythonclaw onboard
|
|
|
125
129
|
| `pythonclaw onboard` | Interactive setup wizard — choose LLM provider, enter API key |
|
|
126
130
|
| `pythonclaw start` | Start the agent as a background daemon |
|
|
127
131
|
| `pythonclaw start -f` | Start in foreground (no daemonize) |
|
|
128
|
-
| `pythonclaw start --channels telegram discord` | Start with messaging channels |
|
|
132
|
+
| `pythonclaw start --channels telegram discord whatsapp` | Start with messaging channels |
|
|
129
133
|
| `pythonclaw stop` | Stop the running daemon |
|
|
130
134
|
| `pythonclaw status` | Show daemon status (PID, uptime, port) |
|
|
131
135
|
| `pythonclaw chat` | Interactive CLI chat (foreground REPL) |
|
|
132
|
-
| `pythonclaw skill search <query>` | Search skills on [
|
|
136
|
+
| `pythonclaw skill search <query>` | Search skills on [ClawHub](https://clawhub.com) |
|
|
133
137
|
| `pythonclaw skill browse` | Browse top-rated skills |
|
|
134
138
|
| `pythonclaw skill install <id>` | Install a community skill |
|
|
135
139
|
| `pythonclaw skill info <id>` | View skill details |
|
|
@@ -170,24 +174,25 @@ $ pythonclaw start
|
|
|
170
174
|
## Architecture
|
|
171
175
|
|
|
172
176
|
```
|
|
173
|
-
|
|
174
|
-
│
|
|
175
|
-
|
|
176
|
-
│ CLI │ Daemon
|
|
177
|
-
│ │
|
|
178
|
-
│ onboard │ start /
|
|
179
|
-
│ chat │ stop /
|
|
180
|
-
│ skill … │ status
|
|
181
|
-
│ │
|
|
182
|
-
│ Web UI ◄─┤ Channels
|
|
183
|
-
│ Voice In │ Telegram
|
|
184
|
-
│ │ Discord
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
│
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
177
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
178
|
+
│ PythonClaw │
|
|
179
|
+
├──────────┬────────────┬───────────┬──────────────────────────┤
|
|
180
|
+
│ CLI │ Daemon │ Sessions │ Core │
|
|
181
|
+
│ │ │ │ │
|
|
182
|
+
│ onboard │ start / │ Store(MD) │ Agent │
|
|
183
|
+
│ chat │ stop / │ Manager │ ├─ Memory (Markdown) │
|
|
184
|
+
│ skill … │ status │ Locks + │ ├─ RAG (Hybrid) │
|
|
185
|
+
│ │ │ Semaphore │ ├─ Skills (3-tier) │
|
|
186
|
+
│ Web UI ◄─┤ Channels │ │ ├─ Compaction │
|
|
187
|
+
│ Voice In │ Telegram │ Per-group │ ├─ Soul + Persona │
|
|
188
|
+
│ │ Discord │ Isolation │ ├─ Group Context │
|
|
189
|
+
│ │ WhatsApp │ │ └─ Tool Execution │
|
|
190
|
+
├──────────┴────────────┴───────────┴──────────────────────────┤
|
|
191
|
+
│ LLM Provider Abstraction Layer │
|
|
192
|
+
│ DeepSeek │ Grok │ Claude │ Gemini │ Kimi │ GLM │
|
|
193
|
+
├──────────────────────────────────────────────────────────────┤
|
|
194
|
+
│ ClawHub Marketplace (clawhub.com) │
|
|
195
|
+
└──────────────────────────────────────────────────────────────┘
|
|
191
196
|
```
|
|
192
197
|
|
|
193
198
|
---
|
|
@@ -199,7 +204,7 @@ Start with `pythonclaw start` and open **http://localhost:7788**.
|
|
|
199
204
|
- **Dashboard** — agent status, soul/persona preview, tool list
|
|
200
205
|
- **Chat** — real-time chat with voice input (Deepgram)
|
|
201
206
|
- **Skill Catalog** — browse installed skills by category
|
|
202
|
-
- **Marketplace** — search and install skills from [
|
|
207
|
+
- **Marketplace** — search and install skills from [ClawHub](https://clawhub.com)
|
|
203
208
|
- **Configuration** — edit LLM provider, API keys, and settings in-browser
|
|
204
209
|
|
|
205
210
|
---
|
|
@@ -220,8 +225,11 @@ See [`pythonclaw.example.json`](pythonclaw.example.json) for the full template.
|
|
|
220
225
|
"web": { "host": "0.0.0.0", "port": 7788 },
|
|
221
226
|
"channels": {
|
|
222
227
|
"telegram": { "token": "" },
|
|
223
|
-
"discord": { "token": "" }
|
|
224
|
-
|
|
228
|
+
"discord": { "token": "" },
|
|
229
|
+
"whatsapp": { "phoneNumberId": "", "token": "", "verifyToken": "pythonclaw_verify" }
|
|
230
|
+
},
|
|
231
|
+
"isolation": { "perGroup": false },
|
|
232
|
+
"concurrency": { "maxAgents": 4 }
|
|
225
233
|
}
|
|
226
234
|
```
|
|
227
235
|
|
|
@@ -235,8 +243,8 @@ Environment variables (e.g. `DEEPSEEK_API_KEY`, `TAVILY_API_KEY`) override JSON
|
|
|
235
243
|
|----------|---------------|---------------|
|
|
236
244
|
| **DeepSeek** | `deepseek-chat` | — |
|
|
237
245
|
| **Grok (xAI)** | `grok-3` | — |
|
|
238
|
-
| **Claude (Anthropic)** | `claude-sonnet-4-20250514` |
|
|
239
|
-
| **Gemini (Google)** | `gemini-2.0-flash` |
|
|
246
|
+
| **Claude (Anthropic)** | `claude-sonnet-4-20250514` | — (included) |
|
|
247
|
+
| **Gemini (Google)** | `gemini-2.0-flash` | — (included) |
|
|
240
248
|
| **Kimi (Moonshot)** | `moonshot-v1-128k` | — |
|
|
241
249
|
| **GLM (Zhipu)** | `glm-4-flash` | — |
|
|
242
250
|
| Any OpenAI-compatible | Custom | — |
|
|
@@ -264,9 +272,9 @@ description: Execute Python code safely in an isolated subprocess.
|
|
|
264
272
|
Run `python {skill_path}/run_code.py "expression"`
|
|
265
273
|
```
|
|
266
274
|
|
|
267
|
-
###
|
|
275
|
+
### ClawHub Marketplace
|
|
268
276
|
|
|
269
|
-
Browse and install
|
|
277
|
+
Browse and install 13,000+ community skills from [ClawHub](https://clawhub.com) — free, no API key required:
|
|
270
278
|
|
|
271
279
|
```bash
|
|
272
280
|
pythonclaw skill search "database backup"
|
|
@@ -282,11 +290,28 @@ Also accessible from the web dashboard **Marketplace** tab.
|
|
|
282
290
|
### Markdown Memory
|
|
283
291
|
|
|
284
292
|
```
|
|
285
|
-
context/memory/
|
|
293
|
+
~/.pythonclaw/context/memory/
|
|
286
294
|
├── MEMORY.md # Curated long-term memory
|
|
287
295
|
└── 2026-02-23.md # Daily append-only log
|
|
288
296
|
```
|
|
289
297
|
|
|
298
|
+
When **per-group isolation** is enabled (`"isolation": { "perGroup": true }` in config),
|
|
299
|
+
each session (Telegram chat, Discord channel, etc.) gets its own `memory/`, `persona/`,
|
|
300
|
+
and `soul/` under `~/.pythonclaw/context/groups/<session-id>/`, while global memories
|
|
301
|
+
remain accessible via read-through fallback.
|
|
302
|
+
|
|
303
|
+
### TOOLS.md — Local Notes
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
~/.pythonclaw/context/tools/
|
|
307
|
+
└── TOOLS.md # Your environment-specific cheat sheet
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Skills define *how* tools work. `TOOLS.md` stores *your* specifics — SSH hosts, device
|
|
311
|
+
nicknames, project paths, preferred defaults, API endpoints. Keeping them apart means
|
|
312
|
+
you can update skills without losing your notes, and share skills without leaking your
|
|
313
|
+
infrastructure. Editable from the web dashboard.
|
|
314
|
+
|
|
290
315
|
### Hybrid RAG Pipeline
|
|
291
316
|
|
|
292
317
|
```
|
|
@@ -326,14 +351,14 @@ PythonClaw/
|
|
|
326
351
|
│ │ ├── agent.py # Core reasoning loop
|
|
327
352
|
│ │ ├── tools.py # Tool schemas and execution
|
|
328
353
|
│ │ ├── skill_loader.py # Three-tier skill system
|
|
329
|
-
│ │ ├── skillhub.py #
|
|
354
|
+
│ │ ├── skillhub.py # ClawHub marketplace client
|
|
330
355
|
│ │ ├── persistent_agent.py
|
|
331
356
|
│ │ ├── compaction.py # Context compaction
|
|
332
357
|
│ │ ├── llm/ # Provider adapters
|
|
333
358
|
│ │ ├── memory/ # Markdown memory
|
|
334
359
|
│ │ ├── knowledge/ # Knowledge-base RAG
|
|
335
360
|
│ │ └── retrieval/ # BM25 + dense + fusion + reranker
|
|
336
|
-
│ ├── channels/ # Telegram, Discord
|
|
361
|
+
│ ├── channels/ # Telegram, Discord, WhatsApp
|
|
337
362
|
│ ├── scheduler/ # Cron jobs, heartbeat
|
|
338
363
|
│ ├── web/ # FastAPI dashboard + static assets
|
|
339
364
|
│ └── templates/ # Built-in skill templates
|
|
@@ -351,7 +376,7 @@ PythonClaw/
|
|
|
351
376
|
git clone https://github.com/ericwang915/PythonClaw.git
|
|
352
377
|
cd PythonClaw
|
|
353
378
|
python -m venv .venv && source .venv/bin/activate
|
|
354
|
-
pip install -e
|
|
379
|
+
pip install -e .
|
|
355
380
|
pytest tests/ -v
|
|
356
381
|
```
|
|
357
382
|
|
|
@@ -372,8 +397,8 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
372
397
|
| CLI | `openclaw start/stop` | `pythonclaw start/stop/status` |
|
|
373
398
|
| Dashboard | Web UI | Web UI (localhost:7788) |
|
|
374
399
|
| Memory | Markdown | Markdown (long-term + daily) |
|
|
375
|
-
| Skills | Plugin system | Three-tier +
|
|
376
|
-
| Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord |
|
|
400
|
+
| Skills | Plugin system | Three-tier + ClawHub marketplace |
|
|
401
|
+
| Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord, WhatsApp |
|
|
377
402
|
| Voice | — | Deepgram STT |
|
|
378
403
|
| LLM Providers | OpenAI, Anthropic, Gemini | DeepSeek, Grok, Claude, Gemini, Kimi, GLM |
|
|
379
404
|
| Daemon | Background process | PID-managed (`start`/`stop`/`status`) |
|
|
@@ -36,15 +36,18 @@
|
|
|
36
36
|
| | Feature | Details |
|
|
37
37
|
|---|---------|---------|
|
|
38
38
|
| 🧠 | **Provider-agnostic** | DeepSeek, Grok, Claude, Gemini, Kimi, GLM — or any OpenAI-compatible API |
|
|
39
|
-
| 🛠️ | **Three-tier skills** | Progressive loading: metadata → instructions → resources. Community marketplace via [
|
|
39
|
+
| 🛠️ | **Three-tier skills** | Progressive loading: metadata → instructions → resources. Community marketplace via [ClawHub](https://clawhub.com) (13K+ free skills) |
|
|
40
40
|
| 💾 | **Persistent memory** | Markdown-based long-term memory with daily logs and semantic recall |
|
|
41
41
|
| 🔍 | **Hybrid RAG** | BM25 + dense embeddings + RRF fusion + LLM re-ranking |
|
|
42
42
|
| 🌐 | **Web dashboard** | Browser UI for chat, config, skill catalog, identity editing, and marketplace |
|
|
43
43
|
| 🎙️ | **Voice input** | Deepgram speech-to-text in the web chat |
|
|
44
44
|
| ⏰ | **Cron jobs** | Schedule tasks via YAML or let the agent create its own |
|
|
45
|
-
| 📡 | **Multi-channel** | CLI, Web, Telegram, Discord — same agent, different interfaces |
|
|
45
|
+
| 📡 | **Multi-channel** | CLI, Web, Telegram, Discord, WhatsApp — same agent, different interfaces |
|
|
46
46
|
| 🔄 | **Daemon mode** | PID-managed background process with `start` / `stop` / `status` |
|
|
47
47
|
| 🧬 | **Soul + Persona** | Separate core identity from swappable role presentation |
|
|
48
|
+
| 🔧 | **TOOLS.md** | Local environment notes — your cheat sheet for the agent |
|
|
49
|
+
| 🔒 | **Per-group isolation** | Each chat session gets its own memory (optional) |
|
|
50
|
+
| 🔁 | **Concurrency control** | Per-session locks + global semaphore prevent interleaving |
|
|
48
51
|
|
|
49
52
|
---
|
|
50
53
|
|
|
@@ -71,7 +74,7 @@ pythonclaw stop
|
|
|
71
74
|
```bash
|
|
72
75
|
git clone https://github.com/ericwang915/PythonClaw.git
|
|
73
76
|
cd PythonClaw
|
|
74
|
-
pip install -e
|
|
77
|
+
pip install -e .
|
|
75
78
|
pythonclaw onboard
|
|
76
79
|
```
|
|
77
80
|
|
|
@@ -84,11 +87,11 @@ pythonclaw onboard
|
|
|
84
87
|
| `pythonclaw onboard` | Interactive setup wizard — choose LLM provider, enter API key |
|
|
85
88
|
| `pythonclaw start` | Start the agent as a background daemon |
|
|
86
89
|
| `pythonclaw start -f` | Start in foreground (no daemonize) |
|
|
87
|
-
| `pythonclaw start --channels telegram discord` | Start with messaging channels |
|
|
90
|
+
| `pythonclaw start --channels telegram discord whatsapp` | Start with messaging channels |
|
|
88
91
|
| `pythonclaw stop` | Stop the running daemon |
|
|
89
92
|
| `pythonclaw status` | Show daemon status (PID, uptime, port) |
|
|
90
93
|
| `pythonclaw chat` | Interactive CLI chat (foreground REPL) |
|
|
91
|
-
| `pythonclaw skill search <query>` | Search skills on [
|
|
94
|
+
| `pythonclaw skill search <query>` | Search skills on [ClawHub](https://clawhub.com) |
|
|
92
95
|
| `pythonclaw skill browse` | Browse top-rated skills |
|
|
93
96
|
| `pythonclaw skill install <id>` | Install a community skill |
|
|
94
97
|
| `pythonclaw skill info <id>` | View skill details |
|
|
@@ -129,24 +132,25 @@ $ pythonclaw start
|
|
|
129
132
|
## Architecture
|
|
130
133
|
|
|
131
134
|
```
|
|
132
|
-
|
|
133
|
-
│
|
|
134
|
-
|
|
135
|
-
│ CLI │ Daemon
|
|
136
|
-
│ │
|
|
137
|
-
│ onboard │ start /
|
|
138
|
-
│ chat │ stop /
|
|
139
|
-
│ skill … │ status
|
|
140
|
-
│ │
|
|
141
|
-
│ Web UI ◄─┤ Channels
|
|
142
|
-
│ Voice In │ Telegram
|
|
143
|
-
│ │ Discord
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
│
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
135
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
136
|
+
│ PythonClaw │
|
|
137
|
+
├──────────┬────────────┬───────────┬──────────────────────────┤
|
|
138
|
+
│ CLI │ Daemon │ Sessions │ Core │
|
|
139
|
+
│ │ │ │ │
|
|
140
|
+
│ onboard │ start / │ Store(MD) │ Agent │
|
|
141
|
+
│ chat │ stop / │ Manager │ ├─ Memory (Markdown) │
|
|
142
|
+
│ skill … │ status │ Locks + │ ├─ RAG (Hybrid) │
|
|
143
|
+
│ │ │ Semaphore │ ├─ Skills (3-tier) │
|
|
144
|
+
│ Web UI ◄─┤ Channels │ │ ├─ Compaction │
|
|
145
|
+
│ Voice In │ Telegram │ Per-group │ ├─ Soul + Persona │
|
|
146
|
+
│ │ Discord │ Isolation │ ├─ Group Context │
|
|
147
|
+
│ │ WhatsApp │ │ └─ Tool Execution │
|
|
148
|
+
├──────────┴────────────┴───────────┴──────────────────────────┤
|
|
149
|
+
│ LLM Provider Abstraction Layer │
|
|
150
|
+
│ DeepSeek │ Grok │ Claude │ Gemini │ Kimi │ GLM │
|
|
151
|
+
├──────────────────────────────────────────────────────────────┤
|
|
152
|
+
│ ClawHub Marketplace (clawhub.com) │
|
|
153
|
+
└──────────────────────────────────────────────────────────────┘
|
|
150
154
|
```
|
|
151
155
|
|
|
152
156
|
---
|
|
@@ -158,7 +162,7 @@ Start with `pythonclaw start` and open **http://localhost:7788**.
|
|
|
158
162
|
- **Dashboard** — agent status, soul/persona preview, tool list
|
|
159
163
|
- **Chat** — real-time chat with voice input (Deepgram)
|
|
160
164
|
- **Skill Catalog** — browse installed skills by category
|
|
161
|
-
- **Marketplace** — search and install skills from [
|
|
165
|
+
- **Marketplace** — search and install skills from [ClawHub](https://clawhub.com)
|
|
162
166
|
- **Configuration** — edit LLM provider, API keys, and settings in-browser
|
|
163
167
|
|
|
164
168
|
---
|
|
@@ -179,8 +183,11 @@ See [`pythonclaw.example.json`](pythonclaw.example.json) for the full template.
|
|
|
179
183
|
"web": { "host": "0.0.0.0", "port": 7788 },
|
|
180
184
|
"channels": {
|
|
181
185
|
"telegram": { "token": "" },
|
|
182
|
-
"discord": { "token": "" }
|
|
183
|
-
|
|
186
|
+
"discord": { "token": "" },
|
|
187
|
+
"whatsapp": { "phoneNumberId": "", "token": "", "verifyToken": "pythonclaw_verify" }
|
|
188
|
+
},
|
|
189
|
+
"isolation": { "perGroup": false },
|
|
190
|
+
"concurrency": { "maxAgents": 4 }
|
|
184
191
|
}
|
|
185
192
|
```
|
|
186
193
|
|
|
@@ -194,8 +201,8 @@ Environment variables (e.g. `DEEPSEEK_API_KEY`, `TAVILY_API_KEY`) override JSON
|
|
|
194
201
|
|----------|---------------|---------------|
|
|
195
202
|
| **DeepSeek** | `deepseek-chat` | — |
|
|
196
203
|
| **Grok (xAI)** | `grok-3` | — |
|
|
197
|
-
| **Claude (Anthropic)** | `claude-sonnet-4-20250514` |
|
|
198
|
-
| **Gemini (Google)** | `gemini-2.0-flash` |
|
|
204
|
+
| **Claude (Anthropic)** | `claude-sonnet-4-20250514` | — (included) |
|
|
205
|
+
| **Gemini (Google)** | `gemini-2.0-flash` | — (included) |
|
|
199
206
|
| **Kimi (Moonshot)** | `moonshot-v1-128k` | — |
|
|
200
207
|
| **GLM (Zhipu)** | `glm-4-flash` | — |
|
|
201
208
|
| Any OpenAI-compatible | Custom | — |
|
|
@@ -223,9 +230,9 @@ description: Execute Python code safely in an isolated subprocess.
|
|
|
223
230
|
Run `python {skill_path}/run_code.py "expression"`
|
|
224
231
|
```
|
|
225
232
|
|
|
226
|
-
###
|
|
233
|
+
### ClawHub Marketplace
|
|
227
234
|
|
|
228
|
-
Browse and install
|
|
235
|
+
Browse and install 13,000+ community skills from [ClawHub](https://clawhub.com) — free, no API key required:
|
|
229
236
|
|
|
230
237
|
```bash
|
|
231
238
|
pythonclaw skill search "database backup"
|
|
@@ -241,11 +248,28 @@ Also accessible from the web dashboard **Marketplace** tab.
|
|
|
241
248
|
### Markdown Memory
|
|
242
249
|
|
|
243
250
|
```
|
|
244
|
-
context/memory/
|
|
251
|
+
~/.pythonclaw/context/memory/
|
|
245
252
|
├── MEMORY.md # Curated long-term memory
|
|
246
253
|
└── 2026-02-23.md # Daily append-only log
|
|
247
254
|
```
|
|
248
255
|
|
|
256
|
+
When **per-group isolation** is enabled (`"isolation": { "perGroup": true }` in config),
|
|
257
|
+
each session (Telegram chat, Discord channel, etc.) gets its own `memory/`, `persona/`,
|
|
258
|
+
and `soul/` under `~/.pythonclaw/context/groups/<session-id>/`, while global memories
|
|
259
|
+
remain accessible via read-through fallback.
|
|
260
|
+
|
|
261
|
+
### TOOLS.md — Local Notes
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
~/.pythonclaw/context/tools/
|
|
265
|
+
└── TOOLS.md # Your environment-specific cheat sheet
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Skills define *how* tools work. `TOOLS.md` stores *your* specifics — SSH hosts, device
|
|
269
|
+
nicknames, project paths, preferred defaults, API endpoints. Keeping them apart means
|
|
270
|
+
you can update skills without losing your notes, and share skills without leaking your
|
|
271
|
+
infrastructure. Editable from the web dashboard.
|
|
272
|
+
|
|
249
273
|
### Hybrid RAG Pipeline
|
|
250
274
|
|
|
251
275
|
```
|
|
@@ -285,14 +309,14 @@ PythonClaw/
|
|
|
285
309
|
│ │ ├── agent.py # Core reasoning loop
|
|
286
310
|
│ │ ├── tools.py # Tool schemas and execution
|
|
287
311
|
│ │ ├── skill_loader.py # Three-tier skill system
|
|
288
|
-
│ │ ├── skillhub.py #
|
|
312
|
+
│ │ ├── skillhub.py # ClawHub marketplace client
|
|
289
313
|
│ │ ├── persistent_agent.py
|
|
290
314
|
│ │ ├── compaction.py # Context compaction
|
|
291
315
|
│ │ ├── llm/ # Provider adapters
|
|
292
316
|
│ │ ├── memory/ # Markdown memory
|
|
293
317
|
│ │ ├── knowledge/ # Knowledge-base RAG
|
|
294
318
|
│ │ └── retrieval/ # BM25 + dense + fusion + reranker
|
|
295
|
-
│ ├── channels/ # Telegram, Discord
|
|
319
|
+
│ ├── channels/ # Telegram, Discord, WhatsApp
|
|
296
320
|
│ ├── scheduler/ # Cron jobs, heartbeat
|
|
297
321
|
│ ├── web/ # FastAPI dashboard + static assets
|
|
298
322
|
│ └── templates/ # Built-in skill templates
|
|
@@ -310,7 +334,7 @@ PythonClaw/
|
|
|
310
334
|
git clone https://github.com/ericwang915/PythonClaw.git
|
|
311
335
|
cd PythonClaw
|
|
312
336
|
python -m venv .venv && source .venv/bin/activate
|
|
313
|
-
pip install -e
|
|
337
|
+
pip install -e .
|
|
314
338
|
pytest tests/ -v
|
|
315
339
|
```
|
|
316
340
|
|
|
@@ -331,8 +355,8 @@ We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
331
355
|
| CLI | `openclaw start/stop` | `pythonclaw start/stop/status` |
|
|
332
356
|
| Dashboard | Web UI | Web UI (localhost:7788) |
|
|
333
357
|
| Memory | Markdown | Markdown (long-term + daily) |
|
|
334
|
-
| Skills | Plugin system | Three-tier +
|
|
335
|
-
| Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord |
|
|
358
|
+
| Skills | Plugin system | Three-tier + ClawHub marketplace |
|
|
359
|
+
| Channels | Discord, Telegram, WhatsApp | CLI, Web, Telegram, Discord, WhatsApp |
|
|
336
360
|
| Voice | — | Deepgram STT |
|
|
337
361
|
| LLM Providers | OpenAI, Anthropic, Gemini | DeepSeek, Grok, Claude, Gemini, Kimi, GLM |
|
|
338
362
|
| Daemon | Background process | PID-managed (`start`/`stop`/`status`) |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythonclaw"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.6.0"
|
|
8
8
|
description = "OpenClaw reimagined in pure Python — autonomous AI agent with memory, RAG, skills, web dashboard, and multi-channel support."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -12,7 +12,7 @@ requires-python = ">=3.10"
|
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "Eric Wang", email = "wangchen2007915@gmail.com"},
|
|
14
14
|
]
|
|
15
|
-
keywords = ["llm", "agent", "ai", "autonomous", "memory", "rag", "skills", "telegram", "chatbot", "framework"]
|
|
15
|
+
keywords = ["llm", "agent", "ai", "autonomous", "memory", "rag", "skills", "telegram", "discord", "whatsapp", "chatbot", "framework"]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 4 - Beta",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
@@ -31,6 +31,7 @@ dependencies = [
|
|
|
31
31
|
"google-generativeai",
|
|
32
32
|
"python-telegram-bot[job-queue]>=20.0",
|
|
33
33
|
"discord.py>=2.3",
|
|
34
|
+
"pywa[fastapi]>=2.8",
|
|
34
35
|
"apscheduler>=3.10",
|
|
35
36
|
"pyyaml>=6.0",
|
|
36
37
|
"fastapi>=0.100",
|
|
@@ -58,6 +59,7 @@ include = ["pythonclaw*"]
|
|
|
58
59
|
[tool.setuptools.package-data]
|
|
59
60
|
pythonclaw = [
|
|
60
61
|
"templates/**/*.py",
|
|
62
|
+
"templates/**/*.sh",
|
|
61
63
|
"templates/**/*.yaml",
|
|
62
64
|
"templates/**/*.md",
|
|
63
65
|
"web/static/**/*",
|
|
@@ -65,3 +67,15 @@ pythonclaw = [
|
|
|
65
67
|
|
|
66
68
|
[tool.setuptools.exclude-package-data]
|
|
67
69
|
"*" = ["__pycache__", "*.pyc", "*.pyo"]
|
|
70
|
+
|
|
71
|
+
[tool.ruff]
|
|
72
|
+
target-version = "py310"
|
|
73
|
+
line-length = 120
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint]
|
|
76
|
+
select = ["E", "F", "W", "I"]
|
|
77
|
+
ignore = ["E501"]
|
|
78
|
+
|
|
79
|
+
[tool.pytest.ini_options]
|
|
80
|
+
testpaths = ["tests"]
|
|
81
|
+
pythonpath = ["."]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Discord channel for PythonClaw.
|
|
3
3
|
|
|
4
|
-
Session IDs: "discord:{user_id}" (DMs) or "discord:{channel_id}" (guilds)
|
|
4
|
+
Session IDs: "discord:dm:{user_id}" (DMs) or "discord:{channel_id}" (guilds)
|
|
5
5
|
|
|
6
6
|
Commands
|
|
7
7
|
--------
|
|
@@ -9,21 +9,29 @@ Commands
|
|
|
9
9
|
!status — show session info
|
|
10
10
|
!compact [hint] — compact conversation history
|
|
11
11
|
<text> — forwarded to Agent.chat(), reply sent back
|
|
12
|
+
<image> — image attachments sent to LLM for analysis
|
|
12
13
|
|
|
13
14
|
The bot responds to:
|
|
14
15
|
- Direct messages (always)
|
|
15
|
-
- Channel mentions (@bot message) in guilds
|
|
16
|
-
-
|
|
16
|
+
- Channel mentions (@bot message) in guilds (when requireMention=true)
|
|
17
|
+
- All messages in whitelisted channels (when requireMention=false)
|
|
17
18
|
|
|
18
19
|
Access control
|
|
19
20
|
--------------
|
|
20
21
|
Set DISCORD_ALLOWED_USERS to a comma-separated list of Discord user IDs.
|
|
21
22
|
Set DISCORD_ALLOWED_CHANNELS to restrict which guild channels the bot listens in.
|
|
22
23
|
Leave empty to allow all.
|
|
24
|
+
|
|
25
|
+
Group behaviour
|
|
26
|
+
---------------
|
|
27
|
+
Set ``channels.discord.requireMention`` to ``true`` to require @bot mention
|
|
28
|
+
in guild channels. Default is ``false`` (respond when mentioned OR in
|
|
29
|
+
whitelisted channels).
|
|
23
30
|
"""
|
|
24
31
|
|
|
25
32
|
from __future__ import annotations
|
|
26
33
|
|
|
34
|
+
import base64
|
|
27
35
|
import logging
|
|
28
36
|
from typing import TYPE_CHECKING
|
|
29
37
|
|
|
@@ -52,11 +60,13 @@ class DiscordBot:
|
|
|
52
60
|
token: str,
|
|
53
61
|
allowed_users: list[int] | None = None,
|
|
54
62
|
allowed_channels: list[int] | None = None,
|
|
63
|
+
require_mention: bool = False,
|
|
55
64
|
) -> None:
|
|
56
65
|
self._sm = session_manager
|
|
57
66
|
self._token = token
|
|
58
67
|
self._allowed_users: set[int] = set(allowed_users) if allowed_users else set()
|
|
59
68
|
self._allowed_channels: set[int] = set(allowed_channels) if allowed_channels else set()
|
|
69
|
+
self._require_mention = require_mention
|
|
60
70
|
|
|
61
71
|
intents = discord.Intents.default()
|
|
62
72
|
intents.message_content = True
|
|
@@ -113,20 +123,27 @@ class DiscordBot:
|
|
|
113
123
|
is_dm = isinstance(message.channel, discord.DMChannel)
|
|
114
124
|
is_mentioned = client.user in message.mentions if not is_dm else False
|
|
115
125
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
126
|
+
if not is_dm:
|
|
127
|
+
if self._require_mention and not is_mentioned:
|
|
128
|
+
return
|
|
129
|
+
if not self._require_mention and not is_mentioned:
|
|
130
|
+
if not self._is_allowed_channel(message.channel.id):
|
|
131
|
+
return
|
|
119
132
|
|
|
120
133
|
if not self._is_allowed_user(message.author.id):
|
|
121
134
|
await message.reply("Sorry, you are not authorised to use this bot.")
|
|
122
135
|
return
|
|
123
136
|
|
|
124
137
|
content = message.content.strip()
|
|
125
|
-
# Remove bot mention from the beginning
|
|
126
138
|
if is_mentioned and client.user:
|
|
127
139
|
content = content.replace(f"<@{client.user.id}>", "").strip()
|
|
128
140
|
|
|
129
|
-
|
|
141
|
+
has_image = any(
|
|
142
|
+
a.content_type and a.content_type.startswith("image/")
|
|
143
|
+
for a in message.attachments
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if not content and not has_image:
|
|
130
147
|
return
|
|
131
148
|
|
|
132
149
|
# Command dispatch
|
|
@@ -141,7 +158,35 @@ class DiscordBot:
|
|
|
141
158
|
await self._cmd_compact(message, is_dm, hint)
|
|
142
159
|
return
|
|
143
160
|
|
|
144
|
-
|
|
161
|
+
chat_input = content or ""
|
|
162
|
+
if has_image:
|
|
163
|
+
chat_input = await self._build_image_input(
|
|
164
|
+
message, content or "What's in this image?"
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
await self._handle_chat(message, chat_input, is_dm)
|
|
168
|
+
|
|
169
|
+
# ── Image handling ────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
@staticmethod
|
|
172
|
+
async def _build_image_input(message: discord.Message, caption: str) -> list:
|
|
173
|
+
"""Download image attachments and build multimodal content array."""
|
|
174
|
+
parts: list[dict] = [{"type": "text", "text": caption}]
|
|
175
|
+
for att in message.attachments:
|
|
176
|
+
if att.content_type and att.content_type.startswith("image/"):
|
|
177
|
+
try:
|
|
178
|
+
data = await att.read()
|
|
179
|
+
b64 = base64.b64encode(data).decode()
|
|
180
|
+
media_type = att.content_type.split(";")[0]
|
|
181
|
+
parts.append({
|
|
182
|
+
"type": "image_url",
|
|
183
|
+
"image_url": {
|
|
184
|
+
"url": f"data:{media_type};base64,{b64}",
|
|
185
|
+
},
|
|
186
|
+
})
|
|
187
|
+
except Exception:
|
|
188
|
+
logger.warning("[Discord] Failed to download attachment %s", att.filename)
|
|
189
|
+
return parts
|
|
145
190
|
|
|
146
191
|
# ── Command implementations ───────────────────────────────────────────────
|
|
147
192
|
|
|
@@ -180,12 +225,24 @@ class DiscordBot:
|
|
|
180
225
|
for chunk in self._split_message(result or "(no result)"):
|
|
181
226
|
await message.reply(chunk)
|
|
182
227
|
|
|
183
|
-
async def _handle_chat(
|
|
228
|
+
async def _handle_chat(
|
|
229
|
+
self,
|
|
230
|
+
message: discord.Message,
|
|
231
|
+
content: str | list,
|
|
232
|
+
is_dm: bool,
|
|
233
|
+
) -> None:
|
|
184
234
|
sid = self._session_id(message.author.id if is_dm else message.channel.id, is_dm)
|
|
185
235
|
agent = self._sm.get_or_create(sid)
|
|
236
|
+
|
|
237
|
+
if self._sm.is_locked(sid):
|
|
238
|
+
await message.reply("Processing previous message\u2026")
|
|
239
|
+
|
|
186
240
|
async with message.channel.typing():
|
|
187
241
|
try:
|
|
188
|
-
|
|
242
|
+
async with self._sm.acquire(sid):
|
|
243
|
+
import asyncio
|
|
244
|
+
loop = asyncio.get_event_loop()
|
|
245
|
+
response = await loop.run_in_executor(None, agent.chat, content)
|
|
189
246
|
except Exception as exc:
|
|
190
247
|
logger.exception("[Discord] Agent.chat() raised an exception")
|
|
191
248
|
response = f"Sorry, something went wrong: {exc}"
|
|
@@ -219,11 +276,15 @@ def create_bot(session_manager: "SessionManager") -> "DiscordBot":
|
|
|
219
276
|
allowed_channels = config.get_int_list(
|
|
220
277
|
"channels", "discord", "allowedChannels", env="DISCORD_ALLOWED_CHANNELS",
|
|
221
278
|
)
|
|
279
|
+
require_mention = config.get_bool(
|
|
280
|
+
"channels", "discord", "requireMention", default=False,
|
|
281
|
+
)
|
|
222
282
|
return DiscordBot(
|
|
223
283
|
session_manager=session_manager,
|
|
224
284
|
token=token,
|
|
225
285
|
allowed_users=allowed_users or None,
|
|
226
286
|
allowed_channels=allowed_channels or None,
|
|
287
|
+
require_mention=require_mention,
|
|
227
288
|
)
|
|
228
289
|
|
|
229
290
|
|