waifucore 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 (148) hide show
  1. waifucore-0.1.0/.claude/settings.local.json +8 -0
  2. waifucore-0.1.0/.editorconfig +15 -0
  3. waifucore-0.1.0/.github/workflows/publish.yml +47 -0
  4. waifucore-0.1.0/.gitignore +10 -0
  5. waifucore-0.1.0/.vscode/settings.json +4 -0
  6. waifucore-0.1.0/CLAUDE.md +129 -0
  7. waifucore-0.1.0/LICENSE +21 -0
  8. waifucore-0.1.0/PKG-INFO +71 -0
  9. waifucore-0.1.0/README.md +36 -0
  10. waifucore-0.1.0/docs/README_zh.md +38 -0
  11. waifucore-0.1.0/install/install.ps1 +15 -0
  12. waifucore-0.1.0/install/install.sh +15 -0
  13. waifucore-0.1.0/pyproject.toml +82 -0
  14. waifucore-0.1.0/test/test_flow.py +105 -0
  15. waifucore-0.1.0/uv.lock +2250 -0
  16. waifucore-0.1.0/waifu/README.md +1 -0
  17. waifucore-0.1.0/waifu/__init__.py +11 -0
  18. waifucore-0.1.0/waifu/_cli_app.py +85 -0
  19. waifucore-0.1.0/waifu/agent/__init__.py +5 -0
  20. waifucore-0.1.0/waifu/agent/context.py +223 -0
  21. waifucore-0.1.0/waifu/agent/runner.py +179 -0
  22. waifucore-0.1.0/waifu/agent/utilai.py +47 -0
  23. waifucore-0.1.0/waifu/card/__init__.py +11 -0
  24. waifucore-0.1.0/waifu/card/importer.py +33 -0
  25. waifucore-0.1.0/waifu/card/parser.py +267 -0
  26. waifucore-0.1.0/waifu/cli/__init__.py +101 -0
  27. waifucore-0.1.0/waifu/cli/_console.py +4 -0
  28. waifucore-0.1.0/waifu/cli/_remote.py +72 -0
  29. waifucore-0.1.0/waifu/cli/character.py +253 -0
  30. waifucore-0.1.0/waifu/cli/config.py +217 -0
  31. waifucore-0.1.0/waifu/cli/conversation.py +205 -0
  32. waifucore-0.1.0/waifu/cli/cron.py +196 -0
  33. waifucore-0.1.0/waifu/cli/init.py +448 -0
  34. waifucore-0.1.0/waifu/cli/mcp.py +318 -0
  35. waifucore-0.1.0/waifu/cli/skill.py +344 -0
  36. waifucore-0.1.0/waifu/cli/start.py +101 -0
  37. waifucore-0.1.0/waifu/cli/version.py +10 -0
  38. waifucore-0.1.0/waifu/config/__init__.py +122 -0
  39. waifucore-0.1.0/waifu/cron/__init__.py +4 -0
  40. waifucore-0.1.0/waifu/cron/executor.py +66 -0
  41. waifucore-0.1.0/waifu/cron/scheduler.py +150 -0
  42. waifucore-0.1.0/waifu/gateway/__init__.py +21 -0
  43. waifucore-0.1.0/waifu/gateway/adapters/__init__.py +23 -0
  44. waifucore-0.1.0/waifu/gateway/adapters/discord.py +320 -0
  45. waifucore-0.1.0/waifu/gateway/adapters/http.py +94 -0
  46. waifucore-0.1.0/waifu/gateway/adapters/telegram.py +264 -0
  47. waifucore-0.1.0/waifu/gateway/adapters/waifu_node.py +448 -0
  48. waifucore-0.1.0/waifu/gateway/adapters/websocket.py +223 -0
  49. waifucore-0.1.0/waifu/gateway/config.py +111 -0
  50. waifucore-0.1.0/waifu/gateway/core.py +84 -0
  51. waifucore-0.1.0/waifu/gateway/lane.py +48 -0
  52. waifucore-0.1.0/waifu/gateway/session.py +55 -0
  53. waifucore-0.1.0/waifu/heartbeat/__init__.py +10 -0
  54. waifucore-0.1.0/waifu/heartbeat/handler.py +75 -0
  55. waifucore-0.1.0/waifu/heartbeat/manager.py +78 -0
  56. waifucore-0.1.0/waifu/hook/__init__.py +79 -0
  57. waifucore-0.1.0/waifu/hook/builtin.py +260 -0
  58. waifucore-0.1.0/waifu/hook/memory.py +126 -0
  59. waifucore-0.1.0/waifu/logger.py +130 -0
  60. waifucore-0.1.0/waifu/mcp/__init__.py +5 -0
  61. waifucore-0.1.0/waifu/mcp/manager.py +48 -0
  62. waifucore-0.1.0/waifu/mcp/sse.py +88 -0
  63. waifucore-0.1.0/waifu/mcp/stdio.py +108 -0
  64. waifucore-0.1.0/waifu/node/__init__.py +3 -0
  65. waifucore-0.1.0/waifu/node/manager.py +97 -0
  66. waifucore-0.1.0/waifu/plugin/__init__.py +207 -0
  67. waifucore-0.1.0/waifu/plugin/builtin.py +29 -0
  68. waifucore-0.1.0/waifu/plugin/loader.py +64 -0
  69. waifucore-0.1.0/waifu/router/__init__.py +143 -0
  70. waifucore-0.1.0/waifu/router/analytics.py +48 -0
  71. waifucore-0.1.0/waifu/router/attachments.py +39 -0
  72. waifucore-0.1.0/waifu/router/character.py +180 -0
  73. waifucore-0.1.0/waifu/router/chat.py +55 -0
  74. waifucore-0.1.0/waifu/router/config.py +35 -0
  75. waifucore-0.1.0/waifu/router/conversation.py +85 -0
  76. waifucore-0.1.0/waifu/router/gateway.py +26 -0
  77. waifucore-0.1.0/waifu/router/health.py +8 -0
  78. waifucore-0.1.0/waifu/router/mcp.py +57 -0
  79. waifucore-0.1.0/waifu/router/plugin.py +68 -0
  80. waifucore-0.1.0/waifu/router/session.py +37 -0
  81. waifucore-0.1.0/waifu/router/skill.py +43 -0
  82. waifucore-0.1.0/waifu/router/status.py +13 -0
  83. waifucore-0.1.0/waifu/router/tools.py +23 -0
  84. waifucore-0.1.0/waifu/router/waifu_node.py +23 -0
  85. waifucore-0.1.0/waifu/router/ws.py +23 -0
  86. waifucore-0.1.0/waifu/service/__init__.py +18 -0
  87. waifucore-0.1.0/waifu/service/character.py +264 -0
  88. waifucore-0.1.0/waifu/service/command.py +243 -0
  89. waifucore-0.1.0/waifu/service/config.py +43 -0
  90. waifucore-0.1.0/waifu/service/gateway.py +76 -0
  91. waifucore-0.1.0/waifu/service/mcp.py +154 -0
  92. waifucore-0.1.0/waifu/service/plugin.py +36 -0
  93. waifucore-0.1.0/waifu/service/skill.py +46 -0
  94. waifucore-0.1.0/waifu/service/status.py +74 -0
  95. waifucore-0.1.0/waifu/skill/__init__.py +14 -0
  96. waifucore-0.1.0/waifu/skill/hook.py +22 -0
  97. waifucore-0.1.0/waifu/skill/installer.py +137 -0
  98. waifucore-0.1.0/waifu/skill/loader.py +53 -0
  99. waifucore-0.1.0/waifu/skill/manifest.py +96 -0
  100. waifucore-0.1.0/waifu/skill/registry.py +29 -0
  101. waifucore-0.1.0/waifu/skill/tool.py +34 -0
  102. waifucore-0.1.0/waifu/store/__init__.py +22 -0
  103. waifucore-0.1.0/waifu/store/character.py +42 -0
  104. waifucore-0.1.0/waifu/store/conversation.py +477 -0
  105. waifucore-0.1.0/waifu/store/cron.py +78 -0
  106. waifucore-0.1.0/waifu/store/history_tree.py +277 -0
  107. waifucore-0.1.0/waifu/store/memory.py +357 -0
  108. waifucore-0.1.0/waifu/store/models.py +215 -0
  109. waifucore-0.1.0/waifu/store/paths.py +115 -0
  110. waifucore-0.1.0/waifu/store/repositories/__init__.py +14 -0
  111. waifucore-0.1.0/waifu/store/repositories/analytics.py +72 -0
  112. waifucore-0.1.0/waifu/store/repositories/character.py +64 -0
  113. waifucore-0.1.0/waifu/store/repositories/config.py +44 -0
  114. waifucore-0.1.0/waifu/store/repositories/mcp.py +30 -0
  115. waifucore-0.1.0/waifu/store/repositories/plugin.py +31 -0
  116. waifucore-0.1.0/waifu/tool/__init__.py +19 -0
  117. waifucore-0.1.0/waifu/tool/cron.py +125 -0
  118. waifucore-0.1.0/waifu/tool/file_ops.py +217 -0
  119. waifucore-0.1.0/waifu/tool/read.py +28 -0
  120. waifucore-0.1.0/waifu/tool/shell.py +50 -0
  121. waifucore-0.1.0/waifu/tool/webfetch.py +60 -0
  122. waifucore-0.1.0/waifu/tool/websearch.py +200 -0
  123. waifucore-0.1.0/waifu/tool/write.py +30 -0
  124. waifucore-0.1.0/waifu/voice/__init__.py +4 -0
  125. waifucore-0.1.0/waifu/voice/lipsync.py +38 -0
  126. waifucore-0.1.0/waifu/voice/service.py +156 -0
  127. waifucore-0.1.0/waifu/waifu.py +735 -0
  128. waifucore-0.1.0/waifu_sdk/README.md +1 -0
  129. waifucore-0.1.0/waifu_sdk/pyproject.toml +27 -0
  130. waifucore-0.1.0/waifu_sdk/waifu_sdk/__init__.py +33 -0
  131. waifucore-0.1.0/waifu_sdk/waifu_sdk/adapter.py +38 -0
  132. waifucore-0.1.0/waifu_sdk/waifu_sdk/hook.py +30 -0
  133. waifucore-0.1.0/waifu_sdk/waifu_sdk/mcp.py +24 -0
  134. waifucore-0.1.0/waifu_sdk/waifu_sdk/plugin.py +47 -0
  135. waifucore-0.1.0/waifu_sdk/waifu_sdk/types.py +51 -0
  136. waifucore-0.1.0/waifu_types/README.md +1 -0
  137. waifucore-0.1.0/waifu_types/pyproject.toml +15 -0
  138. waifucore-0.1.0/waifu_types/waifu_types/__init__.py +59 -0
  139. waifucore-0.1.0/waifu_types/waifu_types/adapter.py +38 -0
  140. waifucore-0.1.0/waifu_types/waifu_types/hook.py +42 -0
  141. waifucore-0.1.0/waifu_types/waifu_types/mcp.py +27 -0
  142. waifucore-0.1.0/waifu_types/waifu_types/msg.py +150 -0
  143. waifucore-0.1.0/waifu_types/waifu_types/plugin.py +69 -0
  144. waifucore-0.1.0/waifu_types/waifu_types/prompt.py +23 -0
  145. waifucore-0.1.0/waifu_types/waifu_types/services.py +98 -0
  146. waifucore-0.1.0/waifu_types/waifu_types/session.py +14 -0
  147. waifucore-0.1.0/waifu_types/waifu_types/trace.py +26 -0
  148. waifucore-0.1.0/waifu_types/waifu_types/types.py +104 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "mcp__plugin_context7_context7__resolve-library-id",
