easyharness 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.
- easyharness-0.1.0/LICENSE +21 -0
- easyharness-0.1.0/PKG-INFO +252 -0
- easyharness-0.1.0/README.md +237 -0
- easyharness-0.1.0/easyharness/__init__.py +18 -0
- easyharness-0.1.0/easyharness/_internal/__init__.py +6 -0
- easyharness-0.1.0/easyharness/_internal/conversation.py +173 -0
- easyharness-0.1.0/easyharness/_internal/model.py +198 -0
- easyharness-0.1.0/easyharness/_internal/runtime.py +500 -0
- easyharness-0.1.0/easyharness/_internal/tools.py +447 -0
- easyharness-0.1.0/easyharness/_internal/types.py +75 -0
- easyharness-0.1.0/easyharness/toolset/__init__.py +10 -0
- easyharness-0.1.0/easyharness/toolset/fileglide.py +818 -0
- easyharness-0.1.0/easyharness.egg-info/PKG-INFO +252 -0
- easyharness-0.1.0/easyharness.egg-info/SOURCES.txt +19 -0
- easyharness-0.1.0/easyharness.egg-info/dependency_links.txt +1 -0
- easyharness-0.1.0/easyharness.egg-info/requires.txt +6 -0
- easyharness-0.1.0/easyharness.egg-info/top_level.txt +1 -0
- easyharness-0.1.0/pyproject.toml +29 -0
- easyharness-0.1.0/setup.cfg +4 -0
- easyharness-0.1.0/tests/test_base.py +13 -0
- easyharness-0.1.0/tests/test_sdk.py +739 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 吴子豪
|
|
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,252 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easyharness
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A minimal Python SDK for agent loops, with streaming events, clean tool contracts, and zero ceremony.
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: litellm<=1.89.3,>=1.75.9
|
|
9
|
+
Requires-Dist: prompt4py>=0.0.6
|
|
10
|
+
Requires-Dist: vortezwohl>=0.1.3
|
|
11
|
+
Requires-Dist: strands-agents-tools>=0.8.2
|
|
12
|
+
Requires-Dist: strands-agents[litellm]>=1.45.0
|
|
13
|
+
Requires-Dist: fileglide>=0.0.1
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
<div align="center">
|
|
17
|
+
<h1>EasyHarness</h1>
|
|
18
|
+
<p>
|
|
19
|
+
<strong>
|
|
20
|
+
A minimal Python SDK for agent loops, with streaming events, clean tool contracts, and zero ceremony.
|
|
21
|
+
</strong>
|
|
22
|
+
</p>
|
|
23
|
+
<p>
|
|
24
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python 3.10+" />
|
|
25
|
+
<a href="https://github.com/vortezwohl/fileglide">
|
|
26
|
+
<img src="https://img.shields.io/badge/fileglide-2563EB?logo=github&logoColor=white" alt="FileGlide" />
|
|
27
|
+
</a>
|
|
28
|
+
<a href="https://github.com/strands-agents/harness-sdk">
|
|
29
|
+
<img src="https://img.shields.io/badge/strands%20agents-0F766E?logo=github&logoColor=white" alt="Strands Agents" />
|
|
30
|
+
</a>
|
|
31
|
+
<a href="https://github.com/BerriAI/litellm">
|
|
32
|
+
<img src="https://img.shields.io/badge/litellm-7C3AED?logo=github&logoColor=white" alt="LiteLLM" />
|
|
33
|
+
</a>
|
|
34
|
+
</p>
|
|
35
|
+
<p>
|
|
36
|
+
<sub>Small public surface. Strong defaults. Explicit control.</sub>
|
|
37
|
+
</p>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
## Why EasyHarness
|
|
41
|
+
|
|
42
|
+
EasyHarness is designed for local agent workflows where a tiny SDK surface
|
|
43
|
+
matters more than a large framework.
|
|
44
|
+
|
|
45
|
+
It works well for coding agents, but it is not limited to them. Any agent that
|
|
46
|
+
benefits from strict tool contracts, streaming events, and explicit runtime
|
|
47
|
+
control can use the same SDK surface.
|
|
48
|
+
|
|
49
|
+
It gives you:
|
|
50
|
+
|
|
51
|
+
- A single primary runtime entry point through `Agent`
|
|
52
|
+
- Strict tool metadata and output contracts through `tool` and `ToolOutput`
|
|
53
|
+
- A unified streaming event model for thinking, tool, assistant, compression,
|
|
54
|
+
and system events
|
|
55
|
+
- An official FileGlide-backed filesystem toolset that can be auto-loaded by
|
|
56
|
+
default or scoped explicitly
|
|
57
|
+
|
|
58
|
+
## Public Surface
|
|
59
|
+
|
|
60
|
+
The root package intentionally exposes only five public names:
|
|
61
|
+
|
|
62
|
+
- `Agent`
|
|
63
|
+
- `ModelConfig`
|
|
64
|
+
- `AgentEvent`
|
|
65
|
+
- `ToolOutput`
|
|
66
|
+
- `tool`
|
|
67
|
+
|
|
68
|
+
Everything else stays behind internal modules or the explicit
|
|
69
|
+
`easyharness.toolset` package.
|
|
70
|
+
|
|
71
|
+
## Typical Usage
|
|
72
|
+
|
|
73
|
+
### 1. Define a tool and run an agent
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from easyharness import Agent, ModelConfig, ToolOutput, tool
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@tool(
|
|
80
|
+
name="ping_tool",
|
|
81
|
+
purpose="Return a fixed response for a minimal tool-flow check.",
|
|
82
|
+
when_to_use=(
|
|
83
|
+
"Use this when the model needs a trivial tool call to verify that the "
|
|
84
|
+
"tool pipeline is available."
|
|
85
|
+
),
|
|
86
|
+
parameters={},
|
|
87
|
+
returns="A fixed pong response.",
|
|
88
|
+
common_failures=["This tool does not fail under normal conditions."],
|
|
89
|
+
)
|
|
90
|
+
def ping_tool() -> ToolOutput:
|
|
91
|
+
return ToolOutput(
|
|
92
|
+
data={"value": "pong"},
|
|
93
|
+
model_text="pong",
|
|
94
|
+
preview="pong",
|
|
95
|
+
detail='{"value": "pong"}',
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
agent = Agent(
|
|
100
|
+
model=ModelConfig(
|
|
101
|
+
model="openai/gpt-4.1-mini",
|
|
102
|
+
api_key="YOUR_API_KEY",
|
|
103
|
+
),
|
|
104
|
+
system_prompt="You are a precise agent.",
|
|
105
|
+
tools=[ping_tool],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
print(agent.run("Call the ping tool and confirm that the tool pipeline works."))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 2. Use the default FileGlide integration
|
|
112
|
+
|
|
113
|
+
`Agent` can auto-load the official FileGlide-backed toolset, so the shortest
|
|
114
|
+
agent setup does not need a manual `tools=[...]` list.
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from easyharness import Agent, ModelConfig
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
agent = Agent(
|
|
121
|
+
model=ModelConfig(
|
|
122
|
+
model="openai/gpt-4.1-mini",
|
|
123
|
+
api_key="YOUR_API_KEY",
|
|
124
|
+
),
|
|
125
|
+
system_prompt="You are a careful agent.",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
print(
|
|
129
|
+
agent.run(
|
|
130
|
+
"List the workspace, read pyproject.toml, and summarize the SDK shape."
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
<details>
|
|
136
|
+
<summary><strong>Official default FileGlide tools</strong></summary>
|
|
137
|
+
|
|
138
|
+
<ul>
|
|
139
|
+
<li><code>fileglide_list_tree</code></li>
|
|
140
|
+
<li><code>fileglide_search_paths</code></li>
|
|
141
|
+
<li><code>fileglide_read_text</code></li>
|
|
142
|
+
<li><code>fileglide_search_text</code></li>
|
|
143
|
+
<li><code>fileglide_edit_text</code></li>
|
|
144
|
+
<li><code>fileglide_manage_paths</code></li>
|
|
145
|
+
<li><code>fileglide_inspect_path</code></li>
|
|
146
|
+
</ul>
|
|
147
|
+
</details>
|
|
148
|
+
|
|
149
|
+
### 3. Disable the default FileGlide tools
|
|
150
|
+
|
|
151
|
+
If you want a stricter default runtime with no filesystem tool auto-loading,
|
|
152
|
+
disable it explicitly.
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from easyharness import Agent, ModelConfig
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
agent = Agent(
|
|
159
|
+
model=ModelConfig(
|
|
160
|
+
model="openai/gpt-4.1-mini",
|
|
161
|
+
api_key="YOUR_API_KEY",
|
|
162
|
+
),
|
|
163
|
+
system_prompt="You are a careful agent.",
|
|
164
|
+
enable_fileglide=False,
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 4. Build a scoped official FileGlide toolset
|
|
169
|
+
|
|
170
|
+
When you need explicit control over the filesystem scope, disable the default
|
|
171
|
+
auto-load and pass a scoped official toolset from `easyharness.toolset`.
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from easyharness import Agent, ModelConfig
|
|
175
|
+
from easyharness.toolset import build_fileglide_tools
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
agent = Agent(
|
|
179
|
+
model=ModelConfig(
|
|
180
|
+
model="openai/gpt-4.1-mini",
|
|
181
|
+
api_key="YOUR_API_KEY",
|
|
182
|
+
),
|
|
183
|
+
system_prompt="You are a careful agent.",
|
|
184
|
+
enable_fileglide=False,
|
|
185
|
+
tools=build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness"),
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`build_fileglide_tools(default_root=...)` creates the official scoped toolset. Paths
|
|
190
|
+
that escape the configured root are rejected by FileGlide scope protection.
|
|
191
|
+
|
|
192
|
+
Each official `fileglide_*` tool also accepts an optional `root` argument for
|
|
193
|
+
that single call. When provided, it overrides the builder's default root
|
|
194
|
+
without additional SDK-level path-range restrictions.
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
tools = build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness")
|
|
198
|
+
tool_map = {tool.tool_name: tool for tool in tools}
|
|
199
|
+
|
|
200
|
+
result = tool_map["fileglide_read_text"](
|
|
201
|
+
target="EasyHarness/pyproject.toml",
|
|
202
|
+
root="D:/Projects/PythonProjects",
|
|
203
|
+
)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Event Stream
|
|
207
|
+
|
|
208
|
+
`agent.stream(prompt)` yields a unified `AgentEvent` stream. The public event
|
|
209
|
+
kinds are:
|
|
210
|
+
|
|
211
|
+
- `thinking`
|
|
212
|
+
- `tool`
|
|
213
|
+
- `assistant`
|
|
214
|
+
- `compress`
|
|
215
|
+
- `system`
|
|
216
|
+
|
|
217
|
+
Each event uses the same status vocabulary:
|
|
218
|
+
|
|
219
|
+
- `started`
|
|
220
|
+
- `delta`
|
|
221
|
+
- `completed`
|
|
222
|
+
- `failed`
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
for event in agent.stream("Inspect the workspace and explain the next step."):
|
|
226
|
+
print(event.kind, event.status, event.text)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Design Boundaries
|
|
230
|
+
|
|
231
|
+
EasyHarness is intentionally small. v1 does not try to solve:
|
|
232
|
+
|
|
233
|
+
- UI components
|
|
234
|
+
- Environment-variable orchestration layers
|
|
235
|
+
- Plugin platforms
|
|
236
|
+
- Multi-agent orchestration
|
|
237
|
+
- Root-package re-export sprawl for toolset builders
|
|
238
|
+
|
|
239
|
+
## Summary
|
|
240
|
+
|
|
241
|
+
EasyHarness is most useful when you want:
|
|
242
|
+
|
|
243
|
+
- A minimal `Agent` API
|
|
244
|
+
- Strict tool definitions
|
|
245
|
+
- Streaming runtime visibility
|
|
246
|
+
- A practical, official FileGlide-backed filesystem toolset
|
|
247
|
+
|
|
248
|
+
If that is your shape of problem, the shortest path is:
|
|
249
|
+
|
|
250
|
+
1. Create an `Agent`
|
|
251
|
+
2. Define tools with `@tool(...)`
|
|
252
|
+
3. Use the default FileGlide integration or pass a scoped official toolset
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>EasyHarness</h1>
|
|
3
|
+
<p>
|
|
4
|
+
<strong>
|
|
5
|
+
A minimal Python SDK for agent loops, with streaming events, clean tool contracts, and zero ceremony.
|
|
6
|
+
</strong>
|
|
7
|
+
</p>
|
|
8
|
+
<p>
|
|
9
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-3776AB?logo=python&logoColor=white" alt="Python 3.10+" />
|
|
10
|
+
<a href="https://github.com/vortezwohl/fileglide">
|
|
11
|
+
<img src="https://img.shields.io/badge/fileglide-2563EB?logo=github&logoColor=white" alt="FileGlide" />
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://github.com/strands-agents/harness-sdk">
|
|
14
|
+
<img src="https://img.shields.io/badge/strands%20agents-0F766E?logo=github&logoColor=white" alt="Strands Agents" />
|
|
15
|
+
</a>
|
|
16
|
+
<a href="https://github.com/BerriAI/litellm">
|
|
17
|
+
<img src="https://img.shields.io/badge/litellm-7C3AED?logo=github&logoColor=white" alt="LiteLLM" />
|
|
18
|
+
</a>
|
|
19
|
+
</p>
|
|
20
|
+
<p>
|
|
21
|
+
<sub>Small public surface. Strong defaults. Explicit control.</sub>
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
## Why EasyHarness
|
|
26
|
+
|
|
27
|
+
EasyHarness is designed for local agent workflows where a tiny SDK surface
|
|
28
|
+
matters more than a large framework.
|
|
29
|
+
|
|
30
|
+
It works well for coding agents, but it is not limited to them. Any agent that
|
|
31
|
+
benefits from strict tool contracts, streaming events, and explicit runtime
|
|
32
|
+
control can use the same SDK surface.
|
|
33
|
+
|
|
34
|
+
It gives you:
|
|
35
|
+
|
|
36
|
+
- A single primary runtime entry point through `Agent`
|
|
37
|
+
- Strict tool metadata and output contracts through `tool` and `ToolOutput`
|
|
38
|
+
- A unified streaming event model for thinking, tool, assistant, compression,
|
|
39
|
+
and system events
|
|
40
|
+
- An official FileGlide-backed filesystem toolset that can be auto-loaded by
|
|
41
|
+
default or scoped explicitly
|
|
42
|
+
|
|
43
|
+
## Public Surface
|
|
44
|
+
|
|
45
|
+
The root package intentionally exposes only five public names:
|
|
46
|
+
|
|
47
|
+
- `Agent`
|
|
48
|
+
- `ModelConfig`
|
|
49
|
+
- `AgentEvent`
|
|
50
|
+
- `ToolOutput`
|
|
51
|
+
- `tool`
|
|
52
|
+
|
|
53
|
+
Everything else stays behind internal modules or the explicit
|
|
54
|
+
`easyharness.toolset` package.
|
|
55
|
+
|
|
56
|
+
## Typical Usage
|
|
57
|
+
|
|
58
|
+
### 1. Define a tool and run an agent
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from easyharness import Agent, ModelConfig, ToolOutput, tool
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@tool(
|
|
65
|
+
name="ping_tool",
|
|
66
|
+
purpose="Return a fixed response for a minimal tool-flow check.",
|
|
67
|
+
when_to_use=(
|
|
68
|
+
"Use this when the model needs a trivial tool call to verify that the "
|
|
69
|
+
"tool pipeline is available."
|
|
70
|
+
),
|
|
71
|
+
parameters={},
|
|
72
|
+
returns="A fixed pong response.",
|
|
73
|
+
common_failures=["This tool does not fail under normal conditions."],
|
|
74
|
+
)
|
|
75
|
+
def ping_tool() -> ToolOutput:
|
|
76
|
+
return ToolOutput(
|
|
77
|
+
data={"value": "pong"},
|
|
78
|
+
model_text="pong",
|
|
79
|
+
preview="pong",
|
|
80
|
+
detail='{"value": "pong"}',
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
agent = Agent(
|
|
85
|
+
model=ModelConfig(
|
|
86
|
+
model="openai/gpt-4.1-mini",
|
|
87
|
+
api_key="YOUR_API_KEY",
|
|
88
|
+
),
|
|
89
|
+
system_prompt="You are a precise agent.",
|
|
90
|
+
tools=[ping_tool],
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
print(agent.run("Call the ping tool and confirm that the tool pipeline works."))
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Use the default FileGlide integration
|
|
97
|
+
|
|
98
|
+
`Agent` can auto-load the official FileGlide-backed toolset, so the shortest
|
|
99
|
+
agent setup does not need a manual `tools=[...]` list.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from easyharness import Agent, ModelConfig
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
agent = Agent(
|
|
106
|
+
model=ModelConfig(
|
|
107
|
+
model="openai/gpt-4.1-mini",
|
|
108
|
+
api_key="YOUR_API_KEY",
|
|
109
|
+
),
|
|
110
|
+
system_prompt="You are a careful agent.",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
print(
|
|
114
|
+
agent.run(
|
|
115
|
+
"List the workspace, read pyproject.toml, and summarize the SDK shape."
|
|
116
|
+
)
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
<details>
|
|
121
|
+
<summary><strong>Official default FileGlide tools</strong></summary>
|
|
122
|
+
|
|
123
|
+
<ul>
|
|
124
|
+
<li><code>fileglide_list_tree</code></li>
|
|
125
|
+
<li><code>fileglide_search_paths</code></li>
|
|
126
|
+
<li><code>fileglide_read_text</code></li>
|
|
127
|
+
<li><code>fileglide_search_text</code></li>
|
|
128
|
+
<li><code>fileglide_edit_text</code></li>
|
|
129
|
+
<li><code>fileglide_manage_paths</code></li>
|
|
130
|
+
<li><code>fileglide_inspect_path</code></li>
|
|
131
|
+
</ul>
|
|
132
|
+
</details>
|
|
133
|
+
|
|
134
|
+
### 3. Disable the default FileGlide tools
|
|
135
|
+
|
|
136
|
+
If you want a stricter default runtime with no filesystem tool auto-loading,
|
|
137
|
+
disable it explicitly.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from easyharness import Agent, ModelConfig
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
agent = Agent(
|
|
144
|
+
model=ModelConfig(
|
|
145
|
+
model="openai/gpt-4.1-mini",
|
|
146
|
+
api_key="YOUR_API_KEY",
|
|
147
|
+
),
|
|
148
|
+
system_prompt="You are a careful agent.",
|
|
149
|
+
enable_fileglide=False,
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4. Build a scoped official FileGlide toolset
|
|
154
|
+
|
|
155
|
+
When you need explicit control over the filesystem scope, disable the default
|
|
156
|
+
auto-load and pass a scoped official toolset from `easyharness.toolset`.
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from easyharness import Agent, ModelConfig
|
|
160
|
+
from easyharness.toolset import build_fileglide_tools
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
agent = Agent(
|
|
164
|
+
model=ModelConfig(
|
|
165
|
+
model="openai/gpt-4.1-mini",
|
|
166
|
+
api_key="YOUR_API_KEY",
|
|
167
|
+
),
|
|
168
|
+
system_prompt="You are a careful agent.",
|
|
169
|
+
enable_fileglide=False,
|
|
170
|
+
tools=build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness"),
|
|
171
|
+
)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`build_fileglide_tools(default_root=...)` creates the official scoped toolset. Paths
|
|
175
|
+
that escape the configured root are rejected by FileGlide scope protection.
|
|
176
|
+
|
|
177
|
+
Each official `fileglide_*` tool also accepts an optional `root` argument for
|
|
178
|
+
that single call. When provided, it overrides the builder's default root
|
|
179
|
+
without additional SDK-level path-range restrictions.
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
tools = build_fileglide_tools(default_root="D:/Projects/PythonProjects/EasyHarness")
|
|
183
|
+
tool_map = {tool.tool_name: tool for tool in tools}
|
|
184
|
+
|
|
185
|
+
result = tool_map["fileglide_read_text"](
|
|
186
|
+
target="EasyHarness/pyproject.toml",
|
|
187
|
+
root="D:/Projects/PythonProjects",
|
|
188
|
+
)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Event Stream
|
|
192
|
+
|
|
193
|
+
`agent.stream(prompt)` yields a unified `AgentEvent` stream. The public event
|
|
194
|
+
kinds are:
|
|
195
|
+
|
|
196
|
+
- `thinking`
|
|
197
|
+
- `tool`
|
|
198
|
+
- `assistant`
|
|
199
|
+
- `compress`
|
|
200
|
+
- `system`
|
|
201
|
+
|
|
202
|
+
Each event uses the same status vocabulary:
|
|
203
|
+
|
|
204
|
+
- `started`
|
|
205
|
+
- `delta`
|
|
206
|
+
- `completed`
|
|
207
|
+
- `failed`
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
for event in agent.stream("Inspect the workspace and explain the next step."):
|
|
211
|
+
print(event.kind, event.status, event.text)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Design Boundaries
|
|
215
|
+
|
|
216
|
+
EasyHarness is intentionally small. v1 does not try to solve:
|
|
217
|
+
|
|
218
|
+
- UI components
|
|
219
|
+
- Environment-variable orchestration layers
|
|
220
|
+
- Plugin platforms
|
|
221
|
+
- Multi-agent orchestration
|
|
222
|
+
- Root-package re-export sprawl for toolset builders
|
|
223
|
+
|
|
224
|
+
## Summary
|
|
225
|
+
|
|
226
|
+
EasyHarness is most useful when you want:
|
|
227
|
+
|
|
228
|
+
- A minimal `Agent` API
|
|
229
|
+
- Strict tool definitions
|
|
230
|
+
- Streaming runtime visibility
|
|
231
|
+
- A practical, official FileGlide-backed filesystem toolset
|
|
232
|
+
|
|
233
|
+
If that is your shape of problem, the shortest path is:
|
|
234
|
+
|
|
235
|
+
1. Create an `Agent`
|
|
236
|
+
2. Define tools with `@tool(...)`
|
|
237
|
+
3. Use the default FileGlide integration or pass a scoped official toolset
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Minimal public SDK surface for EasyHarness.
|
|
2
|
+
|
|
3
|
+
The package exposes only five public names: `Agent`, `ModelConfig`,
|
|
4
|
+
`AgentEvent`, `ToolOutput`, and `tool`. Runtime bridging, tool contract
|
|
5
|
+
validation, event mapping, and conversation compression stay inside private
|
|
6
|
+
modules so ordinary callers never have to touch internal objects.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ._internal.runtime import Agent
|
|
10
|
+
from ._internal.tools import tool
|
|
11
|
+
from ._internal.types import AgentEvent, ModelConfig, ToolOutput
|
|
12
|
+
|
|
13
|
+
__all__ = ["Agent", "ModelConfig", "AgentEvent", "ToolOutput", "tool"]
|
|
14
|
+
|
|
15
|
+
__AUTHOR__ = '吴子豪 / Vortez Wohl'
|
|
16
|
+
__EMAIL__ = 'vortez.wohl@gmail.com'
|
|
17
|
+
__GITHUB__ = 'https://github.com/vortezwohl'
|
|
18
|
+
__BLOG__ = 'https://vortezwohl.github.io'
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Private implementation package for EasyHarness.
|
|
2
|
+
|
|
3
|
+
It contains the runtime bridge, tool contracts, event mapping, and
|
|
4
|
+
conversation compression internals. Callers should depend on the stable public
|
|
5
|
+
entry points exposed from top-level `easyharness` instead of these modules.
|
|
6
|
+
"""
|