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.
- waifucore-0.1.0/.claude/settings.local.json +8 -0
- waifucore-0.1.0/.editorconfig +15 -0
- waifucore-0.1.0/.github/workflows/publish.yml +47 -0
- waifucore-0.1.0/.gitignore +10 -0
- waifucore-0.1.0/.vscode/settings.json +4 -0
- waifucore-0.1.0/CLAUDE.md +129 -0
- waifucore-0.1.0/LICENSE +21 -0
- waifucore-0.1.0/PKG-INFO +71 -0
- waifucore-0.1.0/README.md +36 -0
- waifucore-0.1.0/docs/README_zh.md +38 -0
- waifucore-0.1.0/install/install.ps1 +15 -0
- waifucore-0.1.0/install/install.sh +15 -0
- waifucore-0.1.0/pyproject.toml +82 -0
- waifucore-0.1.0/test/test_flow.py +105 -0
- waifucore-0.1.0/uv.lock +2250 -0
- waifucore-0.1.0/waifu/README.md +1 -0
- waifucore-0.1.0/waifu/__init__.py +11 -0
- waifucore-0.1.0/waifu/_cli_app.py +85 -0
- waifucore-0.1.0/waifu/agent/__init__.py +5 -0
- waifucore-0.1.0/waifu/agent/context.py +223 -0
- waifucore-0.1.0/waifu/agent/runner.py +179 -0
- waifucore-0.1.0/waifu/agent/utilai.py +47 -0
- waifucore-0.1.0/waifu/card/__init__.py +11 -0
- waifucore-0.1.0/waifu/card/importer.py +33 -0
- waifucore-0.1.0/waifu/card/parser.py +267 -0
- waifucore-0.1.0/waifu/cli/__init__.py +101 -0
- waifucore-0.1.0/waifu/cli/_console.py +4 -0
- waifucore-0.1.0/waifu/cli/_remote.py +72 -0
- waifucore-0.1.0/waifu/cli/character.py +253 -0
- waifucore-0.1.0/waifu/cli/config.py +217 -0
- waifucore-0.1.0/waifu/cli/conversation.py +205 -0
- waifucore-0.1.0/waifu/cli/cron.py +196 -0
- waifucore-0.1.0/waifu/cli/init.py +448 -0
- waifucore-0.1.0/waifu/cli/mcp.py +318 -0
- waifucore-0.1.0/waifu/cli/skill.py +344 -0
- waifucore-0.1.0/waifu/cli/start.py +101 -0
- waifucore-0.1.0/waifu/cli/version.py +10 -0
- waifucore-0.1.0/waifu/config/__init__.py +122 -0
- waifucore-0.1.0/waifu/cron/__init__.py +4 -0
- waifucore-0.1.0/waifu/cron/executor.py +66 -0
- waifucore-0.1.0/waifu/cron/scheduler.py +150 -0
- waifucore-0.1.0/waifu/gateway/__init__.py +21 -0
- waifucore-0.1.0/waifu/gateway/adapters/__init__.py +23 -0
- waifucore-0.1.0/waifu/gateway/adapters/discord.py +320 -0
- waifucore-0.1.0/waifu/gateway/adapters/http.py +94 -0
- waifucore-0.1.0/waifu/gateway/adapters/telegram.py +264 -0
- waifucore-0.1.0/waifu/gateway/adapters/waifu_node.py +448 -0
- waifucore-0.1.0/waifu/gateway/adapters/websocket.py +223 -0
- waifucore-0.1.0/waifu/gateway/config.py +111 -0
- waifucore-0.1.0/waifu/gateway/core.py +84 -0
- waifucore-0.1.0/waifu/gateway/lane.py +48 -0
- waifucore-0.1.0/waifu/gateway/session.py +55 -0
- waifucore-0.1.0/waifu/heartbeat/__init__.py +10 -0
- waifucore-0.1.0/waifu/heartbeat/handler.py +75 -0
- waifucore-0.1.0/waifu/heartbeat/manager.py +78 -0
- waifucore-0.1.0/waifu/hook/__init__.py +79 -0
- waifucore-0.1.0/waifu/hook/builtin.py +260 -0
- waifucore-0.1.0/waifu/hook/memory.py +126 -0
- waifucore-0.1.0/waifu/logger.py +130 -0
- waifucore-0.1.0/waifu/mcp/__init__.py +5 -0
- waifucore-0.1.0/waifu/mcp/manager.py +48 -0
- waifucore-0.1.0/waifu/mcp/sse.py +88 -0
- waifucore-0.1.0/waifu/mcp/stdio.py +108 -0
- waifucore-0.1.0/waifu/node/__init__.py +3 -0
- waifucore-0.1.0/waifu/node/manager.py +97 -0
- waifucore-0.1.0/waifu/plugin/__init__.py +207 -0
- waifucore-0.1.0/waifu/plugin/builtin.py +29 -0
- waifucore-0.1.0/waifu/plugin/loader.py +64 -0
- waifucore-0.1.0/waifu/router/__init__.py +143 -0
- waifucore-0.1.0/waifu/router/analytics.py +48 -0
- waifucore-0.1.0/waifu/router/attachments.py +39 -0
- waifucore-0.1.0/waifu/router/character.py +180 -0
- waifucore-0.1.0/waifu/router/chat.py +55 -0
- waifucore-0.1.0/waifu/router/config.py +35 -0
- waifucore-0.1.0/waifu/router/conversation.py +85 -0
- waifucore-0.1.0/waifu/router/gateway.py +26 -0
- waifucore-0.1.0/waifu/router/health.py +8 -0
- waifucore-0.1.0/waifu/router/mcp.py +57 -0
- waifucore-0.1.0/waifu/router/plugin.py +68 -0
- waifucore-0.1.0/waifu/router/session.py +37 -0
- waifucore-0.1.0/waifu/router/skill.py +43 -0
- waifucore-0.1.0/waifu/router/status.py +13 -0
- waifucore-0.1.0/waifu/router/tools.py +23 -0
- waifucore-0.1.0/waifu/router/waifu_node.py +23 -0
- waifucore-0.1.0/waifu/router/ws.py +23 -0
- waifucore-0.1.0/waifu/service/__init__.py +18 -0
- waifucore-0.1.0/waifu/service/character.py +264 -0
- waifucore-0.1.0/waifu/service/command.py +243 -0
- waifucore-0.1.0/waifu/service/config.py +43 -0
- waifucore-0.1.0/waifu/service/gateway.py +76 -0
- waifucore-0.1.0/waifu/service/mcp.py +154 -0
- waifucore-0.1.0/waifu/service/plugin.py +36 -0
- waifucore-0.1.0/waifu/service/skill.py +46 -0
- waifucore-0.1.0/waifu/service/status.py +74 -0
- waifucore-0.1.0/waifu/skill/__init__.py +14 -0
- waifucore-0.1.0/waifu/skill/hook.py +22 -0
- waifucore-0.1.0/waifu/skill/installer.py +137 -0
- waifucore-0.1.0/waifu/skill/loader.py +53 -0
- waifucore-0.1.0/waifu/skill/manifest.py +96 -0
- waifucore-0.1.0/waifu/skill/registry.py +29 -0
- waifucore-0.1.0/waifu/skill/tool.py +34 -0
- waifucore-0.1.0/waifu/store/__init__.py +22 -0
- waifucore-0.1.0/waifu/store/character.py +42 -0
- waifucore-0.1.0/waifu/store/conversation.py +477 -0
- waifucore-0.1.0/waifu/store/cron.py +78 -0
- waifucore-0.1.0/waifu/store/history_tree.py +277 -0
- waifucore-0.1.0/waifu/store/memory.py +357 -0
- waifucore-0.1.0/waifu/store/models.py +215 -0
- waifucore-0.1.0/waifu/store/paths.py +115 -0
- waifucore-0.1.0/waifu/store/repositories/__init__.py +14 -0
- waifucore-0.1.0/waifu/store/repositories/analytics.py +72 -0
- waifucore-0.1.0/waifu/store/repositories/character.py +64 -0
- waifucore-0.1.0/waifu/store/repositories/config.py +44 -0
- waifucore-0.1.0/waifu/store/repositories/mcp.py +30 -0
- waifucore-0.1.0/waifu/store/repositories/plugin.py +31 -0
- waifucore-0.1.0/waifu/tool/__init__.py +19 -0
- waifucore-0.1.0/waifu/tool/cron.py +125 -0
- waifucore-0.1.0/waifu/tool/file_ops.py +217 -0
- waifucore-0.1.0/waifu/tool/read.py +28 -0
- waifucore-0.1.0/waifu/tool/shell.py +50 -0
- waifucore-0.1.0/waifu/tool/webfetch.py +60 -0
- waifucore-0.1.0/waifu/tool/websearch.py +200 -0
- waifucore-0.1.0/waifu/tool/write.py +30 -0
- waifucore-0.1.0/waifu/voice/__init__.py +4 -0
- waifucore-0.1.0/waifu/voice/lipsync.py +38 -0
- waifucore-0.1.0/waifu/voice/service.py +156 -0
- waifucore-0.1.0/waifu/waifu.py +735 -0
- waifucore-0.1.0/waifu_sdk/README.md +1 -0
- waifucore-0.1.0/waifu_sdk/pyproject.toml +27 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/__init__.py +33 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/adapter.py +38 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/hook.py +30 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/mcp.py +24 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/plugin.py +47 -0
- waifucore-0.1.0/waifu_sdk/waifu_sdk/types.py +51 -0
- waifucore-0.1.0/waifu_types/README.md +1 -0
- waifucore-0.1.0/waifu_types/pyproject.toml +15 -0
- waifucore-0.1.0/waifu_types/waifu_types/__init__.py +59 -0
- waifucore-0.1.0/waifu_types/waifu_types/adapter.py +38 -0
- waifucore-0.1.0/waifu_types/waifu_types/hook.py +42 -0
- waifucore-0.1.0/waifu_types/waifu_types/mcp.py +27 -0
- waifucore-0.1.0/waifu_types/waifu_types/msg.py +150 -0
- waifucore-0.1.0/waifu_types/waifu_types/plugin.py +69 -0
- waifucore-0.1.0/waifu_types/waifu_types/prompt.py +23 -0
- waifucore-0.1.0/waifu_types/waifu_types/services.py +98 -0
- waifucore-0.1.0/waifu_types/waifu_types/session.py +14 -0
- waifucore-0.1.0/waifu_types/waifu_types/trace.py +26 -0
- waifucore-0.1.0/waifu_types/waifu_types/types.py +104 -0
|
@@ -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,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)
|
waifucore-0.1.0/LICENSE
ADDED
|
@@ -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.
|
waifucore-0.1.0/PKG-INFO
ADDED
|
@@ -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
|