5
+ "mcp__plugin_context7_context7__query-docs"
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,15 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 4
9
+ end_of_line = crlf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = false
12
+ insert_final_newline = false
13
+
14
+ [*.yml]
15
+ indent_size = 2
@@ -0,0 +1,47 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish-main:
10
+ name: Publish waifucore
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - uses: astral-sh/setup-uv@v8.2.0
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Build waifu-ai
22
+ run: uv build --package waifu
23
+
24
+ - name: Publish waifu-ai
25
+ uses: pypa/gh-action-pypi-publish@release/v1
26
+ with:
27
+ packages-dir: dist/
28
+
29
+ publish-sdk:
30
+ name: Publish waifu_sdk
31
+ runs-on: ubuntu-latest
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - uses: astral-sh/setup-uv@v8.2.0
38
+ with:
39
+ enable-cache: true
40
+
41
+ - name: Build waifu_sdk
42
+ run: uv build --package waifu_sdk --wheel
43
+
44
+ - name: Publish waifu_sdk
45
+ uses: pypa/gh-action-pypi-publish@release/v1
46
+ with:
47
+ packages-dir: dist/
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+
3
+ .DS_Store
4
+ .env
5
+ .pytest_cache
6
+
7
+ *.egg-info/
8
+ .installed.cfg
9
+ *.egg
10
+ /localdocs
@@ -0,0 +1,4 @@
1
+ {
2
+ "python.analysis.typeCheckingMode": "basic",
3
+ "python.analysis.diagnosticMode": "workspace"
4
+ }
@@ -0,0 +1,129 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ # Install dependencies (uv workspace)
9
+ uv sync
10
+
11
+ # Run the server (development with hot reload)
12
+ waifu start --config config.toml --reload
13
+
14
+ # Run the server with a custom FastAPI app
15
+ waifu start --app myapp:app --host 0.0.0.0 --port 8765
16
+
17
+ # Lint
18
+ uvx ruff check waifu/ waifu_types/
19
+ uvx ruff check --fix waifu/ waifu_types/
20
+
21
+ # Format
22
+ uvx ruff format waifu/ waifu_types/
23
+ ```
24
+
25
+ Config file formats supported: `.toml`, `.yaml`/`.yml`, `.json`.
26
+
27
+ ## Architecture
28
+
29
+ This is a **uv workspace monorepo** with three packages:
30
+
31
+ - `waifu_types/` — Shared ABCs/Protocols/Pydantic models (only depends on pydantic). The source of truth for all interfaces.
32
+ - `waifu_sdk/` — Thin re-export of `waifu_types` for third-party plugin authors.
33
+ - `waifu/` — Server implementation (depends on `waifu_types`, `fastapi`, `hai`, `shuvi`).
34
+
35
+ ### Request Lifecycle
36
+
37
+ ```
38
+ Adapter (WS/Discord/Telegram/HTTP)
39
+ → IncomingRequest
40
+ → GatewayCore.dispatch()
41
+ → Lane[conversation_id].enqueue() ← per-conversation serial queue
42
+ → HookPipeline.run_pre()
43
+ → ContextBuilder.build() ← assembles system_prompt + messages + tools
44
+ → AgentRunner.run() ← wraps shuvi.Agent (tool-calling loop)
45
+ → HookPipeline.run_post()
46
+ → OutgoingResponse
47
+ → Adapter.send()
48
+ ```
49
+
50
+ ### Key Modules
51
+
52
+ **`waifu/waifu.py` — `Waifu` class (Composition Root)**
53
+ The top-level class that wires everything together using a fluent builder API:
54
+ ```python
55
+ waifu = (
56
+ Waifu(config)
57
+ .mount(WebSocketAdapter())
58
+ .mount(DiscordAdapter(token=TOKEN))
59
+ .use(RateLimitHook(max_rpm=60))
60
+ )
61
+ await waifu.mcp_service.add("fs", "stdio", command=[...])
62
+ await waifu.start()
63
+ ```
64
+
65
+ **`waifu/gateway/`** — Ingress layer
66
+ - `GatewayCore`: mounts adapters, manages sessions, dispatches to lane queues
67
+ - `SessionManager`: TTL-based session store keyed by `conversation_id`
68
+ - `LaneManager`: one `asyncio.Queue` per `conversation_id` ensuring serial processing
69
+ - `adapters/`: `WebSocketAdapter` (SESSION mode, chunk streaming + Live2D events), `DiscordAdapter`, `TelegramAdapter`, `HTTPAdapter` (SSE)
70
+
71
+ **`waifu/hook/`** — Pre/post middleware chain
72
+ - `BaseHook` (from `waifu_types`): `async pre(ctx)` and `async post(ctx)` with `priority` (lower = earlier)
73
+ - `HookContext`: carries `request`, `session`, `messages`, `response`, `events`, `abort`
74
+ - Built-in hooks in `waifu/hook/builtin.py`: `RateLimitHook`, `MemoryHook`, `EmotionHook`, `LoggingHook`, `TTSHook`
75
+
76
+ **`waifu/plugin/`** — Plugin system
77
+ - `PluginManager.load(plugin, config)` calls `plugin.on_load(ctx)` where plugins register hooks/tools/MCP adapters
78
+ - `PluginRegistry`: holds all registered tools/MCP adapters for the `ContextBuilder`
79
+ - Built-in plugin wrappers in `waifu/plugin/builtin.py`: `ToolPlugin`, `HookPlugin`
80
+
81
+ **`waifu/mcp/`** — MCP bridge (system-level, not a plugin)
82
+ - `MCPManager`: owns adapter lifecycle, registers tools into `PluginRegistry`
83
+ - `StdioMCPAdapter`: spawns a subprocess (JSON-RPC over stdin/stdout)
84
+ - `SSEMCPAdapter`: connects to HTTP SSE MCP server
85
+
86
+ **`waifu/router/`** — FastAPI routers
87
+ - `GET /health`, `WS /ws`, `POST /chat` (SSE), `GET|POST|DELETE /plugins/*`
88
+ - `include_routers(app)` mounts all routers; uses `Depends(get_waifu)` for DI
89
+
90
+ **`waifu/voice/`** — Optional voice pipeline
91
+ - `VoiceService`: STT (Whisper-compatible) → text → TTS → audio + lipsync events
92
+ - Enabled via `VoiceConfig` in `WaifuConfig`; WebSocket adapter handles `type:"voice"` messages
93
+
94
+ ### Conversation Modes
95
+
96
+ | Mode | `conversation_id` key | History scope | Multi-user |
97
+ |---|---|---|---|
98
+ | `SESSION` | `ws:{session_token}` | Per WebSocket session | No |
99
+ | `DIRECT` | `{source}:dm:{user_id}` | Per user, persistent | No |
100
+ | `GROUP` | `{source}:{room/channel}:{id}` | Per room, persistent | Yes (author prefix added) |
101
+
102
+ GROUP mode: history messages get `[DisplayName]:` prefix; adapter only dispatches on `@mention` or `/shuvi`.
103
+
104
+ ### Configuration (`WaifuConfig`)
105
+
106
+ ```python
107
+ class WaifuConfig(BaseModel):
108
+ gateway: GatewayConfig # host, port, session_ttl, lane_max_queue
109
+ context: ContextConfig # system_prompt_template, max_history_messages, history_strategy
110
+ provider: ProviderConfig # api_key, base_url, model, temperature, stream
111
+ voice: Optional[VoiceConfig] # stt, tts, lipsync_fps
112
+ ```
113
+
114
+ ### Third-party Extension
115
+
116
+ Plugin/adapter authors install `waifu-sdk` (re-exports `waifu_types`). All ABCs are in `waifu_types/waifu_types/`:
117
+ - `BasePlugin` + `PluginContext` — `plugin.py`
118
+ - `BaseGatewayAdapter` — `adapter.py`
119
+ - `BaseHook` + `HookContext` — `hook.py`
120
+ - `BaseMCPAdapter` — `mcp.py`
121
+ - `IncomingRequest`, `OutgoingResponse`, `Attachment` — `types.py`
122
+
123
+ ### External Dependencies
124
+
125
+ - **`hai`** — LLM providers (OpenAI/Anthropic/Google/Ollama), tools, loaders, vector/graph stores
126
+ - **`shuvi`** — Agent inner/outer loop with tool-calling; `shuvi.Agent` takes `AgentConfig` + messages
127
+ - **`fastapi`** + **`uvicorn`** — HTTP/WebSocket server
128
+ - **`discord.py`** — Discord adapter (optional)
129
+ - **`python-telegram-bot`** — Telegram adapter (optional)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 waifu lab
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.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: waifucore
3
+ Version: 0.1.0
4
+ Author-email: phillychi3 <phillychi3@gmail.com>
5
+ License: MIT License
6
+ License-File: LICENSE
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: croniter>=2.0
15
+ Requires-Dist: fastapi<0.136.0,>=0.135.1
16
+ Requires-Dist: httpx>=0.27
17
+ Requires-Dist: hydrogen-ai>=0.1.3
18
+ Requires-Dist: loguru>=0.7
19
+ Requires-Dist: peewee>=3.17
20
+ Requires-Dist: python-multipart>=0.0.30
21
+ Requires-Dist: pyyaml>=6.0
22
+ Requires-Dist: rich>=13.0
23
+ Requires-Dist: shuvi>=0.1.2
24
+ Requires-Dist: textual>=0.89.1
25
+ Requires-Dist: tomli-w>=1.0
26
+ Requires-Dist: uvicorn[standard]>=0.30
27
+ Provides-Extra: all
28
+ Requires-Dist: discord-py>=2.7.1; extra == 'all'
29
+ Requires-Dist: python-telegram-bot>=22.7; extra == 'all'
30
+ Provides-Extra: discord
31
+ Requires-Dist: discord-py>=2.7.1; extra == 'discord'
32
+ Provides-Extra: telegram
33
+ Requires-Dist: python-telegram-bot>=22.7; extra == 'telegram'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # waifu
37
+ [English](https://github.com/waifu-lab/waifu/blob/main/README.md) | [Chinese](https://github.com/waifu-lab/waifu/blob/main/docs/README_zh.md)
38
+ A modular, extensible AI companion server built with Python. `waifu` covers conversational AI assistants.
39
+
40
+ ---
41
+
42
+ ## Roadmap
43
+
44
+ - [ ] Support for multi-agent collaboration and orchestration
45
+ - [ ] Plugin marketplace / package repository
46
+ - [ ] Automatic generation of OpenAPI schemas for plugin tools
47
+ - [ ] wait...
48
+
49
+ ---
50
+
51
+ ## Development
52
+
53
+
54
+ **Setup**
55
+
56
+ ```bash
57
+ # Clone the repo
58
+ git clone https://github.com/waifu-lab/waifu
59
+ cd waifu
60
+
61
+ # Install all workspace dependencies
62
+ uv sync
63
+ ```
64
+
65
+ **Running**
66
+
67
+ ```bash
68
+ # Start the server with hot reload
69
+ waifu start --config config.toml --reload
70
+ ```
71
+
@@ -0,0 +1,36 @@
1
+ # waifu
2
+ [English](https://github.com/waifu-lab/waifu/blob/main/README.md) | [Chinese](https://github.com/waifu-lab/waifu/blob/main/docs/README_zh.md)
3
+ A modular, extensible AI companion server built with Python. `waifu` covers conversational AI assistants.
4
+
5
+ ---
6
+
7
+ ## Roadmap
8
+
9
+ - [ ] Support for multi-agent collaboration and orchestration
10
+ - [ ] Plugin marketplace / package repository
11
+ - [ ] Automatic generation of OpenAPI schemas for plugin tools
12
+ - [ ] wait...
13
+
14
+ ---
15
+
16
+ ## Development
17
+
18
+
19
+ **Setup**
20
+
21
+ ```bash
22
+ # Clone the repo
23
+ git clone https://github.com/waifu-lab/waifu
24
+ cd waifu
25
+
26
+ # Install all workspace dependencies
27
+ uv sync
28
+ ```
29
+
30
+ **Running**
31
+
32
+ ```bash
33
+ # Start the server with hot reload
34
+ waifu start --config config.toml --reload
35
+ ```
36
+
@@ -0,0 +1,38 @@
1
+ # waifu
2
+
3
+ [English](https://github.com/waifu-lab/waifu/blob/main/README.md) | [中文](https://github.com/waifu-lab/waifu/blob/main/docs/README_zh.md)
4
+
5
+ 一個以 Python 打造的模組化、可擴展 AI 伴侶伺服器。`waifu` 涵蓋對話式 AI 助理.
6
+
7
+ ---
8
+
9
+ ## Roadmap
10
+
11
+ - [ ] 多 Agent 協作編排支援
12
+ - [ ] Plugin 市集 / 套件倉庫
13
+ - [ ] Plugin 工具的 OpenAPI Schema 自動生成
14
+ - [ ] wait...
15
+
16
+ ---
17
+
18
+ ## 開發
19
+
20
+
21
+ **環境設置**
22
+
23
+ ```bash
24
+ # 複製倉庫
25
+ git clone https://github.com/waifu-lab/waifu
26
+ cd waifu
27
+
28
+ # 安裝所有 workspace 相依套件
29
+ uv sync
30
+ ```
31
+
32
+ **啟動伺服器**
33
+
34
+ ```bash
35
+ # 以熱重載模式啟動
36
+ waifu start --config config.toml --reload
37
+ ```
38
+
@@ -0,0 +1,15 @@
1
+ #Requires -Version 5.1
2
+ $ErrorActionPreference = 'Stop'
3
+
4
+ if (Get-Command uv -ErrorAction SilentlyContinue) {
5
+ uv tool install waifu-ai
6
+ } elseif (Get-Command pip -ErrorAction SilentlyContinue) {
7
+ pip install waifu-ai
8
+ } elseif (Get-Command pip3 -ErrorAction SilentlyContinue) {
9
+ pip3 install waifu-ai
10
+ } else {
11
+ Write-Error "Neither uv nor pip found. Please install Python first."
12
+ exit 1
13
+ }
14
+
15
+ Write-Host "waifu installed. Run 'waifu init' to get started."
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ if command -v uv >/dev/null 2>&1; then
5
+ uv tool install waifu-ai
6
+ elif command -v pip >/dev/null 2>&1; then
7
+ pip install waifu-ai
8
+ elif command -v pip3 >/dev/null 2>&1; then
9
+ pip3 install waifu-ai
10
+ else
11
+ echo "Error: neither uv nor pip found. Please install Python first." >&2
12
+ exit 1
13
+ fi
14
+
15
+ echo "waifu installed. Run 'waifu init' to get started."
@@ -0,0 +1,82 @@
1
+ [project]
2
+ name = "waifucore"
3
+ version = "0.1.0"
4
+ description = ""
5
+ authors = [{ name = "phillychi3", email = "phillychi3@gmail.com" }]
6
+ requires-python = ">=3.10"
7
+ readme = "README.md"
8
+ license = { text = "MIT License" }
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.10",
12
+ "Programming Language :: Python :: 3.11",
13
+ "Programming Language :: Python :: 3.12",
14
+ "Programming Language :: Python :: 3.13",
15
+ "Programming Language :: Python :: 3.14",
16
+ ]
17
+ dependencies = [
18
+ "fastapi (>=0.135.1,<0.136.0)",
19
+ "python-multipart>=0.0.30",
20
+ "httpx>=0.27",
21
+ "uvicorn[standard]>=0.30",
22
+ "textual>=0.89.1",
23
+ "peewee>=3.17",
24
+ "croniter>=2.0",
25
+ "tomli-w>=1.0",
26
+ "pyyaml>=6.0",
27
+ "loguru>=0.7",
28
+ "rich>=13.0",
29
+ "shuvi>=0.1.2",
30
+ "hydrogen-ai>=0.1.3",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ discord = ["discord-py>=2.7.1"]
35
+ telegram = ["python-telegram-bot>=22.7"]
36
+ all = ["discord-py>=2.7.1", "python-telegram-bot>=22.7"]
37
+
38
+ [build-system]
39
+ requires = ["hatchling"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["waifu", "waifu_types/waifu_types"]
44
+
45
+ [project.scripts]
46
+ waifu = "waifu.cli:main"
47
+
48
+ [tool.pytest.ini_options]
49
+ asyncio_mode = "auto"
50
+ pythonpath = [".", "waifu_types", "waifu_sdk"]
51
+ testpaths = ["test"]
52
+
53
+ [tool.ruff]
54
+ lint.select = ["ASYNC", "ARG", "B", "C", "C4", "E", "F", "Q", "W", "I"]
55
+
56
+ lint.ignore = ["F401"]
57
+
58
+ lint.unfixable = ["F401"]
59
+
60
+ line-length = 120
61
+
62
+ [tool.uv.workspace]
63
+ members = ["waifu_sdk", "waifu_types"]
64
+
65
+ [dependency-groups]
66
+ dev = [
67
+ "httpx>=0.27",
68
+ "ftfy>=6.0",
69
+ "pytest>=9.0.3",
70
+ "pytest-asyncio>=1.3.0",
71
+ "discord-py>=2.7.1",
72
+ "python-telegram-bot>=22.7",
73
+ ]
74
+
75
+ [tool.pyright]
76
+ include = ["waifu", "waifu_sdk", "waifu_types"]
77
+
78
+ pythonVersion = "3.10"
79
+
80
+ typeCheckingMode = "basic"
81
+
82
+ reportPrivateUsage = false
@@ -0,0 +1,105 @@
1
+ """
2
+ 之後刪除
3
+ """
4
+
5
+ import uuid
6
+
7
+ import pytest
8
+
9
+ from waifu.plugin.builtin import ToolPlugin
10
+ from waifu.store import AppPaths
11
+ from waifu.tool.read import read_tool
12
+ from waifu.tool.shell import shell_tool
13
+ from waifu.tool.webfetch import webfetch_tool
14
+ from waifu.tool.write import write_tool
15
+ from waifu.waifu import Waifu
16
+ from waifu_types import ConversationMode, IncomingRequest
17
+ from waifu_types.types import Sender
18
+
19
+
20
+ def _load_config():
21
+ """從 AppPaths 找到 config.toml 並解析"""
22
+ paths = AppPaths.resolve()
23
+ config_path = paths.config_file
24
+ if not config_path.exists():
25
+ pytest.skip(f"config not found: {config_path}")
26
+
27
+ try:
28
+ import tomllib
29
+ except ImportError:
30
+ import tomli as tomllib # type: ignore[no-redef]
31
+
32
+ with open(config_path, "rb") as f:
33
+ data = tomllib.load(f)
34
+
35
+ from waifu.config import WaifuConfig
36
+
37
+ return WaifuConfig(**data)
38
+
39
+
40
+ @pytest.fixture(scope="module")
41
+ def config():
42
+ return _load_config()
43
+
44
+
45
+ @pytest.fixture(scope="module")
46
+ async def waifu(config):
47
+ """建立真實 Waifu 實例,掛載所有基礎工具"""
48
+ w = Waifu(config)
49
+ await w.plugin(ToolPlugin("basic", [read_tool, write_tool, shell_tool, webfetch_tool]))
50
+ return w
51
+
52
+
53
+ def make_request(content: str) -> IncomingRequest:
54
+ chunks = []
55
+ return IncomingRequest(
56
+ conversation_id=f"test:{uuid.uuid4()}",
57
+ mode=ConversationMode.session,
58
+ sender=Sender(user_id="test-user", display_name="Tester"),
59
+ source="test",
60
+ content=content,
61
+ reply_fn=lambda **kw: chunks.append(kw.get("chunk", "")),
62
+ )
63
+
64
+
65
+ @pytest.mark.asyncio
66
+ async def test_plain_qa(waifu):
67
+ """基本問答,不需要工具"""
68
+ req = make_request("請用一句話回答:1 + 1 等於多少?")
69
+ response = await waifu._dispatch(req)
70
+
71
+ assert response.content.strip() != ""
72
+ assert "2" in response.content
73
+
74
+
75
+ @pytest.mark.asyncio
76
+ async def test_tool_read(waifu, tmp_path):
77
+ """讓 LLM 讀取一個測試檔案,驗證 read tool 被正確呼叫"""
78
+ test_file = tmp_path / "secret.txt"
79
+ test_file.write_text("waifu_read_ok", encoding="utf-8")
80
+
81
+ req = make_request(f"請使用 read 工具讀取這個檔案並告訴我裡面的內容:{test_file}")
82
+ response = await waifu._dispatch(req)
83
+
84
+ assert "waifu_read_ok" in response.content
85
+
86
+
87
+ @pytest.mark.asyncio
88
+ async def test_tool_write(waifu, tmp_path):
89
+ """讓 LLM 寫入一個檔案,驗證 write tool 被正確呼叫且檔案存在"""
90
+ dest = tmp_path / "waifu_out.txt"
91
+
92
+ req = make_request(f"請使用 write 工具將文字 'hello_from_waifu' 寫入這個路徑:{dest}")
93
+ response = await waifu._dispatch(req)
94
+
95
+ assert dest.exists(), "write tool 沒有建立檔案"
96
+ assert "hello_from_waifu" in dest.read_text(encoding="utf-8")
97
+
98
+
99
+ @pytest.mark.asyncio
100
+ async def test_tool_shell(waifu):
101
+ """讓 LLM 執行 shell 指令,驗證 shell tool 回傳結果"""
102
+ req = make_request("請使用 shell 工具執行 'echo waifu_shell_test',並告訴我輸出結果")
103
+ response = await waifu._dispatch(req)
104
+
105
+ assert "waifu_shell_test" in response.content