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.
@@ -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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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&amp;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
+ """