pyharness-ai 0.6.2__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.
- pyharness_ai-0.6.2/LICENSE +21 -0
- pyharness_ai-0.6.2/PKG-INFO +230 -0
- pyharness_ai-0.6.2/README.md +167 -0
- pyharness_ai-0.6.2/mcp_servers.json +27 -0
- pyharness_ai-0.6.2/pyproject.toml +96 -0
- pyharness_ai-0.6.2/src/pyharness/__init__.py +65 -0
- pyharness_ai-0.6.2/src/pyharness/cli.py +12 -0
- pyharness_ai-0.6.2/src/pyharness/context.py +177 -0
- pyharness_ai-0.6.2/src/pyharness/core.py +726 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/__init__.py +5 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/builtin.py +65 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/cli/__init__.py +10 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/cli/app.py +179 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/cli/ui.py +50 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/context_compaction.py +209 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/embedding.py +109 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/guard_approval.py +220 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/llm/__init__.py +7 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/llm/dummy.py +65 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/llm/entry.py +120 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/llm/http.py +185 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/llm/provider.py +108 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/mcp_client.py +269 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/mcp_config.py +68 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/mcp_transport.py +152 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/session_store.py +555 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/text_splitter.py +135 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/tool_fs.py +240 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/tool_hybrid_search.py +164 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/tool_knowledge.py +274 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/tool_subagent.py +195 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/tool_web.py +253 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/ui_cli.py +76 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/ui_websocket.py +39 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/vector_store.py +145 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/web/__init__.py +18 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/web/__main__.py +15 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/web/app.py +172 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/web_ui.py +361 -0
- pyharness_ai-0.6.2/src/pyharness/plugins/workflow.py +769 -0
- pyharness_ai-0.6.2/src/pyharness/schema.py +439 -0
- pyharness_ai-0.6.2/src/pyharness/specs.py +303 -0
- pyharness_ai-0.6.2/static/app.js +515 -0
- pyharness_ai-0.6.2/static/index.html +96 -0
- pyharness_ai-0.6.2/static/style.css +597 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 PyHarness
|
|
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,230 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pyharness-ai
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: PyHarness: Everything is a Plugin. A Python Agent Framework.
|
|
5
|
+
Project-URL: Homepage, https://github.com/pyharness/pyharness
|
|
6
|
+
Project-URL: Repository, https://github.com/pyharness/pyharness
|
|
7
|
+
Project-URL: Issues, https://github.com/pyharness/pyharness/issues
|
|
8
|
+
Author-email: PyHarness <contact@pyharness.io>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agent,ai,framework,llm,mcp,plugin,rag,workflow
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: pluggy>=1.5
|
|
22
|
+
Requires-Dist: pydantic>=2.7
|
|
23
|
+
Provides-Extra: all
|
|
24
|
+
Requires-Dist: aiofiles>=23.0; extra == 'all'
|
|
25
|
+
Requires-Dist: aiosqlite>=0.20; extra == 'all'
|
|
26
|
+
Requires-Dist: beautifulsoup4>=4.12; extra == 'all'
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == 'all'
|
|
28
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
29
|
+
Requires-Dist: markdownify>=0.12; extra == 'all'
|
|
30
|
+
Requires-Dist: numpy>=1.26; extra == 'all'
|
|
31
|
+
Requires-Dist: rich>=13.7; extra == 'all'
|
|
32
|
+
Requires-Dist: typer>=0.12; extra == 'all'
|
|
33
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'all'
|
|
34
|
+
Requires-Dist: websockets>=12; extra == 'all'
|
|
35
|
+
Provides-Extra: cli
|
|
36
|
+
Requires-Dist: rich>=13.7; extra == 'cli'
|
|
37
|
+
Requires-Dist: typer>=0.12; extra == 'cli'
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: aiofiles>=23.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: fastapi[standard]>=0.110; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
43
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
44
|
+
Requires-Dist: websockets>=12; extra == 'dev'
|
|
45
|
+
Provides-Extra: llm
|
|
46
|
+
Requires-Dist: httpx>=0.27; extra == 'llm'
|
|
47
|
+
Provides-Extra: mcp
|
|
48
|
+
Requires-Dist: httpx>=0.27; extra == 'mcp'
|
|
49
|
+
Provides-Extra: memory
|
|
50
|
+
Requires-Dist: aiosqlite>=0.20; extra == 'memory'
|
|
51
|
+
Provides-Extra: rag
|
|
52
|
+
Requires-Dist: numpy>=1.26; extra == 'rag'
|
|
53
|
+
Provides-Extra: server
|
|
54
|
+
Requires-Dist: aiofiles>=23.0; extra == 'server'
|
|
55
|
+
Requires-Dist: fastapi>=0.110; extra == 'server'
|
|
56
|
+
Requires-Dist: uvicorn[standard]>=0.29; extra == 'server'
|
|
57
|
+
Requires-Dist: websockets>=12; extra == 'server'
|
|
58
|
+
Provides-Extra: web
|
|
59
|
+
Requires-Dist: beautifulsoup4>=4.12; extra == 'web'
|
|
60
|
+
Requires-Dist: httpx>=0.27; extra == 'web'
|
|
61
|
+
Requires-Dist: markdownify>=0.12; extra == 'web'
|
|
62
|
+
Description-Content-Type: text/markdown
|
|
63
|
+
|
|
64
|
+
# PyHarness — Everything is a Plugin
|
|
65
|
+
|
|
66
|
+
[](https://badge.fury.io/py/pyharness)
|
|
67
|
+
[](https://github.com/pyharness/pyharness/actions/workflows/ci.yml)
|
|
68
|
+
[](https://opensource.org/licenses/MIT)
|
|
69
|
+
[](https://www.python.org/downloads/)
|
|
70
|
+
|
|
71
|
+
> **万物皆插件。** 一个基于 pluggy + Pydantic V2 的极简 Python Agent 框架,对标 DeepSeek Harness。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## ✨ Features
|
|
76
|
+
|
|
77
|
+
| 特性 | 说明 |
|
|
78
|
+
|------|------|
|
|
79
|
+
| 🧩 **万物皆插件** | LLM 提供方、工具执行器、CLI、Web UI 全部通过 `@hookimpl` 注册,核心零依赖 |
|
|
80
|
+
| 🧠 **流式思考** | 支持 SSE 流式输出,实时展示 Agent 思考过程 |
|
|
81
|
+
| 📋 **Plan 编排** | LLM 生成结构化执行计划,拓扑排序 + TaskGroup 并行执行 |
|
|
82
|
+
| 🤖 **Subagent 并行** | 自动派生子 Agent,TaskGroup 并行执行,支持超时与重试 |
|
|
83
|
+
| 🔒 **Human-in-the-loop** | 异步审批机制,高危工具执行前人工确认 |
|
|
84
|
+
| 💾 **SQLite 持久化** | 会话状态 + FTS5 全文检索,重启后恢复执行 |
|
|
85
|
+
| 🌐 **Web UI** | FastAPI + WebSocket 实时推送,三栏式对话界面 |
|
|
86
|
+
| ⏪ **可逆状态** | Frozen Pydantic + `model_copy`,支持分支、回滚、多分支搜索 |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🚀 Quick Start
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# 安装(含全部可选依赖)
|
|
94
|
+
pip install pyharness[all]
|
|
95
|
+
|
|
96
|
+
# 配置 API Key
|
|
97
|
+
export DEEPSEEK_API_KEY="sk-..."
|
|
98
|
+
|
|
99
|
+
# 启动 Web UI
|
|
100
|
+
pyharness serve
|
|
101
|
+
# → 打开浏览器访问 http://127.0.0.1:3080
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### CLI 模式
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 单轮对话
|
|
108
|
+
pyharness run --provider http -m deepseek-chat -i "Hello"
|
|
109
|
+
|
|
110
|
+
# 流式 REPL
|
|
111
|
+
pyharness chat --provider http -m deepseek-chat
|
|
112
|
+
|
|
113
|
+
# 查看版本
|
|
114
|
+
pyharness version
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Python API
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from pyharness import Harness, AgentConfig
|
|
121
|
+
from pyharness.plugins.llm import entry as llm
|
|
122
|
+
|
|
123
|
+
# 初始化 Harness(自动加载内置插件)
|
|
124
|
+
h = Harness()
|
|
125
|
+
|
|
126
|
+
# 配置 LLM 提供方
|
|
127
|
+
llm.use_http(
|
|
128
|
+
models=("deepseek-chat",),
|
|
129
|
+
base_url="https://api.deepseek.com/v1",
|
|
130
|
+
api_key="sk-...",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# 运行 Agent 会话
|
|
134
|
+
ctx = await h.run_session(
|
|
135
|
+
AgentConfig(name="demo", model="deepseek-chat"),
|
|
136
|
+
initial_text="用 Python 写一个快速排序",
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# 流式输出
|
|
140
|
+
async for chunk in h.stream_session(
|
|
141
|
+
AgentConfig(name="demo", model="deepseek-chat"),
|
|
142
|
+
initial_text="用 Python 写一个快速排序",
|
|
143
|
+
):
|
|
144
|
+
print(chunk.delta, end="")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## 🏗️ Architecture
|
|
150
|
+
|
|
151
|
+
```mermaid
|
|
152
|
+
graph TD
|
|
153
|
+
subgraph Core["🧠 PyHarness Core"]
|
|
154
|
+
Harness["Harness<br/>(EventBus + Session Lifecycle)"]
|
|
155
|
+
Specs["AgentHooks<br/>(pluggy contract)"]
|
|
156
|
+
Schema["Pydantic V2 Schema<br/>(Frozen + model_copy)"]
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
subgraph Plugins["🔌 Plugins"]
|
|
160
|
+
LLM["LLM Provider<br/>(dummy / http / stream)"]
|
|
161
|
+
Tools["Tool Executors<br/>(python / fs / web)"]
|
|
162
|
+
CLI["CLI Plugin<br/>(typer + rich)"]
|
|
163
|
+
WebUI["Web UI Plugin<br/>(FastAPI + WebSocket)"]
|
|
164
|
+
Store["Session Store<br/>(SQLite + FTS5)"]
|
|
165
|
+
Guard["Guard Approval<br/>(human-in-the-loop)"]
|
|
166
|
+
Workflow["Workflow<br/>(Plan + Subagent)"]
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
Harness --> Specs
|
|
170
|
+
Harness --> Schema
|
|
171
|
+
Harness --> Plugins
|
|
172
|
+
|
|
173
|
+
style Core fill:#1a1a2e,color:#fff,stroke:#e94560,stroke-width:2px
|
|
174
|
+
style Plugins fill:#16213e,color:#fff,stroke:#0f3460,stroke-width:2px
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**设计哲学**:核心只做三件事 —— 事件总线 (`pluggy`)、上下文管理 (`contextvars`)、插件加载。所有领域能力(LLM、工具、UI)都通过插件注入。
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 🛠️ Built-in Plugins
|
|
182
|
+
|
|
183
|
+
| 插件 | 入口 | 说明 |
|
|
184
|
+
|------|------|------|
|
|
185
|
+
| `builtin` | `pyharness.plugins.builtin` | Echo 工具示例 |
|
|
186
|
+
| `llm` | `pyharness.plugins.llm.entry` | LLM 提供方:Dummy / HTTP (OpenAI 兼容) |
|
|
187
|
+
| `ui-cli` | `pyharness.plugins.ui_cli` | 终端 CLI:`pyharness chat` / `run` |
|
|
188
|
+
| `web` | `pyharness.plugins.web_ui` | Web UI:FastAPI REST + WebSocket |
|
|
189
|
+
| `memory` | `pyharness.plugins.session_store` | SQLite 持久化 + FTS5 全文检索 |
|
|
190
|
+
| `guard-approval` | `pyharness.plugins.guard_approval` | 异步人工审批 |
|
|
191
|
+
| `context-compaction` | `pyharness.plugins.context_compaction` | 上下文压缩 |
|
|
192
|
+
| `subagent` | `pyharness.plugins.tool_subagent` | 子 Agent 并行编排 |
|
|
193
|
+
| `workflow` | `pyharness.plugins.workflow` | Plan 拓扑调度 + 自动重试 |
|
|
194
|
+
| `fs` | `pyharness.plugins.tool_fs` | 文件系统工具 |
|
|
195
|
+
| `web` | `pyharness.plugins.tool_web` | 网页抓取工具 |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## 🧪 Testing
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# 安装开发依赖
|
|
203
|
+
pip install -e ".[dev]"
|
|
204
|
+
|
|
205
|
+
# 运行测试
|
|
206
|
+
pytest tests/ -v
|
|
207
|
+
|
|
208
|
+
# Lint
|
|
209
|
+
ruff check src/ tests/
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
> **75 个测试全绿**,覆盖 Core、LLM、CLI、Web UI、Workflow、Session Store。
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 🤝 Contributing
|
|
217
|
+
|
|
218
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
219
|
+
|
|
220
|
+
1. Fork the repository
|
|
221
|
+
2. Create your feature branch (`git checkout -b feat/amazing-feature`)
|
|
222
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
223
|
+
4. Push to the branch (`git push origin feat/amazing-feature`)
|
|
224
|
+
5. Open a Pull Request
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# PyHarness — Everything is a Plugin
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/py/pyharness)
|
|
4
|
+
[](https://github.com/pyharness/pyharness/actions/workflows/ci.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
|
|
8
|
+
> **万物皆插件。** 一个基于 pluggy + Pydantic V2 的极简 Python Agent 框架,对标 DeepSeek Harness。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## ✨ Features
|
|
13
|
+
|
|
14
|
+
| 特性 | 说明 |
|
|
15
|
+
|------|------|
|
|
16
|
+
| 🧩 **万物皆插件** | LLM 提供方、工具执行器、CLI、Web UI 全部通过 `@hookimpl` 注册,核心零依赖 |
|
|
17
|
+
| 🧠 **流式思考** | 支持 SSE 流式输出,实时展示 Agent 思考过程 |
|
|
18
|
+
| 📋 **Plan 编排** | LLM 生成结构化执行计划,拓扑排序 + TaskGroup 并行执行 |
|
|
19
|
+
| 🤖 **Subagent 并行** | 自动派生子 Agent,TaskGroup 并行执行,支持超时与重试 |
|
|
20
|
+
| 🔒 **Human-in-the-loop** | 异步审批机制,高危工具执行前人工确认 |
|
|
21
|
+
| 💾 **SQLite 持久化** | 会话状态 + FTS5 全文检索,重启后恢复执行 |
|
|
22
|
+
| 🌐 **Web UI** | FastAPI + WebSocket 实时推送,三栏式对话界面 |
|
|
23
|
+
| ⏪ **可逆状态** | Frozen Pydantic + `model_copy`,支持分支、回滚、多分支搜索 |
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 🚀 Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 安装(含全部可选依赖)
|
|
31
|
+
pip install pyharness[all]
|
|
32
|
+
|
|
33
|
+
# 配置 API Key
|
|
34
|
+
export DEEPSEEK_API_KEY="sk-..."
|
|
35
|
+
|
|
36
|
+
# 启动 Web UI
|
|
37
|
+
pyharness serve
|
|
38
|
+
# → 打开浏览器访问 http://127.0.0.1:3080
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### CLI 模式
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# 单轮对话
|
|
45
|
+
pyharness run --provider http -m deepseek-chat -i "Hello"
|
|
46
|
+
|
|
47
|
+
# 流式 REPL
|
|
48
|
+
pyharness chat --provider http -m deepseek-chat
|
|
49
|
+
|
|
50
|
+
# 查看版本
|
|
51
|
+
pyharness version
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Python API
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from pyharness import Harness, AgentConfig
|
|
58
|
+
from pyharness.plugins.llm import entry as llm
|
|
59
|
+
|
|
60
|
+
# 初始化 Harness(自动加载内置插件)
|
|
61
|
+
h = Harness()
|
|
62
|
+
|
|
63
|
+
# 配置 LLM 提供方
|
|
64
|
+
llm.use_http(
|
|
65
|
+
models=("deepseek-chat",),
|
|
66
|
+
base_url="https://api.deepseek.com/v1",
|
|
67
|
+
api_key="sk-...",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# 运行 Agent 会话
|
|
71
|
+
ctx = await h.run_session(
|
|
72
|
+
AgentConfig(name="demo", model="deepseek-chat"),
|
|
73
|
+
initial_text="用 Python 写一个快速排序",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# 流式输出
|
|
77
|
+
async for chunk in h.stream_session(
|
|
78
|
+
AgentConfig(name="demo", model="deepseek-chat"),
|
|
79
|
+
initial_text="用 Python 写一个快速排序",
|
|
80
|
+
):
|
|
81
|
+
print(chunk.delta, end="")
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🏗️ Architecture
|
|
87
|
+
|
|
88
|
+
```mermaid
|
|
89
|
+
graph TD
|
|
90
|
+
subgraph Core["🧠 PyHarness Core"]
|
|
91
|
+
Harness["Harness<br/>(EventBus + Session Lifecycle)"]
|
|
92
|
+
Specs["AgentHooks<br/>(pluggy contract)"]
|
|
93
|
+
Schema["Pydantic V2 Schema<br/>(Frozen + model_copy)"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
subgraph Plugins["🔌 Plugins"]
|
|
97
|
+
LLM["LLM Provider<br/>(dummy / http / stream)"]
|
|
98
|
+
Tools["Tool Executors<br/>(python / fs / web)"]
|
|
99
|
+
CLI["CLI Plugin<br/>(typer + rich)"]
|
|
100
|
+
WebUI["Web UI Plugin<br/>(FastAPI + WebSocket)"]
|
|
101
|
+
Store["Session Store<br/>(SQLite + FTS5)"]
|
|
102
|
+
Guard["Guard Approval<br/>(human-in-the-loop)"]
|
|
103
|
+
Workflow["Workflow<br/>(Plan + Subagent)"]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
Harness --> Specs
|
|
107
|
+
Harness --> Schema
|
|
108
|
+
Harness --> Plugins
|
|
109
|
+
|
|
110
|
+
style Core fill:#1a1a2e,color:#fff,stroke:#e94560,stroke-width:2px
|
|
111
|
+
style Plugins fill:#16213e,color:#fff,stroke:#0f3460,stroke-width:2px
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**设计哲学**:核心只做三件事 —— 事件总线 (`pluggy`)、上下文管理 (`contextvars`)、插件加载。所有领域能力(LLM、工具、UI)都通过插件注入。
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 🛠️ Built-in Plugins
|
|
119
|
+
|
|
120
|
+
| 插件 | 入口 | 说明 |
|
|
121
|
+
|------|------|------|
|
|
122
|
+
| `builtin` | `pyharness.plugins.builtin` | Echo 工具示例 |
|
|
123
|
+
| `llm` | `pyharness.plugins.llm.entry` | LLM 提供方:Dummy / HTTP (OpenAI 兼容) |
|
|
124
|
+
| `ui-cli` | `pyharness.plugins.ui_cli` | 终端 CLI:`pyharness chat` / `run` |
|
|
125
|
+
| `web` | `pyharness.plugins.web_ui` | Web UI:FastAPI REST + WebSocket |
|
|
126
|
+
| `memory` | `pyharness.plugins.session_store` | SQLite 持久化 + FTS5 全文检索 |
|
|
127
|
+
| `guard-approval` | `pyharness.plugins.guard_approval` | 异步人工审批 |
|
|
128
|
+
| `context-compaction` | `pyharness.plugins.context_compaction` | 上下文压缩 |
|
|
129
|
+
| `subagent` | `pyharness.plugins.tool_subagent` | 子 Agent 并行编排 |
|
|
130
|
+
| `workflow` | `pyharness.plugins.workflow` | Plan 拓扑调度 + 自动重试 |
|
|
131
|
+
| `fs` | `pyharness.plugins.tool_fs` | 文件系统工具 |
|
|
132
|
+
| `web` | `pyharness.plugins.tool_web` | 网页抓取工具 |
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 🧪 Testing
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# 安装开发依赖
|
|
140
|
+
pip install -e ".[dev]"
|
|
141
|
+
|
|
142
|
+
# 运行测试
|
|
143
|
+
pytest tests/ -v
|
|
144
|
+
|
|
145
|
+
# Lint
|
|
146
|
+
ruff check src/ tests/
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
> **75 个测试全绿**,覆盖 Core、LLM、CLI、Web UI、Workflow、Session Store。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 🤝 Contributing
|
|
154
|
+
|
|
155
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
156
|
+
|
|
157
|
+
1. Fork the repository
|
|
158
|
+
2. Create your feature branch (`git checkout -b feat/amazing-feature`)
|
|
159
|
+
3. Commit your changes (`git commit -m 'feat: add amazing feature'`)
|
|
160
|
+
4. Push to the branch (`git push origin feat/amazing-feature`)
|
|
161
|
+
5. Open a Pull Request
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
Distributed under the MIT License. See `LICENSE` for more information.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"filesystem": {
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "./workspace"],
|
|
6
|
+
"enabled": false
|
|
7
|
+
},
|
|
8
|
+
"github": {
|
|
9
|
+
"command": "npx",
|
|
10
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
11
|
+
"env": {
|
|
12
|
+
"GITHUB_TOKEN": ""
|
|
13
|
+
},
|
|
14
|
+
"enabled": false
|
|
15
|
+
},
|
|
16
|
+
"memory": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "@modelcontextprotocol/server-memory"],
|
|
19
|
+
"enabled": false
|
|
20
|
+
},
|
|
21
|
+
"puppeteer": {
|
|
22
|
+
"command": "npx",
|
|
23
|
+
"args": ["-y", "@modelcontextprotocol/server-puppeteer"],
|
|
24
|
+
"enabled": false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pyharness-ai"
|
|
7
|
+
version = "0.6.2"
|
|
8
|
+
description = "PyHarness: Everything is a Plugin. A Python Agent Framework."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "PyHarness", email = "contact@pyharness.io"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["agent", "ai", "llm", "framework", "plugin", "mcp", "rag", "workflow"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
25
|
+
]
|
|
26
|
+
dependencies = ["pluggy>=1.5", "pydantic>=2.7"]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/pyharness/pyharness"
|
|
30
|
+
Repository = "https://github.com/pyharness/pyharness"
|
|
31
|
+
Issues = "https://github.com/pyharness/pyharness/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
pyharness = "pyharness.cli:app"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
cli = ["typer>=0.12", "rich>=13.7"]
|
|
38
|
+
server = ["fastapi>=0.110", "uvicorn[standard]>=0.29", "websockets>=12", "aiofiles>=23.0"]
|
|
39
|
+
llm = ["httpx>=0.27"]
|
|
40
|
+
web = ["httpx>=0.27", "beautifulsoup4>=4.12", "markdownify>=0.12"]
|
|
41
|
+
memory = ["aiosqlite>=0.20"]
|
|
42
|
+
rag = ["numpy>=1.26"]
|
|
43
|
+
mcp = ["httpx>=0.27"]
|
|
44
|
+
dev = [
|
|
45
|
+
"pytest>=8",
|
|
46
|
+
"pytest-asyncio>=0.23",
|
|
47
|
+
"ruff>=0.5",
|
|
48
|
+
"fastapi[standard]>=0.110",
|
|
49
|
+
"websockets>=12",
|
|
50
|
+
"aiofiles>=23.0",
|
|
51
|
+
]
|
|
52
|
+
all = [
|
|
53
|
+
"typer>=0.12",
|
|
54
|
+
"rich>=13.7",
|
|
55
|
+
"fastapi>=0.110",
|
|
56
|
+
"uvicorn[standard]>=0.29",
|
|
57
|
+
"websockets>=12",
|
|
58
|
+
"aiofiles>=23.0",
|
|
59
|
+
"httpx>=0.27",
|
|
60
|
+
"beautifulsoup4>=4.12",
|
|
61
|
+
"markdownify>=0.12",
|
|
62
|
+
"aiosqlite>=0.20",
|
|
63
|
+
"numpy>=1.26",
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
# Everything is a plugin: plugins discover & register through this group.
|
|
67
|
+
[project.entry-points."pyharness.plugins"]
|
|
68
|
+
builtin = "pyharness.plugins.builtin"
|
|
69
|
+
llm = "pyharness.plugins.llm.entry"
|
|
70
|
+
context-compaction = "pyharness.plugins.context_compaction"
|
|
71
|
+
guard-approval = "pyharness.plugins.guard_approval"
|
|
72
|
+
ui-cli = "pyharness.plugins.ui_cli"
|
|
73
|
+
fs = "pyharness.plugins.tool_fs"
|
|
74
|
+
web = "pyharness.plugins.tool_web"
|
|
75
|
+
memory = "pyharness.plugins.session_store"
|
|
76
|
+
subagent = "pyharness.plugins.tool_subagent"
|
|
77
|
+
workflow = "pyharness.plugins.workflow"
|
|
78
|
+
mcp = "pyharness.plugins.mcp_client"
|
|
79
|
+
|
|
80
|
+
[tool.hatch.build.targets.wheel]
|
|
81
|
+
packages = ["src/pyharness"]
|
|
82
|
+
|
|
83
|
+
[tool.hatch.build.targets.sdist]
|
|
84
|
+
include = ["src/", "README.md", "LICENSE", "static/", "mcp_servers.json"]
|
|
85
|
+
|
|
86
|
+
[tool.pytest.ini_options]
|
|
87
|
+
asyncio_mode = "auto"
|
|
88
|
+
testpaths = ["tests"]
|
|
89
|
+
filterwarnings = ["ignore:.*starlette.testclient.*deprecated.*"]
|
|
90
|
+
|
|
91
|
+
[tool.ruff]
|
|
92
|
+
line-length = 120
|
|
93
|
+
target-version = "py311"
|
|
94
|
+
|
|
95
|
+
[tool.ruff.lint]
|
|
96
|
+
select = ["E", "F", "I", "UP"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""PyHarness — Everything is a Plugin.
|
|
2
|
+
|
|
3
|
+
A thin-core agent harness. The engine owns only the Event Bus (pluggy),
|
|
4
|
+
Context Management (contextvars) and Plugin Loading; every domain capability
|
|
5
|
+
(LLM providers, tool executors, CLIs, Web UIs) arrives as a plugin via
|
|
6
|
+
``@hookimpl``. State is schema-driven (pydantic v2) and reversible
|
|
7
|
+
(``model_copy(update=...)`` snapshots).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pyharness.context import (
|
|
11
|
+
SessionContext,
|
|
12
|
+
current_context,
|
|
13
|
+
open_session,
|
|
14
|
+
require_context,
|
|
15
|
+
)
|
|
16
|
+
from pyharness.core import EventBus, Harness
|
|
17
|
+
from pyharness.schema import (
|
|
18
|
+
AgentConfig,
|
|
19
|
+
Event,
|
|
20
|
+
EventType,
|
|
21
|
+
HarnessConfig,
|
|
22
|
+
LLMRequest,
|
|
23
|
+
LLMResponse,
|
|
24
|
+
LLMStreamChunk,
|
|
25
|
+
Message,
|
|
26
|
+
Role,
|
|
27
|
+
StepStatus,
|
|
28
|
+
ToolArg,
|
|
29
|
+
ToolCall,
|
|
30
|
+
ToolResult,
|
|
31
|
+
ToolResultStatus,
|
|
32
|
+
ToolSpec,
|
|
33
|
+
WorkflowPlan,
|
|
34
|
+
WorkflowStep,
|
|
35
|
+
)
|
|
36
|
+
from pyharness.specs import AgentHooks
|
|
37
|
+
|
|
38
|
+
__version__ = "0.6.2"
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"AgentConfig",
|
|
42
|
+
"AgentHooks",
|
|
43
|
+
"Event",
|
|
44
|
+
"EventBus",
|
|
45
|
+
"EventType",
|
|
46
|
+
"Harness",
|
|
47
|
+
"HarnessConfig",
|
|
48
|
+
"LLMRequest",
|
|
49
|
+
"LLMResponse",
|
|
50
|
+
"LLMStreamChunk",
|
|
51
|
+
"Message",
|
|
52
|
+
"Role",
|
|
53
|
+
"SessionContext",
|
|
54
|
+
"StepStatus",
|
|
55
|
+
"ToolArg",
|
|
56
|
+
"ToolCall",
|
|
57
|
+
"ToolResult",
|
|
58
|
+
"ToolResultStatus",
|
|
59
|
+
"ToolSpec",
|
|
60
|
+
"WorkflowPlan",
|
|
61
|
+
"WorkflowStep",
|
|
62
|
+
"current_context",
|
|
63
|
+
"open_session",
|
|
64
|
+
"require_context",
|
|
65
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""PyHarness top-level CLI entry point.
|
|
2
|
+
|
|
3
|
+
Exposes the ``pyharness`` command as defined in ``pyproject.toml``
|
|
4
|
+
``[project.scripts]``.
|
|
5
|
+
|
|
6
|
+
This thin wrapper delegates to the real Typer app implemented in
|
|
7
|
+
:mod:`pyharness.plugins.cli.app`.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from pyharness.plugins.cli.app import app
|
|
11
|
+
|
|
12
|
+
__all__ = ["app"]
|