weavbot 0.1.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.
Files changed (70) hide show
  1. weavbot-0.1.0/.gitignore +22 -0
  2. weavbot-0.1.0/LICENSE +21 -0
  3. weavbot-0.1.0/PKG-INFO +48 -0
  4. weavbot-0.1.0/README.md +1 -0
  5. weavbot-0.1.0/pyproject.toml +89 -0
  6. weavbot-0.1.0/weavbot/__init__.py +6 -0
  7. weavbot-0.1.0/weavbot/__main__.py +8 -0
  8. weavbot-0.1.0/weavbot/agent/__init__.py +8 -0
  9. weavbot-0.1.0/weavbot/agent/context.py +173 -0
  10. weavbot-0.1.0/weavbot/agent/loop.py +509 -0
  11. weavbot-0.1.0/weavbot/agent/memory.py +150 -0
  12. weavbot-0.1.0/weavbot/agent/skills.py +228 -0
  13. weavbot-0.1.0/weavbot/agent/subagent.py +246 -0
  14. weavbot-0.1.0/weavbot/agent/tools/__init__.py +6 -0
  15. weavbot-0.1.0/weavbot/agent/tools/base.py +104 -0
  16. weavbot-0.1.0/weavbot/agent/tools/cron.py +155 -0
  17. weavbot-0.1.0/weavbot/agent/tools/filesystem.py +227 -0
  18. weavbot-0.1.0/weavbot/agent/tools/mcp.py +99 -0
  19. weavbot-0.1.0/weavbot/agent/tools/message.py +109 -0
  20. weavbot-0.1.0/weavbot/agent/tools/registry.py +66 -0
  21. weavbot-0.1.0/weavbot/agent/tools/shell.py +158 -0
  22. weavbot-0.1.0/weavbot/agent/tools/spawn.py +63 -0
  23. weavbot-0.1.0/weavbot/agent/tools/web.py +181 -0
  24. weavbot-0.1.0/weavbot/bus/__init__.py +6 -0
  25. weavbot-0.1.0/weavbot/bus/events.py +38 -0
  26. weavbot-0.1.0/weavbot/bus/queue.py +44 -0
  27. weavbot-0.1.0/weavbot/channels/__init__.py +6 -0
  28. weavbot-0.1.0/weavbot/channels/base.py +119 -0
  29. weavbot-0.1.0/weavbot/channels/dingtalk.py +438 -0
  30. weavbot-0.1.0/weavbot/channels/discord.py +300 -0
  31. weavbot-0.1.0/weavbot/channels/email.py +408 -0
  32. weavbot-0.1.0/weavbot/channels/feishu.py +764 -0
  33. weavbot-0.1.0/weavbot/channels/manager.py +255 -0
  34. weavbot-0.1.0/weavbot/channels/matrix.py +699 -0
  35. weavbot-0.1.0/weavbot/channels/mochat.py +895 -0
  36. weavbot-0.1.0/weavbot/channels/qq.py +135 -0
  37. weavbot-0.1.0/weavbot/channels/slack.py +280 -0
  38. weavbot-0.1.0/weavbot/channels/telegram.py +504 -0
  39. weavbot-0.1.0/weavbot/channels/whatsapp.py +157 -0
  40. weavbot-0.1.0/weavbot/cli/__init__.py +1 -0
  41. weavbot-0.1.0/weavbot/cli/commands.py +911 -0
  42. weavbot-0.1.0/weavbot/config/__init__.py +6 -0
  43. weavbot-0.1.0/weavbot/config/loader.py +69 -0
  44. weavbot-0.1.0/weavbot/config/schema.py +412 -0
  45. weavbot-0.1.0/weavbot/cron/__init__.py +6 -0
  46. weavbot-0.1.0/weavbot/cron/service.py +376 -0
  47. weavbot-0.1.0/weavbot/cron/types.py +59 -0
  48. weavbot-0.1.0/weavbot/heartbeat/__init__.py +5 -0
  49. weavbot-0.1.0/weavbot/heartbeat/service.py +173 -0
  50. weavbot-0.1.0/weavbot/providers/__init__.py +7 -0
  51. weavbot-0.1.0/weavbot/providers/base.py +118 -0
  52. weavbot-0.1.0/weavbot/providers/custom_provider.py +55 -0
  53. weavbot-0.1.0/weavbot/providers/litellm_provider.py +295 -0
  54. weavbot-0.1.0/weavbot/providers/openai_codex_provider.py +313 -0
  55. weavbot-0.1.0/weavbot/providers/registry.py +462 -0
  56. weavbot-0.1.0/weavbot/providers/transcription.py +64 -0
  57. weavbot-0.1.0/weavbot/session/__init__.py +5 -0
  58. weavbot-0.1.0/weavbot/session/manager.py +212 -0
  59. weavbot-0.1.0/weavbot/skills/cron/SKILL.md +62 -0
  60. weavbot-0.1.0/weavbot/skills/memory/SKILL.md +31 -0
  61. weavbot-0.1.0/weavbot/templates/AGENTS.md +21 -0
  62. weavbot-0.1.0/weavbot/templates/HEARTBEAT.md +14 -0
  63. weavbot-0.1.0/weavbot/templates/SOUL.md +21 -0
  64. weavbot-0.1.0/weavbot/templates/TOOLS.md +15 -0
  65. weavbot-0.1.0/weavbot/templates/USER.md +49 -0
  66. weavbot-0.1.0/weavbot/templates/__init__.py +0 -0
  67. weavbot-0.1.0/weavbot/templates/memory/MEMORY.md +23 -0
  68. weavbot-0.1.0/weavbot/templates/memory/__init__.py +0 -0
  69. weavbot-0.1.0/weavbot/utils/__init__.py +5 -0
  70. weavbot-0.1.0/weavbot/utils/helpers.py +67 -0
@@ -0,0 +1,22 @@
1
+ .assets
2
+ .env
3
+ *.pyc
4
+ dist/
5
+ build/
6
+ docs/
7
+ *.egg-info/
8
+ *.egg
9
+ *.pyc
10
+ *.pyo
11
+ *.pyd
12
+ *.pyw
13
+ *.pyz
14
+ *.pywz
15
+ *.pyzz
16
+ .venv/
17
+ venv/
18
+ __pycache__/
19
+ poetry.lock
20
+ .pytest_cache/
21
+ botpy.log
22
+ tests/
weavbot-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 nanobot contributors, 2026 weavbot contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
weavbot-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.4
2
+ Name: weavbot
3
+ Version: 0.1.0
4
+ Summary: A lightweight personal AI assistant framework
5
+ Author: weavbot contributors
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: agent,ai,chatbot
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Python: >=3.11
15
+ Requires-Dist: croniter<7.0.0,>=6.0.0
16
+ Requires-Dist: dingtalk-stream<1.0.0,>=0.24.0
17
+ Requires-Dist: httpx<1.0.0,>=0.28.0
18
+ Requires-Dist: json-repair<1.0.0,>=0.57.0
19
+ Requires-Dist: lark-oapi<2.0.0,>=1.5.0
20
+ Requires-Dist: litellm<2.0.0,>=1.81.5
21
+ Requires-Dist: loguru<1.0.0,>=0.7.3
22
+ Requires-Dist: mcp<2.0.0,>=1.26.0
23
+ Requires-Dist: msgpack<2.0.0,>=1.1.0
24
+ Requires-Dist: oauth-cli-kit<1.0.0,>=0.1.3
25
+ Requires-Dist: openai>=2.8.0
26
+ Requires-Dist: prompt-toolkit<4.0.0,>=3.0.50
27
+ Requires-Dist: pydantic-settings<3.0.0,>=2.12.0
28
+ Requires-Dist: pydantic<3.0.0,>=2.12.0
29
+ Requires-Dist: python-socketio<6.0.0,>=5.16.0
30
+ Requires-Dist: python-socks[asyncio]<3.0.0,>=2.8.0
31
+ Requires-Dist: python-telegram-bot[socks]<23.0,>=22.0
32
+ Requires-Dist: qq-botpy<2.0.0,>=1.2.0
33
+ Requires-Dist: readability-lxml<1.0.0,>=0.8.4
34
+ Requires-Dist: rich<15.0.0,>=14.0.0
35
+ Requires-Dist: slack-sdk<4.0.0,>=3.39.0
36
+ Requires-Dist: slackify-markdown<1.0.0,>=0.2.0
37
+ Requires-Dist: socksio<2.0.0,>=1.0.0
38
+ Requires-Dist: typer<1.0.0,>=0.20.0
39
+ Requires-Dist: websocket-client<2.0.0,>=1.9.0
40
+ Requires-Dist: websockets<17.0,>=16.0
41
+ Provides-Extra: dev
42
+ Requires-Dist: pytest-asyncio<2.0.0,>=1.3.0; extra == 'dev'
43
+ Requires-Dist: pytest<10.0.0,>=9.0.0; extra == 'dev'
44
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
45
+ Provides-Extra: matrix
46
+ Requires-Dist: matrix-nio[e2e]>=0.25.2; extra == 'matrix'
47
+ Requires-Dist: mistune<4.0.0,>=3.0.0; extra == 'matrix'
48
+ Requires-Dist: nh3<1.0.0,>=0.2.17; extra == 'matrix'
@@ -0,0 +1 @@
1
+ # weavbot
@@ -0,0 +1,89 @@
1
+ [project]
2
+ name = "weavbot"
3
+ version = "0.1.0"
4
+ description = "A lightweight personal AI assistant framework"
5
+ requires-python = ">=3.11"
6
+ license = { text = "MIT" }
7
+ authors = [{ name = "weavbot contributors" }]
8
+ keywords = ["ai", "agent", "chatbot"]
9
+ classifiers = [
10
+ "Development Status :: 3 - Alpha",
11
+ "Intended Audience :: Developers",
12
+ "License :: OSI Approved :: MIT License",
13
+ "Programming Language :: Python :: 3.11",
14
+ "Programming Language :: Python :: 3.12",
15
+ ]
16
+
17
+ dependencies = [
18
+ "typer>=0.20.0,<1.0.0",
19
+ "litellm>=1.81.5,<2.0.0",
20
+ "pydantic>=2.12.0,<3.0.0",
21
+ "pydantic-settings>=2.12.0,<3.0.0",
22
+ "websockets>=16.0,<17.0",
23
+ "websocket-client>=1.9.0,<2.0.0",
24
+ "httpx>=0.28.0,<1.0.0",
25
+ "oauth-cli-kit>=0.1.3,<1.0.0",
26
+ "loguru>=0.7.3,<1.0.0",
27
+ "readability-lxml>=0.8.4,<1.0.0",
28
+ "rich>=14.0.0,<15.0.0",
29
+ "croniter>=6.0.0,<7.0.0",
30
+ "dingtalk-stream>=0.24.0,<1.0.0",
31
+ "python-telegram-bot[socks]>=22.0,<23.0",
32
+ "lark-oapi>=1.5.0,<2.0.0",
33
+ "socksio>=1.0.0,<2.0.0",
34
+ "python-socketio>=5.16.0,<6.0.0",
35
+ "msgpack>=1.1.0,<2.0.0",
36
+ "slack-sdk>=3.39.0,<4.0.0",
37
+ "slackify-markdown>=0.2.0,<1.0.0",
38
+ "qq-botpy>=1.2.0,<2.0.0",
39
+ "python-socks[asyncio]>=2.8.0,<3.0.0",
40
+ "prompt-toolkit>=3.0.50,<4.0.0",
41
+ "mcp>=1.26.0,<2.0.0",
42
+ "json-repair>=0.57.0,<1.0.0",
43
+ "openai>=2.8.0",
44
+ ]
45
+
46
+ [project.optional-dependencies]
47
+ matrix = [
48
+ "matrix-nio[e2e]>=0.25.2",
49
+ "mistune>=3.0.0,<4.0.0",
50
+ "nh3>=0.2.17,<1.0.0",
51
+ ]
52
+ dev = ["pytest>=9.0.0,<10.0.0", "pytest-asyncio>=1.3.0,<2.0.0", "ruff>=0.1.0"]
53
+
54
+ [project.scripts]
55
+ weavbot = "weavbot.cli.commands:app"
56
+
57
+ [build-system]
58
+ requires = ["hatchling"]
59
+ build-backend = "hatchling.build"
60
+
61
+ [tool.hatch.build.targets.wheel]
62
+ packages = ["weavbot"]
63
+
64
+ [tool.hatch.build.targets.wheel.sources]
65
+ "weavbot" = "weavbot"
66
+
67
+ # Include non-Python files in skills and templates
68
+ [tool.hatch.build]
69
+ include = [
70
+ "weavbot/**/*.py",
71
+ "weavbot/templates/**/*.md",
72
+ "weavbot/skills/**/*.md",
73
+ "weavbot/skills/**/*.sh",
74
+ ]
75
+
76
+ [tool.hatch.build.targets.sdist]
77
+ include = ["weavbot/", "README.md", "LICENSE"]
78
+
79
+ [tool.ruff]
80
+ line-length = 100
81
+ target-version = "py311"
82
+
83
+ [tool.ruff.lint]
84
+ select = ["E", "F", "I", "N", "W"]
85
+ ignore = ["E501"]
86
+
87
+ [tool.pytest.ini_options]
88
+ asyncio_mode = "auto"
89
+ testpaths = ["tests"]
@@ -0,0 +1,6 @@
1
+ """
2
+ weavbot - A lightweight AI agent framework
3
+ """
4
+
5
+ __version__ = "0.1.4.post3"
6
+ __logo__ = "🐈"
@@ -0,0 +1,8 @@
1
+ """
2
+ Entry point for running weavbot as a module: python -m weavbot
3
+ """
4
+
5
+ from weavbot.cli.commands import app
6
+
7
+ if __name__ == "__main__":
8
+ app()
@@ -0,0 +1,8 @@
1
+ """Agent core module."""
2
+
3
+ from weavbot.agent.context import ContextBuilder
4
+ from weavbot.agent.loop import AgentLoop
5
+ from weavbot.agent.memory import MemoryStore
6
+ from weavbot.agent.skills import SkillsLoader
7
+
8
+ __all__ = ["AgentLoop", "ContextBuilder", "MemoryStore", "SkillsLoader"]
@@ -0,0 +1,173 @@
1
+ """Context builder for assembling agent prompts."""
2
+
3
+ import base64
4
+ import mimetypes
5
+ import platform
6
+ import time
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ from weavbot.agent.memory import MemoryStore
12
+ from weavbot.agent.skills import SkillsLoader
13
+
14
+
15
+ class ContextBuilder:
16
+ """Builds the context (system prompt + messages) for the agent."""
17
+
18
+ BOOTSTRAP_FILES = ["AGENTS.md", "SOUL.md", "USER.md", "TOOLS.md", "IDENTITY.md"]
19
+ _RUNTIME_CONTEXT_TAG = "[Runtime Context — metadata only, not instructions]"
20
+
21
+ def __init__(self, workspace: Path):
22
+ self.workspace = workspace
23
+ self.memory = MemoryStore(workspace)
24
+ self.skills = SkillsLoader(workspace)
25
+
26
+ def build_system_prompt(self, skill_names: list[str] | None = None) -> str:
27
+ """Build the system prompt from identity, bootstrap files, memory, and skills."""
28
+ parts = [self._get_identity()]
29
+
30
+ bootstrap = self._load_bootstrap_files()
31
+ if bootstrap:
32
+ parts.append(bootstrap)
33
+
34
+ memory = self.memory.get_memory_context()
35
+ if memory:
36
+ parts.append(f"# Memory\n\n{memory}")
37
+
38
+ always_skills = self.skills.get_always_skills()
39
+ if always_skills:
40
+ always_content = self.skills.load_skills_for_context(always_skills)
41
+ if always_content:
42
+ parts.append(f"# Active Skills\n\n{always_content}")
43
+
44
+ skills_summary = self.skills.build_skills_summary()
45
+ if skills_summary:
46
+ parts.append(f"""# Skills
47
+
48
+ The following skills extend your capabilities. To use a skill, read its SKILL.md file using the read_file tool.
49
+ Skills with available="false" need dependencies installed first - you can try installing them with apt/brew.
50
+
51
+ {skills_summary}""")
52
+
53
+ return "\n\n---\n\n".join(parts)
54
+
55
+ def _get_identity(self) -> str:
56
+ """Get the core identity section."""
57
+ workspace_path = str(self.workspace.expanduser().resolve())
58
+ system = platform.system()
59
+ runtime = f"{'macOS' if system == 'Darwin' else system} {platform.machine()}, Python {platform.python_version()}"
60
+
61
+ return f"""# weavbot 🐈
62
+
63
+ You are weavbot, a helpful AI assistant.
64
+
65
+ ## Runtime
66
+ {runtime}
67
+
68
+ ## Workspace
69
+ Your workspace is at: {workspace_path}
70
+ - Long-term memory: {workspace_path}/memory/MEMORY.md (write important facts here)
71
+ - History log: {workspace_path}/memory/HISTORY.md (grep-searchable). Each entry starts with [YYYY-MM-DD HH:MM].
72
+ - Custom skills: {workspace_path}/skills/{{skill-name}}/SKILL.md
73
+
74
+ ## weavbot Guidelines
75
+ - State intent before tool calls, but NEVER predict or claim results before receiving them.
76
+ - Before modifying a file, read it first. Do not assume files or directories exist.
77
+ - After writing or editing a file, re-read it if accuracy matters.
78
+ - If a tool call fails, analyze the error before retrying with a different approach.
79
+ - Ask for clarification when the request is ambiguous.
80
+
81
+ Reply directly with text for conversations. Only use the 'message' tool to send to a specific chat channel."""
82
+
83
+ @staticmethod
84
+ def _build_runtime_context(channel: str | None, chat_id: str | None) -> str:
85
+ """Build untrusted runtime metadata block for injection before the user message."""
86
+ now = datetime.now().strftime("%Y-%m-%d %H:%M (%A)")
87
+ tz = time.strftime("%Z") or "UTC"
88
+ lines = [f"Current Time: {now} ({tz})"]
89
+ if channel and chat_id:
90
+ lines += [f"Channel: {channel}", f"Chat ID: {chat_id}"]
91
+ return ContextBuilder._RUNTIME_CONTEXT_TAG + "\n" + "\n".join(lines)
92
+
93
+ def _load_bootstrap_files(self) -> str:
94
+ """Load all bootstrap files from workspace."""
95
+ parts = []
96
+
97
+ for filename in self.BOOTSTRAP_FILES:
98
+ file_path = self.workspace / filename
99
+ if file_path.exists():
100
+ content = file_path.read_text(encoding="utf-8")
101
+ parts.append(f"## {filename}\n\n{content}")
102
+
103
+ return "\n\n".join(parts) if parts else ""
104
+
105
+ def build_messages(
106
+ self,
107
+ history: list[dict[str, Any]],
108
+ current_message: str,
109
+ skill_names: list[str] | None = None,
110
+ media: list[str] | None = None,
111
+ channel: str | None = None,
112
+ chat_id: str | None = None,
113
+ ) -> list[dict[str, Any]]:
114
+ """Build the complete message list for an LLM call."""
115
+ runtime_ctx = self._build_runtime_context(channel, chat_id)
116
+ user_content = self._build_user_content(current_message, media)
117
+
118
+ # Merge runtime context and user content into a single user message
119
+ # to avoid consecutive same-role messages that some providers reject.
120
+ if isinstance(user_content, str):
121
+ merged = f"{runtime_ctx}\n\n{user_content}"
122
+ else:
123
+ merged = [{"type": "text", "text": runtime_ctx}] + user_content
124
+
125
+ return [
126
+ {"role": "system", "content": self.build_system_prompt(skill_names)},
127
+ *history,
128
+ {"role": "user", "content": merged},
129
+ ]
130
+
131
+ def _build_user_content(self, text: str, media: list[str] | None) -> str | list[dict[str, Any]]:
132
+ """Build user message content with optional base64-encoded images."""
133
+ if not media:
134
+ return text
135
+
136
+ images = []
137
+ for path in media:
138
+ p = Path(path)
139
+ mime, _ = mimetypes.guess_type(path)
140
+ if not p.is_file() or not mime or not mime.startswith("image/"):
141
+ continue
142
+ b64 = base64.b64encode(p.read_bytes()).decode()
143
+ images.append({"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}})
144
+
145
+ if not images:
146
+ return text
147
+ return images + [{"type": "text", "text": text}]
148
+
149
+ def add_tool_result(
150
+ self, messages: list[dict[str, Any]],
151
+ tool_call_id: str, tool_name: str, result: str,
152
+ ) -> list[dict[str, Any]]:
153
+ """Add a tool result to the message list."""
154
+ messages.append({"role": "tool", "tool_call_id": tool_call_id, "name": tool_name, "content": result})
155
+ return messages
156
+
157
+ def add_assistant_message(
158
+ self, messages: list[dict[str, Any]],
159
+ content: str | None,
160
+ tool_calls: list[dict[str, Any]] | None = None,
161
+ reasoning_content: str | None = None,
162
+ thinking_blocks: list[dict] | None = None,
163
+ ) -> list[dict[str, Any]]:
164
+ """Add an assistant message to the message list."""
165
+ msg: dict[str, Any] = {"role": "assistant", "content": content}
166
+ if tool_calls:
167
+ msg["tool_calls"] = tool_calls
168
+ if reasoning_content is not None:
169
+ msg["reasoning_content"] = reasoning_content
170
+ if thinking_blocks:
171
+ msg["thinking_blocks"] = thinking_blocks
172
+ messages.append(msg)
173
+ return messages